Added get_height() and no_children() to common.scad.

Changed the epsilon in one of offset's subfunctions from 1e-4 to
1e-6.
Modified rounded_sweep() to take h, l, or height, and to have default
behavior if height is omitted.  It also quantizes the path to 1/1024.
Added no_children check to module offset_stroke().
This commit is contained in:
Adrian Mariano 2019-08-28 21:15:41 -04:00
parent da1086b71b
commit d2ccdc331f
3 changed files with 59 additions and 24 deletions

View file

@ -98,6 +98,37 @@ function get_radius(r1=undef, r2=undef, r=undef, d1=undef, d2=undef, d=undef, df
dflt dflt
); );
// Function: get_height()
// Usage:
// get_height([h],[l],[height],[dflt])
// Description:
// Given several different parameters for height check that height is not multiply defined
// and return a single value. If the three values `l`, `h`, and `height` are all undefined
// then return the value `dflt`, if given, or undef otherwise.
// Arguments:
// l = l.
// h = h.
// height = height.
// dflt = Value to return if other values are `undef`.
function get_height(h=undef,l=undef,height=undef,dflt=undef) =
assert(num_defined([h,l,height])<=1,"You must specify only one of `l`, `h`, and `height`")
first_defined([h,l,height,dflt]);
// Module: no_children()
// Usage:
// no_children($children);
// Description:
// Assert that the calling module does not support children. Prints an error message to this effect and fails if children are present,
// as indicated by its argument.
// Arguments:
// $children = number of children the module has.
module no_children(count)
{
assert(count==0, str("Module ",parent_module(1),"() does not support child modules"));
}
// Function: scalar_vec3() // Function: scalar_vec3()
// Usage: // Usage:

View file

@ -1026,7 +1026,7 @@ function _good_segments(path, d, shiftsegs, closed, quality) =
) [ ) [
for (i=[0:len(shiftsegs)-1]) for (i=[0:len(shiftsegs)-1])
(i>maxind)? true : (i>maxind)? true :
_segment_good(path,pathseg_unit,pathseg_len, d - 1e-4, shiftsegs[i], alpha) _segment_good(path,pathseg_unit,pathseg_len, d - 1e-7, shiftsegs[i], alpha)
]; ];

View file

