Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions math.scad
Original file line number Diff line number Diff line change
Expand Up @@ -1227,8 +1227,8 @@ function spherical_random_points(n=1, radius=1, seed) =
// the origin to a vertex is always contained in the polygon.
// .
// If size is a vector, then
// The polygon lies inside a circle of radius `size[1]` and also contains a circle whose
// radius is `size[0]`. The first vertex of the polygon will be the first vertex below the X+ axis.
// The polygon's vertices lie inside a ring with inner radius `size[0]` and outer radius `size[1]`.
// The first vertex of the polygon will be the first vertex below the X+ axis.
// .
// The angular position of the vertices are randomly chosen.
// The `angle_sep` parameter controls the separation in angle required between vertices. If you set it
Expand All @@ -1251,8 +1251,7 @@ function random_polygon(n=3,size=1, angle_sep=0.2, seed) =
let(
rmin = is_num(size) ? size/2 : size[0],
rmax = is_num(size) ? size : size[1],
angsep = 0.1, // 0 means no separation, max angle space; 1 means max separation=> uniformly spaced
ang_space = (1-angsep)*360,
ang_space = (1-angle_sep)*360,
// Create random angle list where angles are separated based on ang_sep but all angular differences < 180
randang = function(seed)
let (
Expand All @@ -1263,6 +1262,7 @@ function random_polygon(n=3,size=1, angle_sep=0.2, seed) =
max(dang)<180 ? angs : randang(seed=u_add(seed,angs[0])),
angs = randang(seed),
rads = is_undef(seed) ? rands(rmin,rmax,n) : rands(rmin,rmax,n,seed+angs[0])
,f=echo(rads=rads)
)
[for(i=count(n)) rads[i]*[cos(angs[i]), -sin(angs[i])]];

Expand Down
110 changes: 92 additions & 18 deletions nurbs.scad
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ _BOSL2_NURBS = is_undef(_BOSL2_STD) && (is_undef(BOSL2_NO_STD_WARNING) || !BOSL2
// use it to evaluate the NURBS over its entire domain by giving a splinesteps value. This specifies the number of segments
// to use between each knot and guarantees a point exactly at each knot. This may be important if you set the knot multiplicity
// to the degree somewhere in your curve, which creates a corner at the knot, because it guarantees a sharp corner regardless
// of the number of points.
// of the number of points.
// .
// Instead of providing separate parameters you can give a first parameter of the form of a NURBS parameter list: `[type degree, control, knots, weights]`.
// Arguments:
// control = list of control points in any dimension
// control = list of control points in any dimension or a NURBS parameter list
// degree = degree of NURBS
// splinesteps = evaluate whole spline with this number of segments between each pair of knots
// ---
Expand Down Expand Up @@ -186,7 +188,12 @@ _BOSL2_NURBS = is_undef(_BOSL2_STD) && (is_undef(BOSL2_NO_STD_WARNING) || !BOSL2
// debug_nurbs(control, 3, splinesteps=16,weights=w,mult=[1,3,3],width=.1,size=.2,type="closed",show_knots=true);

function nurbs_curve(control,degree,splinesteps,u, mult,weights,type="clamped",knots) =
assert(num_defined([splinesteps,u])==1, "Must define exactly one of u and splinesteps")
is_list(control) && in_list(control[0], ["closed","open","clamped"]) ?
assert(len(control)>=5, "Invalid NURBS parameter list")
assert(num_defined([degree,mult,weights,knots])==0,
"Cannot give degree, mult, weights or knots when you provide a NURBS parameter list")
nurbs_curve(control[2], control[1], splinesteps, u, weights=control[4], type=control[0], knots=control[3])
: assert(num_defined([splinesteps,u])==1, "Must define exactly one of u and splinesteps")
is_finite(u) ? nurbs_curve(control,degree,u=[u],mult,weights,type=type)[0]
: assert(is_undef(splinesteps) || (is_int(splinesteps) && splinesteps>0), "splinesteps must be a positive integer")
let(u=is_range(u) ? list(u) : u)
Expand All @@ -195,13 +202,13 @@ function nurbs_curve(control,degree,splinesteps,u, mult,weights,type="clamped",
let(
dim = len(control[0]),
control = [for(i=idx(control)) [each control[i]*weights[i],weights[i]]],
curve = nurbs_curve(control,degree,u=u,splinesteps=splinesteps, mult=mult,type=type)
curve = nurbs_curve(control,degree,u=u,splinesteps=splinesteps, mult=mult, knots=knots, type=type)
)
[for(pt=curve) select(pt,0,-2)/last(pt)]
:
let(
uniform = is_undef(knots),
dum=assert(in_list(type, ["closed","open","clamped"]), str("Unknown nurbs spline type", type))
dum=assert(in_list(type, ["closed","open","clamped"]), str("Unknown nurbs spline type: ", type))
assert(type=="closed" || len(control)>=degree+1, str(type," nurbs requires at least degree+1 control points"))
assert(is_undef(mult) || is_vector(mult), "mult must be a vector"),
badmult = is_undef(mult) ? []
Expand Down Expand Up @@ -459,7 +466,7 @@ function is_nurbs_patch(x) =
// give `undef` as the knot vector, e.g., `[undef,vknots]` to have uniform knots in the first dimension and
// specified knots in the second one. You can do the same thing with the `mult` parameter.
// Arguments:
// patch = rectangular list of control points in any dimension
// patch = rectangular list of control points in any dimension, or a NURBS parameter list
// degree = a scalar or 2-vector giving the degree of the NURBS in the two directions
// splinesteps = a scalar or 2-vector giving the number of segments between each knot in the two directions
// ---
Expand Down Expand Up @@ -489,7 +496,13 @@ function is_nurbs_patch(x) =
// move_copies(flatten(pts)) sphere(r=2,$fn=16);

function nurbs_patch_points(patch, degree, splinesteps, u, v, weights, type=["clamped","clamped"], mult=[undef,undef], knots=[undef,undef]) =
assert(is_undef(splinesteps) || !any_defined([u,v]), "Cannot combine splinesteps with u and v")
let(fo=echo(intype=type))
is_list(patch) && _valid_surface_type(patch[0]) ?
assert(len(patch)>=5, "NURBS parameter list is invalid")
assert(num_defined([degree,weights])==0 && mult==[undef,undef] && knots==[undef,undef],
"Cannot give degree, mult, weights or knots when you provide a NURBS parameter list")
nurbs_patch_points(patch[2], patch[1], splinesteps, u, v, patch[4], patch[0], knots=patch[3])
: assert(is_undef(splinesteps) || !any_defined([u,v]), "Cannot combine splinesteps with u and v")
is_def(weights) ?
assert(is_matrix(weights,len(patch),len(patch[0])), "The weights parameter must be a matrix that matches the size of the patch array")
let(
Expand All @@ -504,8 +517,10 @@ function nurbs_patch_points(patch, degree, splinesteps, u, v, weights, type=["cl
let(
u=is_range(u) ? list(u) : u,
v=is_range(v) ? list(v) : v,
degree = force_list(degree,2),
degree = force_list(degree,2),
feee=echo(type=type),
type = force_list(type,2),
fda= echo(type=type, force_list(type,2)),
splinesteps = is_undef(splinesteps) ? [undef,undef] : force_list(splinesteps,2),
mult = is_vector(mult) || is_undef(mult) ? [mult,mult]
: assert((is_undef(mult[0]) || is_vector(mult[0])) && (is_undef(mult[1]) || is_vector(mult[1])), "mult must be a vector or list of two vectors")
Expand All @@ -525,29 +540,38 @@ function nurbs_patch_points(patch, degree, splinesteps, u, v, weights, type=["cl
)
[for (i = idx(vsplines[0])) nurbs_curve(column(vsplines,i), degree[1], splinesteps=splinesteps[1], u=v, mult=mult[1], knots=knots[1], type=type[1])];

// Function: nurbs_vnf()
// Function&Module: nurbs_vnf()
// Synopsis: Generates a (possibly non-manifold) VNF for a single NURBS surface patch.
// SynTags: VNF
// Topics: NURBS Patches
// See Also: nurbs_patch_points()
// Usage:
// vnf = nurbs_vnf(patch, degree, [splinesteps], [mult=], [knots=], [weights=], [type=], [style=]);
// Usage: (as a function)
// vnf = nurbs_vnf(patch, degree, [splinesteps], [mult=], [knots=], [weights=], [type=], [style=], [reverse=], [triangulate=], [caps=], [caps1=], [caps2=]);
// Usage: (as a module)
// nurbs_vnf(patch, degree, [splinesteps], [mult=], [knots=], [weights=], [type=], [style=], [reverse=], [triangulate=], [convexity=],[atype=],[cp=],...) CHILDREN;
// Description:
// Compute a (possibly non-manifold) VNF for a NURBS. The input patch must be an array of control points. If weights is given it
// Compute a (possibly non-manifold) VNF for a NURBS. The input patch must be an array of control points or a NURBS parameter list. If weights is given it
// must be an array of weights that matches the size of the control points. The style parameter
// gives the {{vnf_vertex_array()}} style to use. The other parameters may specify the NURBS parameters in the two directions
// by giving a single value, which applies to both directions, or a list of two values to specify different values in each direction.
// You can specify undef for for a direction to keep the default, such as `mult=[undef,v_multiplicity]`.
// .
// Instead of providing separate parameters you can give a first parameter as a NURBS parameter list: `[type degree, patch, knots, weights]`.
// Arguments:
// patch = rectangular list of control points in any dimension
// patch = rectangular list of control points in any dimension, or a NURBS parameter list
// degree = a scalar or 2-vector giving the degree of the NURBS in the two directions
// splinesteps = a scalar or 2-vector giving the number of segments between each knot in the two directions
// splinesteps = a scalar or 2-vector giving the number of segments between each knot in the two directions. Default: 16
// ---
// mult = a single list or pair of lists giving the knot multiplicity in the two directions. Default: all 1
// knots = a single list of pair of lists giving the knot vector in each of the two directions. Default: uniform
// weights = a single list or pair of lists giving the weight at each control point in the. Default: all 1
// type = a single string or pair of strings giving the NURBS type, where each entry is one of "clamped", "open" or "closed". Default: "clamped"
// caps = If true, add endcap faces to both ends. The type must be ["clamped","closed"] or ["closed","clamped"] to enable caps.
// cap1 = If true, add an endcap face to the first end.
// cap2 = If true, add an endcap face to the second end.
// reverse = If true, reverse all face normals.
// style = {{vnf_vertex_array ()}} style to use for triangulating the surface. Default: "default"
// triangulate = If true, triangulates endcaps to resolve possible CGAL issues. This can be an expensive operation if the endcaps are complex. Default: false
// Example(3D): Quadratic B-spline surface
// patch = [
// [[-50, 50, 0], [-16, 50, 20], [ 16, 50, 20], [50, 50, 0]],
Expand Down Expand Up @@ -600,10 +624,60 @@ function nurbs_patch_points(patch, degree, splinesteps, u, v, weights, type=["cl
// vknots = [0, 1/2, 1/2, 1/2, 1];
// vnf = nurbs_vnf(patch, 3,weights=weights, knots=[undef,vknots]);
// vnf_polyhedron(vnf);
function nurbs_vnf(patch, degree, splinesteps=16, weights, type="clamped", mult, knots, style="default") =
assert(is_nurbs_patch(patch),"Input patch is not a rectangular aray of points")
function nurbs_vnf(patch, degree, splinesteps=16, weights, type="clamped", mult, knots, style="default", reverse=false, triangulate=false, caps,cap1,cap2) =
is_list(patch) && _valid_surface_type(patch[0]) ?
assert(len(patch)>=5, "NURBS parameter list is invalid")
assert(num_defined([degree,mult,weights,knots]==0),
"Cannot give degree, mult, weights or knots when you provide a NURBS parameter list")
nurbs_vnf(patch[2], patch[1], splinesteps, patch[4], patch[0], knots=patch[3], style=style,caps=caps,cap1=cap1,cap2=cap2,
reverse=reverse, triangulate=triangulate)
: assert(is_nurbs_patch(patch),"Input patch is not a rectangular aray of points")
assert(_valid_surface_type(type), "type must be one of or a list of two of: \"closed\", \"clamped\" and \"open\"")
let(havecaps = num_true([caps,cap1,cap2])>0)
assert(!havecaps || type==["clamped","closed"] || type==["closed","clamped"],
"Surface must be [\"closed\",\"clamped\"] or [\"clamped\",\"closed\"] to for caps to be created")
let(
pts = nurbs_patch_points(patch=patch, degree=degree, splinesteps=splinesteps, type=type, mult=mult, knots=knots, weights=weights)
type = force_list(type,2),
havecaps = num_true([caps,cap1,cap2])>0,
flip = havecaps && type[0]=="closed",
pts = nurbs_patch_points(patch=patch, degree=degree, splinesteps=splinesteps, type=type, mult=mult, knots=knots, weights=weights),
tpts = flip ? (transpose(pts)) : pts
)
vnf_vertex_array(pts, style=style, row_wrap=type[0]=="closed", col_wrap=type[1]=="closed");
vnf_vertex_array(tpts, style=style, row_wrap=type[flip?1:0]=="closed", col_wrap=type[flip?0:1]=="closed",reverse=reverse,triangulate=triangulate,
caps=caps,cap1=cap1,cap2=cap2);


function _valid_surface_type(type) =
in_list(type,["closed","clamped","open"]) ? true
: !is_list(type) || len(type)!=2 ? false
: _valid_surface_type(type[0]) && _valid_surface_type(type[1]);



module nurbs_vnf(patch, degree, splinesteps=16, weights, type="clamped", mult, knots, style="default", reverse=false, triangulate=false,
convexity=2, cp="centroid", anchor="origin", spin=0, orient=UP, atype="hull", caps, cap1, cap2)
{
if (is_list(patch) && _valid_surface_type(patch[0])){
assert(len(patch)>=5, "NURBS parameter list is invalid");
assert(num_defined([degree,mult,weights,knots]==0),
"Cannot give degree, mult, weights or knots when you provide a NURBS parameter list");
nurbs_vnf(patch[2], patch[1], splinesteps, patch[4], patch[0], knots=patch[3], style=style, reverse=reverse, triangulate=triangulate,
convexity=convexity, cp=cp, anchor=anchor, spin=spin, orient=orient, atype=atype, caps=caps, cap1=cap1, cap2=cap2) children();
}
else {
type = force_list(type,2);
havecaps = num_true([caps,cap1,cap2])>0;
dummy =
assert(is_nurbs_patch(patch),"Input patch is not a rectangular aray of points")
assert(_valid_surface_type(type), "type must be one of or a list of two of: \"closed\", \"clamped\" and \"open\"")
assert(!havecaps || type==["clamped","closed"] || type==["closed","clamped"],
"Surface must be [\"closed\",\"clamped\"] or [\"clamped\",\"closed\"] to for caps to be created");
flip = havecaps && type[0]=="closed";
pts = nurbs_patch_points(patch=patch, degree=degree, splinesteps=splinesteps, type=type, mult=mult, knots=knots, weights=weights);
tpts = flip ? (transpose(pts)) : pts;
vnf_vertex_array(tpts, style=style, row_wrap=type[flip?1:0]=="closed", col_wrap=type[flip?0:1]=="closed", reverse=reverse, triangulate=triangulate, cp=cp,
convexity=convexity, anchor=anchor, spin=spin, orient=orient, atype=atype, caps=caps, cap1=cap1, cap2=cap2) children();
}
}


Loading