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
1 change: 0 additions & 1 deletion math.scad
Original file line number Diff line number Diff line change
Expand Up @@ -1262,7 +1262,6 @@ 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
87 changes: 46 additions & 41 deletions nurbs.scad
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,6 @@ function _calc_mult(knots) =
deltas(ind);



// Module: debug_nurbs()
// Synopsis: Shows a NURBS curve and its control points, knots and weights
// SynTags: Geom
Expand All @@ -370,9 +369,10 @@ function _calc_mult(knots) =
// debug_nurbs(control, degree, [width], [splinesteps=], [type=], [mult=], [knots=], [size=], [show_weights=], [show_knots=], [show_idx=]);
// Description:
// Displays a 2D or 3D NURBS and the associated control points to help debug NURBS curves. You can display the
// control point indices and weights, and can also display the knot points.
// control point indices and weights, and can also display the knot 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 = control points for NURBS
// control = list of control points in any dimension or a NURBS parameter list
// degree = degree of NURBS
// splinesteps = number of segments between each pair of knots. Default: 16
// width = width of the line. Default: 1
Expand All @@ -395,42 +395,50 @@ function _calc_mult(knots) =
// debug_nurbs(pts,4,type="closed",weights=weights);

module debug_nurbs(control,degree,splinesteps=16,width=1, size, mult,weights,type="clamped",knots, show_weights, show_knots=false, show_index=true)
{
$fn=8;
size = default(size, 3*width);
show_weights = default(show_weights, is_def(weights));
N=len(control);
twodim = len(control[0])==2;
curve = nurbs_curve(control=control,degree=degree,splinesteps=splinesteps, mult=mult,weights=weights, type=type, knots=knots);
stroke(curve, width=width, closed=type=="closed");//, color="green");
stroke(control, width=width/2, color="lightblue", closed=type=="closed");
if (show_knots){
knotpts = nurbs_curve(control=control, degree=degree, splinesteps=1, mult=mult, weights=weights, type=type, knots=knots);
echo(knotpts);
color([1,.5,1])
move_copies(knotpts)
if (twodim)circle(r=width);
else sphere(r=width);
{
if (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")
debug_nurbs(control[2], control[1], splinesteps, width, size, weights=control[4], type=control[0], knots=control[3],
show_weights=show_weights, show_knots=false, show_index=true);
}
color("blue")
if (show_index)
move_copies(control){
let(label = str($idx),
anch = show_weights && is_def(weights[$idx]) && weights[$idx]!=1 ? FWD : CENTER)
if (twodim) text(text=label, size=size, anchor=anch);
else rot($vpr) text3d(text=label, size=size, anchor=anch);
else {
$fn=8;
size = default(size, 3*width);
show_weights = default(show_weights, is_def(weights));
N=len(control);
twodim = len(control[0])==2;
curve = nurbs_curve(control=control,degree=degree,splinesteps=splinesteps, mult=mult,weights=weights, type=type, knots=knots);
stroke(curve, width=width, closed=type=="closed");//, color="green");
stroke(control, width=width/2, color="lightblue", closed=type=="closed");
if (show_knots){
knotpts = nurbs_curve(control=control, degree=degree, splinesteps=1, mult=mult, weights=weights, type=type, knots=knots);
echo(knotpts);
color([1,.5,1])
move_copies(knotpts)
if (twodim)circle(r=width);
else sphere(r=width);
}
color("blue")
if ( show_weights)
move_copies(control){
if(is_def(weights[$idx]) && weights[$idx]!=1)
let(label = str("w=",weights[$idx]),
anch = show_index ? BACK : CENTER
)
if (twodim) fwd(size/2*0)text(text=label, size=size, anchor=anch);
else rot($vpr) text3d(text=label, size=size, anchor=anch);
}

color("blue")
if (show_index)
move_copies(control){
let(label = str($idx),
anch = show_weights && is_def(weights[$idx]) && weights[$idx]!=1 ? FWD : CENTER)
if (twodim) text(text=label, size=size, anchor=anch);
else rot($vpr) text3d(text=label, size=size, anchor=anch);
}
color("blue")
if ( show_weights)
move_copies(control){
if(is_def(weights[$idx]) && weights[$idx]!=1)
let(label = str("w=",weights[$idx]),
anch = show_index ? BACK : CENTER
)
if (twodim) fwd(size/2*0)text(text=label, size=size, anchor=anch);
else rot($vpr) text3d(text=label, size=size, anchor=anch);
}
}
}


Expand Down Expand Up @@ -496,7 +504,6 @@ 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]) =
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],
Expand All @@ -518,9 +525,7 @@ let(fo=echo(intype=type))
u=is_range(u) ? list(u) : u,
v=is_range(v) ? list(v) : v,
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 @@ -535,11 +540,11 @@ fda= echo(type=type, force_list(type,2)),
: is_num(v) ? column(nurbs_patch_points(patch, degree, u=u, v=[v], knots=knots, mult=mult, type=type),0)
:
let(

vsplines = [for (i = idx(patch[0])) nurbs_curve(column(patch,i), degree[0], splinesteps=splinesteps[0],u=u, type=type[0],mult=mult[0],knots=knots[0])]
)
[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&Module: nurbs_vnf()
// Synopsis: Generates a (possibly non-manifold) VNF for a single NURBS surface patch.
// SynTags: VNF
Expand Down
61 changes: 52 additions & 9 deletions rounding.scad
Original file line number Diff line number Diff line change
Expand Up @@ -781,13 +781,15 @@ function _scalar_to_vector(value,length,varname) =
// back by the joint length. Rounding is using continous curvature 4th order bezier splines and
// the parameter `k` specifies how smooth the curvature match is. This parameter ranges from 0 to 1 with
// a default of 0.5. Use a larger k value to get a curve that is bigger for the same joint value. When
// k=1 the curve may be similar to a circle if your curves are symmetric. As the path is built up, the joint
// k=0.9 the curve may be similar to a circle if your curves are symmetric. As the path is built up, the joint
// parameter applies to the growing path, so if you pick a large joint parameter it may interact with the
// previous path sections. See [Types of Roundover](rounding.scad#subsection-types-of-roundover) for more details
// on continuous curvature rounding.
// .
// The rounding is created by extending the two clipped paths to define a corner point. If the extensions of
// the paths do not intersect, the function issues an error. When closed=true the final path should actually close
// the paths do not intersect they will be capped by a curve resembling a semi-circle if this is possible. But sometimes
// the connecting path will need to be S-shaped, which is not supported. If this situation arises, the function issues an error.
// When closed=true the final path should actually close
// the shape, repeating the starting point of the shape. If it does not, then the rounding fills the gap.
// .
// The number of segments in the roundovers is set based on $fn and $fs. If you use $fn it specifies the number of
Expand Down Expand Up @@ -881,6 +883,16 @@ function _scalar_to_vector(value,length,varname) =
// tri = regular_ngon(n=3, r=7);
// stroke(path_join([tri], joint=3,closed=true,$fn=12),
// closed=true,width=.5);
// Example(2D): Here lines are joined that do not intersect when they are extended, so the joint is sort of like a semi-circle. We show the effect of the `k` parameter at its default value of 0.5 or at 0.92 which is close to a circle.
// p=path_join([
// [[1,3],[15,8]],
// [[17,0],[0,0]],
// [[0,-8],[15,-3]]
// ],
// k=[.5,.9],
// relocate=false);
// stroke(p);

module path_join(paths,joint=0,k=0.5,relocate=true,closed=false) { no_module();}
function path_join(paths,joint=0,k=0.5,relocate=true,closed=false)=
assert(is_list(paths),"Input paths must be a list of paths")
Expand Down Expand Up @@ -935,14 +947,21 @@ function _path_join(paths,joint,k=0.5,i=0,result=[],relocate=true,closed=false)
corner = approx(firstcut[0],nextcut[0]) ? firstcut[0]
: line_intersection([firstcut[0], firstcut[0]-first_dir], [nextcut[0], nextcut[0]-next_dir],RAY,RAY)
)
assert(is_def(corner), str("Curve directions at cut points don't intersect in a corner when ",
loop?"closing the path":str("adding path ",i+1)))
let(
bezpts = _smooth_bez_fill([firstcut[0], corner, nextcut[0]],k[i]),
N = max(3,$fn>0 ?$fn : ceil(bezier_length(bezpts)/$fs)),
bezpath = approx(firstcut[0],corner) && approx(corner,nextcut[0])
? []
: bezier_curve(bezpts,N),
bezpath =
is_def(corner) ? let(
bezpts = _smooth_bez_fill([firstcut[0], corner, nextcut[0]],k[i]),
N = max(3,$fn>0 ?$fn : ceil(bezier_length(bezpts)/$fs)),
bezpath = approx(firstcut[0],corner) && approx(corner,nextcut[0])
? []
: bezier_curve(bezpts,N)
)
bezpath
: let(bezpath = _smooth_bezier_cap([firstcut[0]+first_dir, firstcut[0]],
[nextcut[0]+next_dir, nextcut[0]], k[i]))
assert(is_def(bezpath),str("Curve directions at cut points requires an S-curve joint (which is not supported) when ",
loop?"closing the path":str("adding path ",i+1)))
bezpath,
new_result = [each select(result,loop?nextcut[1]:0,len(revresult)-1-firstcut[1]),
each bezpath,
nextcut[0],
Expand All @@ -955,6 +974,30 @@ function _path_join(paths,joint,k=0.5,i=0,result=[],relocate=true,closed=false)



// connect tails of v1 and v2, starting at v1
// returns undef if an S curve is needed instead of a cap
function _smooth_bezier_cap(v1,v2,k=0.8,r) =
let(
r=default(r,norm(v1[1]-v2[1])/2),
n=line_normal(v1[1],v2[1]),
sign = (v1[1]-v1[0])*n>0 ? 1 : -1,
check= (v2[1]-v2[0])*n>0 ? 1 : -1
)
check != sign ? undef
:
let(
tangent = move(sign*n*r, [(v1[1]+v2[1])/2,v2[1]]),
corner1 = line_intersection(tangent, v1, LINE),
corner2 = line_intersection(tangent, v2, LINE)
)
concat(
list_head(_bezcorner([v1[1],corner1, tangent[0]],k)),
_bezcorner([tangent[0], corner2, v2[1]],k)
);




// Function&Module: offset_stroke()
// Synopsis: Draws a line along a path with options to specify angles and roundings at the ends.
// SynTags: Path, Region
Expand Down
Loading