@ -341,7 +341,7 @@ function _circlecorner(points, parm) =
// Module: rounded_sweep() // Module: rounded_sweep()
// //
// Description: // Description:
// Takes a 2d path as input and extrudes it to a specified height with roundovers or chamfers at the ends. The // Takes a 2d path as input and extrudes it to a specified height with roundovers or chamfers, or custom treatments at the ends. The
// rounding is accomplished by using offset to shift the input path. The path is shifted multiple times in sequence // rounding is accomplished by using offset to shift the input path. The path is shifted multiple times in sequence
// to produce the profile (not multiple shifts from one parent), so coarse definition of the input path will degrade // to produce the profile (not multiple shifts from one parent), so coarse definition of the input path will degrade
// from the successive shifts. If the result seems rough or strange try increasing the number of points you use for // from the successive shifts. If the result seems rough or strange try increasing the number of points you use for
@ -351,8 +351,11 @@ function _circlecorner(points, parm) =
// aware that large numbers of points (especially when check_valid is true) can lead to lengthy run times. If your // aware that large numbers of points (especially when check_valid is true) can lead to lengthy run times. If your
// shape doesn't develop corners you may be able to save a lot of time by setting `check_valid=false`. Be aware that // shape doesn't develop corners you may be able to save a lot of time by setting `check_valid=false`. Be aware that
// disabling the validity check when it is needed can generate invalid polyhedra that will produce CGAL errors upon // disabling the validity check when it is needed can generate invalid polyhedra that will produce CGAL errors upon
// rendering. Multiple rounding shapes are available, including circular rounding, teardrop rounding, and chamfer // rendering. Multiple rounding shapes are available, including circular rounding, teardrop rounding, chamfer
// "rounding". Also note that if the rounding radius is negative then the rounding will flare outwards. // "rounding", as well as application of a custom profile. Also note that if the rounding radius is negative
// then the rounding will flare outwards.
// The rounding profile
// will be quantized to 1/1024 steps to avoid failures in offset() that can occur with very tiny offsets.
// //
// Rounding options: // Rounding options:
// - "circle": Circular rounding with radius as specified // - "circle": Circular rounding with radius as specified
@ -365,8 +368,8 @@ function _circlecorner(points, parm) =
// - "type" - type of rounding to apply, one of "circle", "teardrop", "chamfer", "smooth", or "custom" (Default: "circle") // - "type" - type of rounding to apply, one of "circle", "teardrop", "chamfer", "smooth", or "custom" (Default: "circle")
// - "r" - the radius of the roundover, which may be zero for no roundover, or negative to round or flare outward. Default: 0 // - "r" - the radius of the roundover, which may be zero for no roundover, or negative to round or flare outward. Default: 0
// - "cut" - the cut distance for the roundover or chamfer, which may be negative for flares // - "cut" - the cut distance for the roundover or chamfer, which may be negative for flares
// - "width" - the width of a chamfer // - "chamfer_width" - the width of a chamfer
// - "height" - the height of a chamfer // - "chamfer_height" - the height of a chamfer
// - "angle" - the chamfer angle, measured from the vertical (so zero is vertical, 90 is horizontal). Default: 45 // - "angle" - the chamfer angle, measured from the vertical (so zero is vertical, 90 is horizontal). Default: 45
// - "joint" - the joint distance for a "smooth" roundover // - "joint" - the joint distance for a "smooth" roundover
// - "k" - the curvature smoothness parameter for "smooth" roundovers, a value in [0,1]. Default: 0.75 // - "k" - the curvature smoothness parameter for "smooth" roundovers, a value in [0,1]. Default: 0.75
@ -395,7 +398,7 @@ function _circlecorner(points, parm) =
// //
// Arguments: // Arguments:
// path = 2d path (list of points) to extrude // path = 2d path (list of points) to extrude
// height = total height (including rounded portions, but not extra sections) of the output // height / l / h = total height (including rounded portions, but not extra sections) of the output. Default: combined height of top and bottom end treatments.
// top = rounding spec for the top end. // top = rounding spec for the top end.
// bottom = rounding spec for the bottom end // bottom = rounding spec for the bottom end
// offset = default offset, `"round"` or `"delta"`. Default: `"round"` // offset = default offset, `"round"` or `"delta"`. Default: `"round"`
@ -405,8 +408,8 @@ function _circlecorner(points, parm) =
// offset_maxstep = default maxstep value to pass to offset. Default: 1 // offset_maxstep = default maxstep value to pass to offset. Default: 1
// extra = default extra height. Default: 0 // extra = default extra height. Default: 0
// cut = default cut value. // cut = default cut value.
// width = default width value for chamfers. // chamfer_width = default width value for chamfers.
// height = default height value for chamfers. // chamfer_height = default height value for chamfers.
// angle = default angle for chamfers. Default: 45 // angle = default angle for chamfers. Default: 45
// joint = default joint value for smooth roundover. // joint = default joint value for smooth roundover.
// k = default curvature parameter value for "smooth" roundover // k = default curvature parameter value for "smooth" roundover
@ -460,7 +463,7 @@ function _circlecorner(points, parm) =
// rounded_sweep(offset(roundbox, r=-thickness, closed=true), // rounded_sweep(offset(roundbox, r=-thickness, closed=true),
// height=height-thickness, steps=22, // height=height-thickness, steps=22,
// bottom=["r",6], // bottom=["r",6],
// top=["type","chamfer","angle",30,"height",-3,"extra",1,"check_valid",false]); // top=["type","chamfer","angle",30,"chamfer_height",-3,"extra",1,"check_valid",false]);
// } // }
// Example: A box with multiple sections and rounded dividers // Example: A box with multiple sections and rounded dividers
// thickness = 2; // thickness = 2;
@ -519,12 +522,12 @@ function _circlecorner(points, parm) =
// 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( module rounded_sweep(
path, height, path, height, h, l,
top=[], bottom=[], top=[], bottom=[],
offset="round", r=0, steps=16, offset="round", r=0, steps=16,
quality=1, check_valid=true, quality=1, check_valid=true,
offset_maxstep=1, extra=0, offset_maxstep=1, extra=0,
cut=undef, width=undef, cut=undef, chamfer_width=undef, chamfer_height=undef,
joint=undef, k=0.75, angle=45, joint=undef, k=0.75, angle=45,
convexity=10 convexity=10
) { ) {
@ -578,8 +581,8 @@ module rounded_sweep(
first_defined([cut/(sqrt(2)-1),r]) : first_defined([cut/(sqrt(2)-1),r]) :
edgetype=="chamfer"? first_defined([sqrt(2)*cut,r]) : undef, edgetype=="chamfer"? first_defined([sqrt(2)*cut,r]) : undef,
chamf_angle = struct_val(edgespec, "angle"), chamf_angle = struct_val(edgespec, "angle"),
cheight = struct_val(edgespec, "height"), cheight = struct_val(edgespec, "chamfer_height"),
cwidth = struct_val(edgespec, "width"), cwidth = struct_val(edgespec, "chamfer_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([
@ -597,7 +600,7 @@ module rounded_sweep(
let( let(
offsets = offsets =
edgetype == "custom"? scale([-1,z_dir], slice(points,1,-1)) : edgetype == "custom"? scale([-1,z_dir], slice(points,1,-1)) :
edgetype == "chamfer"? width==0 && height==0? [] : [[-chamf_width,z_dir*abs(chamf_height)]] : edgetype == "chamfer"? chamf_width==0 && chamf_height==0? [] : [[-chamf_width,z_dir*abs(chamf_height)]] :
edgetype == "teardrop"? ( edgetype == "teardrop"? (
radius==0? [] : concat( radius==0? [] : concat(
[for(i=[1:N]) [radius*(cos(i*45/N)-1),z_dir*abs(radius)* sin(i*45/N)]], [for(i=[1:N]) [radius*(cos(i*45/N)-1),z_dir*abs(radius)* sin(i*45/N)]],
@ -611,7 +614,7 @@ module rounded_sweep(
1, -1 1, -1
) )
) )
extra > 0? concat(offsets, [select(offsets,-1)+[0,z_dir*extra]]) : offsets; quant(extra > 0? concat(offsets, [select(offsets,-1)+[0,z_dir*extra]]) : offsets, 1/1024);
argspec = [ argspec = [
["r",r], ["r",r],
@ -622,8 +625,8 @@ module rounded_sweep(
["offset_maxstep", offset_maxstep], ["offset_maxstep", offset_maxstep],
["steps",steps], ["steps",steps],
["offset",offset], ["offset",offset],
["width",width], ["chamfer_width",chamfer_width],
["height",undef], ["chamfer_height",chamfer_height],
["angle",angle], ["angle",angle],
["cut",cut], ["cut",cut],
["joint",joint], ["joint",joint],
@ -632,14 +635,11 @@ module rounded_sweep(
]; ];
path = check_and_fix_path(path, [2], closed=true); path = check_and_fix_path(path, [2], closed=true);
clockwise = polygon_is_clockwise(path);
top = struct_set(argspec, top, grow=false); top = struct_set(argspec, top, grow=false);
bottom = struct_set(argspec, bottom, grow=false); bottom = struct_set(argspec, bottom, grow=false);
clockwise = polygon_is_clockwise(path);
assert(height>=0, "Height must be nonnegative");
// This code does not work. It hits the error in make_polyhedron from offset being wrong // This code does not work. It hits the error in make_polyhedron from offset being wrong
// before this code executes. Had to move the test into make_polyhedron, which is ugly since it's in the loop // 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"]) && //offsetsok = in_list(struct_val(top, "offset"),["round","delta"]) &&
@ -653,6 +653,9 @@ module rounded_sweep(
bottom_height = len(offsets_bot)==0 ? 0 : abs(select(offsets_bot,-1)[1]) - struct_val(bottom,"extra"); 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"); top_height = len(offsets_top)==0 ? 0 : abs(select(offsets_top,-1)[1]) - struct_val(top,"extra");
height = get_height(l=l,h=h,height=height,dflt=bottom_height+top_height);
assert(height>=0, "Height must be nonnegative");
middle = height-bottom_height-top_height; middle = height-bottom_height-top_height;
assert( assert(
middle>=0, str( middle>=0, str(
@ -728,8 +731,8 @@ function rs_chamfer(height, width, cut, angle, extra,check_valid, quality,steps,
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, "chamfer_width",width,
"height",height, "chamfer_height",height,
"cut",cut, "cut",cut,
"angle",angle, "angle",angle,
"extra",extra, "extra",extra,
@ -1097,6 +1100,7 @@ function _path_line_intersection(path, line, ind=0) =
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)
{ {
no_children($children);
result = offset_stroke( result = offset_stroke(
path, width=width, rounded=rounded, path, width=width, rounded=rounded,
start=start, end=end, start=start, end=end,