mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
Removed redundant bezier_curve() function. Standardized formatting.
This commit is contained in:
parent
65b78f90ae
commit
5c7fc2eaf6
1 changed files with 447 additions and 360 deletions
807
rounding.scad
807
rounding.scad
|
@ -206,14 +206,14 @@ function round_corners(path, curve="circle", measure="cut", size=undef, k=0.5,
|
||||||
(curve=="circle" && measure=="radius") ||
|
(curve=="circle" && measure=="radius") ||
|
||||||
(curve=="smooth" && measure=="joint")
|
(curve=="smooth" && measure=="joint")
|
||||||
),
|
),
|
||||||
path = is_region(path) ?
|
path = is_region(path)?
|
||||||
assert(len(path)==1, "Region supplied as path does not have exactly one component")
|
assert(len(path)==1, "Region supplied as path does not have exactly one component")
|
||||||
path[0] : path,
|
path[0] : path,
|
||||||
pathdim = array_dim(path,1),
|
pathdim = array_dim(path,1),
|
||||||
have_size = size==undef ? 0 : 1,
|
have_size = size==undef ? 0 : 1,
|
||||||
pathsize_ok = is_num(pathdim) && pathdim >= 3-have_size && pathdim <= 4-have_size,
|
pathsize_ok = is_num(pathdim) && pathdim >= 3-have_size && pathdim <= 4-have_size,
|
||||||
size_ok = !have_size || is_num(size) ||
|
size_ok = !have_size || is_num(size) ||
|
||||||
is_list(size) && ((len(size)==2 && curve=="smooth") || len(size)==len(path))
|
is_list(size) && ((len(size)==2 && curve=="smooth") || len(size)==len(path))
|
||||||
)
|
)
|
||||||
assert(curve=="smooth" || curve=="circle", "Unknown 'curve' setting in round_corners")
|
assert(curve=="smooth" || curve=="circle", "Unknown 'curve' setting in round_corners")
|
||||||
assert(measureok, curve=="circle"?
|
assert(measureok, curve=="circle"?
|
||||||
|
@ -226,17 +226,22 @@ function round_corners(path, curve="circle", measure="cut", size=undef, k=0.5,
|
||||||
2+have_size, " or ", 3+have_size,
|
2+have_size, " or ", 3+have_size,
|
||||||
have_size ? " when 'size' is specified" : "when 'all' is not specified"
|
have_size ? " when 'size' is specified" : "when 'all' is not specified"
|
||||||
))
|
))
|
||||||
assert(len(path)>2,str("Path has length ",len(path),". Length must be 3 or more."))
|
assert(len(path)>2,str("Path has length ",len(path),". Length must be 3 or more."))
|
||||||
assert(size_ok, is_list(size)?
|
assert(size_ok,
|
||||||
(str("Input `size` has length ", len(size),". Length must be ",
|
is_list(size)? (
|
||||||
(curve=="smooth"?"2 or ":""), len(path))) :
|
str(
|
||||||
str("Input `size` is ",size," which is not a number"))
|
"Input `size` has length ", len(size),
|
||||||
|
". Length must be ",
|
||||||
|
(curve=="smooth"?"2 or ":""), len(path)
|
||||||
|
)
|
||||||
|
) : str("Input `size` is ",size," which is not a number")
|
||||||
|
)
|
||||||
let(
|
let(
|
||||||
dim = pathdim - 1 + have_size,
|
dim = pathdim - 1 + have_size,
|
||||||
points = have_size ? path : subindex(path, [0:dim-1]),
|
points = have_size ? path : subindex(path, [0:dim-1]),
|
||||||
parm = have_size && is_list(size) && len(size)>2 ? size :
|
parm = have_size && is_list(size) && len(size)>2? size :
|
||||||
have_size ? replist(size, len(path)) :
|
have_size? replist(size, len(path)) :
|
||||||
subindex(path, dim),
|
subindex(path, dim),
|
||||||
// dk will be a list of parameters, for the "smooth" curve the distance and curvature parameter pair,
|
// dk will be a list of parameters, for the "smooth" curve the distance and curvature parameter pair,
|
||||||
// and for the "circle" curve, distance and radius.
|
// and for the "circle" curve, distance and radius.
|
||||||
dk = [
|
dk = [
|
||||||
|
@ -279,14 +284,13 @@ function round_corners(path, curve="circle", measure="cut", size=undef, k=0.5,
|
||||||
//
|
//
|
||||||
// k is the curvature parameter, ranging from 0 for very slow transition
|
// k is the curvature parameter, ranging from 0 for very slow transition
|
||||||
// up to 1 for a sharp transition that doesn't have continuous curvature any more
|
// up to 1 for a sharp transition that doesn't have continuous curvature any more
|
||||||
function _smooth_bez_fill(points,k) =
|
function _smooth_bez_fill(points,k) = [
|
||||||
[
|
points[0],
|
||||||
points[0],
|
lerp(points[1],points[0],k),
|
||||||
lerp(points[1],points[0],k),
|
points[1],
|
||||||
points[1],
|
lerp(points[1],points[2],k),
|
||||||
lerp(points[1],points[2],k),
|
points[2],
|
||||||
points[2],
|
];
|
||||||
];
|
|
||||||
|
|
||||||
// Computes the points of a continuous curvature roundover given as input
|
// Computes the points of a continuous curvature roundover given as input
|
||||||
// the list of 3 points defining the corner and a parameter specification
|
// the list of 3 points defining the corner and a parameter specification
|
||||||
|
@ -301,22 +305,24 @@ function _smooth_bez_fill(points,k) =
|
||||||
// to calculate the point count.
|
// to calculate the point count.
|
||||||
|
|
||||||
function _bezcorner(points, parm) =
|
function _bezcorner(points, parm) =
|
||||||
let(
|
let(
|
||||||
P = is_list(parm) ?
|
P = is_list(parm)? (
|
||||||
let(
|
let(
|
||||||
d = parm[0],
|
d = parm[0],
|
||||||
k = parm[1],
|
k = parm[1],
|
||||||
prev = normalize(points[0]-points[1]),
|
prev = normalize(points[0]-points[1]),
|
||||||
next = normalize(points[2]-points[1]))
|
next = normalize(points[2]-points[1])
|
||||||
[
|
) [
|
||||||
points[1]+d*prev,
|
points[1]+d*prev,
|
||||||
points[1]+k*d*prev,
|
points[1]+k*d*prev,
|
||||||
points[1],
|
points[1],
|
||||||
points[1]+k*d*next,
|
points[1]+k*d*next,
|
||||||
points[1]+d*next
|
points[1]+d*next
|
||||||
] :
|
]
|
||||||
|
) : (
|
||||||
_smooth_bez_fill(points,parm),
|
_smooth_bez_fill(points,parm),
|
||||||
N = max(3,$fn>0 ?$fn : ceil(bezier_segment_length(P)/$fs))
|
N = max(3,$fn>0 ?$fn : ceil(bezier_segment_length(P)/$fs))
|
||||||
|
)
|
||||||
)
|
)
|
||||||
bezier_curve(P,N);
|
bezier_curve(P,N);
|
||||||
|
|
||||||
|
@ -329,14 +335,11 @@ function _circlecorner(points, parm) =
|
||||||
prev = normalize(points[0]-points[1]),
|
prev = normalize(points[0]-points[1]),
|
||||||
next = normalize(points[2]-points[1]),
|
next = normalize(points[2]-points[1]),
|
||||||
center = r/sin(angle) * normalize(prev+next)+points[1],
|
center = r/sin(angle) * normalize(prev+next)+points[1],
|
||||||
start = points[1]+prev*d,
|
start = points[1]+prev*d,
|
||||||
end = points[1]+next*d
|
end = points[1]+next*d
|
||||||
)
|
)
|
||||||
arc(segs(norm(start-center)), cp=center, points=[start,end]);
|
arc(segs(norm(start-center)), cp=center, points=[start,end]);
|
||||||
|
|
||||||
function bezier_curve(P,N) =
|
|
||||||
[for(i=[0:1:N-1]) bez_point(P, i/(N-1))];
|
|
||||||
|
|
||||||
|
|
||||||
// Module: rounded_sweep()
|
// Module: rounded_sweep()
|
||||||
//
|
//
|
||||||
|
@ -518,217 +521,260 @@ function bezier_curve(P,N) =
|
||||||
// up(1)
|
// up(1)
|
||||||
// rounded_sweep(offset(rhex,r=1), height=9.5, bottom=rs_circle(r=2), top=rs_teardrop(r=-4));
|
// rounded_sweep(offset(rhex,r=1), height=9.5, bottom=rs_circle(r=2), top=rs_teardrop(r=-4));
|
||||||
// }
|
// }
|
||||||
module rounded_sweep(path, height, top=[], bottom=[], offset="round", r=0, steps=16, quality=1, check_valid=true, offset_maxstep=1, extra=0,
|
module rounded_sweep(
|
||||||
cut=undef, width=undef, joint=undef, k=0.75, angle=45, convexity=10)
|
path, height,
|
||||||
{
|
top=[], bottom=[],
|
||||||
// This function does the actual work of repeatedly calling offset() and concatenating the resulting face and vertex lists to produce
|
offset="round", r=0, steps=16,
|
||||||
// the inputs for the polyhedron module.
|
quality=1, check_valid=true,
|
||||||
function make_polyhedron(path,offsets, offset_type, flip_faces, quality, check_valid, maxstep, offsetind=0, vertexcount=0, vertices=[], faces=[] )=
|
offset_maxstep=1, extra=0,
|
||||||
offsetind==len(offsets) ?
|
cut=undef, width=undef,
|
||||||
let( bottom = list_range(n=len(path),s=vertexcount),
|
joint=undef, k=0.75, angle=45,
|
||||||
oriented_bottom = !flip_faces ? bottom : reverse(bottom)
|
convexity=10
|
||||||
)
|
) {
|
||||||
[vertices, concat(faces,[oriented_bottom])] :
|
// This function does the actual work of repeatedly calling offset() and concatenating the resulting face and vertex lists to produce
|
||||||
let( this_offset = offsetind==0 ? offsets[0][0] : offsets[offsetind][0] - offsets[offsetind-1][0],
|
// the inputs for the polyhedron module.
|
||||||
delta = offset_type=="delta" ? this_offset : undef,
|
function make_polyhedron(path,offsets, offset_type, flip_faces, quality, check_valid, maxstep, offsetind=0, vertexcount=0, vertices=[], faces=[] )=
|
||||||
r = offset_type=="round" ? this_offset : undef
|
offsetind==len(offsets)? (
|
||||||
)
|
let(
|
||||||
assert(num_defined([r,delta])==1,"Must set `offset` to \"round\" or \"delta")
|
bottom = list_range(n=len(path),s=vertexcount),
|
||||||
let(
|
oriented_bottom = !flip_faces? bottom : reverse(bottom)
|
||||||
vertices_faces = offset(path, r=r, delta=delta, closed=true, check_valid=check_valid, quality=quality, maxstep=maxstep,
|
) [vertices, concat(faces,[oriented_bottom])]
|
||||||
return_faces=true, firstface_index=vertexcount, flip_faces=flip_faces)
|
) : (
|
||||||
)
|
let(
|
||||||
make_polyhedron(vertices_faces[0], offsets, offset_type, flip_faces, quality, check_valid, maxstep, offsetind+1, vertexcount+len(path),
|
this_offset = offsetind==0? offsets[0][0] : offsets[offsetind][0] - offsets[offsetind-1][0],
|
||||||
vertices=concat(vertices, zip(vertices_faces[0],replist(offsets[offsetind][1],len(vertices_faces[0])))),
|
delta = offset_type=="delta"? this_offset : undef,
|
||||||
faces=concat(faces, vertices_faces[1]));
|
r = offset_type=="round"? this_offset : undef
|
||||||
|
)
|
||||||
|
assert(num_defined([r,delta])==1,"Must set `offset` to \"round\" or \"delta")
|
||||||
|
let(
|
||||||
|
vertices_faces = offset(
|
||||||
|
path, r=r, delta=delta, closed=true,
|
||||||
|
check_valid=check_valid, quality=quality,
|
||||||
|
maxstep=maxstep, return_faces=true,
|
||||||
|
firstface_index=vertexcount,
|
||||||
|
flip_faces=flip_faces
|
||||||
|
)
|
||||||
|
)
|
||||||
|
make_polyhedron(
|
||||||
|
vertices_faces[0], offsets, offset_type,
|
||||||
|
flip_faces, quality, check_valid, maxstep,
|
||||||
|
offsetind+1, vertexcount+len(path),
|
||||||
|
vertices=concat(
|
||||||
|
vertices,
|
||||||
|
zip(vertices_faces[0],replist(offsets[offsetind][1],len(vertices_faces[0])))
|
||||||
|
),
|
||||||
|
faces=concat(faces, vertices_faces[1])
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// Produce edge profile curve from the edge specification
|
// Produce edge profile curve from the edge specification
|
||||||
// z_dir is the direction multiplier (1 to build up, -1 to build down)
|
// z_dir is the direction multiplier (1 to build up, -1 to build down)
|
||||||
function rounding_offsets(edgespec,z_dir=1) =
|
function rounding_offsets(edgespec,z_dir=1) =
|
||||||
let(
|
let(
|
||||||
edgetype = struct_val(edgespec, "type"),
|
edgetype = struct_val(edgespec, "type"),
|
||||||
extra = struct_val(edgespec,"extra"),
|
extra = struct_val(edgespec,"extra"),
|
||||||
N = struct_val(edgespec, "steps"),
|
N = struct_val(edgespec, "steps"),
|
||||||
r = struct_val(edgespec,"r"),
|
r = struct_val(edgespec,"r"),
|
||||||
cut = struct_val(edgespec,"cut"),
|
cut = struct_val(edgespec,"cut"),
|
||||||
k = struct_val(edgespec,"k"),
|
k = struct_val(edgespec,"k"),
|
||||||
radius = in_list(edgetype,["circle","teardrop"]) ?
|
radius = in_list(edgetype,["circle","teardrop"])?
|
||||||
first_defined([cut/(sqrt(2)-1),r]) :
|
first_defined([cut/(sqrt(2)-1),r]) :
|
||||||
edgetype=="chamfer" ? first_defined([sqrt(2)*cut,r]) :
|
edgetype=="chamfer"? first_defined([sqrt(2)*cut,r]) : undef,
|
||||||
undef,
|
chamf_angle = struct_val(edgespec, "angle"),
|
||||||
chamf_angle = struct_val(edgespec, "angle"),
|
cheight = struct_val(edgespec, "height"),
|
||||||
cheight = struct_val(edgespec, "height"),
|
cwidth = struct_val(edgespec, "width"),
|
||||||
cwidth = struct_val(edgespec, "width"),
|
chamf_width = first_defined([cut/cos(chamf_angle), cwidth, cheight*tan(chamf_angle)]),
|
||||||
chamf_width = first_defined([cut/cos(chamf_angle), cwidth, cheight*tan(chamf_angle)]),
|
chamf_height = first_defined([cut/sin(chamf_angle),cheight, cwidth/tan(chamf_angle)]),
|
||||||
chamf_height = first_defined([cut/sin(chamf_angle),cheight, cwidth/tan(chamf_angle)]),
|
joint = first_defined([
|
||||||
joint = first_defined([struct_val(edgespec,"joint"),
|
struct_val(edgespec,"joint"),
|
||||||
16*cut/sqrt(2)/(1+4*k)]),
|
16*cut/sqrt(2)/(1+4*k)
|
||||||
points = struct_val(edgespec, "points"),
|
]),
|
||||||
argsOK = in_list(edgetype,["circle","teardrop"]) ? is_def(radius) :
|
points = struct_val(edgespec, "points"),
|
||||||
edgetype == "chamfer" ? angle>0 && angle<90 && num_defined([chamf_height,chamf_width])==2 :
|
argsOK = in_list(edgetype,["circle","teardrop"])? is_def(radius) :
|
||||||
edgetype == "smooth" ? num_defined([k,joint])==2 :
|
edgetype == "chamfer"? angle>0 && angle<90 && num_defined([chamf_height,chamf_width])==2 :
|
||||||
edgetype == "custom" ? points[0]==[0,0] :
|
edgetype == "smooth"? num_defined([k,joint])==2 :
|
||||||
false)
|
edgetype == "custom"? points[0]==[0,0] :
|
||||||
assert(argsOK,str("Invalid specification with type ",edgetype))
|
false
|
||||||
let(
|
)
|
||||||
offsets = edgetype == "custom" ? scale([-1,z_dir], slice(points,1,-1)) :
|
assert(argsOK,str("Invalid specification with type ",edgetype))
|
||||||
edgetype == "chamfer" ? width==0 && height==0 ? [] : [[-chamf_width,z_dir*abs(chamf_height)]] :
|
let(
|
||||||
edgetype == "teardrop" ? radius==0 ? [] : concat([for(i=[1:N]) [radius*(cos(i*45/N)-1),z_dir*abs(radius)* sin(i*45/N)]],
|
offsets =
|
||||||
[[-2*radius*(1-sqrt(2)/2), z_dir*abs(radius)]]):
|
edgetype == "custom"? scale([-1,z_dir], slice(points,1,-1)) :
|
||||||
edgetype == "circle" ? radius==0 ? [] : [for(i=[1:N]) [radius*(cos(i*90/N)-1), z_dir*abs(radius)*sin(i*90/N)]] :
|
edgetype == "chamfer"? width==0 && height==0? [] : [[-chamf_width,z_dir*abs(chamf_height)]] :
|
||||||
/* smooth */ joint==0 ? [] :
|
edgetype == "teardrop"? (
|
||||||
select(
|
radius==0? [] : concat(
|
||||||
_bezcorner([[0,0],[0,z_dir*abs(joint)],[-joint,z_dir*abs(joint)]], k, $fn=N+2),
|
[for(i=[1:N]) [radius*(cos(i*45/N)-1),z_dir*abs(radius)* sin(i*45/N)]],
|
||||||
1, -1)
|
[[-2*radius*(1-sqrt(2)/2), z_dir*abs(radius)]]
|
||||||
)
|
)
|
||||||
extra > 0 ? concat(offsets, [select(offsets,-1)+[0,z_dir*extra]]) : offsets;
|
) :
|
||||||
|
edgetype == "circle"? radius==0? [] : [for(i=[1:N]) [radius*(cos(i*90/N)-1), z_dir*abs(radius)*sin(i*90/N)]] :
|
||||||
|
/* smooth */ joint==0 ? [] :
|
||||||
|
select(
|
||||||
|
_bezcorner([[0,0],[0,z_dir*abs(joint)],[-joint,z_dir*abs(joint)]], k, $fn=N+2),
|
||||||
|
1, -1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
extra > 0? concat(offsets, [select(offsets,-1)+[0,z_dir*extra]]) : offsets;
|
||||||
|
|
||||||
|
argspec = [
|
||||||
argspec = [["r",r],
|
["r",r],
|
||||||
["extra",extra],
|
["extra",extra],
|
||||||
["type","circle"],
|
["type","circle"],
|
||||||
["check_valid",check_valid],
|
["check_valid",check_valid],
|
||||||
["quality",quality],
|
["quality",quality],
|
||||||
["offset_maxstep", offset_maxstep],
|
["offset_maxstep", offset_maxstep],
|
||||||
["steps",steps],
|
["steps",steps],
|
||||||
["offset",offset],
|
["offset",offset],
|
||||||
["width",width],
|
["width",width],
|
||||||
["height",undef],
|
["height",undef],
|
||||||
["angle",angle],
|
["angle",angle],
|
||||||
["cut",cut],
|
["cut",cut],
|
||||||
["joint",joint],
|
["joint",joint],
|
||||||
["k", k],
|
["k", k],
|
||||||
["points", []],
|
["points", []],
|
||||||
];
|
];
|
||||||
|
|
||||||
path = check_and_fix_path(path, [2], closed=true);
|
path = check_and_fix_path(path, [2], closed=true);
|
||||||
|
|
||||||
top = struct_set(argspec, top, grow=false);
|
|
||||||
bottom = struct_set(argspec, bottom, grow=false);
|
|
||||||
|
|
||||||
clockwise = polygon_clockwise(path);
|
top = struct_set(argspec, top, grow=false);
|
||||||
|
bottom = struct_set(argspec, bottom, grow=false);
|
||||||
|
|
||||||
assert(height>=0, "Height must be nonnegative");
|
clockwise = polygon_clockwise(path);
|
||||||
|
|
||||||
/* This code does not work. It hits the error in make_polyhedron from offset being wrong
|
assert(height>=0, "Height must be nonnegative");
|
||||||
before this code executes. Had to move the test into make_polyhedron, which is ugly since it's in the loop
|
|
||||||
offsetsok = in_list(struct_val(top, "offset"),["round","delta"])
|
|
||||||
&& in_list(struct_val(bottom, "offset"),["round","delta"]);
|
|
||||||
assert(offsetsok,"Offsets must be one of \"round\" or \"delta\"");
|
|
||||||
*/
|
|
||||||
|
|
||||||
offsets_bot = rounding_offsets(bottom, -1);
|
|
||||||
offsets_top = rounding_offsets(top, 1);
|
|
||||||
|
|
||||||
// "Extra" height enlarges the result beyond the requested height, so subtract it
|
// This code does not work. It hits the error in make_polyhedron from offset being wrong
|
||||||
bottom_height = len(offsets_bot)==0 ? 0 : abs(select(offsets_bot,-1)[1]) - struct_val(bottom,"extra");
|
// before this code executes. Had to move the test into make_polyhedron, which is ugly since it's in the loop
|
||||||
top_height = len(offsets_top)==0 ? 0 : abs(select(offsets_top,-1)[1]) - struct_val(top,"extra");
|
//offsetsok = in_list(struct_val(top, "offset"),["round","delta"]) &&
|
||||||
|
// in_list(struct_val(bottom, "offset"),["round","delta"]);
|
||||||
|
//assert(offsetsok,"Offsets must be one of \"round\" or \"delta\"");
|
||||||
|
|
||||||
middle = height-bottom_height-top_height;
|
offsets_bot = rounding_offsets(bottom, -1);
|
||||||
assert(middle>=0,str("Specified end treatments (bottom height = ",bottom_height,
|
offsets_top = rounding_offsets(top, 1);
|
||||||
" top_height = ",top_height,") are too large for extrusion height (",height,")"));
|
|
||||||
initial_vertices_bot = path3d(path);
|
|
||||||
|
|
||||||
vertices_faces_bot = make_polyhedron(path, offsets_bot, struct_val(bottom,"offset"), clockwise,
|
// "Extra" height enlarges the result beyond the requested height, so subtract it
|
||||||
struct_val(bottom,"quality"),struct_val(bottom,"check_valid"),struct_val(bottom,"offset_maxstep"),
|
bottom_height = len(offsets_bot)==0 ? 0 : abs(select(offsets_bot,-1)[1]) - struct_val(bottom,"extra");
|
||||||
vertices=initial_vertices_bot);
|
top_height = len(offsets_top)==0 ? 0 : abs(select(offsets_top,-1)[1]) - struct_val(top,"extra");
|
||||||
|
|
||||||
top_start_ind = len(vertices_faces_bot[0]);
|
middle = height-bottom_height-top_height;
|
||||||
initial_vertices_top = zip(path, replist(middle,len(path)));
|
assert(
|
||||||
vertices_faces_top = make_polyhedron(path, translate_points(offsets_top,[0,middle]), struct_val(top,"offset"), !clockwise,
|
middle>=0, str(
|
||||||
struct_val(top,"quality"),struct_val(top,"check_valid"),struct_val(top,"offset_maxstep"),
|
"Specified end treatments (bottom height = ",bottom_height,
|
||||||
vertexcount=top_start_ind, vertices=initial_vertices_top);
|
" top_height = ",top_height,") are too large for extrusion height (",height,")"
|
||||||
middle_faces = middle==0 ? [] :
|
)
|
||||||
[for(i=[0:len(path)-1]) let(oneface=[i, (i+1)%len(path), top_start_ind+(i+1)%len(path), top_start_ind+i])
|
);
|
||||||
!clockwise ? reverse(oneface) : oneface];
|
initial_vertices_bot = path3d(path);
|
||||||
up(bottom_height)
|
|
||||||
polyhedron(concat(vertices_faces_bot[0],vertices_faces_top[0]),
|
vertices_faces_bot = make_polyhedron(
|
||||||
faces=concat(vertices_faces_bot[1], vertices_faces_top[1], middle_faces),convexity=convexity);
|
path, offsets_bot, struct_val(bottom,"offset"), clockwise,
|
||||||
|
struct_val(bottom,"quality"),
|
||||||
|
struct_val(bottom,"check_valid"),
|
||||||
|
struct_val(bottom,"offset_maxstep"),
|
||||||
|
vertices=initial_vertices_bot
|
||||||
|
);
|
||||||
|
|
||||||
|
top_start_ind = len(vertices_faces_bot[0]);
|
||||||
|
initial_vertices_top = zip(path, replist(middle,len(path)));
|
||||||
|
vertices_faces_top = make_polyhedron(
|
||||||
|
path, translate_points(offsets_top,[0,middle]),
|
||||||
|
struct_val(top,"offset"), !clockwise,
|
||||||
|
struct_val(top,"quality"),
|
||||||
|
struct_val(top,"check_valid"),
|
||||||
|
struct_val(top,"offset_maxstep"),
|
||||||
|
vertexcount=top_start_ind,
|
||||||
|
vertices=initial_vertices_top
|
||||||
|
);
|
||||||
|
middle_faces = middle==0 ? [] : [
|
||||||
|
for(i=[0:len(path)-1]) let(
|
||||||
|
oneface=[i, (i+1)%len(path), top_start_ind+(i+1)%len(path), top_start_ind+i]
|
||||||
|
) !clockwise ? reverse(oneface) : oneface
|
||||||
|
];
|
||||||
|
up(bottom_height) {
|
||||||
|
polyhedron(
|
||||||
|
concat(vertices_faces_bot[0],vertices_faces_top[0]),
|
||||||
|
faces=concat(vertices_faces_bot[1], vertices_faces_top[1], middle_faces),
|
||||||
|
convexity=convexity
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function rs_circle(r,cut,extra,check_valid, quality,steps, offset_maxstep, offset) =
|
function rs_circle(r,cut,extra,check_valid, quality,steps, offset_maxstep, offset) =
|
||||||
assert(num_defined([r,cut])==1, "Must define exactly one of `r` and `cut`")
|
assert(num_defined([r,cut])==1, "Must define exactly one of `r` and `cut`")
|
||||||
_remove_undefined_vals(
|
_remove_undefined_vals([
|
||||||
[
|
"type", "circle",
|
||||||
"type", "circle",
|
"r",r,
|
||||||
"r",r,
|
"cut",cut,
|
||||||
"cut",cut,
|
"extra",extra,
|
||||||
"extra",extra,
|
"check_valid",check_valid,
|
||||||
"check_valid",check_valid,
|
"quality", quality,
|
||||||
"quality", quality,
|
"steps", steps,
|
||||||
"steps", steps,
|
"offset_maxstep", offset_maxstep,
|
||||||
"offset_maxstep", offset_maxstep,
|
"offset", offset
|
||||||
"offset", offset
|
]);
|
||||||
]);
|
|
||||||
|
|
||||||
function rs_teardrop(r,cut,extra,check_valid, quality,steps, offset_maxstep, offset) =
|
function rs_teardrop(r,cut,extra,check_valid, quality,steps, offset_maxstep, offset) =
|
||||||
assert(num_defined([r,cut])==1, "Must define exactly one of `r` and `cut`")
|
assert(num_defined([r,cut])==1, "Must define exactly one of `r` and `cut`")
|
||||||
_remove_undefined_vals(
|
_remove_undefined_vals([
|
||||||
[
|
"type", "teardrop",
|
||||||
"type", "teardrop",
|
"r",r,
|
||||||
"r",r,
|
"cut",cut,
|
||||||
"cut",cut,
|
"extra",extra,
|
||||||
"extra",extra,
|
"check_valid",check_valid,
|
||||||
"check_valid",check_valid,
|
"quality", quality,
|
||||||
"quality", quality,
|
"steps", steps,
|
||||||
"steps", steps,
|
"offset_maxstep", offset_maxstep,
|
||||||
"offset_maxstep", offset_maxstep,
|
"offset", offset
|
||||||
"offset", offset
|
]);
|
||||||
]);
|
|
||||||
|
|
||||||
function rs_chamfer(height, width, cut, angle, extra,check_valid, quality,steps, offset_maxstep, offset) =
|
function rs_chamfer(height, width, cut, angle, extra,check_valid, quality,steps, offset_maxstep, offset) =
|
||||||
let(ok = (is_def(cut) && num_defined([height,width])==0) || num_defined([height,width])>0)
|
let(ok = (is_def(cut) && num_defined([height,width])==0) || num_defined([height,width])>0)
|
||||||
assert(ok, "Must define `cut`, or one or both of `width` and `height`")
|
assert(ok, "Must define `cut`, or one or both of `width` and `height`")
|
||||||
_remove_undefined_vals(
|
_remove_undefined_vals([
|
||||||
[
|
"type", "chamfer",
|
||||||
"type", "chamfer",
|
"width",width,
|
||||||
"width",width,
|
"height",height,
|
||||||
"height",height,
|
"cut",cut,
|
||||||
"cut",cut,
|
"angle",angle,
|
||||||
"angle",angle,
|
"extra",extra,
|
||||||
"extra",extra,
|
"check_valid",check_valid,
|
||||||
"check_valid",check_valid,
|
"quality", quality,
|
||||||
"quality", quality,
|
"steps", steps,
|
||||||
"steps", steps,
|
"offset_maxstep", offset_maxstep,
|
||||||
"offset_maxstep", offset_maxstep,
|
"offset", offset
|
||||||
"offset", offset
|
]);
|
||||||
]);
|
|
||||||
|
|
||||||
function rs_smooth(cut, joint, k, extra,check_valid, quality,steps, offset_maxstep, offset) =
|
function rs_smooth(cut, joint, k, extra,check_valid, quality,steps, offset_maxstep, offset) =
|
||||||
assert(num_defined([joint,cut])==1, "Must define exactly one of `joint` and `cut`")
|
assert(num_defined([joint,cut])==1, "Must define exactly one of `joint` and `cut`")
|
||||||
_remove_undefined_vals(
|
_remove_undefined_vals([
|
||||||
[
|
"type", "smooth",
|
||||||
"type", "smooth",
|
"joint",joint,
|
||||||
"joint",joint,
|
"k",k,
|
||||||
"k",k,
|
"cut",cut,
|
||||||
"cut",cut,
|
"extra",extra,
|
||||||
"extra",extra,
|
"check_valid",check_valid,
|
||||||
"check_valid",check_valid,
|
"quality", quality,
|
||||||
"quality", quality,
|
"steps", steps,
|
||||||
"steps", steps,
|
"offset_maxstep", offset_maxstep,
|
||||||
"offset_maxstep", offset_maxstep,
|
"offset", offset
|
||||||
"offset", offset
|
]);
|
||||||
]);
|
|
||||||
|
|
||||||
function rs_custom(points, extra,check_valid, quality,steps, offset_maxstep, offset) =
|
function rs_custom(points, extra,check_valid, quality,steps, offset_maxstep, offset) =
|
||||||
//assert(is_path(points),"Custom point list is not valid")
|
//assert(is_path(points),"Custom point list is not valid")
|
||||||
_remove_undefined_vals(
|
_remove_undefined_vals([
|
||||||
[
|
"type", "custom",
|
||||||
"type", "custom",
|
"points", points,
|
||||||
"points", points,
|
"extra",extra,
|
||||||
"extra",extra,
|
"check_valid",check_valid,
|
||||||
"check_valid",check_valid,
|
"quality", quality,
|
||||||
"quality", quality,
|
"steps", steps,
|
||||||
"steps", steps,
|
"offset_maxstep", offset_maxstep,
|
||||||
"offset_maxstep", offset_maxstep,
|
"offset", offset
|
||||||
"offset", offset
|
]);
|
||||||
]);
|
|
||||||
|
|
||||||
|
|
||||||
function _remove_undefined_vals(list) =
|
function _remove_undefined_vals(list) =
|
||||||
let(ind=search([undef],list,0)[0])
|
let(ind=search([undef],list,0)[0])
|
||||||
list_remove(list, concat(ind, add_scalar(ind,-1)));
|
list_remove(list, concat(ind, add_scalar(ind,-1)));
|
||||||
|
|
||||||
|
|
||||||
// Function&Module: offset_stroke()
|
// Function&Module: offset_stroke()
|
||||||
|
@ -868,163 +914,204 @@ function _remove_undefined_vals(list) =
|
||||||
// right(12)
|
// right(12)
|
||||||
// offset_stroke(path, width=1, closed=true);
|
// offset_stroke(path, width=1, closed=true);
|
||||||
function offset_stroke(path, width=1, rounded=true, start="flat", end="flat", check_valid=true, quality=1, maxstep=0.1, chamfer=false, closed=false) =
|
function offset_stroke(path, width=1, rounded=true, start="flat", end="flat", check_valid=true, quality=1, maxstep=0.1, chamfer=false, closed=false) =
|
||||||
let(closedok = !closed || (is_undef(start) && is_undef(end)))
|
let(closedok = !closed || (is_undef(start) && is_undef(end)))
|
||||||
assert(closedok, "Parameters `start` and `end` not allowed with closed path")
|
assert(closedok, "Parameters `start` and `end` not allowed with closed path")
|
||||||
let(
|
let(
|
||||||
start = closed ? [] : _parse_stroke_end(default(start,"flat")),
|
start = closed? [] : _parse_stroke_end(default(start,"flat")),
|
||||||
end = closed ? [] : _parse_stroke_end(default(end,"flat")),
|
end = closed? [] : _parse_stroke_end(default(end,"flat")),
|
||||||
width = is_list(width) ? reverse(sort(width)) : [1,-1]*width/2,
|
width = is_list(width)? reverse(sort(width)) : [1,-1]*width/2,
|
||||||
left_r = !rounded ? undef : width[0],
|
left_r = !rounded? undef : width[0],
|
||||||
left_delta = rounded ? undef : width[0],
|
left_delta = rounded? undef : width[0],
|
||||||
right_r = !rounded ? undef : width[1],
|
right_r = !rounded? undef : width[1],
|
||||||
right_delta = rounded ? undef : width[1],
|
right_delta = rounded? undef : width[1],
|
||||||
left_path = offset(path, delta=left_delta, r=left_r, closed=closed, check_valid=check_valid, quality=quality, chamfer=chamfer, maxstep=maxstep),
|
left_path = offset(
|
||||||
right_path = offset(path, delta=right_delta, r=right_r, closed=closed, check_valid=check_valid, quality=quality,chamfer=chamfer, maxstep=maxstep)
|
path, delta=left_delta, r=left_r, closed=closed,
|
||||||
)
|
check_valid=check_valid, quality=quality,
|
||||||
closed ? [left_path, right_path] :
|
chamfer=chamfer, maxstep=maxstep
|
||||||
let(
|
),
|
||||||
startpath = _stroke_end(width,left_path, right_path, start),
|
right_path = offset(
|
||||||
endpath = _stroke_end(reverse(width),reverse(right_path), reverse(left_path),end),
|
path, delta=right_delta, r=right_r, closed=closed,
|
||||||
clipping_ok = startpath[1]+endpath[2]<=len(left_path) && startpath[2]+endpath[1]<=len(right_path)
|
check_valid=check_valid, quality=quality,
|
||||||
)
|
chamfer=chamfer, maxstep=maxstep
|
||||||
assert(clipping_ok, "End treatment removed the whole stroke")
|
)
|
||||||
concat(slice(left_path,startpath[1],-1-endpath[2]), endpath[0],
|
)
|
||||||
reverse(slice(right_path,startpath[2],-1-endpath[1])),startpath[0]);
|
closed? [left_path, right_path] :
|
||||||
|
let(
|
||||||
|
startpath = _stroke_end(width,left_path, right_path, start),
|
||||||
|
endpath = _stroke_end(reverse(width),reverse(right_path), reverse(left_path),end),
|
||||||
|
clipping_ok = startpath[1]+endpath[2]<=len(left_path) && startpath[2]+endpath[1]<=len(right_path)
|
||||||
|
)
|
||||||
|
assert(clipping_ok, "End treatment removed the whole stroke")
|
||||||
|
concat(
|
||||||
|
slice(left_path,startpath[1],-1-endpath[2]),
|
||||||
|
endpath[0],
|
||||||
|
reverse(slice(right_path,startpath[2],-1-endpath[1])),
|
||||||
|
startpath[0]
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
function os_pointed(loc=0,dist) =
|
function os_pointed(loc=0,dist) =
|
||||||
assert(is_def(dist), "Must specify `dist`")
|
assert(is_def(dist), "Must specify `dist`")
|
||||||
[
|
[
|
||||||
"type", "shifted_point",
|
"type", "shifted_point",
|
||||||
"loc",loc,
|
"loc",loc,
|
||||||
"dist",dist
|
"dist",dist
|
||||||
];
|
];
|
||||||
|
|
||||||
function os_round(cut, angle, abs_angle, k) =
|
function os_round(cut, angle, abs_angle, k) =
|
||||||
let(
|
let(
|
||||||
acount = num_defined([angle,abs_angle]),
|
acount = num_defined([angle,abs_angle]),
|
||||||
use_angle = first_defined([angle,abs_angle,0])
|
use_angle = first_defined([angle,abs_angle,0])
|
||||||
)
|
)
|
||||||
assert(acount<2, "You must define only one of `angle` and `abs_angle`")
|
assert(acount<2, "You must define only one of `angle` and `abs_angle`")
|
||||||
assert(is_def(cut), "Parameter `cut` not defined.")
|
assert(is_def(cut), "Parameter `cut` not defined.")
|
||||||
[
|
[
|
||||||
"type", "roundover",
|
"type", "roundover",
|
||||||
"angle", use_angle,
|
"angle", use_angle,
|
||||||
"absolute", is_def(abs_angle),
|
"absolute", is_def(abs_angle),
|
||||||
"cut", is_vector(cut) ? point2d(cut) : [cut,cut],
|
"cut", is_vector(cut)? point2d(cut) : [cut,cut],
|
||||||
"k", first_defined([k, 0.75])
|
"k", first_defined([k, 0.75])
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
function os_flat(angle, abs_angle) =
|
function os_flat(angle, abs_angle) =
|
||||||
let( acount = num_defined([angle,abs_angle]),
|
let(
|
||||||
use_angle = first_defined([angle,abs_angle,0])
|
acount = num_defined([angle,abs_angle]),
|
||||||
)
|
use_angle = first_defined([angle,abs_angle,0])
|
||||||
assert(acount<2, "You must define only one of `angle` and `abs_angle`")
|
)
|
||||||
[
|
assert(acount<2, "You must define only one of `angle` and `abs_angle`")
|
||||||
"type", "flat",
|
[
|
||||||
"angle", use_angle,
|
"type", "flat",
|
||||||
"absolute", is_def(abs_angle)
|
"angle", use_angle,
|
||||||
];
|
"absolute", is_def(abs_angle)
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Return angle in (-90,90] required to map line1 onto line2 (lines specified as lists of two points)
|
// Return angle in (-90,90] required to map line1 onto line2 (lines specified as lists of two points)
|
||||||
function angle_between_lines(line1,line2) = let(angle = atan2(det2([line1,line2]),line1*line2))
|
function angle_between_lines(line1,line2) =
|
||||||
angle > 90 ? angle-180 :
|
let(angle = atan2(det2([line1,line2]),line1*line2))
|
||||||
angle <= -90 ? angle+180 :
|
angle > 90 ? angle-180 :
|
||||||
angle;
|
angle <= -90 ? angle+180 :
|
||||||
|
angle;
|
||||||
|
|
||||||
|
|
||||||
function _parse_stroke_end(spec) =
|
function _parse_stroke_end(spec) =
|
||||||
is_string(spec) ? assert(in_list(spec,["flat","round","pointed"]),str("Unknown end string specification \"", spec,"\". Must be \"flat\", \"round\", or \"pointed\""))
|
is_string(spec)?
|
||||||
[["type", spec]] :
|
assert(
|
||||||
struct_set([], spec);
|
in_list(spec,["flat","round","pointed"]),
|
||||||
|
str("Unknown end string specification \"", spec,"\". Must be \"flat\", \"round\", or \"pointed\"")
|
||||||
|
)
|
||||||
|
[["type", spec]] :
|
||||||
|
struct_set([], spec);
|
||||||
|
|
||||||
|
|
||||||
function _stroke_end(width,left, right, spec) =
|
function _stroke_end(width,left, right, spec) =
|
||||||
let(
|
let(
|
||||||
type = struct_val(spec, "type"),
|
type = struct_val(spec, "type"),
|
||||||
user_angle = default(struct_val(spec, "angle"), 0),
|
user_angle = default(struct_val(spec, "angle"), 0),
|
||||||
normal_seg = _normal_segment(right[0], left[0]),
|
normal_seg = _normal_segment(right[0], left[0]),
|
||||||
normal_pt = normal_seg[1],
|
normal_pt = normal_seg[1],
|
||||||
center = normal_seg[0],
|
center = normal_seg[0],
|
||||||
parallel_dir = normalize(left[0]-right[0]),
|
parallel_dir = normalize(left[0]-right[0]),
|
||||||
normal_dir = normalize(normal_seg[1]-normal_seg[0]),
|
normal_dir = normalize(normal_seg[1]-normal_seg[0]),
|
||||||
width_dir = sign(width[0]-width[1])
|
width_dir = sign(width[0]-width[1])
|
||||||
)
|
)
|
||||||
type == "round" ? [arc(points=[right[0],normal_pt,left[0]],N=50),1,1] :
|
type == "round"? [arc(points=[right[0],normal_pt,left[0]],N=50),1,1] :
|
||||||
type == "pointed" ? [[normal_pt],0,0] :
|
type == "pointed"? [[normal_pt],0,0] :
|
||||||
type == "shifted_point" ? let( shiftedcenter = center + width_dir * parallel_dir * struct_val(spec, "loc"))
|
type == "shifted_point"? (
|
||||||
[[shiftedcenter+normal_dir*struct_val(spec, "dist")],0,0] :
|
let(shiftedcenter = center + width_dir * parallel_dir * struct_val(spec, "loc"))
|
||||||
// Remaining types all support angled cutoff, so compute that
|
[[shiftedcenter+normal_dir*struct_val(spec, "dist")],0,0]
|
||||||
assert(abs(user_angle)<=90, "End angle must be in [-90,90]")
|
) :
|
||||||
let(
|
// Remaining types all support angled cutoff, so compute that
|
||||||
angle = struct_val(spec,"absolute") ? angle_between_lines(left[0]-right[0],[cos(user_angle),sin(user_angle)])
|
assert(abs(user_angle)<=90, "End angle must be in [-90,90]")
|
||||||
: user_angle,
|
let(
|
||||||
endseg = [center, rotate_points2d([left[0]],angle, cp=center)[0]],
|
angle = struct_val(spec,"absolute")?
|
||||||
intright = angle>0,
|
angle_between_lines(left[0]-right[0],[cos(user_angle),sin(user_angle)]) :
|
||||||
pathclip = _path_line_intersection(intright ? right : left, endseg),
|
user_angle,
|
||||||
pathextend = line_intersection(endseg, select(intright ? left:right,0,1))
|
endseg = [center, rotate_points2d([left[0]],angle, cp=center)[0]],
|
||||||
)
|
intright = angle>0,
|
||||||
type == "flat" ? (intright ? [[pathclip[0], pathextend], 1, pathclip[1]] :
|
pathclip = _path_line_intersection(intright? right : left, endseg),
|
||||||
[[pathextend, pathclip[0]], pathclip[1],1]) :
|
pathextend = line_intersection(endseg, select(intright? left:right,0,1))
|
||||||
type == "roundover" ?
|
)
|
||||||
let(
|
type == "flat"? (
|
||||||
bez_k = struct_val(spec,"k"),
|
intright?
|
||||||
cut = struct_val(spec,"cut"),
|
[[pathclip[0], pathextend], 1, pathclip[1]] :
|
||||||
cutleft = cut[0],
|
[[pathextend, pathclip[0]], pathclip[1],1]
|
||||||
cutright = cut[1],
|
) :
|
||||||
// Create updated paths taking into account clipping for end rotation
|
type == "roundover"? (
|
||||||
newright = intright ? concat([pathclip[0]],select(right,pathclip[1],-1)) :
|
let(
|
||||||
concat([pathextend],select(right,1,-1)),
|
bez_k = struct_val(spec,"k"),
|
||||||
newleft = !intright ? concat([pathclip[0]],select(left,pathclip[1],-1)) :
|
cut = struct_val(spec,"cut"),
|
||||||
concat([pathextend],select(left,1,-1)),
|
cutleft = cut[0],
|
||||||
// calculate corner angles, which are different when the cut is negative (outside corner)
|
cutright = cut[1],
|
||||||
leftangle = cutleft>=0 ? vector_angle([newleft[1],newleft[0],newright[0]])/2 :
|
// Create updated paths taking into account clipping for end rotation
|
||||||
90-vector_angle([newleft[1],newleft[0],newright[0]])/2,
|
newright = intright?
|
||||||
rightangle = cutright>=0 ? vector_angle([newright[1],newright[0],newleft[0]])/2 :
|
concat([pathclip[0]],select(right,pathclip[1],-1)) :
|
||||||
90-vector_angle([newright[1],newright[0],newleft[0]])/2,
|
concat([pathextend],select(right,1,-1)),
|
||||||
jointleft = 8*cutleft/cos(leftangle)/(1+4*bez_k),
|
newleft = !intright?
|
||||||
jointright = 8*cutright/cos(rightangle)/(1+4*bez_k),
|
concat([pathclip[0]],select(left,pathclip[1],-1)) :
|
||||||
pathcutleft = path_cut(newleft,abs(jointleft)),
|
concat([pathextend],select(left,1,-1)),
|
||||||
pathcutright = path_cut(newright,abs(jointright)),
|
// calculate corner angles, which are different when the cut is negative (outside corner)
|
||||||
leftdelete = intright ? pathcutleft[1] : pathcutleft[1] + pathclip[1] -1,
|
leftangle = cutleft>=0?
|
||||||
rightdelete = intright ? pathcutright[1] + pathclip[1] -1 : pathcutright[1],
|
vector_angle([newleft[1],newleft[0],newright[0]])/2 :
|
||||||
leftcorner = line_intersection([pathcutleft[0], newleft[pathcutleft[1]]], [newright[0],newleft[0]]),
|
90-vector_angle([newleft[1],newleft[0],newright[0]])/2,
|
||||||
rightcorner = line_intersection([pathcutright[0], newright[pathcutright[1]]], [newright[0],newleft[0]]),
|
rightangle = cutright>=0?
|
||||||
roundover_fits = jointleft+jointright < norm(rightcorner-leftcorner)
|
vector_angle([newright[1],newright[0],newleft[0]])/2 :
|
||||||
)
|
90-vector_angle([newright[1],newright[0],newleft[0]])/2,
|
||||||
assert(roundover_fits,"Roundover too large to fit")
|
jointleft = 8*cutleft/cos(leftangle)/(1+4*bez_k),
|
||||||
let(
|
jointright = 8*cutright/cos(rightangle)/(1+4*bez_k),
|
||||||
angled_dir = normalize(newleft[0]-newright[0]),
|
pathcutleft = path_cut(newleft,abs(jointleft)),
|
||||||
nPleft = [leftcorner - jointleft*angled_dir,
|
pathcutright = path_cut(newright,abs(jointright)),
|
||||||
leftcorner,
|
leftdelete = intright? pathcutleft[1] : pathcutleft[1] + pathclip[1] -1,
|
||||||
pathcutleft[0]],
|
rightdelete = intright? pathcutright[1] + pathclip[1] -1 : pathcutright[1],
|
||||||
nPright = [pathcutright[0],
|
leftcorner = line_intersection([pathcutleft[0], newleft[pathcutleft[1]]], [newright[0],newleft[0]]),
|
||||||
rightcorner,
|
rightcorner = line_intersection([pathcutright[0], newright[pathcutright[1]]], [newright[0],newleft[0]]),
|
||||||
rightcorner + jointright*angled_dir],
|
roundover_fits = jointleft+jointright < norm(rightcorner-leftcorner)
|
||||||
leftcurve = _bezcorner(nPleft, bez_k),
|
)
|
||||||
rightcurve = _bezcorner(nPright, bez_k)
|
assert(roundover_fits,"Roundover too large to fit")
|
||||||
)
|
let(
|
||||||
[concat(rightcurve, leftcurve), leftdelete, rightdelete] :
|
angled_dir = normalize(newleft[0]-newright[0]),
|
||||||
[[],0,0]; // This case shouldn't occur
|
nPleft = [
|
||||||
|
leftcorner - jointleft*angled_dir,
|
||||||
|
leftcorner,
|
||||||
|
pathcutleft[0]
|
||||||
|
],
|
||||||
|
nPright = [
|
||||||
|
pathcutright[0],
|
||||||
|
rightcorner,
|
||||||
|
rightcorner + jointright*angled_dir
|
||||||
|
],
|
||||||
|
leftcurve = _bezcorner(nPleft, bez_k),
|
||||||
|
rightcurve = _bezcorner(nPright, bez_k)
|
||||||
|
)
|
||||||
|
[concat(rightcurve, leftcurve), leftdelete, rightdelete]
|
||||||
|
) : [[],0,0]; // This case shouldn't occur
|
||||||
|
|
||||||
// returns [intersection_pt, index of first point in path after the intersection]
|
// returns [intersection_pt, index of first point in path after the intersection]
|
||||||
function _path_line_intersection(path, line, ind=0) =
|
function _path_line_intersection(path, line, ind=0) =
|
||||||
ind==len(path)-1 ? undef :
|
ind==len(path)-1 ? undef :
|
||||||
let(intersect=line_segment_intersection(line, select(path,ind,ind+1)))
|
let(intersect=line_segment_intersection(line, select(path,ind,ind+1)))
|
||||||
// If it intersects the segment excluding it's final point, then we're done
|
// If it intersects the segment excluding it's final point, then we're done
|
||||||
// The final point is treated as part of the next segment
|
// The final point is treated as part of the next segment
|
||||||
is_def(intersect) && intersect != path[ind+1] ? [intersect, ind+1] :
|
is_def(intersect) && intersect != path[ind+1]?
|
||||||
_path_line_intersection(path, line, ind+1);
|
[intersect, ind+1] :
|
||||||
|
_path_line_intersection(path, line, ind+1);
|
||||||
|
|
||||||
module offset_stroke(path, width=1, rounded=true, start, end, check_valid=true, quality=1, maxstep=0.1, chamfer=false, closed=false)
|
module offset_stroke(path, width=1, rounded=true, start, end, check_valid=true, quality=1, maxstep=0.1, chamfer=false, closed=false)
|
||||||
{
|
{
|
||||||
result = offset_stroke(path, width=width, rounded=rounded, start=start, end=end, check_valid=check_valid, quality=quality,
|
result = offset_stroke(
|
||||||
maxstep=maxstep, chamfer=chamfer, closed=closed);
|
path, width=width, rounded=rounded,
|
||||||
if (closed) region(result);
|
start=start, end=end,
|
||||||
else polygon(result);
|
check_valid=check_valid, quality=quality,
|
||||||
|
maxstep=maxstep, chamfer=chamfer,
|
||||||
|
closed=closed
|
||||||
|
);
|
||||||
|
if (closed) {
|
||||||
|
region(result);
|
||||||
|
} else {
|
||||||
|
polygon(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue