diff --git a/beziers.scad b/beziers.scad index e9a6e98..5589f2c 100644 --- a/beziers.scad +++ b/beziers.scad @@ -887,15 +887,19 @@ function is_bezier_patch(x) = // Description: // Returns a flat rectangular bezier patch of degree `N`, centered on the XY plane. // Arguments: -// size = 2D XY size of the patch. +// size = scalar or 2-vector giving the X and Y dimensions of the patch. // --- -// N = Degree of the patch to generate. Since this is flat, a degree of 1 should usually be sufficient. -// orient = The orientation to rotate the edge patch into. Given as an [X,Y,Z] rotation angle list. -// trans = Amount to translate patch, after rotating to `orient`. +// N = Degree of the patch to generate. Since this is flat, a degree of 1 should usually be sufficient. Default: 1 +// orient = A direction vector. Point the patch normal in this direction. +// spin = Spin angle to apply to the patch +// trans = Amount to translate patch, after orient and spin. // Example(3D): -// patch = bezier_patch_flat(size=[100,100], N=3); +// patch = bezier_patch_flat(size=[100,100]); // debug_bezier_patches([patch], size=1, showcps=true); -function bezier_patch_flat(size=[100,100], N=4, spin=0, orient=UP, trans=[0,0,0]) = +function bezier_patch_flat(size, N=1, spin=0, orient=UP, trans=[0,0,0]) = + assert(N>0) + let(size = force_list(size,2)) + assert(is_vector(size,2)) let( patch = [ for (x=[0:1:N]) [ @@ -994,6 +998,7 @@ function _bezier_rectangle(patch, splinesteps=16, style="default") = // It can be a scalar, which gives a uniform grid, or // it can be [USTEPS, VSTEPS], which gives difference spacing in the U and V parameters. // Note that the surface you produce may be disconnected and is not necessarily a valid manifold in OpenSCAD. +// You must also ensure that the patches mate exactly along their edges, or the VNF will be invalid. // Arguments: // patches = The bezier patch or list of bezier patches to convert into a vnf. // splinesteps = Number of segments on the border of the bezier surface. You can specify [USTEPS,VSTEPS]. Default: 16 @@ -1010,21 +1015,23 @@ function _bezier_rectangle(patch, splinesteps=16, style="default") = // vnf = bezier_vnf(patch, splinesteps=16); // vnf_polyhedron(vnf); // Example(3D,FlatSpin,VPD=444): Combining multiple patches -// patch = [ +// patch = 100*[ // // u=0,v=0 u=1,v=0 -// [[0, 0,0], [33, 0, 0], [67, 0, 0], [100, 0,0]], -// [[0, 33,0], [33, 33, 33], [67, 33, 33], [100, 33,0]], -// [[0, 67,0], [33, 67, 33], [67, 67, 33], [100, 67,0]], -// [[0,100,0], [33,100, 0], [67,100, 0], [100,100,0]], +// [[0, 0,0], [1/3, 0, 0], [2/3, 0, 0], [1, 0,0]], +// [[0,1/3,0], [1/3,1/3,1/3], [2/3,1/3,1/3], [1,1/3,0]], +// [[0,2/3,0], [1/3,2/3,1/3], [2/3,2/3,1/3], [1,2/3,0]], +// [[0, 1,0], [1/3, 1, 0], [2/3, 1, 0], [1, 1,0]], // // u=0,v=1 u=1,v=1 // ]; +// fpatch = bezier_patch_flat([100,100]); // tpatch = translate([-50,-50,50], patch); +// flatpatch = translate([0,0,50], fpatch); // vnf = bezier_vnf([ // tpatch, // xrot(90, tpatch), // xrot(-90, tpatch), // xrot(180, tpatch), -// yrot(90, tpatch), +// yrot(90, flatpatch), // yrot(-90, tpatch)]); // vnf_polyhedron(vnf); // Example(3D): diff --git a/drawing.scad b/drawing.scad index 62738e5..4d85321 100644 --- a/drawing.scad +++ b/drawing.scad @@ -21,6 +21,7 @@ // stroke(path, [width], [closed], [endcaps], [endcap_width], [endcap_length], [endcap_extent], [trim]); // stroke(path, [width], [closed], [endcap1], [endcap2], [endcap_width1], [endcap_width2], [endcap_length1], [endcap_length2], [endcap_extent1], [endcap_extent2], [trim1], [trim2]); // Topics: Paths (2D), Paths (3D), Drawing Tools +// See Also: offset_stroke(), path_sweep() // Description: // Draws a 2D or 3D path with a given line width. Joints and each endcap can be replaced with // various marker shapes, and can be assigned different colors. If passed a region instead of @@ -28,7 +29,13 @@ // given with a region or list of paths, then each path is drawn without the closing line segment. // To facilitate debugging, stroke() accepts "paths" that have a single point. These are drawn with // the style of endcap1, but have their own scale parameter, `singleton_scale`, which defaults to 2 -// so that singleton dots with endcap "round" are clearly visible. +// so that singleton dots with endcap "round" are clearly visible. +// . +// In 2d the stroke module works by creating a sequence of rectangles (or trapezoids if line width varies) and +// filling in the gaps with rounded wedges. This is fast and produces a good result. In 3d the modules +// creates a cylinders (or cones) and fills the gaps with rounded wedges made using rotate_extrude. This process will be slow for +// long paths due to the 3d unions, and the faces on sequential cylinders may not line up. In many cases, {{path_sweep()}} will be +// a better choice, both running faster and producing superior output, when working in three dimensions. // Figure(Med,NoAxes,2D,VPR=[0,0,0],VPD=250): Endcap Types // cap_pairs = [ // ["butt", "chisel" ], diff --git a/lists.scad b/lists.scad index a749969..37b3e1a 100644 --- a/lists.scad +++ b/lists.scad @@ -798,17 +798,18 @@ function list_remove_values(list,values=[],all=false) = - -// Section: Lists of Subsets +// Section: List Iteration Index Helper // Function: idx() // Usage: -// rng = idx(list, [s=], [e=], [step=]); +// range = idx(list, [s=], [e=], [step=]); // for(i=idx(list, [s=], [e=], [step=])) ... // Topics: List Handling, Iteration -// See Also: pair(), triplet(), combinations(), permutations() +// See Also: count() // Description: -// Returns the range of indexes for the given list. +// Returns the range that gives the indices for a given list. This makes is a little bit +// easier to loop over a list by index, when you need the index numbers and looping of list values isn't enough. +// Note that the return is a **range** not a list. // Arguments: // list = The list to returns the index range of. // --- @@ -828,6 +829,8 @@ function idx(list, s=0, e=-1, step=1) = ) [_s : step : _e]; +// Section: Lists of Subsets + // Function: pair() // Usage: diff --git a/masks3d.scad b/masks3d.scad index 36f3258..de03599 100644 --- a/masks3d.scad +++ b/masks3d.scad @@ -15,13 +15,13 @@ // Module: chamfer_edge_mask() // Usage: -// chamfer_edge_mask(l, chamfer, [excess]) [ATTACHMENTS]; +// chamfer_edge_mask(l|h=|length=|height=, chamfer, [excess]) [ATTACHMENTS]; // Description: // Creates a shape that can be used to chamfer a 90 degree edge. // Difference it from the object to be chamfered. The center of // the mask object should align exactly with the edge to be chamfered. // Arguments: -// l = Length of mask. +// l/h/length/height = Length of mask. // chamfer = Size of chamfer. // excess = The extra amount to add to the length of the mask so that it differences away from other shapes cleanly. Default: `0.1` // --- @@ -41,8 +41,9 @@ // edge_mask(TOP+RIGHT) // #chamfer_edge_mask(l=50, chamfer=10); // } -function chamfer_edge_mask(l=1, chamfer=1, excess=0.1, anchor=CENTER, spin=0, orient=UP) = no_function("chamfer_edge_mask"); -module chamfer_edge_mask(l=1, chamfer=1, excess=0.1, anchor=CENTER, spin=0, orient=UP) { +function chamfer_edge_mask(l, chamfer=1, excess=0.1, h, length, height, anchor=CENTER, spin=0, orient=UP) = no_function("chamfer_edge_mask"); +module chamfer_edge_mask(l, chamfer=1, excess=0.1, h, length, height, anchor=CENTER, spin=0, orient=UP) { + l = one_defined([l, h, height, length], "l,h,height,length"); attachable(anchor,spin,orient, size=[chamfer*2, chamfer*2, l]) { cylinder(r=chamfer, h=l+excess, center=true, $fn=4); children(); @@ -149,14 +150,14 @@ module chamfer_cylinder_mask(r, chamfer, d, ang=45, from_end=false, anchor=CENTE // Module: rounding_edge_mask() // Usage: -// rounding_edge_mask(l|h, r|d, [excess=]) [ATTACHMENTS]; -// rounding_edge_mask(l|h, r1=|d1=, r2=|d2=, [excess=]) [ATTACHMENTS]; +// rounding_edge_mask(l|h=|length=|height=, r|d=, [excess=]) [ATTACHMENTS]; +// rounding_edge_mask(l|h=|length=|height=, r1=|d1=, r2=|d2=, [excess=]) [ATTACHMENTS]; // Description: // Creates a shape that can be used to round a vertical 90 degree edge. // Difference it from the object to be rounded. The center of the mask // object should align exactly with the edge to be rounded. // Arguments: -// l/h = Length of mask. +// l/h/length/height = Length of mask. // r = Radius of the rounding. // --- // r1 = Bottom radius of rounding. @@ -193,10 +194,10 @@ module chamfer_cylinder_mask(r, chamfer, d, ang=45, from_end=false, anchor=CENTE // rounding_edge_mask(l=p.z, r=25); // } // } -function rounding_edge_mask(l, r, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, spin=0, orient=UP, h=undef) = no_function("rounding_edge_mask"); -module rounding_edge_mask(l, r, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, spin=0, orient=UP, h=undef) +function rounding_edge_mask(l, r, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, spin=0, orient=UP, h,height,length) = no_function("rounding_edge_mask"); +module rounding_edge_mask(l, r, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, spin=0, orient=UP, h,height,length) { - l = first_defined([l, h, 1]); + l = one_defined([l, h, height, length], "l,h,height,length"); r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1); r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1); sides = quantup(segs(max(r1,r2)),4); @@ -275,14 +276,14 @@ module rounding_corner_mask(r, d, style="octa", excess=0.1, anchor=CENTER, spin= // Module: rounding_angled_edge_mask() // Usage: -// rounding_angled_edge_mask(h, r|d=, [ang=]) [ATTACHMENTS]; -// rounding_angled_edge_mask(h, r1=|d1=, r2=|d2=, [ang=]) [ATTACHMENTS]; +// rounding_angled_edge_mask(h|l=|length=|height=, r|d=, [ang=]) [ATTACHMENTS]; +// rounding_angled_edge_mask(h|l=|length=|height=, r1=|d1=, r2=|d2=, [ang=]) [ATTACHMENTS]; // Description: // Creates a vertical mask that can be used to round the edge where two face meet, at any arbitrary // angle. Difference it from the object to be rounded. The center of the mask should align exactly // with the edge to be rounded. // Arguments: -// h = Height of vertical mask. +// h/l/height/length = Height of vertical mask. // r = Radius of the rounding. // --- // r1 = Bottom radius of rounding. @@ -304,8 +305,8 @@ module rounding_corner_mask(r, d, style="octa", excess=0.1, anchor=CENTER, spin= // pie_slice(ang=70, h=50, d=100, center=true); // #rounding_angled_edge_mask(h=51, r1=10, r2=25, ang=70, $fn=32); // } -function rounding_angled_edge_mask(h, r, r1, r2, d, d1, d2, ang=90, anchor=CENTER, spin=0, orient=UP) = no_function("rounding_angled_edge_mask"); -module rounding_angled_edge_mask(h, r, r1, r2, d, d1, d2, ang=90, anchor=CENTER, spin=0, orient=UP) +function rounding_angled_edge_mask(h, r, r1, r2, d, d1, d2, ang=90, anchor=CENTER, spin=0, orient=UP,l,height,length) = no_function("rounding_angled_edge_mask"); +module rounding_angled_edge_mask(h, r, r1, r2, d, d1, d2, ang=90, anchor=CENTER, spin=0, orient=UP,l,height,length) { function _mask_shape(r) = [ for (i = [0:1:n]) let (a=90+ang+i*sweep/n) [r*cos(a)+x, r*sin(a)+r], @@ -313,7 +314,7 @@ module rounding_angled_edge_mask(h, r, r1, r2, d, d1, d2, ang=90, anchor=CENTER, [min(-1, r*cos(270-ang)+x-1), r*sin(270-ang)-r], [min(-1, r*cos(90+ang)+x-1), r*sin(90+ang)+r], ]; - + h = one_defined([l, h, height, length], "l,h,height,length"); sweep = 180-ang; r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1); r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1); @@ -445,10 +446,10 @@ module rounding_cylinder_mask(r, rounding, d, anchor=CENTER, spin=0, orient=UP) // hole to be rounded. // Arguments: // r = Radius of hole. -// d = Diameter of hole to rounding. // rounding = Radius of the rounding. // excess = The extra thickness of the mask. Default: `0.1`. // --- +// d = Diameter of hole to rounding. // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` @@ -486,11 +487,11 @@ module rounding_hole_mask(r, rounding, excess=0.1, d, anchor=CENTER, spin=0, ori // Module: teardrop_edge_mask() // Usage: -// teardrop_edge_mask(l, r|d=, [angle], [excess], [anchor], [spin], [orient]) [ATTACHMENTS]; +// teardrop_edge_mask(l|h=|length=|height=, r|d=, [angle], [excess], [anchor], [spin], [orient]) [ATTACHMENTS]; // Description: // Makes an apropriate 3D corner rounding mask that keeps within `angle` degrees of vertical. // Arguments: -// l = length of mask +// l/h/length/height = length of mask // r = Radius of the mask rounding. // angle = Maximum angle from vertical. Default: 45 // excess = Excess mask size. Default: 0.1 @@ -509,9 +510,10 @@ module rounding_hole_mask(r, rounding, excess=0.1, d, anchor=CENTER, spin=0, ori // corner_mask(BOT) // teardrop_corner_mask(r=10, angle=40); // } -function teardrop_edge_mask(l, r, angle, excess=0.1, d, anchor, spin, orient) = no_function("teardrop_edge_mask"); -module teardrop_edge_mask(l, r, angle, excess=0.1, d, anchor=CTR, spin=0, orient=UP) +function teardrop_edge_mask(l, r, angle=45, excess=0.1, d, anchor, spin, orient,h,height,length) = no_function("teardrop_edge_mask"); +module teardrop_edge_mask(l, r, angle=45, excess=0.1, d, anchor=CTR, spin=0, orient=UP,h,height,length) { + l = one_defined([l, h, height, length], "l,h,height,length"); check = assert(is_num(l) && l>0, "Length of mask must be positive") assert(is_num(angle) && angle>0 && angle<90, "Angle must be a number between 0 and 90") @@ -546,8 +548,8 @@ module teardrop_edge_mask(l, r, angle, excess=0.1, d, anchor=CTR, spin=0, orient // corner_mask(BOT) // teardrop_corner_mask(r=10, angle=40); // } -function teardrop_corner_mask(r, angle, excess=0.1, d, anchor, spin, orient) = no_function("teardrop_corner_mask"); -module teardrop_corner_mask(r, angle, excess=0.1, d, anchor=CTR, spin=0, orient=UP) +function teardrop_corner_mask(r, angle=45, excess=0.1, d, anchor, spin, orient) = no_function("teardrop_corner_mask"); +module teardrop_corner_mask(r, angle=45, excess=0.1, d, anchor=CTR, spin=0, orient=UP) { assert(is_num(angle)); assert(is_num(excess)); diff --git a/screws.scad b/screws.scad index 761ce61..26345c5 100644 --- a/screws.scad +++ b/screws.scad @@ -426,7 +426,7 @@ Torx values: https://www.stanleyengineeredfastening.com/-/media/web/sef/resourc // screw("1/4-20,3/8", head="hex",orient=UP,anchor=BOTTOM,tolerance="1A"); // down(INCH*1/20*1.395) nut("1/4-20", thickness=8, nutwidth=0.5*INCH, tolerance="1B"); // } -// Example: Here is a screw with nonstadard threading and a weird head size, which we create by modifying the screw structure: +// Example: Here is a screw with nonstandard threading and a weird head size, which we create by modifying the screw structure: // spec = screw_info("M6x2,12",head="socket"); // newspec = struct_set(spec,["head_size",20,"head_height",3]); // screw(newspec); diff --git a/utility.scad b/utility.scad index 7f0f638..b321cf0 100644 --- a/utility.scad +++ b/utility.scad @@ -935,7 +935,7 @@ module shape_compare(eps=1/1024) { } -// Section: Looping Helpers +// Section: C-Style For Loop Helpers // You can use a list comprehension with a C-style for loop to iteratively make a calculation. // . // The syntax is: `[for (INIT; CONDITION; NEXT) RETVAL]` where: