Skip to content

Commit 630a7db

Browse files
committed
Update nurbs.scad
1 parent 204eefd commit 630a7db

1 file changed

Lines changed: 151 additions & 83 deletions

File tree

nurbs.scad

Lines changed: 151 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ _BOSL2_NURBS = is_undef(_BOSL2_STD) && (is_undef(BOSL2_NO_STD_WARNING) || !BOSL2
4545
// number of control points. A single NURBS is more like a bezier **path** than like a single bezier spline.
4646
// .
4747
// A NURBS or B-spline is a curve made from a moving average of several Bezier curves. The knots specify when one Bezier fades
48-
// away to be replaced by the next one. The knot list is a non-increasing list of values that you specify using two parameters,
48+
// away to be replaced by the next one. The knot list is a non-decreasing list of values that you specify using two parameters,
4949
// `knots` and `mult`. In practice changing the knot values doesn't have a strong effect on the curve, so it usually suffices
5050
// to use a uniform knot vector, which is the default. The major exception to this is repeated knot values.
5151
// At generic points in the NURBS, the curve is infinitely differentiable, but at a point that
@@ -647,7 +647,7 @@ module debug_nurbs(control,degree,splinesteps=16,width=1, size, mult,weights,typ
647647
// data = [[0,0], [30,50], [60,40], [80,10], [50,-20], [20,-10]];
648648
// path = nurbs_curve(nurbs_interp(data, 3, closed = true));
649649
// stroke(path, closed = true);
650-
// color("red") move_copies(data) circle(r=0.25, $fn=16);
650+
// color("red") move_copies(data) circle(r=1, $fn=16);
651651
//
652652
// Example(2D): Closed polygon - All data points lie exactly on the polygon boundary.
653653
// data = [[0,0], [30,50], [60,40], [80,10], [50,-20], [20,-10]];
@@ -661,37 +661,13 @@ module debug_nurbs(control,degree,splinesteps=16,width=1, size, mult,weights,typ
661661
// stroke(path, width=0.5);
662662
// color("red") move_copies(data) circle(r=1, $fn=16);
663663
//
664-
// Example(2D): Low-level NURBS parameter list - nurbs_interp() returns a BOSL2 NURBS parameter list compatible with nurbs_curve(), debug_nurbs(), etc.
664+
// Example(2D): Low-level NURBS parameter list nurbs_interp() returns a BOSL2 NURBS parameter list compatible with nurbs_curve(), debug_nurbs(), etc.
665665
// data = [[0,0], [10,30], [25,15], [40,35], [60,10], [80,25]];
666666
// result = nurbs_interp(data, 3);
667667
// curve = nurbs_curve(result, splinesteps=24);
668668
// stroke(curve, width=0.5);
669669
// color("red") move_copies(data) circle(r=1, $fn=16);
670670
//
671-
// Example(3D): 3D closed curve
672-
// data3d = [[20,0,0],[0,20,10],[-20,0,20],[0,-20,10]];
673-
// path = nurbs_curve(nurbs_interp(data3d, 3, closed=true));
674-
// stroke(path, width=1, closed=true);
675-
// color("red") move_copies(data3d) sphere(r=1, $fn=16);
676-
//
677-
// Example(2D): Corner added at data point 3
678-
// data = [[0,0], [10,30], [25,15], [40,35], [60,10], [80,25]];
679-
// path = nurbs_curve(nurbs_interp(data, 3, corners = [3]));
680-
// stroke(path);
681-
// color("red") move_copies(data) circle(r=1, $fn=16);
682-
//
683-
// Example(2D): Controlling the curvature at data point 3
684-
// data = [[0,0], [10,30], [25,15], [40,35], [60,10], [80,25]];
685-
// path = nurbs_curve(nurbs_interp(data, 3));
686-
// stroke(path);
687-
// color("red") move_copies(data) circle(r=1, $fn=16);
688-
//
689-
// Example(2D): Clamped curve (default)
690-
// data = [[0,0], [10,30], [25,15], [40,35], [60,10], [80,25]];
691-
// path = nurbs_curve(nurbs_interp(data, 3));
692-
// stroke(path);
693-
// color("red") move_copies(data) circle(r=1, $fn=16);
694-
//
695671
// Example(2D,Med): Endpoint tangent control - Specify start and/or end tangent vectors. Each vector is automatically scaled by the total chord length; a unit vector produces natural arc-length speed. Magnitude > 1 increases pull, < 1 weakens it.
696672
// data = [[0,0], [20,30], [50,25], [80,0]];
697673
// // No tangent control (natural):
@@ -704,6 +680,109 @@ module debug_nurbs(control,degree,splinesteps=16,width=1, size, mult,weights,typ
704680
// nurbs_curve(nurbs_interp(data, 3, start_deriv=[1,0], end_deriv=[1,0])), width=0.3);
705681
// color("black") move_copies(data) circle(r=0.5, $fn=16);
706682
//
683+
// Example(2D,Huge,NoAxes,VPT=[55,50,0],VPR=[0,0,0],VPD=600): Controlling the start and end of an otherwise unconstrained NURBS curve using derivitives.
684+
// data = [[0,0], [20,30], [30,90], [36,111],[50,25], [80,0]];
685+
// xdistribute(90){
686+
// union(){
687+
// debug_nurbs_interp(data,3, splinesteps=32, data_size=1);
688+
// fwd(15)text("unconstrained",size=6);
689+
// }
690+
// union(){
691+
// debug_nurbs_interp(data,3, splinesteps=32, data_size=1,
692+
// start_deriv=RIGHT);
693+
// fwd(15)text("start=RIGHT",size=6);
694+
// }
695+
// union(){
696+
// debug_nurbs_interp(data,3, splinesteps=32, data_size=1,
697+
// start_deriv=2*RIGHT,end_deriv=RIGHT);
698+
// fwd(15)text("start=[2,0] end=[1,0]",size=6);
699+
// }
700+
// }
701+
//
702+
// Example(2D,Huge,NoAxes,VPT=[45,50,0],VPR=[0,0,0],VPD=600): Controlling the shape with derivatives and corners
703+
// data = [[0,0], [20,30], [30,90], [36,111],[50,25], [80,0]];
704+
// xdistribute(110){
705+
// union(){
706+
// debug_nurbs_interp(data,3, splinesteps=32, data_size=1,
707+
// deriv=[2*RIGHT,[0,1],undef,undef,undef,RIGHT],
708+
// );
709+
// fwd(15)text("derivs at 0, 1, 5",size=6);
710+
// }
711+
// union(){
712+
// debug_nurbs_interp(data,3, splinesteps=32, data_size=1,
713+
// deriv=[undef,[0,1],undef,undef,RIGHT,undef],
714+
// );
715+
// fwd(15)text("derivs at pts 1, 4",size=6);
716+
// }
717+
// union(){
718+
// debug_nurbs_interp(data,3, splinesteps=32, data_size=1,
719+
// deriv=[undef,[0,1],undef,undef,NAN,undef],
720+
// );
721+
// fwd(15)text("corner and pt 1 deriv",size=6);
722+
// }
723+
// }
724+
//
725+
// Example(2D,Huge,NoAxes,VPT=[70,50,0],VPR=[0,0,0],VPD=725):
726+
// data = [[0,0], [20,30], [30,90], [36,111],[50,25], [80,0]];
727+
// xdistribute(110){
728+
// union(){
729+
// debug_nurbs_interp(data,3, splinesteps=32, data_size=1,
730+
// start_deriv=RIGHT,end_deriv=RIGHT, start_curvature=0,end_curvature=0
731+
// );
732+
// fwd(15)text("ends curvature=0",size=6);
733+
// }
734+
// union(){
735+
// debug_nurbs_interp(data,3, splinesteps=32, data_size=1,
736+
// start_deriv=RIGHT,end_deriv=RIGHT, start_curvature=1/10*unit([1000,1]),end_curvature=1/5
737+
// );
738+
// color("lime") {
739+
// stroke(arc(angle=[180,270], cp=[0,10],r=10));
740+
// stroke(arc(angle=[270,360], cp=last(data)+[0,5], r=5,$fn=32));
741+
// }
742+
// fwd(15)text("ends curvature>0",size=6);
743+
// }
744+
// union(){
745+
// debug_nurbs_interp(data,3, splinesteps=32, data_size=1,
746+
// deriv=[undef,[0,1],undef,[1,0],undef,undef],
747+
// curvature=[undef,-1/10,undef,0,undef,undef]
748+
// );
749+
// fwd(15)text("curvature at points 1 and 3",size=6);
750+
// }
751+
// }
752+
//
753+
// Example(2D,NoAxes,Big): Unconstrained NURBS through the same data points vary depending on the paramaterization method chosen
754+
// data = [[0,0], [20,30], [35,120], [50,30], [70,0]];
755+
// method = ["length", "centripetal", "dynamic", "foley", "fang"];
756+
// color = ["blue","lime","yellow","orange","red"];
757+
// for (i = [0:4]) {
758+
// color(color[i]) {
759+
// debug_nurbs_interp(data, 3, closed = true, method = method[i], size = 5, data_size = 3);
760+
// move([80,100-i*15]) text(method[i]);
761+
// }
762+
// }
763+
//
764+
// Example(2D,NoAxes,Big): Adding extra points reduces the differences between the methods.
765+
// data = [[0,0], [20,30], [35,120], [50,30], [70,0]];
766+
// method = ["length", "centripetal", "dynamic", "foley", "fang"];
767+
// color = ["blue","lime","yellow","orange","red"];
768+
// for (i = [0:4]) {
769+
// color(color[i]) {
770+
// debug_nurbs_interp(data, 3, closed = true, method = method[i], extra_pts = 3, size = 5, data_size = 3);
771+
// move([80,100-i*15]) text(method[i]);
772+
// }
773+
// }
774+
//
775+
// Example(2D,NoAxes,Big): Switching from the default to smooth = 1 improves things further.
776+
// data = [[0,0], [20,30], [35,120], [50,30], [70,0]];
777+
// method = ["length", "centripetal", "dynamic", "foley", "fang"];
778+
// color = ["blue","lime","yellow","orange","red"];
779+
// for (i = [0:4]) {
780+
// color(color[i]) {
781+
// debug_nurbs_interp(data, 3, closed = true, method = method[i], extra_pts = 3, smooth = 1, size = 5, data_size = 3);
782+
// move([80,100-i*15]) text(method[i]);
783+
// }
784+
// }
785+
//
707786
// Example(2D,NoAxes,Med,VPT=[37.5,0,0],VPD=275): We can generate a heart shape with a clamped NURBS where the first and last data points are co-incident, and we insert a corner at data point 4.
708787
// data = [[0,10], [25,20], [30,0], [20,-15], [0,-30], [-20,-15], [-30,0], [-25,20], [0,10]];
709788
// debug_nurbs_interp(data, 3, closed = false, method = "centripetal", corners=[4]);
@@ -739,10 +818,10 @@ module debug_nurbs(control,degree,splinesteps=16,width=1, size, mult,weights,typ
739818
// Example(2D,Big): Parameterization methods for sharp turns. For data with sudden direction changes or uneven chord spacing, "centripetal" and "dynamic" reduce unwanted oscillations.
740819
// // "length" (blue), "centripetal" (red), "dynamic" (orange) compared.
741820
// sharp = [[0,0], [5,40],[6,40], [10,0], [50,0], [55,40],[56,42], [60,0]];
742-
// color("blue") stroke(nurbs_curve(nurbs_interp(sharp, 3, method = "centripetal"), splinesteps=32), width=0.1);
743-
// color("red") stroke(nurbs_curve(nurbs_interp(sharp, 3, method="foley"), splinesteps=32), width=0.1);
744-
// color("orange") stroke(nurbs_curve(nurbs_interp(sharp, 3, method="dynamic"), splinesteps=32), width=0.1);
745-
// color("green") move_copies(sharp) circle(r=.1, $fn=16);
821+
// color("blue") stroke(nurbs_curve(nurbs_interp(sharp, 3, method = "centripetal"), splinesteps=32), width=0.25);
822+
// color("red") stroke(nurbs_curve(nurbs_interp(sharp, 3, method="foley"), splinesteps=32), width=0.25);
823+
// color("orange") stroke(nurbs_curve(nurbs_interp(sharp, 3, method="dynamic"), splinesteps=32), width=0.25);
824+
// color("green") move_copies(sharp) circle(r=.5, $fn=16);
746825

747826

748827
function nurbs_interp(points, degree, method="centripetal", closed=false,
@@ -872,40 +951,7 @@ function nurbs_interp(points, degree, method="centripetal", closed=false,
872951
// show_deriv = Show derivative-constraint arrows. Default: `true`
873952
// show_curvature = Show curvature-constraint circles / disks. Default: `true`
874953
//
875-
// Example(2D,NoAxes,Med): Unconstrained NURBS through the same data points vary depending on the paramaterization method chosen
876-
// data = [[0,0], [20,30], [35,120], [50,30], [70,0]];
877-
// method = ["length", "centripetal", "dynamic", "foley", "fang"];
878-
// color = ["blue","lime","yellow","orange","red"];
879-
// for (i = [0:4]) {
880-
// color(color[i]) {
881-
// debug_nurbs_interp(data, 3, closed = true, method = method[i], size = 5, data_size = 3);
882-
// move([80,100-i*15]) text(method[i]);
883-
// }
884-
// }
885-
//
886-
//
887-
// Example(2D,NoAxes,Med): Adding extra points reduces the differences between the methods.
888-
// data = [[0,0], [20,30], [35,120], [50,30], [70,0]];
889-
// method = ["length", "centripetal", "dynamic", "foley", "fang"];
890-
// color = ["blue","lime","yellow","orange","red"];
891-
// for (i = [0:4]) {
892-
// color(color[i]) {
893-
// debug_nurbs_interp(data, 3, closed = true, method = method[i], extra_pts = 3, size = 5, data_size = 3);
894-
// move([80,100-i*15]) text(method[i]);
895-
// }
896-
// }
897-
//
898-
// Example(2D,NoAxes,Med): Switching from the default to smooth = 1 improves things further.
899-
// data = [[0,0], [20,30], [35,120], [50,30], [70,0]];
900-
// method = ["length", "centripetal", "dynamic", "foley", "fang"];
901-
// color = ["blue","lime","yellow","orange","red"];
902-
// for (i = [0:4]) {
903-
// color(color[i]) {
904-
// debug_nurbs_interp(data, 3, closed = true, method = method[i], extra_pts = 3, smooth = 1, size = 5, data_size = 3);
905-
// move([80,100-i*15]) text(method[i]);
906-
// }
907-
// }
908-
//
954+
909955
// Example(2D,NoAxes): Keyhole Shape: Simply interpolating a NURBS through the data points yields disappointing results.
910956
// data = [[0,0],[0,10],[-5,20],[5,30],[15,20],[10,10],[10,0],[0,0]];
911957
// debug_nurbs_interp(data,3, method="centripetal");
@@ -1672,18 +1718,8 @@ module nurbs_vnf(patch, degree, splinesteps=16, weights, type="clamped", mult, k
16721718
// ];
16731719
// nurbs_interp_surface(surface,3, row_edges = 3);
16741720
//
1675-
// Example(3D,VPD=320,VPT=[8,10,13]): Setting first and last row/column derivitives
1676-
// surface = [
1677-
// [[-50, 50, 0], [-16, 50, 0], [ 16, 50, 0], [50, 50, 0], [80, 50, 0]],
1678-
// [[-50, 25, 0], [-16, 25, 40], [ 16, 25, 30], [50, 25, 20], [80, 25, 0]],
1679-
// [[-50, 0, 0], [-16, 0, 40], [ 16, 0, 30], [50, 0, 30], [80, 0, 0]],
1680-
// [[-50,-25, 0], [-16,-25, 35], [ 16,-25, 40], [50,-25, 15], [80,-25, 0]],
1681-
// [[-50,-50, 0], [-16,-50, 0], [ 16,-50, 0], [50,-50, 0], [80,-50, 0]],
1682-
// ];
1683-
// nurbs_interp_surface(surface,3, first_row_deriv = UP+FWD, last_row_deriv = DOWN+FWD,
1684-
// first_col_deriv = UP+RIGHT/2, last_col_deriv = DOWN+RIGHT/2);
16851721
//
1686-
// Example(3D): Tube - Surface closed around the column direction (the rings), clamped along rows (the axis). Uses 5 rings: a cubic closed direction needs at least p+2 = 5 data points to have interior knot freedom.
1722+
// Example(3D): Tube - Surface closed around the column direction (the rings), clamped along rows (the axis).
16871723
// r = 20;
16881724
// data = [for (u = [0:15:60])
16891725
// [for (i = [0:1:5])
@@ -1692,22 +1728,38 @@ module nurbs_vnf(patch, degree, splinesteps=16, weights, type="clamped", mult, k
16921728
// ];
16931729
// nurbs_interp_surface(data, 3, splinesteps=8, col_wrap=true);
16941730
//
1695-
// Example(3D,VPR=[80,0,45],VPT=[0,0,20],VPD = 320): Rotated star cross section surface closed in one direction. Degenerate end rows close the shape in the other direction.
1731+
//// Example(3D): We can close the ends of mixed surfaces such as our tube, using caps.
1732+
// r = 20;
1733+
// data = [for (u = [0:15:60])
1734+
// [for (i = [0:1:5])
1735+
// let(a = i * 360/6)
1736+
// [r*cos(a), r*sin(a), u]]
1737+
// ];
1738+
// nurbs_interp_surface(data, 3, splinesteps=8, col_wrap=true, caps = true);
1739+
//
1740+
//
1741+
// Example(3D,Med,VPR=[80,0,45],VPT=[0,0,20],VPD = 320): Rotated star cross section surface closed in one direction. Degenerate end rows close the shape in the other direction.
16961742
// surface = [ repeat([0,0,-15],14),
16971743
// for(i=[0:4]) zrot(i*15,path3d(star(or=15,ir=13, n=7),i*15)),
16981744
// repeat([0,0,5*15],14)
16991745
// ];
17001746
// nurbs_interp_surface(surface, 3, col_wrap = true);
17011747
//
1702-
// Example(3D,VPR=[80,0,45],VPT=[0,0,20],VPD = 320): Controlling end shape with normals.
1748+
// Example(3D,Med,VPR=[80,0,45],VPT=[0,0,20],VPD = 320): Controlling end shape with normals.
17031749
// surface = [ repeat([0,0,-15],14),
17041750
// for(i=[0:4]) zrot(i*15,path3d(star(or=15,ir=13, n=7),i*15)),
17051751
// repeat([0,0,5*15],14)
17061752
// ];
17071753
// nurbs_interp_surface(surface, 3, col_wrap = true, normal1 = DOWN*4, normal2 = UP*2);
17081754
//
1709-
//
1710-
// Example(3D): EGG Smooth parametric ovoid. ~103 long, ~82 wide. Blunt at +z, pointed at -z. Profile: r = 40·sin(φ)·(1 − 0.25·cos(φ)), z = −52·cos(φ) The asymmetry term shifts the belly toward the blunt end. Grid: 9 rings × 8 angles
1755+
// Example(3D,Med,VPR=[80,0,45],VPT=[0,0,20],VPD = 320): Controlling end shape with normals.
1756+
// surface = [ repeat([0,0,-15],14),
1757+
// for(i=[0:4]) zrot(i*15,path3d(star(or=15,ir=13, n=7),i*15)),
1758+
// repeat([0,0,5*15],14)
1759+
// ];
1760+
// nurbs_interp_surface(surface, 3, col_wrap = true, normal1 = DOWN*4, normal2 = UP*2+RIGHT*4);
1761+
//
1762+
// Example(3D): EGG Smooth parametric ovoid. ~103 long, ~82 wide. 9 rings × 8 angles
17111763
// egg = [for (i = [0:8])
17121764
// let(phi = i * 180/8,
17131765
// r = 40 * sin(phi) * (1 - 0.25*cos(phi)),
@@ -1718,15 +1770,31 @@ module nurbs_vnf(patch, degree, splinesteps=16, weights, type="clamped", mult, k
17181770
// ]
17191771
// ];
17201772
// nurbs_interp_surface(egg, 3, col_wrap = true);
1773+
// color("red") move_copies(flatten(egg)) circle(r=1, $fn=16);
17211774
//
1722-
// Example(3D,VPT=[10,-25,60],VPR=[100,0,30],VPD=375): A Mushroom
1775+
// Example(3D,VPT=[10,-25,60],VPR=[100,0,30],VPD=425): A Mushroom
17231776
// shape = [ repeat([0,0,-1],8),
17241777
// for(i=[0:5]) path3d(regular_ngon(n = 8, side = 15),i*15),
17251778
// path3d(regular_ngon(n = 8, side = 50), 5 * 15),
17261779
// path3d(regular_ngon(n = 8, side = 55), 6.5 * 15),
1727-
// repeat([0,0,8*15],8)
1780+
// repeat([0,0,9*15],8)
17281781
// ];
1729-
// nurbs_interp_surface(shape, 3, normal1 = DOWN, normal2 = UP, col_wrap = true, row_edges = 7);
1782+
// nurbs_interp_surface(shape, 3, normal1 = DOWN, normal2 = UP*0.8, col_wrap = true, row_edges = 7);
1783+
//
1784+
//
1785+
// Example(3D,Med,NoAxes,VPT=[0,0,75],VPR=[90,0,0],VPD=900): Controlling mushroom crown shape with normal2.
1786+
// shape = [ repeat([0,0,-1],8),
1787+
// for(i=[0:5]) path3d(regular_ngon(n = 8, side = 15),i*15),
1788+
// path3d(regular_ngon(n = 8, side = 50), 5 * 15),
1789+
// path3d(regular_ngon(n = 8, side = 55), 6.5 * 15),
1790+
// repeat([0,0,9*15],8)
1791+
// ];
1792+
// for (i = [0.4:0.2:0.8]) {
1793+
// right( i * 800 -480){
1794+
// nurbs_interp_surface(shape, 3, normal1 = DOWN, normal2 = UP*i, col_wrap = true, row_edges = 7);
1795+
// up(150) xrot(90) color("blue") text(str("UP * ",i), size = 14, anchor = CENTER);
1796+
// }
1797+
// }
17301798
//
17311799
// Example(3D,VPR[80,0,40]): A 3d Heart Shape - Based on the 2d Shape from nurbs_interp() example 14.
17321800
// data = [[0,10], [25,20], [30,0], [20,-15], [0,-30], [-20,-15], [-30,0], [-25,20]];

0 commit comments

Comments
 (0)