mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 00:09:41 +00:00
Changed offset() to check polygon orientation when closed==true.
Changed rounded_sweep() to work with updated offset(). Added make_path_valid() and use it in rounded_sweep for better error handling and to support single component regions. Added divided box example to rounded_sweep. Updated examples to work with updated offset().
This commit is contained in:
parent
5abc7db530
commit
20eae2b5d2
2 changed files with 85 additions and 52 deletions
|
@ -702,6 +702,27 @@ function is_region(x) = is_list(x) && is_path(x.x);
|
|||
// Closes all paths within a given region.
|
||||
function close_region(region, eps=EPSILON) = [for (path=region) close_path(path, eps=eps)];
|
||||
|
||||
// Function: make_path_valid()
|
||||
// Usage:
|
||||
// make_path_valid(path, [valid_dim], [closed])
|
||||
// Description:
|
||||
// Checks that the input is a path. If it is a region with one component, converts it to a path.
|
||||
// valid_dim specfies the allowed dimension of the points in the path.
|
||||
// If the path is closed, removed duplicate endpoint if present.
|
||||
function make_path_valid(path,valid_dim=undef,closed=false) =
|
||||
let( path = is_region(path) ?
|
||||
assert(len(path)==1,"Region supplied as path does not have exactly one component")
|
||||
path[0] :
|
||||
assert(is_path(path), "Input is not a path") path,
|
||||
dim = array_dim(path))
|
||||
assert(dim[0]>1,"Path must have at least 2 points")
|
||||
assert(len(dim)==2,"Invalid path: path is either a list of scalars or a list of matrices")
|
||||
assert(is_def(dim[1]), "Invalid path: entries in the path have variable length")
|
||||
let(valid=is_undef(valid_dim) || in_list(dim[1],valid_dim))
|
||||
assert(valid, str("The points on the path have length ",dim[1]," but length must be ",
|
||||
len(valid_dim)==1? valid_dim[0] : str("one of ",valid_dim)))
|
||||
closed && approx(path[0],select(path,-1)) ? slice(path,0,-2) : path;
|
||||
|
||||
|
||||
// Function: region_path_crossings()
|
||||
// Usage:
|
||||
|
@ -908,6 +929,18 @@ function _offset_region(
|
|||
// Example(2D):
|
||||
// star = star(5, r=100, ir=30);
|
||||
// #stroke(closed=true, star);
|
||||
// stroke(closed=true, offset(star, delta=10, closed=true));
|
||||
// Example(2D):
|
||||
// star = star(5, r=100, ir=30);
|
||||
// #stroke(closed=true, star);
|
||||
// stroke(closed=true, offset(star, delta=10, chamfer=true, closed=true));
|
||||
// Example(2D):
|
||||
// star = star(5, r=100, ir=30);
|
||||
// #stroke(closed=true, star);
|
||||
// stroke(closed=true, offset(star, r=10, closed=true));
|
||||
// Example(2D):
|
||||
// star = star(5, r=100, ir=30);
|
||||
// #stroke(closed=true, star);
|
||||
// stroke(closed=true, offset(star, delta=-10, closed=true));
|
||||
// Example(2D):
|
||||
// star = star(5, r=100, ir=30);
|
||||
|
@ -917,46 +950,34 @@ function _offset_region(
|
|||
// star = star(5, r=100, ir=30);
|
||||
// #stroke(closed=true, star);
|
||||
// stroke(closed=true, offset(star, r=-10, closed=true));
|
||||
// Example(2D):
|
||||
// star = star(5, r=100, ir=30);
|
||||
// #stroke(closed=true, star);
|
||||
// stroke(closed=true, offset(star, delta=10, closed=true));
|
||||
// Example(2D):
|
||||
// star = star(5, r=100, ir=30);
|
||||
// #stroke(closed=true, star);
|
||||
// stroke(closed=true, offset(star, delta=-10, chamfer=true, closed=true));
|
||||
// Example(2D):
|
||||
// star = star(5, r=100, ir=30);
|
||||
// #stroke(closed=true, star);
|
||||
// stroke(closed=true, offset(star, r=10, closed=true));
|
||||
// Example(2D): This case needs `quality=2` for success
|
||||
// test = [[0,0],[10,0],[10,7],[0,7], [-1,-3]];
|
||||
// polygon(offset(test,r=1.9, closed=true, quality=2));
|
||||
// //polygon(offset(test,r=1.9, closed=true, quality=1)); // Fails with erroneous 180 deg path error
|
||||
// polygon(offset(test,r=-1.9, closed=true, quality=2));
|
||||
// //polygon(offset(test,r=-1.9, closed=true, quality=1)); // Fails with erroneous 180 deg path error
|
||||
// %down(.1)polygon(test);
|
||||
// Example(2D): This case fails if `check_valid=true` when delta is large enough because segments are too close to the opposite side of the curve.
|
||||
// star = star(5, r=22, ir=13);
|
||||
// stroke(star,width=.1,closed=true);
|
||||
// stroke(star,width=.2,closed=true);
|
||||
// color("green")
|
||||
// stroke(offset(star, delta=9, closed=true),width=.1,closed=true); // Works with check_valid=true (the default)
|
||||
// stroke(offset(star, delta=-9, closed=true),width=.2,closed=true); // Works with check_valid=true (the default)
|
||||
// color("red")
|
||||
// stroke(offset(star, delta=10, closed=true, check_valid=false), // Fails if check_valid=true
|
||||
// width=.1,closed=true);
|
||||
// stroke(offset(star, delta=-10, closed=true, check_valid=false), // Fails if check_valid=true
|
||||
// width=.2,closed=true);
|
||||
// Example(2D): But if you use rounding with offset then you need `check_valid=true` when `r` is big enough. It works without the validity check as long as the offset shape retains a some of the straight edges at the star tip, but once the shape shrinks smaller than that, it fails. There is no simple way to get a correct result for the case with `r=10`, because as in the previous example, it will fail if you turn on validity checks.
|
||||
// star = star(5, r=22, ir=13);
|
||||
// color("green")
|
||||
// stroke(offset(star, r=8, closed=true,check_valid=false), width=.1, closed=true);
|
||||
// stroke(offset(star, r=-8, closed=true,check_valid=false), width=.1, closed=true);
|
||||
// color("red")
|
||||
// stroke(offset(star, r=10, closed=true,check_valid=false), width=.1, closed=true);
|
||||
// stroke(offset(star, r=-10, closed=true,check_valid=false), width=.1, closed=true);
|
||||
// Example(2D): The extra triangles in this example show that the validity check cannot be skipped
|
||||
// ellipse = scale([20,4], p=circle(r=1));
|
||||
// ellipse = scale([20,4], p=circle(r=1,$fn=64));
|
||||
// stroke(ellipse, closed=true, width=0.3);
|
||||
// stroke(offset(ellipse, r=-3, check_valid=false, closed=true), width=0.3, closed=true);
|
||||
// Example(2D): The triangles are removed by the validity check
|
||||
// ellipse = scale([20,4], p=circle(r=1));
|
||||
// ellipse = scale([20,4], p=circle(r=1,$fn=64));
|
||||
// stroke(ellipse, closed=true, width=0.3);
|
||||
// stroke(offset(ellipse, r=-3, check_valid=true, closed=true), width=0.3, closed=true);
|
||||
// Example(2D):
|
||||
// Example(2D): Open path. The path moves from left to right and the positive offset shifts to the left of the initial red path.
|
||||
// sinpath = 2*[for(theta=[-180:5:180]) [theta/4,45*sin(theta)]];
|
||||
// #stroke(sinpath);
|
||||
// stroke(offset(sinpath, r=17.5));
|
||||
|
@ -994,7 +1015,8 @@ function offset(
|
|||
let(
|
||||
chamfer = is_def(r) ? false : chamfer,
|
||||
quality = max(0,round(quality)),
|
||||
d = is_def(r)? r : delta,
|
||||
flip_dir = closed && !polygon_clockwise(path) ? -1 : 1,
|
||||
d = flip_dir * (is_def(r) ? r : delta),
|
||||
shiftsegs = [for(i=[0:len(path)-1]) _shift_segment(select(path,i,i+1), d)],
|
||||
// good segments are ones where no point on the segment is less than distance d from any point on the path
|
||||
good = check_valid ? _good_segments(path, abs(d), shiftsegs, closed, quality) : replist(true,len(shiftsegs)),
|
||||
|
|
|
@ -374,10 +374,10 @@ function bezier_curve(P,N) =
|
|||
// - "steps" - number of vertical steps to use for the roundover. Default: 16.
|
||||
// - "offset_maxstep" - maxstep distance for offset() calls; controls the horizontal step density. Default: 1
|
||||
// - "offset" - select "round" (r=) or "delta" (delta=) offset type for offset. Default: "round"
|
||||
//
|
||||
//
|
||||
// You can change the the defaults by passing an argument to the rounded_sweep, which is more convenient if you want
|
||||
// a setting to be the same at both ends.
|
||||
//
|
||||
//
|
||||
// You can use several helper functions to provide the rounding spec. These use function arguments to set the same parameters listed above, where the
|
||||
// function name indicates the type of rounding and only parameters valid for that rounding type are accepted:
|
||||
// - rs_circle(r,cut,extra,check_valid, quality,steps, offset_maxstep, offset)
|
||||
|
@ -385,11 +385,11 @@ function bezier_curve(P,N) =
|
|||
// - rs_chamfer(height, width, cut, extra,check_valid, quality,steps, offset_maxstep, offset)
|
||||
// - rs_smooth(cut, joint, extra,check_valid, quality,steps, offset_maxstep, offset)
|
||||
// - rs_custom(points, extra,check_valid, quality,steps, offset_maxstep, offset)
|
||||
//
|
||||
//
|
||||
// For example, you could round a path using `rounded_sweep(path, top=rs_teardrop(r=10), bottom=rs_chamfer(height=-10,extra=1))`
|
||||
// Many of the arguments are described as setting "default" values because they establish settings which may be overridden by
|
||||
// the top and bottom rounding specifications.
|
||||
//
|
||||
//
|
||||
// Arguments:
|
||||
// path = 2d path (list of points) to extrude
|
||||
// height = total height (including rounded portions, but not extra sections) of the output
|
||||
|
@ -408,7 +408,7 @@ function bezier_curve(P,N) =
|
|||
// joint = default joint value for smooth roundover.
|
||||
// k = default curvature parameter value for "smooth" roundover
|
||||
// convexity = convexity setting for use with polyhedron. Default: 10
|
||||
//
|
||||
//
|
||||
// Example: Rounding a star shaped prism with postive radius values
|
||||
// star = star(5, r=22, ir=13);
|
||||
// rounded_star = round_corners(zip(star, flatten(replist([.5,0],5))), curve="circle", measure="cut", $fn=12);
|
||||
|
@ -441,7 +441,7 @@ function bezier_curve(P,N) =
|
|||
// roundbox = round_corners(smallbox, curve="smooth", measure="cut", size=4, $fn=36);
|
||||
// thickness=4;
|
||||
// height=50;
|
||||
// back_half(y=37, s=200)
|
||||
// back_half(y=25, s=200)
|
||||
// difference(){
|
||||
// rounded_sweep(roundbox, height=height, bottom=["r",10,"type","teardrop"], top=["r",2], steps = 22, check_valid=false);
|
||||
// up(thickness)
|
||||
|
@ -450,6 +450,24 @@ function bezier_curve(P,N) =
|
|||
// bottom=["r",6],
|
||||
// top=["type","chamfer","angle",30,"height",-3,"extra",1,"check_valid",false]);
|
||||
// }
|
||||
// Example: A box with multiple sections and rounded dividers
|
||||
// thickness = 2;
|
||||
// box = ([[0,0], [0,50], [255,50], [255,0]]);
|
||||
// cutpoints = [0, 125, 190, 255];
|
||||
// rbox = round_corners(box, curve="smooth", measure="cut", size=4, $fn=36);
|
||||
// back_half(y=25, s=700)
|
||||
// difference(){
|
||||
// rounded_sweep(rbox, height=50, check_valid=false, steps=22, bottom=rs_teardrop(r=2), top=rs_circle(r=1));
|
||||
// up(thickness)
|
||||
// for(i=[0:2]){
|
||||
// ofs = i==1 ? 2 : 0;
|
||||
// hole = round_corners([[cutpoints[i]-ofs,0], [cutpoints[i]-ofs,50], [cutpoints[i+1]+ofs, 50], [cutpoints[i+1]+ofs,0]],
|
||||
// curve="smooth", measure="cut", size=4, $fn=36);
|
||||
// rounded_sweep(offset(hole, r=-thickness, closed=true,check_valid=false),
|
||||
// height=48, steps=22, check_valid=false, bottom=rs_circle(r=4), top=rs_circle(r=-1,extra=1));
|
||||
//
|
||||
// }
|
||||
// }
|
||||
// Example: Star shaped box
|
||||
// star = star(5, r=22, ir=13);
|
||||
// rounded_star = round_corners(zip(star, flatten(replist([.5,0],5))), curve="circle", measure="cut", $fn=12);
|
||||
|
@ -458,7 +476,7 @@ function bezier_curve(P,N) =
|
|||
// difference(){
|
||||
// rounded_sweep(rounded_star, height=ht, bottom=["r",4], top=["r",1], steps=15);
|
||||
// up(thickness)
|
||||
// rounded_sweep(offset(rounded_star,r=thickness,closed=true),
|
||||
// rounded_sweep(offset(rounded_star,r=-thickness,closed=true),
|
||||
// height=ht-thickness, check_valid=false,
|
||||
// bottom=rs_circle(r=7), top=rs_circle(r=-1, extra=1));
|
||||
// }
|
||||
|
@ -488,7 +506,7 @@ function bezier_curve(P,N) =
|
|||
// up(1)
|
||||
// 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=undef, steps=16, quality=1, check_valid=true, offset_maxstep=1, extra=0,
|
||||
module rounded_sweep(path, height, top=[], bottom=[], offset="round", r=0, steps=16, quality=1, check_valid=true, offset_maxstep=1, extra=0,
|
||||
cut=undef, width=undef, joint=undef, k=0.75, angle=45, convexity=10)
|
||||
{
|
||||
// This function does the actual work of repeatedly calling offset() and concatenating the resulting face and vertex lists to produce
|
||||
|
@ -514,13 +532,13 @@ module rounded_sweep(path, height, top=[], bottom=[], offset="round", r=undef, s
|
|||
|
||||
// Produce edge profile curve from the edge specification
|
||||
// z_dir is the direction multiplier (1 to build up, -1 to build down)
|
||||
function rounding_offsets(edgespec,flipR,z_dir=1) =
|
||||
function rounding_offsets(edgespec,z_dir=1) =
|
||||
let(
|
||||
edgetype = struct_val(edgespec, "type"),
|
||||
extra = struct_val(edgespec,"extra"),
|
||||
N = struct_val(edgespec, "steps"),
|
||||
r = flipR * struct_val(edgespec,"r"),
|
||||
cut = flipR * struct_val(edgespec,"cut"),
|
||||
r = struct_val(edgespec,"r"),
|
||||
cut = struct_val(edgespec,"cut"),
|
||||
k = struct_val(edgespec,"k"),
|
||||
radius = in_list(edgetype,["circle","teardrop"]) ?
|
||||
first_defined([cut/(sqrt(2)-1),r]) :
|
||||
|
@ -528,10 +546,10 @@ module rounded_sweep(path, height, top=[], bottom=[], offset="round", r=undef, s
|
|||
undef,
|
||||
chamf_angle = struct_val(edgespec, "angle"),
|
||||
cheight = struct_val(edgespec, "height"),
|
||||
cwidth = flipR * struct_val(edgespec, "width"),
|
||||
cwidth = struct_val(edgespec, "width"),
|
||||
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)]),
|
||||
joint = first_defined([flipR*struct_val(edgespec,"joint"),
|
||||
joint = first_defined([struct_val(edgespec,"joint"),
|
||||
16*cut/sqrt(2)/(1+4*k)]),
|
||||
points = struct_val(edgespec, "points"),
|
||||
argsOK = in_list(edgetype,["circle","teardrop"]) ? is_def(radius) :
|
||||
|
@ -541,7 +559,7 @@ module rounded_sweep(path, height, top=[], bottom=[], offset="round", r=undef, s
|
|||
false)
|
||||
assert(argsOK,str("Invalid specification with type ",edgetype))
|
||||
let(
|
||||
offsets = edgetype == "custom" ? scale([-flipR,z_dir], slice(points,1,-1)) :
|
||||
offsets = edgetype == "custom" ? scale([-1,z_dir], slice(points,1,-1)) :
|
||||
edgetype == "chamfer" ? width==0 && height==0 ? [] : [[-chamf_width,z_dir*abs(chamf_height)]] :
|
||||
edgetype == "teardrop" ? radius==0 ? [] : concat([for(i=[1:N]) [radius*(cos(i*45/N)-1),z_dir*abs(radius)* sin(i*45/N)]],
|
||||
[[-2*radius*(1-sqrt(2)/2), z_dir*abs(radius)]]):
|
||||
|
@ -554,8 +572,8 @@ module rounded_sweep(path, height, top=[], bottom=[], offset="round", r=undef, s
|
|||
extra > 0 ? concat(offsets, [select(offsets,-1)+[0,z_dir*extra]]) : offsets;
|
||||
|
||||
|
||||
argspec = [["r",0],
|
||||
["extra",0],
|
||||
argspec = [["r",r],
|
||||
["extra",extra],
|
||||
["type","circle"],
|
||||
["check_valid",check_valid],
|
||||
["quality",quality],
|
||||
|
@ -570,13 +588,13 @@ module rounded_sweep(path, height, top=[], bottom=[], offset="round", r=undef, s
|
|||
["k", k],
|
||||
["points", []],
|
||||
];
|
||||
|
||||
path = make_path_valid(path, [2], closed=true);
|
||||
|
||||
top = struct_set(argspec, top, grow=false);
|
||||
bottom = struct_set(argspec, bottom, grow=false);
|
||||
|
||||
struct_echo(top,"top");
|
||||
|
||||
clockwise = polygon_clockwise(path);
|
||||
flipR = clockwise ? 1 : -1;
|
||||
|
||||
assert(height>=0, "Height must be nonnegative");
|
||||
|
||||
|
@ -587,11 +605,9 @@ module rounded_sweep(path, height, top=[], bottom=[], offset="round", r=undef, s
|
|||
assert(offsetsok,"Offsets must be one of \"round\" or \"delta\"");
|
||||
*/
|
||||
|
||||
offsets_bot = rounding_offsets(bottom, flipR,-1);
|
||||
offsets_top = rounding_offsets(top, flipR,1);
|
||||
offsets_bot = rounding_offsets(bottom, -1);
|
||||
offsets_top = rounding_offsets(top, 1);
|
||||
|
||||
echo(ofstop = offsets_top);
|
||||
|
||||
// "Extra" height enlarges the result beyond the requested height, so subtract it
|
||||
bottom_height = len(offsets_bot)==0 ? 0 : abs(select(offsets_bot,-1)[1]) - struct_val(bottom,"extra");
|
||||
top_height = len(offsets_top)==0 ? 0 : abs(select(offsets_top,-1)[1]) - struct_val(top,"extra");
|
||||
|
@ -616,11 +632,6 @@ module rounded_sweep(path, height, top=[], bottom=[], offset="round", r=undef, s
|
|||
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);
|
||||
echo(botv=vertices_faces_bot[0]);
|
||||
echo(topv=vertices_faces_top[0]);
|
||||
echo(fbot=vertices_faces_bot[1]);
|
||||
echo(ftop=vertices_faces_top[1]);
|
||||
echo(fmid=middle_faces);
|
||||
}
|
||||
|
||||
function rs_circle(r,cut,extra,check_valid, quality,steps, offset_maxstep, offset) =
|
||||
|
|
Loading…
Reference in a new issue