diff --git a/arrays.scad b/arrays.scad index c09fd95..f6c9336 100644 --- a/arrays.scad +++ b/arrays.scad @@ -161,12 +161,12 @@ function last(list) = // Arguments: // list = The list to get the head of. // to = The last index to include. If negative, adds the list length to it. ie: -1 is the last list item. -// Examples: -// hlist = list_head(["foo", "bar", "baz"]); // Returns: ["foo", "bar"] -// hlist = list_head(["foo", "bar", "baz"], -3); // Returns: ["foo"] -// hlist = list_head(["foo", "bar", "baz"], 2); // Returns: ["foo","bar"] -// hlist = list_head(["foo", "bar", "baz"], -5); // Returns: [] -// hlist = list_head(["foo", "bar", "baz"], 5); // Returns: ["foo","bar","baz"] +// Example: +// hlist1 = list_head(["foo", "bar", "baz"]); // Returns: ["foo", "bar"] +// hlist2 = list_head(["foo", "bar", "baz"], -3); // Returns: ["foo"] +// hlist3 = list_head(["foo", "bar", "baz"], 2); // Returns: ["foo","bar"] +// hlist4 = list_head(["foo", "bar", "baz"], -5); // Returns: [] +// hlist5 = list_head(["foo", "bar", "baz"], 5); // Returns: ["foo","bar","baz"] function list_head(list, to=-2) = assert(is_list(list)) assert(is_finite(to)) @@ -188,12 +188,12 @@ function list_head(list, to=-2) = // Arguments: // list = The list to get the tail of. // from = The first index to include. If negative, adds the list length to it. ie: -1 is the last list item. -// Examples: -// tlist = list_tail(["foo", "bar", "baz"]); // Returns: ["bar", "baz"] -// tlist = list_tail(["foo", "bar", "baz"], -1); // Returns: ["baz"] -// tlist = list_tail(["foo", "bar", "baz"], 2); // Returns: ["baz"] -// tlist = list_tail(["foo", "bar", "baz"], -5); // Returns: ["foo","bar","baz"] -// tlist = list_tail(["foo", "bar", "baz"], 5); // Returns: [] +// Example: +// tlist1 = list_tail(["foo", "bar", "baz"]); // Returns: ["bar", "baz"] +// tlist2 = list_tail(["foo", "bar", "baz"], -1); // Returns: ["baz"] +// tlist3 = list_tail(["foo", "bar", "baz"], 2); // Returns: ["baz"] +// tlist4 = list_tail(["foo", "bar", "baz"], -5); // Returns: ["foo","bar","baz"] +// tlist5 = list_tail(["foo", "bar", "baz"], 5); // Returns: [] function list_tail(list, from=1) = assert(is_list(list)) assert(is_finite(from)) @@ -236,7 +236,7 @@ function list(l) = is_list(l)? l : [for (x=l) x]; // value = The value or list to coerce into a list. // n = The number of items in the coerced list. Default: 1 // fill = The value to pad the coerced list with, after the firt value. Default: undef (pad with copies of `value`) -// Examples: +// Example: // x = force_list([3,4,5]); // Returns: [3,4,5] // y = force_list(5); // Returns: [5] // z = force_list(7, n=3); // Returns: [7,7,7] @@ -509,7 +509,7 @@ function list_rotate(list,n=1) = // list = The list to deduplicate. // closed = If true, drops trailing items if they match the first list item. // eps = The maximum tolerance between items. -// Examples: +// Example: // a = deduplicate([8,3,4,4,4,8,2,3,3,8,8]); // Returns: [8,3,4,8,2,3,8] // b = deduplicate(closed=true, [8,3,4,4,4,8,2,3,3,8,8]); // Returns: [8,3,4,8,2,3] // c = deduplicate("Hello"); // Returns: "Helo" @@ -539,7 +539,7 @@ function deduplicate(list, closed=false, eps=EPSILON) = // indices = The list of indices to deduplicate. // closed = If true, drops trailing indices if what they index matches what the first index indexes. // eps = The maximum difference to allow between numbers or vectors. -// Examples: +// Example: // a = deduplicate_indexed([8,6,4,6,3], [1,4,3,1,2,2,0,1]); // Returns: [1,4,3,2,0,1] // b = deduplicate_indexed([8,6,4,6,3], [1,4,3,1,2,2,0,1], closed=true); // Returns: [1,4,3,2,0] // c = deduplicate_indexed([[7,undef],[7,undef],[1,4],[1,4],[1,4+1e-12]],eps=0); // Returns: [0,2,4] @@ -592,7 +592,7 @@ function deduplicate_indexed(list, indices, closed=false, eps=EPSILON) = // list = list whose entries will be repeated // N = scalar total number of points desired or vector requesting N[i] copies of vertex i. // exact = if true return exactly the requested number of points, possibly sacrificing uniformity. If false, return uniform points that may not match the number of points requested. Default: True -// Examples: +// Example: // list = [0,1,2,3]; // a = repeat_entries(list, 6); // Returns: [0,0,1,2,2,3] // b = repeat_entries(list, 6, exact=false); // Returns: [0,0,1,1,2,2,3,3] @@ -629,7 +629,7 @@ function repeat_entries(list, N, exact=true) = // values = List of values to set. // dflt = Default value to store in sparse skipped indices. // minlen = Minimum length to expand list to. -// Examples: +// Example: // a = list_set([2,3,4,5], 2, 21); // Returns: [2,3,21,5] // b = list_set([2,3,4,5], [1,3], [81,47]); // Returns: [2,81,4,47] function list_set(list=[],indices,values,dflt=0,minlen=0) = @@ -1890,7 +1890,7 @@ function _array_dim_recurse(v) = // Arguments: // v = Array to get dimensions of. // depth = Dimension to get size of. If not given, returns a list of dimension lengths. -// Examples: +// Example: // a = array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]); // Returns [2,2,3] // b = array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 0); // Returns 2 // c = array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 2); // Returns 3 diff --git a/attachments.scad b/attachments.scad index 097985b..3dece51 100644 --- a/attachments.scad +++ b/attachments.scad @@ -13,7 +13,7 @@ // Default values for attachment code. $tags = ""; $overlap = 0; -$color = undef; +$color = "yellow"; $attach_to = undef; $attach_anchor = [CENTER, CENTER, UP, 0]; @@ -1008,9 +1008,9 @@ module attachable( -// Function: anchorpt() +// Function: named_anchor() // Usage: -// a = anchorpt(name, pos, [orient], [spin]); +// a = named_anchor(name, pos, [orient], [spin]); // Topics: Attachments // See Also: reorient(), attachable() // Description: @@ -1021,7 +1021,7 @@ module attachable( // pos = The [X,Y,Z] position of the anchor. // orient = A vector pointing in the direction parts should project from the anchor position. // spin = If needed, the angle to rotate the part around the direction vector. -function anchorpt(name, pos=[0,0,0], orient=UP, spin=0) = [name, pos, orient, spin]; +function named_anchor(name, pos=[0,0,0], orient=UP, spin=0) = [name, pos, orient, spin]; // Function: reorient() @@ -1759,94 +1759,5 @@ function _attachment_is_shown(tags) = ) shown && !hidden; -// Section: Attachable Text - -// Module: atext() -// Topics: Attachments, Text -// Usage: -// atext(text, [h], [size], [font]); -// Description: -// Creates a 3D text block that can be attached to other attachable objects. -// NOTE: This cannot have children attached to it. -// Arguments: -// text = The text string to instantiate as an object. -// h = The height to which the text should be extruded. Default: 1 -// size = The font size used to create the text block. Default: 10 -// font = The name of the font used to create the text block. Default: "Courier" -// --- -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `"baseline"` -// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP` -// See Also: attachable() -// Extra Anchors: -// "baseline" = Anchors at the baseline of the text, at the start of the string. -// str("baseline",VECTOR) = Anchors at the baseline of the text, modified by the X and Z components of the appended vector. -// Examples: -// atext("Foobar", h=3, size=10); -// atext("Foobar", h=2, size=12, font="Helvetica"); -// atext("Foobar", h=2, anchor=CENTER); -// atext("Foobar", h=2, anchor=str("baseline",CENTER)); -// atext("Foobar", h=2, anchor=str("baseline",BOTTOM+RIGHT)); -// Example: Using line_of() distributor -// txt = "This is the string."; -// line_of(spacing=[10,-5],n=len(txt)) -// atext(txt[$idx], size=10, anchor=CENTER); -// Example: Using arc_of() distributor -// txt = "This is the string"; -// arc_of(r=50, n=len(txt), sa=0, ea=180) -// atext(select(txt,-1-$idx), size=10, anchor=str("baseline",CENTER), spin=-90); -module atext(text, h=1, size=9, font="Courier", anchor="baseline", spin=0, orient=UP) { - no_children($children); - dummy1 = - assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Got: ",anchor)) - assert(is_undef(spin) || is_vector(spin,3) || is_num(spin), str("Got: ",spin)) - assert(is_undef(orient) || is_vector(orient,3), str("Got: ",orient)); - anchor = default(anchor, CENTER); - spin = default(spin, 0); - orient = default(orient, UP); - geom = _attach_geom(size=[size,size,h]); - anch = !any([for (c=anchor) c=="["])? anchor : - let( - parts = str_split(str_split(str_split(anchor,"]")[0],"[")[1],","), - vec = [for (p=parts) str_float(str_strip_leading(p," "))] - ) vec; - ha = anchor=="baseline"? "left" : - anchor==anch && is_string(anchor)? "center" : - anch.x<0? "left" : - anch.x>0? "right" : - "center"; - va = starts_with(anchor,"baseline")? "baseline" : - anchor==anch && is_string(anchor)? "center" : - anch.y<0? "bottom" : - anch.y>0? "top" : - "center"; - base = anchor=="baseline"? CENTER : - anchor==anch && is_string(anchor)? CENTER : - anch.z<0? BOTTOM : - anch.z>0? TOP : - CENTER; - m = _attach_transform(base,spin,orient,geom); - multmatrix(m) { - $parent_anchor = anchor; - $parent_spin = spin; - $parent_orient = orient; - $parent_geom = geom; - $parent_size = _attach_geom_size(geom); - $attach_to = undef; - do_show = _attachment_is_shown($tags); - if (do_show) { - if (is_undef($color)) { - linear_extrude(height=h, center=true) - text(text=text, size=size, halign=ha, valign=va, font=font); - } else color($color) { - $color = undef; - linear_extrude(height=h, center=true) - text(text=text, size=size, halign=ha, valign=va, font=font); - } - } - } -} - - // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/bottlecaps.scad b/bottlecaps.scad index 35aa242..7d88ac0 100644 --- a/bottlecaps.scad +++ b/bottlecaps.scad @@ -67,8 +67,8 @@ module pco1810_neck(wall=2, anchor="support-ring", spin=0, orient=UP) h = support_h+neck_h; thread_h = (thread_od-threadbase_d)/2; anchors = [ - anchorpt("support-ring", [0,0,neck_h-h/2]), - anchorpt("tamper-ring", [0,0,h/2-tamper_base_h]) + named_anchor("support-ring", [0,0,neck_h-h/2]), + named_anchor("tamper-ring", [0,0,h/2-tamper_base_h]) ]; attachable(anchor,spin,orient, d1=neck_d, d2=lip_recess_d+2*lip_leadin_r, l=h, anchors=anchors) { down(h/2) { @@ -173,7 +173,7 @@ module pco1810_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) w = cap_id + 2*wall; h = tamper_ring_h + wall; anchors = [ - anchorpt("inside-top", [0,0,-(h/2-wall)]) + named_anchor("inside-top", [0,0,-(h/2-wall)]) ]; attachable(anchor,spin,orient, d=w, l=h, anchors=anchors) { down(h/2) zrot(45) { @@ -260,8 +260,8 @@ module pco1881_neck(wall=2, anchor="support-ring", spin=0, orient=UP) h = support_h+neck_h; thread_h = (thread_od-threadbase_d)/2; anchors = [ - anchorpt("support-ring", [0,0,neck_h-h/2]), - anchorpt("tamper-ring", [0,0,h/2-tamper_base_h]) + named_anchor("support-ring", [0,0,neck_h-h/2]), + named_anchor("tamper-ring", [0,0,h/2-tamper_base_h]) ]; attachable(anchor,spin,orient, d1=neck_d, d2=lip_recess_d+2*lip_leadin_r, l=h, anchors=anchors) { down(h/2) { @@ -357,7 +357,7 @@ module pco1881_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) w = 28.58 + 2*wall; h = 11.2 + wall; anchors = [ - anchorpt("inside-top", [0,0,-(h/2-wall)]) + named_anchor("inside-top", [0,0,-(h/2-wall)]) ]; attachable(anchor,spin,orient, d=w, l=h, anchors=anchors) { down(h/2) zrot(45) { @@ -448,7 +448,7 @@ module generic_bottle_neck( $fn = segs(33 / 2); thread_h = (thread_od - threadbase_d) / 2; anchors = [ - anchorpt("support-ring", [0, 0, 0 - h / 2]) + named_anchor("support-ring", [0, 0, 0 - h / 2]) ]; attachable(anchor, spin, orient, d1 = neck_d, d2 = 0, l = h, anchors = anchors) { down(h / 2) { @@ -563,7 +563,7 @@ module generic_bottle_cap( heightMagMult = (height > 11.2) ? height / 11.2 : 1; anchors = [ - anchorpt("inside-top", [0, 0, -(h / 2 - wall)]) + named_anchor("inside-top", [0, 0, -(h / 2 - wall)]) ]; attachable(anchor, spin, orient, d = w, l = h, anchors = anchors) { down(h / 2) { diff --git a/coords.scad b/coords.scad index 217a4a6..3bc5ca1 100644 --- a/coords.scad +++ b/coords.scad @@ -18,7 +18,7 @@ // Arguments: // p = The coordinates to force into a 2D vector/point. // fill = Value to fill missing values in vector with. -function point2d(p, fill=0) = [for (i=[0:1]) (p[i]==undef)? fill : p[i]]; +function point2d(p, fill=0) = assert(is_list(p)) [for (i=[0:1]) (p[i]==undef)? fill : p[i]]; // Function: path2d() @@ -49,7 +49,9 @@ function path2d(points) = // Arguments: // p = The coordinates to force into a 3D vector/point. // fill = Value to fill missing values in vector with. -function point3d(p, fill=0) = [for (i=[0:2]) (p[i]==undef)? fill : p[i]]; +function point3d(p, fill=0) = + assert(is_list(p)) + [for (i=[0:2]) (p[i]==undef)? fill : p[i]]; // Function: path3d() @@ -86,7 +88,8 @@ function path3d(points, fill=0) = // Arguments: // p = The coordinates to force into a 4D vector/point. // fill = Value to fill missing values in vector with. -function point4d(p, fill=0) = [for (i=[0:3]) (p[i]==undef)? fill : p[i]]; +function point4d(p, fill=0) = assert(is_list(p)) + [for (i=[0:3]) (p[i]==undef)? fill : p[i]]; // Function: path4d() @@ -133,7 +136,7 @@ function path4d(points, fill=0) = // Arguments: // r = distance from the origin. // theta = angle in degrees, counter-clockwise of X+. -// Examples: +// Example: // xy = polar_to_xy(20,45); // Returns: ~[14.1421365, 14.1421365] // xy = polar_to_xy(40,30); // Returns: ~[34.6410162, 15] // xy = polar_to_xy([40,30]); // Returns: ~[34.6410162, 15] @@ -162,7 +165,7 @@ function polar_to_xy(r,theta=undef) = let( // Arguments: // x = X coordinate. // y = Y coordinate. -// Examples: +// Example: // plr = xy_to_polar(20,30); // plr = xy_to_polar([40,60]); // Example(2D): @@ -317,7 +320,7 @@ function lift_plane(plane, p) = // r = distance from the Z axis. // theta = angle in degrees, counter-clockwise of X+ on the XY plane. // z = Height above XY plane. -// Examples: +// Example: // xyz = cylindrical_to_xyz(20,30,40); // xyz = cylindrical_to_xyz([40,60,50]); function cylindrical_to_xyz(r,theta=undef,z=undef) = let( @@ -340,7 +343,7 @@ function cylindrical_to_xyz(r,theta=undef,z=undef) = let( // x = X coordinate. // y = Y coordinate. // z = Z coordinate. -// Examples: +// Example: // cyl = xyz_to_cylindrical(20,30,40); // cyl = xyz_to_cylindrical([40,50,70]); function xyz_to_cylindrical(x,y=undef,z=undef) = let( @@ -360,7 +363,7 @@ function xyz_to_cylindrical(x,y=undef,z=undef) = let( // r = distance from origin. // theta = angle in degrees, counter-clockwise of X+ on the XY plane. // phi = angle in degrees from the vertical Z+ axis. -// Examples: +// Example: // xyz = spherical_to_xyz(20,30,40); // xyz = spherical_to_xyz([40,60,50]); function spherical_to_xyz(r,theta=undef,phi=undef) = let( @@ -383,7 +386,7 @@ function spherical_to_xyz(r,theta=undef,phi=undef) = let( // x = X coordinate. // y = Y coordinate. // z = Z coordinate. -// Examples: +// Example: // sph = xyz_to_spherical(20,30,40); // sph = xyz_to_spherical([40,50,70]); function xyz_to_spherical(x,y=undef,z=undef) = let( @@ -404,7 +407,7 @@ function xyz_to_spherical(x,y=undef,z=undef) = let( // alt = altitude angle in degrees above the XY plane. // az = azimuth angle in degrees clockwise of Y+ on the XY plane. // r = distance from origin. -// Examples: +// Example: // xyz = altaz_to_xyz(20,30,40); // xyz = altaz_to_xyz([40,60,50]); function altaz_to_xyz(alt,az=undef,r=undef) = let( @@ -429,7 +432,7 @@ function altaz_to_xyz(alt,az=undef,r=undef) = let( // x = X coordinate. // y = Y coordinate. // z = Z coordinate. -// Examples: +// Example: // aa = xyz_to_altaz(20,30,40); // aa = xyz_to_altaz([40,50,70]); function xyz_to_altaz(x,y=undef,z=undef) = let( diff --git a/gears.scad b/gears.scad index 448f209..f44acd7 100644 --- a/gears.scad +++ b/gears.scad @@ -579,12 +579,12 @@ function rack2d( xd = d * sin(pressure_angle), l = teeth * pitch, anchors = [ - anchorpt("adendum", [ 0, a,0], BACK), - anchorpt("adendum-left", [-l/2, a,0], LEFT), - anchorpt("adendum-right", [ l/2, a,0], RIGHT), - anchorpt("dedendum", [ 0,-d,0], BACK), - anchorpt("dedendum-left", [-l/2,-d,0], LEFT), - anchorpt("dedendum-right", [ l/2,-d,0], RIGHT), + named_anchor("adendum", [ 0, a,0], BACK), + named_anchor("adendum-left", [-l/2, a,0], LEFT), + named_anchor("adendum-right", [ l/2, a,0], RIGHT), + named_anchor("dedendum", [ 0,-d,0], BACK), + named_anchor("dedendum-left", [-l/2,-d,0], LEFT), + named_anchor("dedendum-right", [ l/2,-d,0], RIGHT), ], path = [ [-(teeth-1)/2 * pitch + -1/2 * pitch, a-height], @@ -619,12 +619,12 @@ module rack2d( d = dedendum(pitch, clearance); l = teeth * pitch; anchors = [ - anchorpt("adendum", [ 0, a,0], BACK), - anchorpt("adendum-left", [-l/2, a,0], LEFT), - anchorpt("adendum-right", [ l/2, a,0], RIGHT), - anchorpt("dedendum", [ 0,-d,0], BACK), - anchorpt("dedendum-left", [-l/2,-d,0], LEFT), - anchorpt("dedendum-right", [ l/2,-d,0], RIGHT), + named_anchor("adendum", [ 0, a,0], BACK), + named_anchor("adendum-left", [-l/2, a,0], LEFT), + named_anchor("adendum-right", [ l/2, a,0], RIGHT), + named_anchor("dedendum", [ 0,-d,0], BACK), + named_anchor("dedendum-left", [-l/2,-d,0], LEFT), + named_anchor("dedendum-right", [ l/2,-d,0], RIGHT), ]; path = rack2d( pitch = pitch, @@ -994,9 +994,9 @@ function bevel_gear( lvnf = left_handed? vnf1 : xflip(p=vnf1), vnf = down(cpz, p=lvnf), anchors = [ - anchorpt("pitchbase", [0,0,pitchoff-thickness/2]), - anchorpt("flattop", [0,0,thickness/2]), - anchorpt("apex", [0,0,hyp_ang_to_opp(ocone_rad,90-pitch_angle)+pitchoff-thickness/2]) + named_anchor("pitchbase", [0,0,pitchoff-thickness/2]), + named_anchor("flattop", [0,0,thickness/2]), + named_anchor("apex", [0,0,hyp_ang_to_opp(ocone_rad,90-pitch_angle)+pitchoff-thickness/2]) ] ) reorient(anchor,spin,orient, vnf=vnf, extent=true, anchors=anchors, p=vnf); @@ -1048,9 +1048,9 @@ module bevel_gear( axis_zs = [for (p=vnf[0]) if(norm(point2d(p)) < EPSILON) p.z]; thickness = max(axis_zs) - min(axis_zs); anchors = [ - anchorpt("pitchbase", [0,0,pitchoff-thickness/2]), - anchorpt("flattop", [0,0,thickness/2]), - anchorpt("apex", [0,0,adj_ang_to_opp(pr,90-pitch_angle)+pitchoff-thickness/2]) + named_anchor("pitchbase", [0,0,pitchoff-thickness/2]), + named_anchor("flattop", [0,0,thickness/2]), + named_anchor("apex", [0,0,adj_ang_to_opp(pr,90-pitch_angle)+pitchoff-thickness/2]) ]; attachable(anchor,spin,orient, r1=pr, r2=ipr, h=thickness, anchors=anchors) { difference() { @@ -1137,16 +1137,16 @@ module rack( d = dedendum(pitch, clearance); l = teeth * pitch; anchors = [ - anchorpt("adendum", [0,0,a], BACK), - anchorpt("adendum-left", [-l/2,0,a], LEFT), - anchorpt("adendum-right", [ l/2,0,a], RIGHT), - anchorpt("adendum-front", [0,-thickness/2,a], DOWN), - anchorpt("adendum-back", [0, thickness/2,a], UP), - anchorpt("dedendum", [0,0,-d], BACK), - anchorpt("dedendum-left", [-l/2,0,-d], LEFT), - anchorpt("dedendum-right", [ l/2,0,-d], RIGHT), - anchorpt("dedendum-front", [0,-thickness/2,-d], DOWN), - anchorpt("dedendum-back", [0, thickness/2,-d], UP), + named_anchor("adendum", [0,0,a], BACK), + named_anchor("adendum-left", [-l/2,0,a], LEFT), + named_anchor("adendum-right", [ l/2,0,a], RIGHT), + named_anchor("adendum-front", [0,-thickness/2,a], DOWN), + named_anchor("adendum-back", [0, thickness/2,a], UP), + named_anchor("dedendum", [0,0,-d], BACK), + named_anchor("dedendum-left", [-l/2,0,-d], LEFT), + named_anchor("dedendum-right", [ l/2,0,-d], RIGHT), + named_anchor("dedendum-front", [0,-thickness/2,-d], DOWN), + named_anchor("dedendum-back", [0, thickness/2,-d], UP), ]; attachable(anchor,spin,orient, size=[l, thickness, 2*abs(a-height)], anchors=anchors) { skew(sxy=tan(helical)) xrot(90) { @@ -1186,16 +1186,16 @@ function rack( d = dedendum(pitch, clearance), l = teeth * pitch, anchors = [ - anchorpt("adendum", [0,0,a], BACK), - anchorpt("adendum-left", [-l/2,0,a], LEFT), - anchorpt("adendum-right", [ l/2,0,a], RIGHT), - anchorpt("adendum-front", [0,-thickness/2,a], DOWN), - anchorpt("adendum-back", [0, thickness/2,a], UP), - anchorpt("dedendum", [0,0,-d], BACK), - anchorpt("dedendum-left", [-l/2,0,-d], LEFT), - anchorpt("dedendum-right", [ l/2,0,-d], RIGHT), - anchorpt("dedendum-front", [0,-thickness/2,-d], DOWN), - anchorpt("dedendum-back", [0, thickness/2,-d], UP), + named_anchor("adendum", [0,0,a], BACK), + named_anchor("adendum-left", [-l/2,0,a], LEFT), + named_anchor("adendum-right", [ l/2,0,a], RIGHT), + named_anchor("adendum-front", [0,-thickness/2,a], DOWN), + named_anchor("adendum-back", [0, thickness/2,a], UP), + named_anchor("dedendum", [0,0,-d], BACK), + named_anchor("dedendum-left", [-l/2,0,-d], LEFT), + named_anchor("dedendum-right", [ l/2,0,-d], RIGHT), + named_anchor("dedendum-front", [0,-thickness/2,-d], DOWN), + named_anchor("dedendum-back", [0, thickness/2,-d], UP), ], path = rack2d( pitch = pitch, diff --git a/geometry.scad b/geometry.scad index 82b0e33..1dde87c 100644 --- a/geometry.scad +++ b/geometry.scad @@ -943,10 +943,10 @@ function are_points_on_plane(points, plane, eps=EPSILON) = _pointlist_greatest_distance(points,plane) < eps; -// Function: is_above_plane() +/// Internal Function: is_point_above_plane() // Usage: -// test = in_front_of_plane(plane, point); -// Topics: Geometry, Planes +// test = _is_point_above_plane(plane, point); +/// Topics: Geometry, Planes // Description: // Given a plane as [A,B,C,D] where the cartesian equation for that plane // is Ax+By+Cz=D, determines if the given 3D point is on the side of that @@ -955,7 +955,7 @@ function are_points_on_plane(points, plane, eps=EPSILON) = // Arguments: // plane = The [A,B,C,D] coefficients for the first plane equation `Ax+By+Cz=D`. // point = The 3D point to test. -function is_above_plane(plane, point) = +function _is_point_above_plane(plane, point) = point_plane_distance(plane, point) > EPSILON; diff --git a/hull.scad b/hull.scad index 0e2fc51..4035ed0 100644 --- a/hull.scad +++ b/hull.scad @@ -186,7 +186,7 @@ function hull3d_faces(points) = remaining = [for (i = [0:1:len(points)-1]) if (i!=a && i!=b && i!=c && i!=d) i], // Build an initial tetrahedron. // Swap b, c if d is in front of triangle t. - ifop = is_above_plane(plane, points[d]), + ifop = _is_point_above_plane(plane, points[d]), bc = ifop? [c,b] : [b,c], b = bc[0], c = bc[1], diff --git a/linear_bearings.scad b/linear_bearings.scad index cd7b7d2..ff71a68 100644 --- a/linear_bearings.scad +++ b/linear_bearings.scad @@ -86,9 +86,9 @@ module linear_bearing_housing(d=15, l=24, tab=7, gap=5, wall=3, tabwall=5, screw tabh = tab/2+od/2*sqrt(2)-ogap/2; h = od+tab/2; anchors = [ - anchorpt("axis", [0,0,-tab/2/2]), - anchorpt("screw", [0,2-ogap/2,tabh-tab/2/2],FWD), - anchorpt("nut", [0,ogap/2-2,tabh-tab/2/2],FWD) + named_anchor("axis", [0,0,-tab/2/2]), + named_anchor("screw", [0,2-ogap/2,tabh-tab/2/2],FWD), + named_anchor("nut", [0,ogap/2-2,tabh-tab/2/2],FWD) ]; attachable(anchor,spin,orient, size=[l, od, h], anchors=anchors) { down(tab/2/2) diff --git a/math.scad b/math.scad index db4f47d..0e15a4d 100644 --- a/math.scad +++ b/math.scad @@ -35,7 +35,7 @@ NAN = acos(2); // If given a number, returns the square of that number, // If given a vector, returns the sum-of-squares/dot product of the vector elements. // If given a matrix, returns the matrix multiplication of the matrix with itself. -// Examples: +// Example: // sqr(3); // Returns: 9 // sqr(-4); // Returns: 16 // sqr([2,3,4]); // Returns: 29 @@ -50,7 +50,7 @@ function sqr(x) = // foo = log2(x); // Description: // Returns the logarithm base 2 of the value given. -// Examples: +// Example: // log2(0.125); // Returns: -3 // log2(16); // Returns: 4 // log2(256); // Returns: 8 @@ -187,7 +187,7 @@ function lerp(a,b,u) = // b = Second value or vector. // n = The number of values to return. // endpoint = If true, the last value will be exactly `b`. If false, the last value will be one step less. -// Examples: +// Example: // l = lerpn(-4,4,9); // Returns: [-4,-3,-2,-1,0,1,2,3,4] // l = lerpn(-4,4,8,false); // Returns: [-4,-3,-2,-1,0,1,2,3] // l = lerpn(0,1,6); // Returns: [0, 0.2, 0.4, 0.6, 0.8, 1] @@ -350,7 +350,7 @@ function quant(x,y) = // Arguments: // x = The value to quantize. // y = The non-zero integer quantum of the quantization. -// Examples: +// Example: // a = quantdn(12,4); // Returns: 12 // b = quantdn(13,4); // Returns: 12 // c = quantdn(13.1,4); // Returns: 12 @@ -387,7 +387,7 @@ function quantdn(x,y) = // Arguments: // x = The value to quantize. // y = The non-zero integer quantum of the quantization. -// Examples: +// Example: // a = quantup(12,4); // Returns: 12 // b = quantup(13,4); // Returns: 16 // c = quantup(13.1,4); // Returns: 16 @@ -639,7 +639,7 @@ function _cumsum(v,_i=0,_acc=[]) = // Arguments: // a = Angle to get the value for. // sines = List of [amplitude, frequency, offset] items, where the frequency is the number of times the cycle repeats around the circle. -// Examples: +// Example: // v = sum_of_sines(30, [[10,3,0], [5,5.5,60]]); function sum_of_sines(a, sines) = assert( is_finite(a) && is_matrix(sines,undef,3), "Invalid input.") @@ -1225,7 +1225,7 @@ function all_equal(vec,eps=0) = // true if every item of the list is an integer. Otherwise, returns false. // Arguments: // x = The value to check. -// Examples: +// Example: // b = all_integer(true); // Returns: false // b = all_integer("foo"); // Returns: false // b = all_integer(4); // Returns: true diff --git a/metric_screws.scad b/metric_screws.scad index 73cfa78..c460d79 100644 --- a/metric_screws.scad +++ b/metric_screws.scad @@ -395,8 +395,8 @@ module generic_screw( ) { sides = max(12, segs(screwsize/2)); anchors = [ - anchorpt("countersunk", [0,0,(headlen+screwlen)/2-0.01]), - anchorpt("base", [0,0,-headlen/2+screwlen/2]) + named_anchor("countersunk", [0,0,(headlen+screwlen)/2-0.01]), + named_anchor("base", [0,0,-headlen/2+screwlen/2]) ]; attachable(anchor,spin,orient, d=screwsize, l=headlen+screwlen, anchors=anchors) { down(headlen/2-screwlen/2) { @@ -517,9 +517,9 @@ module metric_bolt( ); anchors = [ - anchorpt("countersunk", [0,0,base+sunklen]), - anchorpt("base", [0,0,base]), - anchorpt("shank", [0,0,base-shank]) + named_anchor("countersunk", [0,0,base+sunklen]), + named_anchor("base", [0,0,base]), + named_anchor("shank", [0,0,base-shank]) ]; //color("silver") diff --git a/nema_steppers.scad b/nema_steppers.scad index d385192..d0653dc 100644 --- a/nema_steppers.scad +++ b/nema_steppers.scad @@ -122,14 +122,14 @@ module nema11_stepper(h=24, shaft=5, shaft_len=20, anchor=TOP, spin=0, orient=UP screw_depth = nema_motor_screw_depth(size); anchors = [ - anchorpt("shaft-top", [0,0,h/2+shaft_len]), - anchorpt("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), - anchorpt("shaft-bottom", [0,0,h/2+plinth_height+0.1]), - anchorpt("plinth-top", [0,0,h/2+plinth_height]), - anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), - anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), + named_anchor("shaft-top", [0,0,h/2+shaft_len]), + named_anchor("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), + named_anchor("shaft-bottom", [0,0,h/2+plinth_height+0.1]), + named_anchor("plinth-top", [0,0,h/2+plinth_height]), + named_anchor("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), + named_anchor("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), + named_anchor("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), + named_anchor("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), ]; attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) { up(h/2) @@ -187,14 +187,14 @@ module nema14_stepper(h=24, shaft=5, shaft_len=24, anchor=TOP, spin=0, orient=UP screw_depth = nema_motor_screw_depth(size); anchors = [ - anchorpt("shaft-top", [0,0,h/2+shaft_len]), - anchorpt("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), - anchorpt("shaft-bottom", [0,0,h/2+plinth_height+0.1]), - anchorpt("plinth-top", [0,0,h/2+plinth_height]), - anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), - anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), + named_anchor("shaft-top", [0,0,h/2+shaft_len]), + named_anchor("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), + named_anchor("shaft-bottom", [0,0,h/2+plinth_height+0.1]), + named_anchor("plinth-top", [0,0,h/2+plinth_height]), + named_anchor("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), + named_anchor("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), + named_anchor("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), + named_anchor("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), ]; attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) { up(h/2) @@ -252,14 +252,14 @@ module nema17_stepper(h=34, shaft=5, shaft_len=20, anchor=TOP, spin=0, orient=UP screw_depth = nema_motor_screw_depth(size); anchors = [ - anchorpt("shaft-top", [0,0,h/2+shaft_len]), - anchorpt("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), - anchorpt("shaft-bottom", [0,0,h/2+plinth_height+0.1]), - anchorpt("plinth-top", [0,0,h/2+plinth_height]), - anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), - anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), + named_anchor("shaft-top", [0,0,h/2+shaft_len]), + named_anchor("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), + named_anchor("shaft-bottom", [0,0,h/2+plinth_height+0.1]), + named_anchor("plinth-top", [0,0,h/2+plinth_height]), + named_anchor("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), + named_anchor("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), + named_anchor("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), + named_anchor("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), ]; attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) { up(h/2) @@ -337,14 +337,14 @@ module nema23_stepper(h=50, shaft=6.35, shaft_len=25, anchor=TOP, spin=0, orient screw_inset = motor_width - screw_spacing + 1; anchors = [ - anchorpt("shaft-top", [0,0,h/2+shaft_len]), - anchorpt("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), - anchorpt("shaft-bottom", [0,0,h/2+plinth_height+0.1]), - anchorpt("plinth-top", [0,0,h/2+plinth_height]), - anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), - anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), + named_anchor("shaft-top", [0,0,h/2+shaft_len]), + named_anchor("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), + named_anchor("shaft-bottom", [0,0,h/2+plinth_height+0.1]), + named_anchor("plinth-top", [0,0,h/2+plinth_height]), + named_anchor("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), + named_anchor("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), + named_anchor("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), + named_anchor("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), ]; attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) { up(h/2) @@ -404,14 +404,14 @@ module nema34_stepper(h=75, shaft=12.7, shaft_len=32, anchor=TOP, spin=0, orient screw_inset = motor_width - screw_spacing + 1; anchors = [ - anchorpt("shaft-top", [0,0,h/2+shaft_len]), - anchorpt("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), - anchorpt("shaft-bottom", [0,0,h/2+plinth_height+0.1]), - anchorpt("plinth-top", [0,0,h/2+plinth_height]), - anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), - anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), + named_anchor("shaft-top", [0,0,h/2+shaft_len]), + named_anchor("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), + named_anchor("shaft-bottom", [0,0,h/2+plinth_height+0.1]), + named_anchor("plinth-top", [0,0,h/2+plinth_height]), + named_anchor("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), + named_anchor("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), + named_anchor("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), + named_anchor("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), ]; attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) { up(h/2) @@ -472,10 +472,10 @@ module nema_mount_holes(size=17, depth=5, l=5, anchor=CENTER, spin=0, orient=UP) screw_size = nema_motor_screw_size(size)+$slop; anchors = [ - anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, depth/2]), - anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, depth/2]), - anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, depth/2]), - anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, depth/2]), + named_anchor("screw1", [+screw_spacing/2, +screw_spacing/2, depth/2]), + named_anchor("screw2", [-screw_spacing/2, +screw_spacing/2, depth/2]), + named_anchor("screw3", [-screw_spacing/2, -screw_spacing/2, depth/2]), + named_anchor("screw4", [+screw_spacing/2, -screw_spacing/2, depth/2]), ]; screwfn = quantup(max(8,segs(screw_size/2)),4); plinthfn = quantup(max(8,segs(plinth_diam/2)),4); diff --git a/partitions.scad b/partitions.scad index 53741f8..418f6d4 100644 --- a/partitions.scad +++ b/partitions.scad @@ -106,6 +106,8 @@ module partition_mask(l=100, w=100, h=100, cutsize=10, cutpath="jigsaw", gap=0, // partition_cut_mask(l, w, h, [cutsize], [cutpath], [gap], [inverse], [spin], [orient]); // Description: // Creates a mask that you can use to difference with an object to cut it into two sub-parts that can be assembled. +// The `$slop` value is important to get the proper fit and should probably be smaller than 0.2. The examples below +// use larger values to make the mask easier to see. // Arguments: // l = The length of the cut axis. // w = The width of the part to be masked, back from the cut plane. @@ -115,20 +117,20 @@ module partition_mask(l=100, w=100, h=100, cutsize=10, cutpath="jigsaw", gap=0, // gap = Empty gaps between cutpath iterations. Default: 0 // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0` // orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP` -// $slop = The width of the cut mask, to correct for printer-specific fitting. Min: 0.1. +// $slop = The width of the cut mask, to correct for printer-specific fitting. Min: 0.05. // Examples: // partition_cut_mask(gap=0, cutpath="dovetail"); // partition_cut_mask(gap=30, cutpath="dovetail"); // partition_cut_mask(gap=30, cutsize=15, cutpath="dovetail"); // partition_cut_mask(gap=30, cutsize=[20,20], cutpath="dovetail"); // Examples(2DMed): -// partition_cut_mask(cutpath="sawtooth"); -// partition_cut_mask(cutpath="sinewave"); -// partition_cut_mask(cutpath="comb"); -// partition_cut_mask(cutpath="finger"); -// partition_cut_mask(cutpath="dovetail"); -// partition_cut_mask(cutpath="hammerhead"); -// partition_cut_mask(cutpath="jigsaw"); +// partition_cut_mask(cutpath="sawtooth",$slop=0.5); +// partition_cut_mask(cutpath="sinewave",$slop=0.5); +// partition_cut_mask(cutpath="comb",$slop=0.5); +// partition_cut_mask(cutpath="finger",$slop=0.5); +// partition_cut_mask(cutpath="dovetail",$slop=1); +// partition_cut_mask(cutpath="hammerhead",$slop=1); +// partition_cut_mask(cutpath="jigsaw",$slop=0.5); module partition_cut_mask(l=100, h=100, cutsize=10, cutpath="jigsaw", gap=0, anchor=CENTER, spin=0, orient=UP) { cutsize = is_vector(cutsize)? cutsize : [cutsize*2, cutsize]; diff --git a/paths.scad b/paths.scad index a0e7c52..551769b 100644 --- a/paths.scad +++ b/paths.scad @@ -17,22 +17,22 @@ // All vectors must of the same size, and may only contain numbers that are not inf or nan. // By default the vectors in a path must be 2d or 3d. Set the `dim` parameter to specify a list // of allowed dimensions, or set it to `undef` to allow any dimension. -// Examples: -// is_path([[3,4],[5,6]]); // Returns true -// is_path([[3,4]]); // Returns false -// is_path([[3,4],[4,5]],2); // Returns true -// is_path([[3,4,3],[5,4,5]],2); // Returns false -// is_path([[3,4,3],[5,4,5]],2); // Returns false -// is_path([[3,4,5],undef,[4,5,6]]); // Returns false -// is_path([[3,5],[undef,undef],[4,5]]); // Returns false -// is_path([[3,4],[5,6],[5,3]]); // Returns true -// is_path([3,4,5,6,7,8]); // Returns false -// is_path([[3,4],[5,6]], dim=[2,3]);// Returns true -// is_path([[3,4],[5,6]], dim=[1,3]);// Returns false -// is_path([[3,4],"hello"], fast=true); // Returns true -// is_path([[3,4],[3,4,5]]); // Returns false -// is_path([[1,2,3,4],[2,3,4,5]]); // Returns false -// is_path([[1,2,3,4],[2,3,4,5]],undef);// Returns true +// Example: +// bool1 = is_path([[3,4],[5,6]]); // Returns true +// bool2 = is_path([[3,4]]); // Returns false +// bool3 = is_path([[3,4],[4,5]],2); // Returns true +// bool4 = is_path([[3,4,3],[5,4,5]],2); // Returns false +// bool5 = is_path([[3,4,3],[5,4,5]],2); // Returns false +// bool6 = is_path([[3,4,5],undef,[4,5,6]]); // Returns false +// bool7 = is_path([[3,5],[undef,undef],[4,5]]); // Returns false +// bool8 = is_path([[3,4],[5,6],[5,3]]); // Returns true +// bool9 = is_path([3,4,5,6,7,8]); // Returns false +// bool10 = is_path([[3,4],[5,6]], dim=[2,3]);// Returns true +// bool11 = is_path([[3,4],[5,6]], dim=[1,3]);// Returns false +// bool12 = is_path([[3,4],"hello"], fast=true); // Returns true +// bool13 = is_path([[3,4],[3,4,5]]); // Returns false +// bool14 = is_path([[1,2,3,4],[2,3,4,5]]); // Returns false +// bool15 = is_path([[1,2,3,4],[2,3,4,5]],undef);// Returns true // Arguments: // list = list to check // dim = list of allowed dimensions of the vectors in the path. Default: [2,3] @@ -1399,182 +1399,6 @@ module path_extrude(path, convexity=10, clipsize=100) { } -function _cut_interp(pathcut, path, data) = - [for(entry=pathcut) - let( - a = path[entry[1]-1], - b = path[entry[1]], - c = entry[0], - i = max_index(v_abs(b-a)), - factor = (c[i]-a[i])/(b[i]-a[i]) - ) - (1-factor)*data[entry[1]-1]+ factor * data[entry[1]] - ]; - - -// Module: path_text() -// Usage: -// path_text(path, text, [size], [thickness], [font], [lettersize], [offset], [reverse], [normal], [top], [textmetrics]) -// Description: -// Place the text letter by letter onto the specified path using textmetrics (if available and requested) -// or user specified letter spacing. The path can be 2D or 3D. In 2D the text appears along the path with letters upright -// as determined by the path direction. In 3D by default letters are positioned on the tangent line to the path with the path normal -// pointing toward the reader. The path normal points away from the center of curvature (the opposite of the normal produced -// by path_normals()). Note that this means that if the center of curvature switches sides the text will flip upside down. -// If you want text on such a path you must supply your own normal or top vector. -// . -// Text appears starting at the beginning of the path, so if the 3D path moves right to left -// then a left-to-right reading language will display in the wrong order. (For a 2D path text will appear upside down.) -// The text for a 3D path appears positioned to be read from "outside" of the curve (from a point on the other side of the -// curve from the center of curvature). If you need the text to read properly from the inside, you can set reverse to -// true to flip the text, or supply your own normal. -// . -// If you do not have the experimental textmetrics feature enabled then you must specify the space for the letters -// using lettersize, which can be a scalar or array. You will have the easiest time getting good results by using -// a monospace font such as Courier. Note that even with text metrics, spacing may be different because path_text() -// doesn't do kerning to adjust positions of individual glyphs. Also if your font has ligatures they won't be used. -// . -// By default letters appear centered on the path. The offset can be specified to shift letters toward the reader (in -// the direction of the normal). -// . -// You can specify your own normal by setting `normal` to a direction or a list of directions. Your normal vector should -// point toward the reader. You can also specify -// top, which directs the top of the letters in a desired direction. If you specify your own directions and they -// are not perpendicular to the path then the direction you specify will take priority and the -// letters will not rest on the tangent line of the path. Note that the normal or top directions that you -// specify must not be parallel to the path. -// Arguments: -// path = path to place the text on -// text = text to create -// size = font size -// thickness = thickness of letters (not allowed for 2D path) -// font = font to use -// --- -// lettersize = scalar or array giving size of letters -// offset = distance to shift letters "up" (towards the reader). Not allowed for 2D path. Default: 0 -// normal = direction or list of directions pointing towards the reader of the text. Not allowed for 2D path. -// top = direction or list of directions pointing toward the top of the text -// reverse = reverse the letters if true. Not allowed for 2D path. Default: false -// textmetrics = if set to true and lettersize is not given then use the experimental textmetrics feature. You must be running a dev snapshot that includes this feature and have the feature turned on in your preferences. Default: false -// Example: The examples use Courier, a monospaced font. The width is 1/1.2 times the specified size for this font. This text could wrap around a cylinder. -// path = path3d(arc(100, r=25, angle=[245, 370])); -// color("red")stroke(path, width=.3); -// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2); -// Example: By setting the normal to UP we can get text that lies flat, for writing around the edge of a disk: -// path = path3d(arc(100, r=25, angle=[245, 370])); -// color("red")stroke(path, width=.3); -// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, normal=UP); -// Example: If we want text that reads from the other side we can use reverse. Note we have to reverse the direction of the path and also set the reverse option. -// path = reverse(path3d(arc(100, r=25, angle=[65, 190]))); -// color("red")stroke(path, width=.3); -// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, reverse=true); -// Example: text debossed onto a cylinder in a spiral. The text is 1 unit deep because it is half in, half out. -// text = ("A long text example to wrap around a cylinder, possibly for a few times."); -// L = 5*len(text); -// maxang = 360*L/(PI*50); -// spiral = [for(a=[0:1:maxang]) [25*cos(a), 25*sin(a), 10-30/maxang*a]]; -// difference(){ -// cyl(d=50, l=50, $fn=120); -// path_text(spiral, text, size=5, lettersize=5/1.2, font="Courier", thickness=2); -// } -// Example: Same example but text embossed. Make sure you have enough depth for the letters to fully overlap the object. -// text = ("A long text example to wrap around a cylinder, possibly for a few times."); -// L = 5*len(text); -// maxang = 360*L/(PI*50); -// spiral = [for(a=[0:1:maxang]) [25*cos(a), 25*sin(a), 10-30/maxang*a]]; -// cyl(d=50, l=50, $fn=120); -// path_text(spiral, text, size=5, lettersize=5/1.2, font="Courier", thickness=2); -// Example: Here the text baseline sits on the path. (Note the default orientation makes text readable from below, so we specify the normal.) -// path = arc(100, points = [[-20, 0, 20], [0,0,5], [20,0,20]]); -// color("red")stroke(path,width=.2); -// path_text(path, "Example Text", size=5, lettersize=5/1.2, font="Courier", normal=FRONT); -// Example: If we use top to orient the text upward, the text baseline is no longer aligned with the path. -// path = arc(100, points = [[-20, 0, 20], [0,0,5], [20,0,20]]); -// color("red")stroke(path,width=.2); -// path_text(path, "Example Text", size=5, lettersize=5/1.2, font="Courier", top=UP); -// Example: This sine wave wrapped around the cylinder has a twisting normal that produces wild letter layout. We fix it with a custom normal which is different at every path point. -// path = [for(theta = [0:360]) [25*cos(theta), 25*sin(theta), 4*cos(theta*4)]]; -// normal = [for(theta = [0:360]) [cos(theta), sin(theta),0]]; -// zrot(-120) -// difference(){ -// cyl(r=25, h=20, $fn=120); -// path_text(path, "A sine wave wiggles", font="Courier", lettersize=5/1.2, size=5, normal=normal); -// } -// Example: The path center of curvature changes, and the text flips. -// path = zrot(-120,p=path3d( concat(arc(100, r=25, angle=[0,90]), back(50,p=arc(100, r=25, angle=[268, 180]))))); -// color("red")stroke(path,width=.2); -// path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Courier", thickness=2); -// Example: We can fix it with top: -// path = zrot(-120,p=path3d( concat(arc(100, r=25, angle=[0,90]), back(50,p=arc(100, r=25, angle=[268, 180]))))); -// color("red")stroke(path,width=.2); -// path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Courier", thickness=2, top=UP); -// Example(2D): With a 2D path instead of 3D there's no ambiguity about direction and it works by default: -// path = zrot(-120,p=concat(arc(100, r=25, angle=[0,90]), back(50,p=arc(100, r=25, angle=[268, 180])))); -// color("red")stroke(path,width=.2); -// path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Courier"); -module path_text(path, text, font, size, thickness, lettersize, offset=0, reverse=false, normal, top, textmetrics=false) -{ - dummy2=assert(is_path(path,[2,3]),"Must supply a 2d or 3d path") - assert(num_defined([normal,top])<=1, "Cannot define both \"normal\" and \"top\""); - dim = len(path[0]); - normalok = is_undef(normal) || is_vector(normal,3) || (is_path(normal,3) && len(normal)==len(path)); - topok = is_undef(top) || is_vector(top,dim) || (dim==2 && is_vector(top,3) && top[2]==0) - || (is_path(top,dim) && len(top)==len(path)); - dummy4 = assert(dim==3 || is_undef(thickness), "Cannot give a thickness with 2d path") - assert(dim==3 || !reverse, "Reverse not allowed with 2d path") - assert(dim==3 || offset==0, "Cannot give offset with 2d path") - assert(dim==3 || is_undef(normal), "Cannot define \"normal\" for a 2d path, only \"top\"") - assert(normalok,"\"normal\" must be a vector or path compatible with the given path") - assert(topok,"\"top\" must be a vector or path compatible with the given path"); - thickness = first_defined([thickness,1]); - normal = is_vector(normal) ? repeat(normal, len(path)) - : is_def(normal) ? normal - : undef; - - top = is_vector(top) ? repeat(dim==2?point2d(top):top, len(path)) - : is_def(top) ? top - : undef; - - lsize = is_def(lettersize) ? force_list(lettersize, len(text)) - : textmetrics ? [for(letter=text) let(t=textmetrics(letter, font=font, size=size)) t.advance[0]] - : assert(false, "textmetrics disabled: Must specify letter size"); - - dummy1 = assert(sum(lsize)<=path_length(path),"Path is too short for the text"); - - pts = path_cut_points(path, add_scalar([0, each cumsum(lsize)],lsize[0]/2), direction=true); - - usernorm = is_def(normal); - usetop = is_def(top); - - normpts = is_undef(normal) ? (reverse?1:-1)*subindex(pts,3) : _cut_interp(pts,path, normal); - toppts = is_undef(top) ? undef : _cut_interp(pts,path,top); - for(i=idx(text)) - let( tangent = pts[i][2] ) - assert(!usetop || !approx(tangent*toppts[i],norm(top[i])*norm(tangent)), - str("Specified top direction parallel to path at character ",i)) - assert(usetop || !approx(tangent*normpts[i],norm(normpts[i])*norm(tangent)), - str("Specified normal direction parallel to path at character ",i)) - let( - adjustment = usetop ? (tangent*toppts[i])*toppts[i]/(toppts[i]*toppts[i]) - : usernorm ? (tangent*normpts[i])*normpts[i]/(normpts[i]*normpts[i]) - : [0,0,0] - ) - move(pts[i][0]) - if(dim==3){ - frame_map(x=tangent-adjustment, - z=usetop ? undef : normpts[i], - y=usetop ? toppts[i] : undef) - up(offset-thickness/2) - linear_extrude(height=thickness) - left(lsize[0]/2)text(text[i], font=font, size=size); - } else { - frame_map(x=point3d(tangent-adjustment), y=point3d(usetop ? toppts[i] : -normpts[i])) - left(lsize[0]/2)text(text[i], font=font, size=size); - } -} - - - // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/primitives.scad b/primitives.scad deleted file mode 100644 index 87e04b0..0000000 --- a/primitives.scad +++ /dev/null @@ -1,307 +0,0 @@ -////////////////////////////////////////////////////////////////////// -// LibFile: primitives.scad -// The basic built-in shapes, reworked to integrate better with -// other BOSL2 library shapes and utilities. -// Includes: -// include -////////////////////////////////////////////////////////////////////// - - -// Section: 2D Primitives - - -// Function&Module: square() -// Topics: Shapes (2D), Path Generators (2D) -// Usage: As a Built-in Module -// square(size, [center]); -// Usage: As a Function -// path = square(size, [center]); -// See Also: rect() -// Description: -// When called as the builtin module, creates a 2D square or rectangle of the given size. -// When called as a function, returns a 2D path/list of points for a square/rectangle of the given size. -// Arguments: -// size = The size of the square to create. If given as a scalar, both X and Y will be the same size. -// center = If given and true, overrides `anchor` to be `CENTER`. If given and false, overrides `anchor` to be `FRONT+LEFT`. -// --- -// anchor = (Function only) Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = (Function only) Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// Example(2D): -// square(40); -// Example(2D): Centered -// square([40,30], center=true); -// Example(2D): Called as Function -// path = square([40,30], anchor=FRONT, spin=30); -// stroke(path, closed=true); -// move_copies(path) color("blue") circle(d=2,$fn=8); -function square(size=1, center, anchor, spin=0) = - let( - anchor = get_anchor(anchor, center, [-1,-1], [-1,-1]), - size = is_num(size)? [size,size] : point2d(size), - path = [ - [ size.x,-size.y], - [-size.x,-size.y], - [-size.x, size.y], - [ size.x, size.y] - ] / 2 - ) reorient(anchor,spin, two_d=true, size=size, p=path); - - -// Function&Module: circle() -// Topics: Shapes (2D), Path Generators (2D) -// Usage: As a Built-in Module -// circle(r|d=, ...); -// Usage: As a Function -// path = circle(r|d=, ...); -// See Also: oval() -// Description: -// When called as the builtin module, creates a 2D polygon that approximates a circle of the given size. -// When called as a function, returns a 2D list of points (path) for a polygon that approximates a circle of the given size. -// Arguments: -// r = The radius of the circle to create. -// d = The diameter of the circle to create. -// --- -// anchor = (Function only) Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = (Function only) Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// Example(2D): By Radius -// circle(r=25); -// Example(2D): By Diameter -// circle(d=50); -// Example(NORENDER): Called as Function -// path = circle(d=50, anchor=FRONT, spin=45); -function circle(r, d, anchor=CENTER, spin=0) = - let( - r = get_radius(r=r, d=d, dflt=1), - sides = segs(r), - path = [for (i=[0:1:sides-1]) let(a=360-i*360/sides) r*[cos(a),sin(a)]] - ) reorient(anchor,spin, two_d=true, r=r, p=path); - - - -// Section: Primitive 3D Shapes - - -// Function&Module: cube() -// Topics: Shapes (3D), Attachable, VNF Generators -// Usage: As Module -// cube(size, [center], ...); -// Usage: With Attachments -// cube(size, [center], ...) { attachments } -// Usage: As Function -// vnf = cube(size, [center], ...); -// See Also: cuboid(), prismoid() -// Description: -// Creates a 3D cubic object with support for anchoring and attachments. -// This can be used as a drop-in replacement for the built-in `cube()` module. -// When called as a function, returns a [VNF](vnf.scad) for a cube. -// Arguments: -// size = The size of the cube. -// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=ALLNEG`. -// --- -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// Example: Simple cube. -// cube(40); -// Example: Rectangular cube. -// cube([20,40,50]); -// Example: Anchoring. -// cube([20,40,50], anchor=BOTTOM+FRONT); -// Example: Spin. -// cube([20,40,50], anchor=BOTTOM+FRONT, spin=30); -// Example: Orientation. -// cube([20,40,50], anchor=BOTTOM+FRONT, spin=30, orient=FWD); -// Example: Standard Connectors. -// cube(40, center=true) show_anchors(); -// Example: Called as Function -// vnf = cube([20,40,50]); -// vnf_polyhedron(vnf); -module cube(size=1, center, anchor, spin=0, orient=UP) -{ - anchor = get_anchor(anchor, center, ALLNEG, ALLNEG); - size = scalar_vec3(size); - attachable(anchor,spin,orient, size=size) { - if (size.z > 0) { - linear_extrude(height=size.z, center=true, convexity=2) { - square([size.x,size.y], center=true); - } - } - children(); - } -} - -function cube(size=1, center, anchor, spin=0, orient=UP) = - let( - siz = scalar_vec3(size), - anchor = get_anchor(anchor, center, ALLNEG, ALLNEG), - unscaled = [ - [-1,-1,-1],[1,-1,-1],[1,1,-1],[-1,1,-1], - [-1,-1, 1],[1,-1, 1],[1,1, 1],[-1,1, 1], - ]/2, - verts = is_num(size)? unscaled * size : - is_vector(size,3)? [for (p=unscaled) v_mul(p,size)] : - assert(is_num(size) || is_vector(size,3)), - faces = [ - [0,1,2], [0,2,3], //BOTTOM - [0,4,5], [0,5,1], //FRONT - [1,5,6], [1,6,2], //RIGHT - [2,6,7], [2,7,3], //BACK - [3,7,4], [3,4,0], //LEFT - [6,4,7], [6,5,4] //TOP - ] - ) [reorient(anchor,spin,orient, size=siz, p=verts), faces]; - - -// Function&Module: cylinder() -// Topics: Shapes (3D), Attachable, VNF Generators -// Usage: As Module -// cylinder(h, r=/d=, [center=], ...); -// cylinder(h, r1/d1=, r2/d2=, [center=], ...); -// Usage: With Attachments -// cylinder(h, r=/d=, [center=]) {attachments} -// Usage: As Function -// vnf = cylinder(h, r=/d=, [center=], ...); -// vnf = cylinder(h, r1/d1=, r2/d2=, [center=], ...); -// See Also: cyl() -// Description: -// Creates a 3D cylinder or conic object with support for anchoring and attachments. -// This can be used as a drop-in replacement for the built-in `cylinder()` module. -// When called as a function, returns a [VNF](vnf.scad) for a cylinder. -// Arguments: -// l / h = The height of the cylinder. -// r1 = The bottom radius of the cylinder. (Before orientation.) -// r2 = The top radius of the cylinder. (Before orientation.) -// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=BOTTOM`. -// --- -// d1 = The bottom diameter of the cylinder. (Before orientation.) -// d2 = The top diameter of the cylinder. (Before orientation.) -// r = The radius of the cylinder. -// d = The diameter of the cylinder. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// Example: By Radius -// xdistribute(30) { -// cylinder(h=40, r=10); -// cylinder(h=40, r1=10, r2=5); -// } -// Example: By Diameter -// xdistribute(30) { -// cylinder(h=40, d=25); -// cylinder(h=40, d1=25, d2=10); -// } -// Example(Med): Anchoring -// cylinder(h=40, r1=10, r2=5, anchor=BOTTOM+FRONT); -// Example(Med): Spin -// cylinder(h=40, r1=10, r2=5, anchor=BOTTOM+FRONT, spin=45); -// Example(Med): Orient -// cylinder(h=40, r1=10, r2=5, anchor=BOTTOM+FRONT, spin=45, orient=FWD); -// Example(Big): Standard Connectors -// xdistribute(40) { -// cylinder(h=30, d=25) show_anchors(); -// cylinder(h=30, d1=25, d2=10) show_anchors(); -// } -module cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP) -{ - anchor = get_anchor(anchor, center, BOTTOM, BOTTOM); - 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); - l = first_defined([h, l, 1]); - sides = segs(max(r1,r2)); - attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { - if (r1 > r2) { - if (l > 0) { - linear_extrude(height=l, center=true, convexity=2, scale=r2/r1) { - circle(r=r1); - } - } - } else { - zflip() { - if (l > 0) { - linear_extrude(height=l, center=true, convexity=2, scale=r1/r2) { - circle(r=r2); - } - } - } - } - children(); - } -} - -function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP) = - let( - anchor = get_anchor(anchor, center, BOTTOM, BOTTOM), - 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), - l = first_defined([h, l, 1]), - sides = segs(max(r1,r2)), - verts = [ - for (i=[0:1:sides-1]) let(a=360*(1-i/sides)) [r1*cos(a),r1*sin(a),-l/2], - for (i=[0:1:sides-1]) let(a=360*(1-i/sides)) [r2*cos(a),r2*sin(a), l/2], - ], - faces = [ - [for (i=[0:1:sides-1]) sides-1-i], - for (i=[0:1:sides-1]) [i, ((i+1)%sides)+sides, i+sides], - for (i=[0:1:sides-1]) [i, (i+1)%sides, ((i+1)%sides)+sides], - [for (i=[0:1:sides-1]) sides+i] - ] - ) [reorient(anchor,spin,orient, l=l, r1=r1, r2=r2, p=verts), faces]; - - - -// Function&Module: sphere() -// Topics: Shapes (3D), Attachable, VNF Generators -// Usage: As Module -// sphere(r|d=, [circum=], [style=], ...); -// Usage: With Attachments -// sphere(r|d=, ...) { attachments } -// Usage: As Function -// vnf = sphere(r|d=, [circum=], [style=], ...); -// See Also: spheroid() -// Description: -// Creates a sphere object, with support for anchoring and attachments. -// This is a drop-in replacement for the built-in `sphere()` module. -// When called as a function, returns a [VNF](vnf.scad) for a sphere. -// Arguments: -// r = Radius of the sphere. -// --- -// d = Diameter of the sphere. -// circum = If true, the sphere is made large enough to circumscribe the sphere of the ideal side. Otherwise inscribes. Default: false (inscribes) -// style = The style of the sphere's construction. One of "orig", "aligned", "stagger", "octa", or "icosa". Default: "orig" -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// Example: By Radius -// sphere(r=50); -// Example: By Diameter -// sphere(d=100); -// Example: style="orig" -// sphere(d=100, style="orig", $fn=10); -// Example: style="aligned" -// sphere(d=100, style="aligned", $fn=10); -// Example: style="stagger" -// sphere(d=100, style="stagger", $fn=10); -// Example: style="icosa" -// sphere(d=100, style="icosa", $fn=10); -// // In "icosa" style, $fn is quantized -// // to the nearest multiple of 5. -// Example: Anchoring -// sphere(d=100, anchor=FRONT); -// Example: Spin -// sphere(d=100, anchor=FRONT, spin=45); -// Example: Orientation -// sphere(d=100, anchor=FRONT, spin=45, orient=FWD); -// Example: Standard Connectors -// sphere(d=50) show_anchors(); -// Example: Called as Function -// vnf = sphere(d=100, style="icosa"); -// vnf_polyhedron(vnf); -module sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient=UP) - spheroid(r=r, d=d, circum=circum, style=style, anchor=anchor, spin=spin, orient=orient) children(); - - -function sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient=UP) = - spheroid(r=r, d=d, circum=circum, style=style, anchor=anchor, spin=spin, orient=orient); - - -// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/shapes2d.scad b/shapes2d.scad index 51b70e2..1849c83 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -1,13 +1,15 @@ ////////////////////////////////////////////////////////////////////// // LibFile: shapes2d.scad -// This file lets you create regular polygons +// This file includes redefinitions of the core modules to +// work with attachment. You can also create regular polygons // with optional rounded corners and alignment features not // available with circle(). The file also provides teardrop2d, // which is useful for 3d printable holes. Lastly you can use the // masks to produce edge treatments common in furniture from the // simple roundover or cove molding to the more elaborate ogee. // Many of the commands have module forms that produce geometry and -// function forms that produce a path. +// function forms that produce a path. This file defines function +// forms of the core OpenSCAD modules that produce paths. // Includes: // include ////////////////////////////////////////////////////////////////////// @@ -15,6 +17,44 @@ // Section: 2D Primitives +// Function&Module: square() +// Topics: Shapes (2D), Path Generators (2D) +// Usage: As a Built-in Module +// square(size, [center]); +// Usage: As a Function +// path = square(size, [center]); +// See Also: rect() +// Description: +// When called as the builtin module, creates a 2D square or rectangle of the given size. +// When called as a function, returns a 2D path/list of points for a square/rectangle of the given size. +// Arguments: +// size = The size of the square to create. If given as a scalar, both X and Y will be the same size. +// center = If given and true, overrides `anchor` to be `CENTER`. If given and false, overrides `anchor` to be `FRONT+LEFT`. +// --- +// anchor = (Function only) Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = (Function only) Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// Example(2D): +// square(40); +// Example(2D): Centered +// square([40,30], center=true); +// Example(2D): Called as Function +// path = square([40,30], anchor=FRONT, spin=30); +// stroke(path, closed=true); +// move_copies(path) color("blue") circle(d=2,$fn=8); +function square(size=1, center, anchor, spin=0) = + let( + anchor = get_anchor(anchor, center, [-1,-1], [-1,-1]), + size = is_num(size)? [size,size] : point2d(size), + path = [ + [ size.x,-size.y], + [-size.x,-size.y], + [-size.x, size.y], + [ size.x, size.y] + ] / 2 + ) reorient(anchor,spin, two_d=true, size=size, p=path); + + + // Function&Module: rect() // Usage: As Module // rect(size, [center], [rounding], [chamfer], ...); @@ -119,6 +159,38 @@ function rect(size=1, center, rounding=0, chamfer=0, anchor, spin=0) = reorient(anchor,spin, two_d=true, size=size, p=path); +// Function&Module: circle() +// Topics: Shapes (2D), Path Generators (2D) +// Usage: As a Built-in Module +// circle(r|d=, ...); +// Usage: As a Function +// path = circle(r|d=, ...); +// See Also: oval() +// Description: +// When called as the builtin module, creates a 2D polygon that approximates a circle of the given size. +// When called as a function, returns a 2D list of points (path) for a polygon that approximates a circle of the given size. +// Arguments: +// r = The radius of the circle to create. +// d = The diameter of the circle to create. +// --- +// anchor = (Function only) Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = (Function only) Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// Example(2D): By Radius +// circle(r=25); +// Example(2D): By Diameter +// circle(d=50); +// Example(NORENDER): Called as Function +// path = circle(d=50, anchor=FRONT, spin=45); +function circle(r, d, anchor=CENTER, spin=0) = + let( + r = get_radius(r=r, d=d, dflt=1), + sides = segs(r), + path = [for (i=[0:1:sides-1]) let(a=360-i*360/sides) r*[cos(a),sin(a)]] + ) reorient(anchor,spin, two_d=true, r=r, p=path); + + + + // Function&Module: oval() // Usage: // oval(r|d=, [realign=], [circum=]) @@ -279,8 +351,8 @@ function regular_ngon(n=6, r, d, or, od, ir, id, side, rounding=0, realign=false tipp = apply(mat, polar_to_xy(r-inset+rounding,a1)), pos = (p1+p2)/2 ) each [ - anchorpt(str("tip",i), tipp, unit(tipp,BACK), 0), - anchorpt(str("side",i), pos, unit(pos,BACK), 0), + named_anchor(str("tip",i), tipp, unit(tipp,BACK), 0), + named_anchor(str("side",i), pos, unit(pos,BACK), 0), ] ] ) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path, anchors=anchors); @@ -308,8 +380,8 @@ module regular_ngon(n=6, r, d, or, od, ir, id, side, rounding=0, realign=false, tipp = apply(mat, polar_to_xy(r-inset+rounding,a1)), pos = (p1+p2)/2 ) each [ - anchorpt(str("tip",i), tipp, unit(tipp,BACK), 0), - anchorpt(str("side",i), pos, unit(pos,BACK), 0), + named_anchor(str("tip",i), tipp, unit(tipp,BACK), 0), + named_anchor(str("side",i), pos, unit(pos,BACK), 0), ] ]; path = regular_ngon(n=n, r=r, rounding=rounding, _mat=mat, _anchs=anchors); @@ -694,9 +766,9 @@ function star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit p3 = apply(mat, polar_to_xy(r,a3)), pos = (p1+p3)/2 ) each [ - anchorpt(str("tip",i), p1, unit(p1,BACK), 0), - anchorpt(str("pit",i), p2, unit(p2,BACK), 0), - anchorpt(str("midpt",i), pos, unit(pos,BACK), 0), + named_anchor(str("tip",i), p1, unit(p1,BACK), 0), + named_anchor(str("pit",i), p2, unit(p2,BACK), 0), + named_anchor(str("midpt",i), pos, unit(pos,BACK), 0), ] ] ) reorient(anchor,spin, two_d=true, path=path, p=path, anchors=anchors); @@ -724,9 +796,9 @@ module star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit, p3 = apply(mat, polar_to_xy(r,a3)), pos = (p1+p3)/2 ) each [ - anchorpt(str("tip",i), p1, unit(p1,BACK), 0), - anchorpt(str("pit",i), p2, unit(p2,BACK), 0), - anchorpt(str("midpt",i), pos, unit(pos,BACK), 0), + named_anchor(str("tip",i), p1, unit(p1,BACK), 0), + named_anchor(str("pit",i), p2, unit(p2,BACK), 0), + named_anchor(str("midpt",i), pos, unit(pos,BACK), 0), ] ]; path = star(n=n, r=r, ir=ir, realign=realign, _mat=mat, _anchs=anchors); @@ -1007,7 +1079,7 @@ module reuleaux_polygon(N=3, r, d, anchor=CENTER, spin=0) { for (i = [0:1:N-1]) let( ca = 360 - i * 360/N, cp = polar_to_xy(r, ca) - ) anchorpt(str("tip",i), cp, unit(cp,BACK), 0), + ) named_anchor(str("tip",i), cp, unit(cp,BACK), 0), ]; attachable(anchor,spin, two_d=true, path=path, anchors=anchors) { polygon(path); @@ -1034,7 +1106,7 @@ function reuleaux_polygon(N=3, r, d, anchor=CENTER, spin=0) = for (i = [0:1:N-1]) let( ca = 360 - i * 360/N, cp = polar_to_xy(r, ca) - ) anchorpt(str("tip",i), cp, unit(cp,BACK), 0), + ) named_anchor(str("tip",i), cp, unit(cp,BACK), 0), ] ) reorient(anchor,spin, two_d=true, path=path, anchors=anchors, p=path); diff --git a/shapes3d.scad b/shapes3d.scad index 6e870a2..7143802 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -6,7 +6,79 @@ ////////////////////////////////////////////////////////////////////// -// Section: Cuboids +// Section: Cuboids, Prismoids and Pyramids + +// Function&Module: cube() +// Topics: Shapes (3D), Attachable, VNF Generators +// Usage: As Module +// cube(size, [center], ...); +// Usage: With Attachments +// cube(size, [center], ...) { attachments } +// Usage: As Function +// vnf = cube(size, [center], ...); +// See Also: cuboid(), prismoid() +// Description: +// Creates a 3D cubic object with support for anchoring and attachments. +// This can be used as a drop-in replacement for the built-in `cube()` module. +// When called as a function, returns a [VNF](vnf.scad) for a cube. +// Arguments: +// size = The size of the cube. +// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=ALLNEG`. +// --- +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// Example: Simple cube. +// cube(40); +// Example: Rectangular cube. +// cube([20,40,50]); +// Example: Anchoring. +// cube([20,40,50], anchor=BOTTOM+FRONT); +// Example: Spin. +// cube([20,40,50], anchor=BOTTOM+FRONT, spin=30); +// Example: Orientation. +// cube([20,40,50], anchor=BOTTOM+FRONT, spin=30, orient=FWD); +// Example: Standard Connectors. +// cube(40, center=true) show_anchors(); +// Example: Called as Function +// vnf = cube([20,40,50]); +// vnf_polyhedron(vnf); +module cube(size=1, center, anchor, spin=0, orient=UP) +{ + anchor = get_anchor(anchor, center, ALLNEG, ALLNEG); + size = scalar_vec3(size); + attachable(anchor,spin,orient, size=size) { + if (size.z > 0) { + linear_extrude(height=size.z, center=true, convexity=2) { + square([size.x,size.y], center=true); + } + } + children(); + } +} + +function cube(size=1, center, anchor, spin=0, orient=UP) = + let( + siz = scalar_vec3(size), + anchor = get_anchor(anchor, center, ALLNEG, ALLNEG), + unscaled = [ + [-1,-1,-1],[1,-1,-1],[1,1,-1],[-1,1,-1], + [-1,-1, 1],[1,-1, 1],[1,1, 1],[-1,1, 1], + ]/2, + verts = is_num(size)? unscaled * size : + is_vector(size,3)? [for (p=unscaled) v_mul(p,size)] : + assert(is_num(size) || is_vector(size,3)), + faces = [ + [0,1,2], [0,2,3], //BOTTOM + [0,4,5], [0,5,1], //FRONT + [1,5,6], [1,6,2], //RIGHT + [2,6,7], [2,7,3], //BACK + [3,7,4], [3,4,0], //LEFT + [6,4,7], [6,5,4] //TOP + ] + ) [reorient(anchor,spin,orient, size=siz, p=verts), faces]; + + // Module: cuboid() // @@ -365,9 +437,6 @@ function cuboid( -// Section: Prismoids - - // Function&Module: prismoid() // // Usage: Typical Prismoids @@ -804,7 +873,104 @@ function right_triangle(size=[1,1,1], center, anchor, spin=0, orient=UP) = no_function("right_triangle"); -// Section: Cylindroids +// Section: Cylinders + + +// Function&Module: cylinder() +// Topics: Shapes (3D), Attachable, VNF Generators +// Usage: As Module +// cylinder(h, r=/d=, [center=], ...); +// cylinder(h, r1/d1=, r2/d2=, [center=], ...); +// Usage: With Attachments +// cylinder(h, r=/d=, [center=]) {attachments} +// Usage: As Function +// vnf = cylinder(h, r=/d=, [center=], ...); +// vnf = cylinder(h, r1/d1=, r2/d2=, [center=], ...); +// See Also: cyl() +// Description: +// Creates a 3D cylinder or conic object with support for anchoring and attachments. +// This can be used as a drop-in replacement for the built-in `cylinder()` module. +// When called as a function, returns a [VNF](vnf.scad) for a cylinder. +// Arguments: +// l / h = The height of the cylinder. +// r1 = The bottom radius of the cylinder. (Before orientation.) +// r2 = The top radius of the cylinder. (Before orientation.) +// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=BOTTOM`. +// --- +// d1 = The bottom diameter of the cylinder. (Before orientation.) +// d2 = The top diameter of the cylinder. (Before orientation.) +// r = The radius of the cylinder. +// d = The diameter of the cylinder. +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// Example: By Radius +// xdistribute(30) { +// cylinder(h=40, r=10); +// cylinder(h=40, r1=10, r2=5); +// } +// Example: By Diameter +// xdistribute(30) { +// cylinder(h=40, d=25); +// cylinder(h=40, d1=25, d2=10); +// } +// Example(Med): Anchoring +// cylinder(h=40, r1=10, r2=5, anchor=BOTTOM+FRONT); +// Example(Med): Spin +// cylinder(h=40, r1=10, r2=5, anchor=BOTTOM+FRONT, spin=45); +// Example(Med): Orient +// cylinder(h=40, r1=10, r2=5, anchor=BOTTOM+FRONT, spin=45, orient=FWD); +// Example(Big): Standard Connectors +// xdistribute(40) { +// cylinder(h=30, d=25) show_anchors(); +// cylinder(h=30, d1=25, d2=10) show_anchors(); +// } +module cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP) +{ + anchor = get_anchor(anchor, center, BOTTOM, BOTTOM); + 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); + l = first_defined([h, l, 1]); + sides = segs(max(r1,r2)); + attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { + if (r1 > r2) { + if (l > 0) { + linear_extrude(height=l, center=true, convexity=2, scale=r2/r1) { + circle(r=r1); + } + } + } else { + zflip() { + if (l > 0) { + linear_extrude(height=l, center=true, convexity=2, scale=r1/r2) { + circle(r=r2); + } + } + } + } + children(); + } +} + +function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP) = + let( + anchor = get_anchor(anchor, center, BOTTOM, BOTTOM), + 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), + l = first_defined([h, l, 1]), + sides = segs(max(r1,r2)), + verts = [ + for (i=[0:1:sides-1]) let(a=360*(1-i/sides)) [r1*cos(a),r1*sin(a),-l/2], + for (i=[0:1:sides-1]) let(a=360*(1-i/sides)) [r2*cos(a),r2*sin(a), l/2], + ], + faces = [ + [for (i=[0:1:sides-1]) sides-1-i], + for (i=[0:1:sides-1]) [i, ((i+1)%sides)+sides, i+sides], + for (i=[0:1:sides-1]) [i, (i+1)%sides, ((i+1)%sides)+sides], + [for (i=[0:1:sides-1]) sides+i] + ] + ) [reorient(anchor,spin,orient, l=l, r1=r1, r2=r2, p=verts), faces]; + // Module: cyl() @@ -1229,106 +1395,64 @@ module tube( } -// Module: torus() -// -// Usage: Typical -// torus(r_maj|d_maj, r_min|d_min, [center], ...); -// torus(or|od, ir|id, ...); -// torus(r_maj|d_maj, or|od, ...); -// torus(r_maj|d_maj, ir|id, ...); -// torus(r_min|d_min, or|od, ...); -// torus(r_min|d_min, ir|id, ...); -// Usage: Attaching Children -// torus(or|od, ir|id, ...) [attachments]; -// + + +// Section: Other Round Objects + + +// Function&Module: sphere() +// Topics: Shapes (3D), Attachable, VNF Generators +// Usage: As Module +// sphere(r|d=, [circum=], [style=], ...); +// Usage: With Attachments +// sphere(r|d=, ...) { attachments } +// Usage: As Function +// vnf = sphere(r|d=, [circum=], [style=], ...); +// See Also: spheroid() // Description: -// Creates a torus shape. -// -// Figure(2D,Med): -// module text3d(t,size=8) text(text=t,size=size,font="Helvetica", halign="center",valign="center"); -// module dashcirc(r,start=0,angle=359.9,dashlen=5) let(step=360*dashlen/(2*r*PI)) for(a=[start:step:start+angle]) stroke(arc(r=r,start=a,angle=step/2)); -// r = 75; r2 = 30; -// down(r2+0.1) #torus(r_maj=r, r_min=r2, $fn=72); -// color("blue") linear_extrude(height=0.01) { -// dashcirc(r=r,start=15,angle=45); -// dashcirc(r=r-r2, start=90+15, angle=60); -// dashcirc(r=r+r2, start=180+45, angle=30); -// dashcirc(r=r+r2, start=15, angle=30); -// } -// rot(240) color("blue") linear_extrude(height=0.01) { -// stroke([[0,0],[r+r2,0]], endcaps="arrow2",width=2); -// right(r) fwd(9) rot(-240) text3d("or",size=10); -// } -// rot(135) color("blue") linear_extrude(height=0.01) { -// stroke([[0,0],[r-r2,0]], endcaps="arrow2",width=2); -// right((r-r2)/2) back(8) rot(-135) text3d("ir",size=10); -// } -// rot(45) color("blue") linear_extrude(height=0.01) { -// stroke([[0,0],[r,0]], endcaps="arrow2",width=2); -// right(r/2) back(8) text3d("r_maj",size=9); -// } -// rot(30) color("blue") linear_extrude(height=0.01) { -// stroke([[r,0],[r+r2,0]], endcaps="arrow2",width=2); -// right(r+r2/2) fwd(8) text3d("r_min",size=7); -// } -// +// Creates a sphere object, with support for anchoring and attachments. +// This is a drop-in replacement for the built-in `sphere()` module. +// When called as a function, returns a [VNF](vnf.scad) for a sphere. // Arguments: -// r_maj = major radius of torus ring. (use with 'r_min', or 'd_min') -// r_min = minor radius of torus ring. (use with 'r_maj', or 'd_maj') -// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=DOWN`. +// r = Radius of the sphere. // --- -// d_maj = major diameter of torus ring. (use with 'r_min', or 'd_min') -// d_min = minor diameter of torus ring. (use with 'r_maj', or 'd_maj') -// or = outer radius of the torus. (use with 'ir', or 'id') -// ir = inside radius of the torus. (use with 'or', or 'od') -// od = outer diameter of the torus. (use with 'ir' or 'id') -// id = inside diameter of the torus. (use with 'or' or 'od') +// d = Diameter of the sphere. +// circum = If true, the sphere is made large enough to circumscribe the sphere of the ideal side. Otherwise inscribes. Default: false (inscribes) +// style = The style of the sphere's construction. One of "orig", "aligned", "stagger", "octa", or "icosa". Default: "orig" // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` -// -// Example: -// // These all produce the same torus. -// torus(r_maj=22.5, r_min=7.5); -// torus(d_maj=45, d_min=15); -// torus(or=30, ir=15); -// torus(od=60, id=30); -// torus(d_maj=45, id=30); -// torus(d_maj=45, od=60); -// torus(d_min=15, id=30); -// torus(d_min=15, od=60); +// Example: By Radius +// sphere(r=50); +// Example: By Diameter +// sphere(d=100); +// Example: style="orig" +// sphere(d=100, style="orig", $fn=10); +// Example: style="aligned" +// sphere(d=100, style="aligned", $fn=10); +// Example: style="stagger" +// sphere(d=100, style="stagger", $fn=10); +// Example: style="icosa" +// sphere(d=100, style="icosa", $fn=10); +// // In "icosa" style, $fn is quantized +// // to the nearest multiple of 5. +// Example: Anchoring +// sphere(d=100, anchor=FRONT); +// Example: Spin +// sphere(d=100, anchor=FRONT, spin=45); +// Example: Orientation +// sphere(d=100, anchor=FRONT, spin=45, orient=FWD); // Example: Standard Connectors -// torus(od=60, id=30) show_anchors(); -module torus( - r_maj, r_min, center, - d_maj, d_min, - or, od, ir, id, - anchor, spin=0, orient=UP -) { - _or = get_radius(r=or, d=od, dflt=undef); - _ir = get_radius(r=ir, d=id, dflt=undef); - _r_maj = get_radius(r=r_maj, d=d_maj, dflt=undef); - _r_min = get_radius(r=r_min, d=d_min, dflt=undef); - majrad = is_finite(_r_maj)? _r_maj : - is_finite(_ir) && is_finite(_or)? (_or + _ir)/2 : - is_finite(_ir) && is_finite(_r_min)? (_ir + _r_min) : - is_finite(_or) && is_finite(_r_min)? (_or - _r_min) : - assert(false, "Bad Parameters"); - minrad = is_finite(_r_min)? _r_min : - is_finite(_ir)? (majrad - _ir) : - is_finite(_or)? (_or - majrad) : - assert(false, "Bad Parameters"); - anchor = get_anchor(anchor, center, BOT, CENTER); - attachable(anchor,spin,orient, r=(majrad+minrad), l=minrad*2) { - rotate_extrude(convexity=4) { - right(majrad) circle(r=minrad); - } - children(); - } -} +// sphere(d=50) show_anchors(); +// Example: Called as Function +// vnf = sphere(d=100, style="icosa"); +// vnf_polyhedron(vnf); +module sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient=UP) + spheroid(r=r, d=d, circum=circum, style=style, anchor=anchor, spin=spin, orient=orient) children(); - -// Section: Spheroid +function sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient=UP) = + spheroid(r=r, d=d, circum=circum, style=style, anchor=anchor, spin=spin, orient=orient); // Function&Module: spheroid() @@ -1550,7 +1674,102 @@ function spheroid(r, style="aligned", d, circum=false, anchor=CENTER, spin=0, or -// Section: 3D Printing Shapes +// Module: torus() +// +// Usage: Typical +// torus(r_maj|d_maj, r_min|d_min, [center], ...); +// torus(or|od, ir|id, ...); +// torus(r_maj|d_maj, or|od, ...); +// torus(r_maj|d_maj, ir|id, ...); +// torus(r_min|d_min, or|od, ...); +// torus(r_min|d_min, ir|id, ...); +// Usage: Attaching Children +// torus(or|od, ir|id, ...) [attachments]; +// +// Description: +// Creates a torus shape. +// +// Figure(2D,Med): +// module text3d(t,size=8) text(text=t,size=size,font="Helvetica", halign="center",valign="center"); +// module dashcirc(r,start=0,angle=359.9,dashlen=5) let(step=360*dashlen/(2*r*PI)) for(a=[start:step:start+angle]) stroke(arc(r=r,start=a,angle=step/2)); +// r = 75; r2 = 30; +// down(r2+0.1) #torus(r_maj=r, r_min=r2, $fn=72); +// color("blue") linear_extrude(height=0.01) { +// dashcirc(r=r,start=15,angle=45); +// dashcirc(r=r-r2, start=90+15, angle=60); +// dashcirc(r=r+r2, start=180+45, angle=30); +// dashcirc(r=r+r2, start=15, angle=30); +// } +// rot(240) color("blue") linear_extrude(height=0.01) { +// stroke([[0,0],[r+r2,0]], endcaps="arrow2",width=2); +// right(r) fwd(9) rot(-240) text3d("or",size=10); +// } +// rot(135) color("blue") linear_extrude(height=0.01) { +// stroke([[0,0],[r-r2,0]], endcaps="arrow2",width=2); +// right((r-r2)/2) back(8) rot(-135) text3d("ir",size=10); +// } +// rot(45) color("blue") linear_extrude(height=0.01) { +// stroke([[0,0],[r,0]], endcaps="arrow2",width=2); +// right(r/2) back(8) text3d("r_maj",size=9); +// } +// rot(30) color("blue") linear_extrude(height=0.01) { +// stroke([[r,0],[r+r2,0]], endcaps="arrow2",width=2); +// right(r+r2/2) fwd(8) text3d("r_min",size=7); +// } +// +// Arguments: +// r_maj = major radius of torus ring. (use with 'r_min', or 'd_min') +// r_min = minor radius of torus ring. (use with 'r_maj', or 'd_maj') +// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=DOWN`. +// --- +// d_maj = major diameter of torus ring. (use with 'r_min', or 'd_min') +// d_min = minor diameter of torus ring. (use with 'r_maj', or 'd_maj') +// or = outer radius of the torus. (use with 'ir', or 'id') +// ir = inside radius of the torus. (use with 'or', or 'od') +// od = outer diameter of the torus. (use with 'ir' or 'id') +// id = inside diameter of the torus. (use with 'or' or 'od') +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP` +// +// Example: +// // These all produce the same torus. +// torus(r_maj=22.5, r_min=7.5); +// torus(d_maj=45, d_min=15); +// torus(or=30, ir=15); +// torus(od=60, id=30); +// torus(d_maj=45, id=30); +// torus(d_maj=45, od=60); +// torus(d_min=15, id=30); +// torus(d_min=15, od=60); +// Example: Standard Connectors +// torus(od=60, id=30) show_anchors(); +module torus( + r_maj, r_min, center, + d_maj, d_min, + or, od, ir, id, + anchor, spin=0, orient=UP +) { + _or = get_radius(r=or, d=od, dflt=undef); + _ir = get_radius(r=ir, d=id, dflt=undef); + _r_maj = get_radius(r=r_maj, d=d_maj, dflt=undef); + _r_min = get_radius(r=r_min, d=d_min, dflt=undef); + majrad = is_finite(_r_maj)? _r_maj : + is_finite(_ir) && is_finite(_or)? (_or + _ir)/2 : + is_finite(_ir) && is_finite(_r_min)? (_ir + _r_min) : + is_finite(_or) && is_finite(_r_min)? (_or - _r_min) : + assert(false, "Bad Parameters"); + minrad = is_finite(_r_min)? _r_min : + is_finite(_ir)? (majrad - _ir) : + is_finite(_or)? (_or - majrad) : + assert(false, "Bad Parameters"); + anchor = get_anchor(anchor, center, BOT, CENTER); + attachable(anchor,spin,orient, r=(majrad+minrad), l=minrad*2) { + rotate_extrude(convexity=4) { + right(majrad) circle(r=minrad); + } + children(); + } +} // Module: teardrop() @@ -1614,9 +1833,9 @@ module teardrop(h, r, ang=45, cap_h, r1, r2, d, d1, d2, cap_h1, cap_h2, l, ancho cap_h2 = min(first_defined([cap_h2, cap_h, tip_y2]), tip_y2); capvec = unit([0, cap_h1-cap_h2, l]); anchors = [ - anchorpt("cap", [0,0,(cap_h1+cap_h2)/2], capvec), - anchorpt("cap_fwd", [0,-l/2,cap_h1], unit((capvec+FWD)/2)), - anchorpt("cap_back", [0,+l/2,cap_h2], unit((capvec+BACK)/2), 180), + named_anchor("cap", [0,0,(cap_h1+cap_h2)/2], capvec), + named_anchor("cap_fwd", [0,-l/2,cap_h1], unit((capvec+FWD)/2)), + named_anchor("cap_back", [0,+l/2,cap_h2], unit((capvec+BACK)/2), 180), ]; attachable(anchor,spin,orient, r1=r1, r2=r2, l=l, axis=BACK, anchors=anchors) { rot(from=UP,to=FWD) { @@ -1697,6 +1916,273 @@ module onion(r, ang=45, cap_h, d, anchor=CENTER, spin=0, orient=UP) } +// Section: Text + +// Module: atext() +// Topics: Attachments, Text +// Usage: +// atext(text, [h], [size], [font]); +// Description: +// Creates a 3D text block that can be attached to other attachable objects. +// NOTE: This cannot have children attached to it. +// Arguments: +// text = The text string to instantiate as an object. +// h = The height to which the text should be extruded. Default: 1 +// size = The font size used to create the text block. Default: 10 +// font = The name of the font used to create the text block. Default: "Courier" +// --- +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `"baseline"` +// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0` +// orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP` +// See Also: attachable() +// Extra Anchors: +// "baseline" = Anchors at the baseline of the text, at the start of the string. +// str("baseline",VECTOR) = Anchors at the baseline of the text, modified by the X and Z components of the appended vector. +// Examples: +// atext("Foobar", h=3, size=10); +// atext("Foobar", h=2, size=12, font="Helvetica"); +// atext("Foobar", h=2, anchor=CENTER); +// atext("Foobar", h=2, anchor=str("baseline",CENTER)); +// atext("Foobar", h=2, anchor=str("baseline",BOTTOM+RIGHT)); +// Example: Using line_of() distributor +// txt = "This is the string."; +// line_of(spacing=[10,-5],n=len(txt)) +// atext(txt[$idx], size=10, anchor=CENTER); +// Example: Using arc_of() distributor +// txt = "This is the string"; +// arc_of(r=50, n=len(txt), sa=0, ea=180) +// atext(select(txt,-1-$idx), size=10, anchor=str("baseline",CENTER), spin=-90); +module atext(text, h=1, size=9, font="Courier", anchor="baseline", spin=0, orient=UP) { + no_children($children); + dummy1 = + assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Got: ",anchor)) + assert(is_undef(spin) || is_vector(spin,3) || is_num(spin), str("Got: ",spin)) + assert(is_undef(orient) || is_vector(orient,3), str("Got: ",orient)); + anchor = default(anchor, CENTER); + spin = default(spin, 0); + orient = default(orient, UP); + geom = _attach_geom(size=[size,size,h]); + anch = !any([for (c=anchor) c=="["])? anchor : + let( + parts = str_split(str_split(str_split(anchor,"]")[0],"[")[1],","), + vec = [for (p=parts) str_float(str_strip_leading(p," "))] + ) vec; + ha = anchor=="baseline"? "left" : + anchor==anch && is_string(anchor)? "center" : + anch.x<0? "left" : + anch.x>0? "right" : + "center"; + va = starts_with(anchor,"baseline")? "baseline" : + anchor==anch && is_string(anchor)? "center" : + anch.y<0? "bottom" : + anch.y>0? "top" : + "center"; + base = anchor=="baseline"? CENTER : + anchor==anch && is_string(anchor)? CENTER : + anch.z<0? BOTTOM : + anch.z>0? TOP : + CENTER; + m = _attach_transform(base,spin,orient,geom); + multmatrix(m) { + $parent_anchor = anchor; + $parent_spin = spin; + $parent_orient = orient; + $parent_geom = geom; + $parent_size = _attach_geom_size(geom); + $attach_to = undef; + do_show = _attachment_is_shown($tags); + if (do_show) { + if (is_undef($color)) { + linear_extrude(height=h, center=true) + text(text=text, size=size, halign=ha, valign=va, font=font); + } else color($color) { + $color = undef; + linear_extrude(height=h, center=true) + text(text=text, size=size, halign=ha, valign=va, font=font); + } + } + } +} + + + + + +function _cut_interp(pathcut, path, data) = + [for(entry=pathcut) + let( + a = path[entry[1]-1], + b = path[entry[1]], + c = entry[0], + i = max_index(v_abs(b-a)), + factor = (c[i]-a[i])/(b[i]-a[i]) + ) + (1-factor)*data[entry[1]-1]+ factor * data[entry[1]] + ]; + + +// Module: path_text() +// Usage: +// path_text(path, text, [size], [thickness], [font], [lettersize], [offset], [reverse], [normal], [top], [textmetrics]) +// Description: +// Place the text letter by letter onto the specified path using textmetrics (if available and requested) +// or user specified letter spacing. The path can be 2D or 3D. In 2D the text appears along the path with letters upright +// as determined by the path direction. In 3D by default letters are positioned on the tangent line to the path with the path normal +// pointing toward the reader. The path normal points away from the center of curvature (the opposite of the normal produced +// by path_normals()). Note that this means that if the center of curvature switches sides the text will flip upside down. +// If you want text on such a path you must supply your own normal or top vector. +// . +// Text appears starting at the beginning of the path, so if the 3D path moves right to left +// then a left-to-right reading language will display in the wrong order. (For a 2D path text will appear upside down.) +// The text for a 3D path appears positioned to be read from "outside" of the curve (from a point on the other side of the +// curve from the center of curvature). If you need the text to read properly from the inside, you can set reverse to +// true to flip the text, or supply your own normal. +// . +// If you do not have the experimental textmetrics feature enabled then you must specify the space for the letters +// using lettersize, which can be a scalar or array. You will have the easiest time getting good results by using +// a monospace font such as Courier. Note that even with text metrics, spacing may be different because path_text() +// doesn't do kerning to adjust positions of individual glyphs. Also if your font has ligatures they won't be used. +// . +// By default letters appear centered on the path. The offset can be specified to shift letters toward the reader (in +// the direction of the normal). +// . +// You can specify your own normal by setting `normal` to a direction or a list of directions. Your normal vector should +// point toward the reader. You can also specify +// top, which directs the top of the letters in a desired direction. If you specify your own directions and they +// are not perpendicular to the path then the direction you specify will take priority and the +// letters will not rest on the tangent line of the path. Note that the normal or top directions that you +// specify must not be parallel to the path. +// Arguments: +// path = path to place the text on +// text = text to create +// size = font size +// thickness = thickness of letters (not allowed for 2D path) +// font = font to use +// --- +// lettersize = scalar or array giving size of letters +// offset = distance to shift letters "up" (towards the reader). Not allowed for 2D path. Default: 0 +// normal = direction or list of directions pointing towards the reader of the text. Not allowed for 2D path. +// top = direction or list of directions pointing toward the top of the text +// reverse = reverse the letters if true. Not allowed for 2D path. Default: false +// textmetrics = if set to true and lettersize is not given then use the experimental textmetrics feature. You must be running a dev snapshot that includes this feature and have the feature turned on in your preferences. Default: false +// Example: The examples use Courier, a monospaced font. The width is 1/1.2 times the specified size for this font. This text could wrap around a cylinder. +// path = path3d(arc(100, r=25, angle=[245, 370])); +// color("red")stroke(path, width=.3); +// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2); +// Example: By setting the normal to UP we can get text that lies flat, for writing around the edge of a disk: +// path = path3d(arc(100, r=25, angle=[245, 370])); +// color("red")stroke(path, width=.3); +// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, normal=UP); +// Example: If we want text that reads from the other side we can use reverse. Note we have to reverse the direction of the path and also set the reverse option. +// path = reverse(path3d(arc(100, r=25, angle=[65, 190]))); +// color("red")stroke(path, width=.3); +// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, reverse=true); +// Example: text debossed onto a cylinder in a spiral. The text is 1 unit deep because it is half in, half out. +// text = ("A long text example to wrap around a cylinder, possibly for a few times."); +// L = 5*len(text); +// maxang = 360*L/(PI*50); +// spiral = [for(a=[0:1:maxang]) [25*cos(a), 25*sin(a), 10-30/maxang*a]]; +// difference(){ +// cyl(d=50, l=50, $fn=120); +// path_text(spiral, text, size=5, lettersize=5/1.2, font="Courier", thickness=2); +// } +// Example: Same example but text embossed. Make sure you have enough depth for the letters to fully overlap the object. +// text = ("A long text example to wrap around a cylinder, possibly for a few times."); +// L = 5*len(text); +// maxang = 360*L/(PI*50); +// spiral = [for(a=[0:1:maxang]) [25*cos(a), 25*sin(a), 10-30/maxang*a]]; +// cyl(d=50, l=50, $fn=120); +// path_text(spiral, text, size=5, lettersize=5/1.2, font="Courier", thickness=2); +// Example: Here the text baseline sits on the path. (Note the default orientation makes text readable from below, so we specify the normal.) +// path = arc(100, points = [[-20, 0, 20], [0,0,5], [20,0,20]]); +// color("red")stroke(path,width=.2); +// path_text(path, "Example Text", size=5, lettersize=5/1.2, font="Courier", normal=FRONT); +// Example: If we use top to orient the text upward, the text baseline is no longer aligned with the path. +// path = arc(100, points = [[-20, 0, 20], [0,0,5], [20,0,20]]); +// color("red")stroke(path,width=.2); +// path_text(path, "Example Text", size=5, lettersize=5/1.2, font="Courier", top=UP); +// Example: This sine wave wrapped around the cylinder has a twisting normal that produces wild letter layout. We fix it with a custom normal which is different at every path point. +// path = [for(theta = [0:360]) [25*cos(theta), 25*sin(theta), 4*cos(theta*4)]]; +// normal = [for(theta = [0:360]) [cos(theta), sin(theta),0]]; +// zrot(-120) +// difference(){ +// cyl(r=25, h=20, $fn=120); +// path_text(path, "A sine wave wiggles", font="Courier", lettersize=5/1.2, size=5, normal=normal); +// } +// Example: The path center of curvature changes, and the text flips. +// path = zrot(-120,p=path3d( concat(arc(100, r=25, angle=[0,90]), back(50,p=arc(100, r=25, angle=[268, 180]))))); +// color("red")stroke(path,width=.2); +// path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Courier", thickness=2); +// Example: We can fix it with top: +// path = zrot(-120,p=path3d( concat(arc(100, r=25, angle=[0,90]), back(50,p=arc(100, r=25, angle=[268, 180]))))); +// color("red")stroke(path,width=.2); +// path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Courier", thickness=2, top=UP); +// Example(2D): With a 2D path instead of 3D there's no ambiguity about direction and it works by default: +// path = zrot(-120,p=concat(arc(100, r=25, angle=[0,90]), back(50,p=arc(100, r=25, angle=[268, 180])))); +// color("red")stroke(path,width=.2); +// path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Courier"); +module path_text(path, text, font, size, thickness, lettersize, offset=0, reverse=false, normal, top, textmetrics=false) +{ + dummy2=assert(is_path(path,[2,3]),"Must supply a 2d or 3d path") + assert(num_defined([normal,top])<=1, "Cannot define both \"normal\" and \"top\""); + dim = len(path[0]); + normalok = is_undef(normal) || is_vector(normal,3) || (is_path(normal,3) && len(normal)==len(path)); + topok = is_undef(top) || is_vector(top,dim) || (dim==2 && is_vector(top,3) && top[2]==0) + || (is_path(top,dim) && len(top)==len(path)); + dummy4 = assert(dim==3 || is_undef(thickness), "Cannot give a thickness with 2d path") + assert(dim==3 || !reverse, "Reverse not allowed with 2d path") + assert(dim==3 || offset==0, "Cannot give offset with 2d path") + assert(dim==3 || is_undef(normal), "Cannot define \"normal\" for a 2d path, only \"top\"") + assert(normalok,"\"normal\" must be a vector or path compatible with the given path") + assert(topok,"\"top\" must be a vector or path compatible with the given path"); + thickness = first_defined([thickness,1]); + normal = is_vector(normal) ? repeat(normal, len(path)) + : is_def(normal) ? normal + : undef; + + top = is_vector(top) ? repeat(dim==2?point2d(top):top, len(path)) + : is_def(top) ? top + : undef; + + lsize = is_def(lettersize) ? force_list(lettersize, len(text)) + : textmetrics ? [for(letter=text) let(t=textmetrics(letter, font=font, size=size)) t.advance[0]] + : assert(false, "textmetrics disabled: Must specify letter size"); + + dummy1 = assert(sum(lsize)<=path_length(path),"Path is too short for the text"); + + pts = path_cut_points(path, add_scalar([0, each cumsum(lsize)],lsize[0]/2), direction=true); + + usernorm = is_def(normal); + usetop = is_def(top); + + normpts = is_undef(normal) ? (reverse?1:-1)*subindex(pts,3) : _cut_interp(pts,path, normal); + toppts = is_undef(top) ? undef : _cut_interp(pts,path,top); + for(i=idx(text)) + let( tangent = pts[i][2] ) + assert(!usetop || !approx(tangent*toppts[i],norm(top[i])*norm(tangent)), + str("Specified top direction parallel to path at character ",i)) + assert(usetop || !approx(tangent*normpts[i],norm(normpts[i])*norm(tangent)), + str("Specified normal direction parallel to path at character ",i)) + let( + adjustment = usetop ? (tangent*toppts[i])*toppts[i]/(toppts[i]*toppts[i]) + : usernorm ? (tangent*normpts[i])*normpts[i]/(normpts[i]*normpts[i]) + : [0,0,0] + ) + move(pts[i][0]) + if(dim==3){ + frame_map(x=tangent-adjustment, + z=usetop ? undef : normpts[i], + y=usetop ? toppts[i] : undef) + up(offset-thickness/2) + linear_extrude(height=thickness) + left(lsize[0]/2)text(text[i], font=font, size=size); + } else { + frame_map(x=point3d(tangent-adjustment), y=point3d(usetop ? toppts[i] : -normpts[i])) + left(lsize[0]/2)text(text[i], font=font, size=size); + } +} + + // Section: Miscellaneous diff --git a/std.scad b/std.scad index c9b7f53..cd2c542 100644 --- a/std.scad +++ b/std.scad @@ -14,7 +14,6 @@ include include include include -include include include include @@ -34,7 +33,7 @@ include include include include -include +include include diff --git a/tests/test_drawing.scad b/tests/test_drawing.scad new file mode 100644 index 0000000..a7e5fa1 --- /dev/null +++ b/tests/test_drawing.scad @@ -0,0 +1,51 @@ +include <../std.scad> + +module test_turtle() { + assert_approx( + turtle([ + "move", 10, + "ymove", 5, + "xmove", 5, + "xymove", [10,15], + "left", 135, + "untilx", 0, + "turn", 90, + "untily", 0, + "right", 135, + "arcsteps", 5, + "arcright", 15, 30, + "arcleft", 15, 30, + "arcsteps", 0, + "arcrightto", 15, 90, + "arcleftto", 15, 180, + "jump", [10,10], + "xjump", 15, + "yjump", 15, + "angle", 30, + "length", 15, + "right", + "move", + "scale", 2, + "left", + "move", + "addlength", 5, + "repeat", 3, ["move"], + ], $fn=24), + [[0,0],[10,0],[10,5],[15,5],[25,20],[-3.5527136788e-15,45],[-45,0],[-44.8716729206,1.9578928833],[-44.4888873943,3.88228567654],[-43.8581929877,5.74025148548],[-42.9903810568,7.5],[-42.1225691259,9.25974851452],[-41.4918747192,11.1177143235],[-41.1090891929,13.0421071167],[-40.9807621135,15],[-41.0157305757,16.0236362005],[-41.120472923,17.0424997364],[-41.2945007983,18.0518401958],[-41.5370028033,19.0469515674],[-41.8468482818,20.0231941826],[-42.222592591,20.9760163477],[-42.6624838375,21.900975566],[-43.1644710453,22.7937592505],[-43.7262137184,23.6502048317],[-44.345092753,24.4663191649],[-45.0182226494,25.2382971483],[-45.7424649653,25.9625394642],[-46.5144429486,26.6356693606],[-47.3305572818,27.2545483952],[-48.187002863,27.8162910682],[-49.0797865476,28.318278276],[-50.0047457658,28.7581695226],[-50.957567931,29.1339138318],[-51.9338105462,29.4437593102],[-52.9289219177,29.6862613152],[-53.9382623771,29.8602891905],[-54.9571259131,29.9650315379],[-55.9807621135,30],[10,10],[15,10],[15,15],[2.00961894323,22.5],[-27.9903810568,22.5],[-62.9903810568,22.5],[-97.9903810568,22.5],[-132.990381057,22.5]] + ); +} +test_turtle(); + + +module test_arc() { + assert_approx(arc(N=8, d=100, angle=135, cp=[10,10]), [[60,10],[57.1941665154,26.5139530978],[49.0915741234,41.1744900929],[36.6016038258,52.3362099614],[21.1260466978,58.7463956091],[4.40177619483,59.6856104947],[-11.6941869559,55.0484433951],[-25.3553390593,45.3553390593]]); + assert_approx(arc(N=8, d=100, angle=135, cp=[10,10],endpoint=false), [[60,10],[57.8470167866,24.5142338627],[51.5734806151,37.778511651],[41.7196642082,48.6505226681],[29.1341716183,56.1939766256],[14.9008570165,59.7592363336],[0.245483899194,59.0392640202],[-13.5698368413,54.0960632174]]); + assert_approx(arc(N=8, d=100, angle=[45,225], cp=[10,10]), [[45.3553390593,45.3553390593],[26.5139530978,57.1941665154],[4.40177619483,59.6856104947],[-16.6016038258,52.3362099614],[-32.3362099614,36.6016038258],[-39.6856104947,15.5982238052],[-37.1941665154,-6.51395309776],[-25.3553390593,-25.3553390593]]); + assert_approx(arc(N=8, d=100, start=45, angle=135, cp=[10,10]), [[45.3553390593,45.3553390593],[31.6941869559,55.0484433951],[15.5982238052,59.6856104947],[-1.12604669782,58.7463956091],[-16.6016038258,52.3362099614],[-29.0915741234,41.1744900929],[-37.1941665154,26.5139530978],[-40,10]]); + assert_approx(arc(N=8, d=100, start=45, angle=-90, cp=[10,10]), [[45.3553390593,45.3553390593],[52.3362099614,36.6016038258],[57.1941665154,26.5139530978],[59.6856104947,15.5982238052],[59.6856104947,4.40177619483],[57.1941665154,-6.51395309776],[52.3362099614,-16.6016038258],[45.3553390593,-25.3553390593]]); + assert_approx(arc(N=8, width=100, thickness=30), [[50,-3.5527136788e-15],[39.5300788555,13.9348601124],[25.3202618476,24.0284558904],[8.71492362453,29.3258437015],[-8.71492362453,29.3258437015],[-25.3202618476,24.0284558904],[-39.5300788555,13.9348601124],[-50,-1.42108547152e-14]]); + assert_approx(arc(N=8, cp=[10,10], points=[[45,45],[-25,45]]), [[45,45],[36.3342442379,51.9107096148],[26.3479795075,56.7198412457],[15.5419588213,59.1862449514],[4.45804117867,59.1862449514],[-6.34797950747,56.7198412457],[-16.3342442379,51.9107096148],[-25,45]]); + assert_approx(arc(N=24, cp=[10,10], points=[[45,45],[-25,45]], long=true), [[45,45],[51.3889035257,37.146982612],[56.0464336973,28.1583574081],[58.7777575294,18.4101349813],[59.4686187624,8.31010126292],[58.0901174104,-1.71924090789],[54.6999187001,-11.2583458482],[49.4398408296,-19.9081753929],[42.5299224539,-27.3068913894],[34.2592180667,-33.1449920477],[24.9737063235,-37.1782589647],[15.0618171232,-39.2379732261],[4.93818287676,-39.2379732261],[-4.97370632349,-37.1782589647],[-14.2592180667,-33.1449920477],[-22.5299224539,-27.3068913894],[-29.4398408296,-19.9081753929],[-34.6999187001,-11.2583458482],[-38.0901174104,-1.71924090789],[-39.4686187624,8.31010126292],[-38.7777575294,18.4101349813],[-36.0464336973,28.1583574081],[-31.3889035257,37.146982612],[-25,45]]); + assert_approx(arc($fn=24, cp=[10,10], points=[[45,45],[-25,45]], long=true), [[45,45],[53.2421021636,34.0856928585],[58.1827254512,21.3324740498],[59.4446596304,7.71403542491],[56.9315576496,-5.72987274525],[50.8352916125,-17.9728253654],[41.6213035891,-28.0800887515],[29.9930697126,-35.2799863457],[16.8383906815,-39.0228152281],[3.16160931847,-39.0228152281],[-9.9930697126,-35.2799863457],[-21.6213035891,-28.0800887515],[-30.8352916125,-17.9728253654],[-36.9315576496,-5.72987274525],[-39.4446596304,7.71403542491],[-38.1827254512,21.3324740498],[-33.2421021636,34.0856928585],[-25,45]]); +} +test_arc(); diff --git a/tests/test_geometry.scad b/tests/test_geometry.scad index 844b807..09dd6a8 100644 --- a/tests/test_geometry.scad +++ b/tests/test_geometry.scad @@ -12,7 +12,7 @@ test_point_line_distance(); test_segment_distance(); test_line_normal(); test_line_intersection(); -//test_line_ray_intersection(); // should add this typ eof case +//test_line_ray_intersection(); // should add this type of case //test_ray_intersection(); // should add this type of case //test_ray_segment_intersection(); // should add this type of case test_line_closest_point(); @@ -35,7 +35,7 @@ test_polygon_line_intersection(); test_plane_intersection(); test_is_coplanar(); test_are_points_on_plane(); -test_is_above_plane(); +test__is_point_above_plane(); test_circle_2tangents(); test_circle_3points(); test_circle_point_tangents(); @@ -56,13 +56,6 @@ test_reverse_polygon(); test_polygon_normal(); //tests to migrate to other files -test_is_path(); -test_is_closed_path(); -test_close_path(); -test_cleanup_path(); -test_simplify_path(); -test_simplify_path_indexed(); -test_is_region(); test_convex_distance(); test_convex_collision(); @@ -738,53 +731,19 @@ module test_is_coplanar() { *test_is_coplanar(); -module test_is_above_plane() { +module test__is_point_above_plane() { plane = plane3pt([0,0,0], [0,10,10], [10,0,10]); - assert(is_above_plane(plane, [5,5,10]) == false); - assert(is_above_plane(plane, [-5,0,0]) == true); - assert(is_above_plane(plane, [5,0,0]) == false); - assert(is_above_plane(plane, [0,-5,0]) == true); - assert(is_above_plane(plane, [0,5,0]) == false); - assert(is_above_plane(plane, [0,0,5]) == true); - assert(is_above_plane(plane, [0,0,-5]) == false); + assert(_is_point_above_plane(plane, [5,5,10]) == false); + assert(_is_point_above_plane(plane, [-5,0,0]) == true); + assert(_is_point_above_plane(plane, [5,0,0]) == false); + assert(_is_point_above_plane(plane, [0,-5,0]) == true); + assert(_is_point_above_plane(plane, [0,5,0]) == false); + assert(_is_point_above_plane(plane, [0,0,5]) == true); + assert(_is_point_above_plane(plane, [0,0,-5]) == false); } -*test_is_above_plane(); +*test__is_point_above_plane(); -module test_is_path() { - assert(is_path([[1,2,3],[4,5,6]])); - assert(is_path([[1,2,3],[4,5,6],[7,8,9]])); - assert(!is_path(123)); - assert(!is_path("foo")); - assert(!is_path(true)); - assert(!is_path([])); - assert(!is_path([[]])); - assert(!is_path([["foo","bar","baz"]])); - assert(!is_path([[1,2,3]])); - assert(!is_path([["foo","bar","baz"],["qux","quux","quuux"]])); -} -*test_is_path(); - - -module test_is_closed_path() { - assert(!is_closed_path([[1,2,3],[4,5,6],[1,8,9]])); - assert(is_closed_path([[1,2,3],[4,5,6],[1,8,9],[1,2,3]])); -} -*test_is_closed_path(); - - -module test_close_path() { - assert(close_path([[1,2,3],[4,5,6],[1,8,9]]) == [[1,2,3],[4,5,6],[1,8,9],[1,2,3]]); - assert(close_path([[1,2,3],[4,5,6],[1,8,9],[1,2,3]]) == [[1,2,3],[4,5,6],[1,8,9],[1,2,3]]); -} -*test_close_path(); - - -module test_cleanup_path() { - assert(cleanup_path([[1,2,3],[4,5,6],[1,8,9]]) == [[1,2,3],[4,5,6],[1,8,9]]); - assert(cleanup_path([[1,2,3],[4,5,6],[1,8,9],[1,2,3]]) == [[1,2,3],[4,5,6],[1,8,9]]); -} -*test_cleanup_path(); module test_polygon_area() { @@ -869,19 +828,6 @@ module test_centroid() { *test_centroid(); -module test_simplify_path() { - path = [[-20,-20], [-10,-20], [0,-10], [10,0], [20,10], [20,20], [15,30]]; - assert(simplify_path(path) == [[-20,-20], [-10,-20], [20,10], [20,20], [15,30]]); -} -*test_simplify_path(); - - -module test_simplify_path_indexed() { - pts = [[10,0], [0,-10], [20,20], [20,10], [-20,-20], [15,30], [-10,-20]]; - path = [4,6,1,0,3,2,5]; - assert(simplify_path_indexed(pts, path) == [4,6,3,2,5]); -} -*test_simplify_path_indexed(); module test_point_in_polygon() { @@ -946,17 +892,6 @@ module test_reverse_polygon() { *test_reverse_polygon(); -module test_is_region() { - assert(is_region([circle(d=10),square(10)])); - assert(is_region([circle(d=10),square(10),circle(d=50)])); - assert(is_region([square(10)])); - assert(!is_region([])); - assert(!is_region(23)); - assert(!is_region(true)); - assert(!is_region("foo")); -} -*test_is_region(); - module test_convex_distance() { // 2D c1 = circle(10,$fn=24); diff --git a/tests/test_paths.scad b/tests/test_paths.scad new file mode 100644 index 0000000..76950c2 --- /dev/null +++ b/tests/test_paths.scad @@ -0,0 +1,51 @@ +include<../std.scad> + +module test_is_path() { + assert(is_path([[1,2,3],[4,5,6]])); + assert(is_path([[1,2,3],[4,5,6],[7,8,9]])); + assert(!is_path(123)); + assert(!is_path("foo")); + assert(!is_path(true)); + assert(!is_path([])); + assert(!is_path([[]])); + assert(!is_path([["foo","bar","baz"]])); + assert(!is_path([[1,2,3]])); + assert(!is_path([["foo","bar","baz"],["qux","quux","quuux"]])); +} +test_is_path(); + + +module test_is_closed_path() { + assert(!is_closed_path([[1,2,3],[4,5,6],[1,8,9]])); + assert(is_closed_path([[1,2,3],[4,5,6],[1,8,9],[1,2,3]])); +} +test_is_closed_path(); + + +module test_close_path() { + assert(close_path([[1,2,3],[4,5,6],[1,8,9]]) == [[1,2,3],[4,5,6],[1,8,9],[1,2,3]]); + assert(close_path([[1,2,3],[4,5,6],[1,8,9],[1,2,3]]) == [[1,2,3],[4,5,6],[1,8,9],[1,2,3]]); +} +test_close_path(); + + +module test_cleanup_path() { + assert(cleanup_path([[1,2,3],[4,5,6],[1,8,9]]) == [[1,2,3],[4,5,6],[1,8,9]]); + assert(cleanup_path([[1,2,3],[4,5,6],[1,8,9],[1,2,3]]) == [[1,2,3],[4,5,6],[1,8,9]]); +} +test_cleanup_path(); + + +module test_simplify_path() { + path = [[-20,-20], [-10,-20], [0,-10], [10,0], [20,10], [20,20], [15,30]]; + assert(simplify_path(path) == [[-20,-20], [-10,-20], [20,10], [20,20], [15,30]]); +} +test_simplify_path(); + + +module test_simplify_path_indexed() { + pts = [[10,0], [0,-10], [20,20], [20,10], [-20,-20], [15,30], [-10,-20]]; + path = [4,6,1,0,3,2,5]; + assert(simplify_path_indexed(pts, path) == [4,6,3,2,5]); +} +test_simplify_path_indexed(); diff --git a/tests/test_primitives.scad b/tests/test_primitives.scad deleted file mode 100644 index b90cf24..0000000 --- a/tests/test_primitives.scad +++ /dev/null @@ -1,65 +0,0 @@ -include <../std.scad> - - -module test_square() { - assert(square(100, center=true) == [[50,-50],[-50,-50],[-50,50],[50,50]]); - assert(square(100, center=false) == [[100,0],[0,0],[0,100],[100,100]]); - assert(square(100, anchor=FWD+LEFT) == [[100,0],[0,0],[0,100],[100,100]]); - assert(square(100, anchor=BACK+RIGHT) == [[0,-100],[-100,-100],[-100,0],[0,0]]); -} -test_square(); - - -module test_circle() { - for (pt = circle(d=200)) { - assert(approx(norm(pt),100)); - } - for (pt = circle(r=100)) { - assert(approx(norm(pt),100)); - } - assert(is_polygon_clockwise(circle(d=200))); - assert(is_polygon_clockwise(circle(r=100))); - assert(len(circle(d=100,$fn=6)) == 6); - assert(len(circle(d=100,$fn=36)) == 36); -} -test_circle(); - - -module test_cube() { - assert_equal(cube(100,center=true), [[[-50,-50,-50],[50,-50,-50],[50,50,-50],[-50,50,-50],[-50,-50,50],[50,-50,50],[50,50,50],[-50,50,50]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[6,4,7],[6,5,4]]]); - assert_equal(cube([60,80,100],center=true), [[[-30,-40,-50],[30,-40,-50],[30,40,-50],[-30,40,-50],[-30,-40,50],[30,-40,50],[30,40,50],[-30,40,50]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[6,4,7],[6,5,4]]]); - assert_equal(cube([60,80,100],anchor=CENTER), [[[-30,-40,-50],[30,-40,-50],[30,40,-50],[-30,40,-50],[-30,-40,50],[30,-40,50],[30,40,50],[-30,40,50]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[6,4,7],[6,5,4]]]); - assert_equal(cube([60,80,100],center=false), [[[0,0,0],[60,0,0],[60,80,0],[0,80,0],[0,0,100],[60,0,100],[60,80,100],[0,80,100]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[6,4,7],[6,5,4]]]); - assert_equal(cube([60,80,100]), [[[0,0,0],[60,0,0],[60,80,0],[0,80,0],[0,0,100],[60,0,100],[60,80,100],[0,80,100]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[6,4,7],[6,5,4]]]); - assert_equal(cube([60,80,100],anchor=ALLNEG), [[[0,0,0],[60,0,0],[60,80,0],[0,80,0],[0,0,100],[60,0,100],[60,80,100],[0,80,100]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[6,4,7],[6,5,4]]]); - assert_equal(cube([60,80,100],anchor=TOP), [[[-30,-40,-100],[30,-40,-100],[30,40,-100],[-30,40,-100],[-30,-40,0],[30,-40,0],[30,40,0],[-30,40,0]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[6,4,7],[6,5,4]]]); -} -test_cube(); - - -module test_cylinder() { - $fn=12; - assert_approx(cylinder(r=40,h=100,center=true), [[[40,0,-50],[34.6410161514,-20,-50],[20,-34.6410161514,-50],[0,-40,-50],[-20,-34.6410161514,-50],[-34.6410161514,-20,-50],[-40,0,-50],[-34.6410161514,20,-50],[-20,34.6410161514,-50],[0,40,-50],[20,34.6410161514,-50],[34.6410161514,20,-50],[40,0,50],[34.6410161514,-20,50],[20,-34.6410161514,50],[0,-40,50],[-20,-34.6410161514,50],[-34.6410161514,-20,50],[-40,0,50],[-34.6410161514,20,50],[-20,34.6410161514,50],[0,40,50],[20,34.6410161514,50],[34.6410161514,20,50]],[[11,10,9,8,7,6,5,4,3,2,1,0],[0,13,12],[1,14,13],[2,15,14],[3,16,15],[4,17,16],[5,18,17],[6,19,18],[7,20,19],[8,21,20],[9,22,21],[10,23,22],[11,12,23],[0,1,13],[1,2,14],[2,3,15],[3,4,16],[4,5,17],[5,6,18],[6,7,19],[7,8,20],[8,9,21],[9,10,22],[10,11,23],[11,0,12],[12,13,14,15,16,17,18,19,20,21,22,23]]]); - assert_approx(cylinder(d=80,h=100,center=true), [[[40,0,-50],[34.6410161514,-20,-50],[20,-34.6410161514,-50],[0,-40,-50],[-20,-34.6410161514,-50],[-34.6410161514,-20,-50],[-40,0,-50],[-34.6410161514,20,-50],[-20,34.6410161514,-50],[0,40,-50],[20,34.6410161514,-50],[34.6410161514,20,-50],[40,0,50],[34.6410161514,-20,50],[20,-34.6410161514,50],[0,-40,50],[-20,-34.6410161514,50],[-34.6410161514,-20,50],[-40,0,50],[-34.6410161514,20,50],[-20,34.6410161514,50],[0,40,50],[20,34.6410161514,50],[34.6410161514,20,50]],[[11,10,9,8,7,6,5,4,3,2,1,0],[0,13,12],[1,14,13],[2,15,14],[3,16,15],[4,17,16],[5,18,17],[6,19,18],[7,20,19],[8,21,20],[9,22,21],[10,23,22],[11,12,23],[0,1,13],[1,2,14],[2,3,15],[3,4,16],[4,5,17],[5,6,18],[6,7,19],[7,8,20],[8,9,21],[9,10,22],[10,11,23],[11,0,12],[12,13,14,15,16,17,18,19,20,21,22,23]]]); - assert_approx(cylinder(d=80,h=100,anchor=CENTER), [[[40,0,-50],[34.6410161514,-20,-50],[20,-34.6410161514,-50],[0,-40,-50],[-20,-34.6410161514,-50],[-34.6410161514,-20,-50],[-40,0,-50],[-34.6410161514,20,-50],[-20,34.6410161514,-50],[0,40,-50],[20,34.6410161514,-50],[34.6410161514,20,-50],[40,0,50],[34.6410161514,-20,50],[20,-34.6410161514,50],[0,-40,50],[-20,-34.6410161514,50],[-34.6410161514,-20,50],[-40,0,50],[-34.6410161514,20,50],[-20,34.6410161514,50],[0,40,50],[20,34.6410161514,50],[34.6410161514,20,50]],[[11,10,9,8,7,6,5,4,3,2,1,0],[0,13,12],[1,14,13],[2,15,14],[3,16,15],[4,17,16],[5,18,17],[6,19,18],[7,20,19],[8,21,20],[9,22,21],[10,23,22],[11,12,23],[0,1,13],[1,2,14],[2,3,15],[3,4,16],[4,5,17],[5,6,18],[6,7,19],[7,8,20],[8,9,21],[9,10,22],[10,11,23],[11,0,12],[12,13,14,15,16,17,18,19,20,21,22,23]]]); - assert_approx(cylinder(d=80,h=100,center=false), [[[40,0,0],[34.6410161514,-20,0],[20,-34.6410161514,0],[0,-40,0],[-20,-34.6410161514,0],[-34.6410161514,-20,0],[-40,0,0],[-34.6410161514,20,0],[-20,34.6410161514,0],[0,40,0],[20,34.6410161514,0],[34.6410161514,20,0],[40,0,100],[34.6410161514,-20,100],[20,-34.6410161514,100],[0,-40,100],[-20,-34.6410161514,100],[-34.6410161514,-20,100],[-40,0,100],[-34.6410161514,20,100],[-20,34.6410161514,100],[0,40,100],[20,34.6410161514,100],[34.6410161514,20,100]],[[11,10,9,8,7,6,5,4,3,2,1,0],[0,13,12],[1,14,13],[2,15,14],[3,16,15],[4,17,16],[5,18,17],[6,19,18],[7,20,19],[8,21,20],[9,22,21],[10,23,22],[11,12,23],[0,1,13],[1,2,14],[2,3,15],[3,4,16],[4,5,17],[5,6,18],[6,7,19],[7,8,20],[8,9,21],[9,10,22],[10,11,23],[11,0,12],[12,13,14,15,16,17,18,19,20,21,22,23]]]); - assert_approx(cylinder(d=80,h=100,anchor=BOT), [[[40,0,0],[34.6410161514,-20,0],[20,-34.6410161514,0],[0,-40,0],[-20,-34.6410161514,0],[-34.6410161514,-20,0],[-40,0,0],[-34.6410161514,20,0],[-20,34.6410161514,0],[0,40,0],[20,34.6410161514,0],[34.6410161514,20,0],[40,0,100],[34.6410161514,-20,100],[20,-34.6410161514,100],[0,-40,100],[-20,-34.6410161514,100],[-34.6410161514,-20,100],[-40,0,100],[-34.6410161514,20,100],[-20,34.6410161514,100],[0,40,100],[20,34.6410161514,100],[34.6410161514,20,100]],[[11,10,9,8,7,6,5,4,3,2,1,0],[0,13,12],[1,14,13],[2,15,14],[3,16,15],[4,17,16],[5,18,17],[6,19,18],[7,20,19],[8,21,20],[9,22,21],[10,23,22],[11,12,23],[0,1,13],[1,2,14],[2,3,15],[3,4,16],[4,5,17],[5,6,18],[6,7,19],[7,8,20],[8,9,21],[9,10,22],[10,11,23],[11,0,12],[12,13,14,15,16,17,18,19,20,21,22,23]]]); - assert_approx(cylinder(d=80,h=100), [[[40,0,0],[34.6410161514,-20,0],[20,-34.6410161514,0],[0,-40,0],[-20,-34.6410161514,0],[-34.6410161514,-20,0],[-40,0,0],[-34.6410161514,20,0],[-20,34.6410161514,0],[0,40,0],[20,34.6410161514,0],[34.6410161514,20,0],[40,0,100],[34.6410161514,-20,100],[20,-34.6410161514,100],[0,-40,100],[-20,-34.6410161514,100],[-34.6410161514,-20,100],[-40,0,100],[-34.6410161514,20,100],[-20,34.6410161514,100],[0,40,100],[20,34.6410161514,100],[34.6410161514,20,100]],[[11,10,9,8,7,6,5,4,3,2,1,0],[0,13,12],[1,14,13],[2,15,14],[3,16,15],[4,17,16],[5,18,17],[6,19,18],[7,20,19],[8,21,20],[9,22,21],[10,23,22],[11,12,23],[0,1,13],[1,2,14],[2,3,15],[3,4,16],[4,5,17],[5,6,18],[6,7,19],[7,8,20],[8,9,21],[9,10,22],[10,11,23],[11,0,12],[12,13,14,15,16,17,18,19,20,21,22,23]]]); -} -test_cylinder(); - - -module test_sphere() { - $fn=6; - assert_approx(sphere(r=40), [[[20,0,34.6410161514],[10,17.3205080757,34.6410161514],[-10,17.3205080757,34.6410161514],[-20,0,34.6410161514],[-10,-17.3205080757,34.6410161514],[10,-17.3205080757,34.6410161514],[40,0,0],[20,34.6410161514,0],[-20,34.6410161514,0],[-40,0,0],[-20,-34.6410161514,0],[20,-34.6410161514,0],[20,0,-34.6410161514],[10,17.3205080757,-34.6410161514],[-10,17.3205080757,-34.6410161514],[-20,0,-34.6410161514],[-10,-17.3205080757,-34.6410161514],[10,-17.3205080757,-34.6410161514]],[[5,4,3,2,1,0],[12,13,14,15,16,17],[6,0,1],[6,1,7],[7,1,2],[7,2,8],[8,2,3],[8,3,9],[9,3,4],[9,4,10],[10,4,5],[10,5,11],[11,5,0],[11,0,6],[12,6,7],[12,7,13],[13,7,8],[13,8,14],[14,8,9],[14,9,15],[15,9,10],[15,10,16],[16,10,11],[16,11,17],[17,11,6],[17,6,12]]]); - assert_approx(sphere(r=40,style="orig"), [[[20,0,34.6410161514],[10,17.3205080757,34.6410161514],[-10,17.3205080757,34.6410161514],[-20,0,34.6410161514],[-10,-17.3205080757,34.6410161514],[10,-17.3205080757,34.6410161514],[40,0,0],[20,34.6410161514,0],[-20,34.6410161514,0],[-40,0,0],[-20,-34.6410161514,0],[20,-34.6410161514,0],[20,0,-34.6410161514],[10,17.3205080757,-34.6410161514],[-10,17.3205080757,-34.6410161514],[-20,0,-34.6410161514],[-10,-17.3205080757,-34.6410161514],[10,-17.3205080757,-34.6410161514]],[[5,4,3,2,1,0],[12,13,14,15,16,17],[6,0,1],[6,1,7],[7,1,2],[7,2,8],[8,2,3],[8,3,9],[9,3,4],[9,4,10],[10,4,5],[10,5,11],[11,5,0],[11,0,6],[12,6,7],[12,7,13],[13,7,8],[13,8,14],[14,8,9],[14,9,15],[15,9,10],[15,10,16],[16,10,11],[16,11,17],[17,11,6],[17,6,12]]]); - assert_approx(sphere(r=40,style="aligned"), [[[0,0,40],[34.6410161514,0,20],[17.3205080757,30,20],[-17.3205080757,30,20],[-34.6410161514,0,20],[-17.3205080757,-30,20],[17.3205080757,-30,20],[34.6410161514,0,-20],[17.3205080757,30,-20],[-17.3205080757,30,-20],[-34.6410161514,0,-20],[-17.3205080757,-30,-20],[17.3205080757,-30,-20],[0,0,-40]],[[1,0,2],[13,7,8],[2,0,3],[13,8,9],[3,0,4],[13,9,10],[4,0,5],[13,10,11],[5,0,6],[13,11,12],[6,0,1],[13,12,7],[1,2,8],[1,8,7],[2,3,9],[2,9,8],[3,4,10],[3,10,9],[4,5,11],[4,11,10],[5,6,12],[5,12,11],[6,1,7],[6,7,12]]]); - assert_approx(sphere(r=40,style="stagger"), [[[0,0,40],[30,17.3205080757,20],[0,34.6410161514,20],[-30,17.3205080757,20],[-30,-17.3205080757,20],[0,-34.6410161514,20],[30,-17.3205080757,20],[34.6410161514,0,-20],[17.3205080757,30,-20],[-17.3205080757,30,-20],[-34.6410161514,0,-20],[-17.3205080757,-30,-20],[17.3205080757,-30,-20],[0,0,-40]],[[1,0,2],[13,7,8],[2,0,3],[13,8,9],[3,0,4],[13,9,10],[4,0,5],[13,10,11],[5,0,6],[13,11,12],[6,0,1],[13,12,7],[1,2,8],[1,8,7],[2,3,9],[2,9,8],[3,4,10],[3,10,9],[4,5,11],[4,11,10],[5,6,12],[5,12,11],[6,1,7],[6,7,12]]]); - assert_approx(sphere(r=40,style="octa"), [[[0,0,40],[28.2842712475,0,28.2842712475],[0,28.2842712475,28.2842712475],[-28.2842712475,0,28.2842712475],[0,-28.2842712475,28.2842712475],[40,0,0],[28.2842712475,28.2842712475,0],[0,40,0],[-28.2842712475,28.2842712475,0],[-40,0,0],[-28.2842712475,-28.2842712475,0],[0,-40,0],[28.2842712475,-28.2842712475,0],[28.2842712475,0,-28.2842712475],[0,28.2842712475,-28.2842712475],[-28.2842712475,0,-28.2842712475],[0,-28.2842712475,-28.2842712475],[0,0,-40]],[[0,2,1],[0,3,2],[0,4,3],[0,1,4],[17,15,16],[17,14,15],[17,13,14],[17,16,13],[1,6,5],[1,2,6],[13,5,6],[13,6,14],[2,7,6],[14,6,7],[2,8,7],[2,3,8],[14,7,8],[14,8,15],[3,9,8],[15,8,9],[3,10,9],[3,4,10],[15,9,10],[15,10,16],[4,11,10],[16,10,11],[4,12,11],[4,1,12],[16,11,12],[16,12,13],[1,5,12],[13,12,5]]]); - assert_approx(sphere(r=40,style="icosa"), [[[0,0,40],[28.0251707689,-20.3614784182,20],[28.0251707689,20.3614784182,20],[0,0,40],[28.0251707689,20.3614784182,20],[-10.7046626932,32.9455641419,20],[0,0,40],[-10.7046626932,32.9455641419,20],[-34.6410161514,6.66133814775e-15,20],[0,0,40],[-34.6410161514,0,20],[-10.7046626932,-32.9455641419,20],[0,0,40],[-10.7046626932,-32.9455641419,20],[28.0251707689,-20.3614784182,20],[34.6410161514,0,-20],[28.0251707689,-20.3614784182,20],[28.0251707689,20.3614784182,20],[10.7046626932,32.9455641419,-20],[28.0251707689,20.3614784182,20],[-10.7046626932,32.9455641419,20],[-28.0251707689,20.3614784182,-20],[-10.7046626932,32.9455641419,20],[-34.6410161514,-4.4408920985e-15,20],[-28.0251707689,-20.3614784182,-20],[-34.6410161514,1.11022302463e-15,20],[-10.7046626932,-32.9455641419,20],[10.7046626932,-32.9455641419,-20],[-10.7046626932,-32.9455641419,20],[28.0251707689,-20.3614784182,20],[0,0,-40],[-28.0251707689,20.3614784182,-20],[-28.0251707689,-20.3614784182,-20],[0,0,-40],[-28.0251707689,-20.3614784182,-20],[10.7046626932,-32.9455641419,-20],[0,0,-40],[10.7046626932,-32.9455641419,-20],[34.6410161514,-6.66133814775e-15,-20],[0,0,-40],[34.6410161514,0,-20],[10.7046626932,32.9455641419,-20],[0,0,-40],[10.7046626932,32.9455641419,-20],[-28.0251707689,20.3614784182,-20],[-34.6410161514,0,20],[-28.0251707689,20.3614784182,-20],[-28.0251707689,-20.3614784182,-20],[-10.7046626932,-32.9455641419,20],[-28.0251707689,-20.3614784182,-20],[10.7046626932,-32.9455641419,-20],[28.0251707689,-20.3614784182,20],[10.7046626932,-32.9455641419,-20],[34.6410161514,4.4408920985e-15,-20],[28.0251707689,20.3614784182,20],[34.6410161514,-1.11022302463e-15,-20],[10.7046626932,32.9455641419,-20],[-10.7046626932,32.9455641419,20],[10.7046626932,32.9455641419,-20],[-28.0251707689,20.3614784182,-20]],[[0,2,1],[3,5,4],[6,8,7],[9,11,10],[12,14,13],[16,17,15],[19,20,18],[22,23,21],[25,26,24],[28,29,27],[31,32,30],[34,35,33],[37,38,36],[40,41,39],[43,44,42],[45,47,46],[48,50,49],[51,53,52],[54,56,55],[57,59,58]]]); -} -test_sphere(); - - - -// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_regions.scad b/tests/test_regions.scad new file mode 100644 index 0000000..3369cd3 --- /dev/null +++ b/tests/test_regions.scad @@ -0,0 +1,12 @@ +include<../std.scad> + +module test_is_region() { + assert(is_region([circle(d=10),square(10)])); + assert(is_region([circle(d=10),square(10),circle(d=50)])); + assert(is_region([square(10)])); + assert(!is_region([])); + assert(!is_region(23)); + assert(!is_region(true)); + assert(!is_region("foo")); +} +test_is_region(); diff --git a/tests/test_shapes2d.scad b/tests/test_shapes2d.scad index d535e71..1db2c02 100644 --- a/tests/test_shapes2d.scad +++ b/tests/test_shapes2d.scad @@ -1,6 +1,29 @@ include <../std.scad> +module test_square() { + assert(square(100, center=true) == [[50,-50],[-50,-50],[-50,50],[50,50]]); + assert(square(100, center=false) == [[100,0],[0,0],[0,100],[100,100]]); + assert(square(100, anchor=FWD+LEFT) == [[100,0],[0,0],[0,100],[100,100]]); + assert(square(100, anchor=BACK+RIGHT) == [[0,-100],[-100,-100],[-100,0],[0,0]]); +} +test_square(); + + +module test_circle() { + for (pt = circle(d=200)) { + assert(approx(norm(pt),100)); + } + for (pt = circle(r=100)) { + assert(approx(norm(pt),100)); + } + assert(is_polygon_clockwise(circle(d=200))); + assert(is_polygon_clockwise(circle(r=100))); + assert(len(circle(d=100,$fn=6)) == 6); + assert(len(circle(d=100,$fn=36)) == 36); +} +test_circle(); + module test_rect() { diff --git a/tests/test_shapes3d.scad b/tests/test_shapes3d.scad index ca904d7..c102351 100644 --- a/tests/test_shapes3d.scad +++ b/tests/test_shapes3d.scad @@ -1,6 +1,44 @@ include <../std.scad> include <../hull.scad> + +module test_cube() { + assert_equal(cube(100,center=true), [[[-50,-50,-50],[50,-50,-50],[50,50,-50],[-50,50,-50],[-50,-50,50],[50,-50,50],[50,50,50],[-50,50,50]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[6,4,7],[6,5,4]]]); + assert_equal(cube([60,80,100],center=true), [[[-30,-40,-50],[30,-40,-50],[30,40,-50],[-30,40,-50],[-30,-40,50],[30,-40,50],[30,40,50],[-30,40,50]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[6,4,7],[6,5,4]]]); + assert_equal(cube([60,80,100],anchor=CENTER), [[[-30,-40,-50],[30,-40,-50],[30,40,-50],[-30,40,-50],[-30,-40,50],[30,-40,50],[30,40,50],[-30,40,50]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[6,4,7],[6,5,4]]]); + assert_equal(cube([60,80,100],center=false), [[[0,0,0],[60,0,0],[60,80,0],[0,80,0],[0,0,100],[60,0,100],[60,80,100],[0,80,100]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[6,4,7],[6,5,4]]]); + assert_equal(cube([60,80,100]), [[[0,0,0],[60,0,0],[60,80,0],[0,80,0],[0,0,100],[60,0,100],[60,80,100],[0,80,100]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[6,4,7],[6,5,4]]]); + assert_equal(cube([60,80,100],anchor=ALLNEG), [[[0,0,0],[60,0,0],[60,80,0],[0,80,0],[0,0,100],[60,0,100],[60,80,100],[0,80,100]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[6,4,7],[6,5,4]]]); + assert_equal(cube([60,80,100],anchor=TOP), [[[-30,-40,-100],[30,-40,-100],[30,40,-100],[-30,40,-100],[-30,-40,0],[30,-40,0],[30,40,0],[-30,40,0]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[6,4,7],[6,5,4]]]); +} +test_cube(); + + +module test_cylinder() { + $fn=12; + assert_approx(cylinder(r=40,h=100,center=true), [[[40,0,-50],[34.6410161514,-20,-50],[20,-34.6410161514,-50],[0,-40,-50],[-20,-34.6410161514,-50],[-34.6410161514,-20,-50],[-40,0,-50],[-34.6410161514,20,-50],[-20,34.6410161514,-50],[0,40,-50],[20,34.6410161514,-50],[34.6410161514,20,-50],[40,0,50],[34.6410161514,-20,50],[20,-34.6410161514,50],[0,-40,50],[-20,-34.6410161514,50],[-34.6410161514,-20,50],[-40,0,50],[-34.6410161514,20,50],[-20,34.6410161514,50],[0,40,50],[20,34.6410161514,50],[34.6410161514,20,50]],[[11,10,9,8,7,6,5,4,3,2,1,0],[0,13,12],[1,14,13],[2,15,14],[3,16,15],[4,17,16],[5,18,17],[6,19,18],[7,20,19],[8,21,20],[9,22,21],[10,23,22],[11,12,23],[0,1,13],[1,2,14],[2,3,15],[3,4,16],[4,5,17],[5,6,18],[6,7,19],[7,8,20],[8,9,21],[9,10,22],[10,11,23],[11,0,12],[12,13,14,15,16,17,18,19,20,21,22,23]]]); + assert_approx(cylinder(d=80,h=100,center=true), [[[40,0,-50],[34.6410161514,-20,-50],[20,-34.6410161514,-50],[0,-40,-50],[-20,-34.6410161514,-50],[-34.6410161514,-20,-50],[-40,0,-50],[-34.6410161514,20,-50],[-20,34.6410161514,-50],[0,40,-50],[20,34.6410161514,-50],[34.6410161514,20,-50],[40,0,50],[34.6410161514,-20,50],[20,-34.6410161514,50],[0,-40,50],[-20,-34.6410161514,50],[-34.6410161514,-20,50],[-40,0,50],[-34.6410161514,20,50],[-20,34.6410161514,50],[0,40,50],[20,34.6410161514,50],[34.6410161514,20,50]],[[11,10,9,8,7,6,5,4,3,2,1,0],[0,13,12],[1,14,13],[2,15,14],[3,16,15],[4,17,16],[5,18,17],[6,19,18],[7,20,19],[8,21,20],[9,22,21],[10,23,22],[11,12,23],[0,1,13],[1,2,14],[2,3,15],[3,4,16],[4,5,17],[5,6,18],[6,7,19],[7,8,20],[8,9,21],[9,10,22],[10,11,23],[11,0,12],[12,13,14,15,16,17,18,19,20,21,22,23]]]); + assert_approx(cylinder(d=80,h=100,anchor=CENTER), [[[40,0,-50],[34.6410161514,-20,-50],[20,-34.6410161514,-50],[0,-40,-50],[-20,-34.6410161514,-50],[-34.6410161514,-20,-50],[-40,0,-50],[-34.6410161514,20,-50],[-20,34.6410161514,-50],[0,40,-50],[20,34.6410161514,-50],[34.6410161514,20,-50],[40,0,50],[34.6410161514,-20,50],[20,-34.6410161514,50],[0,-40,50],[-20,-34.6410161514,50],[-34.6410161514,-20,50],[-40,0,50],[-34.6410161514,20,50],[-20,34.6410161514,50],[0,40,50],[20,34.6410161514,50],[34.6410161514,20,50]],[[11,10,9,8,7,6,5,4,3,2,1,0],[0,13,12],[1,14,13],[2,15,14],[3,16,15],[4,17,16],[5,18,17],[6,19,18],[7,20,19],[8,21,20],[9,22,21],[10,23,22],[11,12,23],[0,1,13],[1,2,14],[2,3,15],[3,4,16],[4,5,17],[5,6,18],[6,7,19],[7,8,20],[8,9,21],[9,10,22],[10,11,23],[11,0,12],[12,13,14,15,16,17,18,19,20,21,22,23]]]); + assert_approx(cylinder(d=80,h=100,center=false), [[[40,0,0],[34.6410161514,-20,0],[20,-34.6410161514,0],[0,-40,0],[-20,-34.6410161514,0],[-34.6410161514,-20,0],[-40,0,0],[-34.6410161514,20,0],[-20,34.6410161514,0],[0,40,0],[20,34.6410161514,0],[34.6410161514,20,0],[40,0,100],[34.6410161514,-20,100],[20,-34.6410161514,100],[0,-40,100],[-20,-34.6410161514,100],[-34.6410161514,-20,100],[-40,0,100],[-34.6410161514,20,100],[-20,34.6410161514,100],[0,40,100],[20,34.6410161514,100],[34.6410161514,20,100]],[[11,10,9,8,7,6,5,4,3,2,1,0],[0,13,12],[1,14,13],[2,15,14],[3,16,15],[4,17,16],[5,18,17],[6,19,18],[7,20,19],[8,21,20],[9,22,21],[10,23,22],[11,12,23],[0,1,13],[1,2,14],[2,3,15],[3,4,16],[4,5,17],[5,6,18],[6,7,19],[7,8,20],[8,9,21],[9,10,22],[10,11,23],[11,0,12],[12,13,14,15,16,17,18,19,20,21,22,23]]]); + assert_approx(cylinder(d=80,h=100,anchor=BOT), [[[40,0,0],[34.6410161514,-20,0],[20,-34.6410161514,0],[0,-40,0],[-20,-34.6410161514,0],[-34.6410161514,-20,0],[-40,0,0],[-34.6410161514,20,0],[-20,34.6410161514,0],[0,40,0],[20,34.6410161514,0],[34.6410161514,20,0],[40,0,100],[34.6410161514,-20,100],[20,-34.6410161514,100],[0,-40,100],[-20,-34.6410161514,100],[-34.6410161514,-20,100],[-40,0,100],[-34.6410161514,20,100],[-20,34.6410161514,100],[0,40,100],[20,34.6410161514,100],[34.6410161514,20,100]],[[11,10,9,8,7,6,5,4,3,2,1,0],[0,13,12],[1,14,13],[2,15,14],[3,16,15],[4,17,16],[5,18,17],[6,19,18],[7,20,19],[8,21,20],[9,22,21],[10,23,22],[11,12,23],[0,1,13],[1,2,14],[2,3,15],[3,4,16],[4,5,17],[5,6,18],[6,7,19],[7,8,20],[8,9,21],[9,10,22],[10,11,23],[11,0,12],[12,13,14,15,16,17,18,19,20,21,22,23]]]); + assert_approx(cylinder(d=80,h=100), [[[40,0,0],[34.6410161514,-20,0],[20,-34.6410161514,0],[0,-40,0],[-20,-34.6410161514,0],[-34.6410161514,-20,0],[-40,0,0],[-34.6410161514,20,0],[-20,34.6410161514,0],[0,40,0],[20,34.6410161514,0],[34.6410161514,20,0],[40,0,100],[34.6410161514,-20,100],[20,-34.6410161514,100],[0,-40,100],[-20,-34.6410161514,100],[-34.6410161514,-20,100],[-40,0,100],[-34.6410161514,20,100],[-20,34.6410161514,100],[0,40,100],[20,34.6410161514,100],[34.6410161514,20,100]],[[11,10,9,8,7,6,5,4,3,2,1,0],[0,13,12],[1,14,13],[2,15,14],[3,16,15],[4,17,16],[5,18,17],[6,19,18],[7,20,19],[8,21,20],[9,22,21],[10,23,22],[11,12,23],[0,1,13],[1,2,14],[2,3,15],[3,4,16],[4,5,17],[5,6,18],[6,7,19],[7,8,20],[8,9,21],[9,10,22],[10,11,23],[11,0,12],[12,13,14,15,16,17,18,19,20,21,22,23]]]); +} +test_cylinder(); + + +module test_sphere() { + $fn=6; + assert_approx(sphere(r=40), [[[20,0,34.6410161514],[10,17.3205080757,34.6410161514],[-10,17.3205080757,34.6410161514],[-20,0,34.6410161514],[-10,-17.3205080757,34.6410161514],[10,-17.3205080757,34.6410161514],[40,0,0],[20,34.6410161514,0],[-20,34.6410161514,0],[-40,0,0],[-20,-34.6410161514,0],[20,-34.6410161514,0],[20,0,-34.6410161514],[10,17.3205080757,-34.6410161514],[-10,17.3205080757,-34.6410161514],[-20,0,-34.6410161514],[-10,-17.3205080757,-34.6410161514],[10,-17.3205080757,-34.6410161514]],[[5,4,3,2,1,0],[12,13,14,15,16,17],[6,0,1],[6,1,7],[7,1,2],[7,2,8],[8,2,3],[8,3,9],[9,3,4],[9,4,10],[10,4,5],[10,5,11],[11,5,0],[11,0,6],[12,6,7],[12,7,13],[13,7,8],[13,8,14],[14,8,9],[14,9,15],[15,9,10],[15,10,16],[16,10,11],[16,11,17],[17,11,6],[17,6,12]]]); + assert_approx(sphere(r=40,style="orig"), [[[20,0,34.6410161514],[10,17.3205080757,34.6410161514],[-10,17.3205080757,34.6410161514],[-20,0,34.6410161514],[-10,-17.3205080757,34.6410161514],[10,-17.3205080757,34.6410161514],[40,0,0],[20,34.6410161514,0],[-20,34.6410161514,0],[-40,0,0],[-20,-34.6410161514,0],[20,-34.6410161514,0],[20,0,-34.6410161514],[10,17.3205080757,-34.6410161514],[-10,17.3205080757,-34.6410161514],[-20,0,-34.6410161514],[-10,-17.3205080757,-34.6410161514],[10,-17.3205080757,-34.6410161514]],[[5,4,3,2,1,0],[12,13,14,15,16,17],[6,0,1],[6,1,7],[7,1,2],[7,2,8],[8,2,3],[8,3,9],[9,3,4],[9,4,10],[10,4,5],[10,5,11],[11,5,0],[11,0,6],[12,6,7],[12,7,13],[13,7,8],[13,8,14],[14,8,9],[14,9,15],[15,9,10],[15,10,16],[16,10,11],[16,11,17],[17,11,6],[17,6,12]]]); + assert_approx(sphere(r=40,style="aligned"), [[[0,0,40],[34.6410161514,0,20],[17.3205080757,30,20],[-17.3205080757,30,20],[-34.6410161514,0,20],[-17.3205080757,-30,20],[17.3205080757,-30,20],[34.6410161514,0,-20],[17.3205080757,30,-20],[-17.3205080757,30,-20],[-34.6410161514,0,-20],[-17.3205080757,-30,-20],[17.3205080757,-30,-20],[0,0,-40]],[[1,0,2],[13,7,8],[2,0,3],[13,8,9],[3,0,4],[13,9,10],[4,0,5],[13,10,11],[5,0,6],[13,11,12],[6,0,1],[13,12,7],[1,2,8],[1,8,7],[2,3,9],[2,9,8],[3,4,10],[3,10,9],[4,5,11],[4,11,10],[5,6,12],[5,12,11],[6,1,7],[6,7,12]]]); + assert_approx(sphere(r=40,style="stagger"), [[[0,0,40],[30,17.3205080757,20],[0,34.6410161514,20],[-30,17.3205080757,20],[-30,-17.3205080757,20],[0,-34.6410161514,20],[30,-17.3205080757,20],[34.6410161514,0,-20],[17.3205080757,30,-20],[-17.3205080757,30,-20],[-34.6410161514,0,-20],[-17.3205080757,-30,-20],[17.3205080757,-30,-20],[0,0,-40]],[[1,0,2],[13,7,8],[2,0,3],[13,8,9],[3,0,4],[13,9,10],[4,0,5],[13,10,11],[5,0,6],[13,11,12],[6,0,1],[13,12,7],[1,2,8],[1,8,7],[2,3,9],[2,9,8],[3,4,10],[3,10,9],[4,5,11],[4,11,10],[5,6,12],[5,12,11],[6,1,7],[6,7,12]]]); + assert_approx(sphere(r=40,style="octa"), [[[0,0,40],[28.2842712475,0,28.2842712475],[0,28.2842712475,28.2842712475],[-28.2842712475,0,28.2842712475],[0,-28.2842712475,28.2842712475],[40,0,0],[28.2842712475,28.2842712475,0],[0,40,0],[-28.2842712475,28.2842712475,0],[-40,0,0],[-28.2842712475,-28.2842712475,0],[0,-40,0],[28.2842712475,-28.2842712475,0],[28.2842712475,0,-28.2842712475],[0,28.2842712475,-28.2842712475],[-28.2842712475,0,-28.2842712475],[0,-28.2842712475,-28.2842712475],[0,0,-40]],[[0,2,1],[0,3,2],[0,4,3],[0,1,4],[17,15,16],[17,14,15],[17,13,14],[17,16,13],[1,6,5],[1,2,6],[13,5,6],[13,6,14],[2,7,6],[14,6,7],[2,8,7],[2,3,8],[14,7,8],[14,8,15],[3,9,8],[15,8,9],[3,10,9],[3,4,10],[15,9,10],[15,10,16],[4,11,10],[16,10,11],[4,12,11],[4,1,12],[16,11,12],[16,12,13],[1,5,12],[13,12,5]]]); + assert_approx(sphere(r=40,style="icosa"), [[[0,0,40],[28.0251707689,-20.3614784182,20],[28.0251707689,20.3614784182,20],[0,0,40],[28.0251707689,20.3614784182,20],[-10.7046626932,32.9455641419,20],[0,0,40],[-10.7046626932,32.9455641419,20],[-34.6410161514,6.66133814775e-15,20],[0,0,40],[-34.6410161514,0,20],[-10.7046626932,-32.9455641419,20],[0,0,40],[-10.7046626932,-32.9455641419,20],[28.0251707689,-20.3614784182,20],[34.6410161514,0,-20],[28.0251707689,-20.3614784182,20],[28.0251707689,20.3614784182,20],[10.7046626932,32.9455641419,-20],[28.0251707689,20.3614784182,20],[-10.7046626932,32.9455641419,20],[-28.0251707689,20.3614784182,-20],[-10.7046626932,32.9455641419,20],[-34.6410161514,-4.4408920985e-15,20],[-28.0251707689,-20.3614784182,-20],[-34.6410161514,1.11022302463e-15,20],[-10.7046626932,-32.9455641419,20],[10.7046626932,-32.9455641419,-20],[-10.7046626932,-32.9455641419,20],[28.0251707689,-20.3614784182,20],[0,0,-40],[-28.0251707689,20.3614784182,-20],[-28.0251707689,-20.3614784182,-20],[0,0,-40],[-28.0251707689,-20.3614784182,-20],[10.7046626932,-32.9455641419,-20],[0,0,-40],[10.7046626932,-32.9455641419,-20],[34.6410161514,-6.66133814775e-15,-20],[0,0,-40],[34.6410161514,0,-20],[10.7046626932,32.9455641419,-20],[0,0,-40],[10.7046626932,32.9455641419,-20],[-28.0251707689,20.3614784182,-20],[-34.6410161514,0,20],[-28.0251707689,20.3614784182,-20],[-28.0251707689,-20.3614784182,-20],[-10.7046626932,-32.9455641419,20],[-28.0251707689,-20.3614784182,-20],[10.7046626932,-32.9455641419,-20],[28.0251707689,-20.3614784182,20],[10.7046626932,-32.9455641419,-20],[34.6410161514,4.4408920985e-15,-20],[28.0251707689,20.3614784182,20],[34.6410161514,-1.11022302463e-15,-20],[10.7046626932,32.9455641419,-20],[-10.7046626932,32.9455641419,20],[10.7046626932,32.9455641419,-20],[-28.0251707689,20.3614784182,-20]],[[0,2,1],[3,5,4],[6,8,7],[9,11,10],[12,14,13],[16,17,15],[19,20,18],[22,23,21],[25,26,24],[28,29,27],[31,32,30],[34,35,33],[37,38,36],[40,41,39],[43,44,42],[45,47,46],[48,50,49],[51,53,52],[54,56,55],[57,59,58]]]); +} +test_sphere(); + + + module test_prismoid() { $fn=24; assert_approx(prismoid([100,80],[50,40],h=50), [[[25,20,50],[25,-20,50],[-25,-20,50],[-25,20,50],[50,40,0],[50,-40,0],[-50,-40,0],[-50,40,0]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[4,7,6],[4,6,5]]]); diff --git a/tests/test_common.scad b/tests/test_utility.scad similarity index 100% rename from tests/test_common.scad rename to tests/test_utility.scad diff --git a/tests/test_vnf.scad b/tests/test_vnf.scad index b83775e..7ac7c5e 100644 --- a/tests/test_vnf.scad +++ b/tests/test_vnf.scad @@ -81,11 +81,18 @@ test_vnf_centroid(); module test_vnf_volume() { assert_approx(vnf_volume(cube(100, center=false)), 1000000); - assert(approx(vnf_volume(sphere(d=100, anchor=BOT, $fn=144)), 4/3*PI*pow(50,3), eps=1e3)); + assert(approx(vnf_volume(sphere(d=100, anchor=BOT, $fn=144)) / (4/3*PI*pow(50,3)),1, eps=.001)); } test_vnf_volume(); + +module test_vnf_area(){ + assert(approx(vnf_area(sphere(d=100, $fn=144)) / (4*PI*50*50),1, eps=1e-3)); +} +test_vnf_area(); + + module test_vnf_merge() { vnf1 = vnf_add_face(pts=[[-1,-1,-1],[1,-1,-1],[0,1,-1]]); vnf2 = vnf_add_face(pts=[[1,1,1],[-1,1,1],[0,1,-1]]); diff --git a/trigonometry.scad b/trigonometry.scad index 3db6aaf..07a35f1 100644 --- a/trigonometry.scad +++ b/trigonometry.scad @@ -96,7 +96,7 @@ function law_of_sines(a, A, b, B) = // p1 = The first vertex of the triangle. // p2 = The second vertex of the triangle. // p3 = The third vertex of the triangle. -// Examples: +// Example: // triangle_area([0,0], [5,10], [10,0]); // Returns -50 // triangle_area([10,0], [5,10], [0,0]); // Returns 50 function triangle_area(p1,p2,p3) = diff --git a/common.scad b/utility.scad similarity index 96% rename from common.scad rename to utility.scad index 29fdd30..61219f9 100644 --- a/common.scad +++ b/utility.scad @@ -1,6 +1,6 @@ ////////////////////////////////////////////////////////////////////// -// LibFile: common.scad -// Common functions used in argument processing. +// LibFile: utility.scad +// Utility functions used in argument processing. // Includes: // include ////////////////////////////////////////////////////////////////////// @@ -18,7 +18,7 @@ // Description: // Returns a string representing the type of the value. One of "undef", "boolean", "number", "nan", "string", "list", "range", "function" or "invalid". // Some malformed "ranges", like '[0:NAN:INF]' and '[0:"a":INF]', may be classified as "undef" or "invalid". -// Examples: +// Example: // typ = typeof(undef); // Returns: "undef" // typ = typeof(true); // Returns: "boolean" // typ = typeof(42); // Returns: "number" @@ -294,7 +294,7 @@ function default(v,dflt=undef) = is_undef(v)? dflt : v; // Arguments: // v = The list whose items are being checked. // recursive = If true, sublists are checked recursively for defined values. The first sublist that has a defined item is returned. -// Examples: +// Example: // val = first_defined([undef,7,undef,true]); // Returns: 7 function first_defined(v,recursive=false,_i=0) = _i=EPSILON,"Tried to normalize a zero vector") : error) : @@ -191,13 +191,13 @@ function unit(v, error=[[["ASSERT"]]]) = // v1 = First vector or point. // v2 = Second vector or point. // v3 = Third point in three point mode. -// Examples: -// vector_angle(UP,LEFT); // Returns: 90 -// vector_angle(RIGHT,LEFT); // Returns: 180 -// vector_angle(UP+RIGHT,RIGHT); // Returns: 45 -// vector_angle([10,10], [0,0], [10,-10]); // Returns: 90 -// vector_angle([10,0,10], [0,0,0], [-10,10,0]); // Returns: 120 -// vector_angle([[10,0,10], [0,0,0], [-10,10,0]]); // Returns: 120 +// Example: +// ang1 = vector_angle(UP,LEFT); // Returns: 90 +// ang2 = vector_angle(RIGHT,LEFT); // Returns: 180 +// ang3 = vector_angle(UP+RIGHT,RIGHT); // Returns: 45 +// ang4 = vector_angle([10,10], [0,0], [10,-10]); // Returns: 90 +// ang5 = vector_angle([10,0,10], [0,0,0], [-10,10,0]); // Returns: 120 +// ang6 = vector_angle([[10,0,10], [0,0,0], [-10,10,0]]); // Returns: 120 function vector_angle(v1,v2,v3) = assert( ( is_undef(v3) && ( is_undef(v2) || same_shape(v1,v2) ) ) || is_consistent([v1,v2,v3]) , @@ -233,13 +233,13 @@ function vector_angle(v1,v2,v3) = // v1 = First vector or point. // v2 = Second vector or point. // v3 = Third point in three point mode. -// Examples: -// vector_axis(UP,LEFT); // Returns: [0,-1,0] (FWD) -// vector_axis(RIGHT,LEFT); // Returns: [0,-1,0] (FWD) -// vector_axis(UP+RIGHT,RIGHT); // Returns: [0,1,0] (BACK) -// vector_axis([10,10], [0,0], [10,-10]); // Returns: [0,0,-1] (DOWN) -// vector_axis([10,0,10], [0,0,0], [-10,10,0]); // Returns: [-0.57735, -0.57735, 0.57735] -// vector_axis([[10,0,10], [0,0,0], [-10,10,0]]); // Returns: [-0.57735, -0.57735, 0.57735] +// Example: +// axis1 = vector_axis(UP,LEFT); // Returns: [0,-1,0] (FWD) +// axis2 = vector_axis(RIGHT,LEFT); // Returns: [0,-1,0] (FWD) +// axis3 = vector_axis(UP+RIGHT,RIGHT); // Returns: [0,1,0] (BACK) +// axis4 = vector_axis([10,10], [0,0], [10,-10]); // Returns: [0,0,-1] (DOWN) +// axis5 = vector_axis([10,0,10], [0,0,0], [-10,10,0]); // Returns: [-0.57735, -0.57735, 0.57735] +// axis6 = vector_axis([[10,0,10], [0,0,0], [-10,10,0]]); // Returns: [-0.57735, -0.57735, 0.57735] function vector_axis(v1,v2=undef,v3=undef) = is_vector(v3) ? assert(is_consistent([v3,v2,v1]), "Bad arguments.") diff --git a/vnf.scad b/vnf.scad index 58afed8..0cfe001 100644 --- a/vnf.scad +++ b/vnf.scad @@ -21,63 +21,6 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces. -// Function: is_vnf() -// Usage: -// bool = is_vnf(x); -// Description: -// Returns true if the given value looks like a VNF structure. -function is_vnf(x) = - is_list(x) && - len(x)==2 && - is_list(x[0]) && - is_list(x[1]) && - (x[0]==[] || (len(x[0])>=3 && is_vector(x[0][0]))) && - (x[1]==[] || is_vector(x[1][0])); - - -// Function: is_vnf_list() -// Description: Returns true if the given value looks passingly like a list of VNF structures. -function is_vnf_list(x) = is_list(x) && all([for (v=x) is_vnf(v)]); - - -// Function: vnf_vertices() -// Description: Given a VNF structure, returns the list of vertex points. -function vnf_vertices(vnf) = vnf[0]; - - -// Function: vnf_faces() -// Description: Given a VNF structure, returns the list of faces, where each face is a list of indices into the VNF vertex list. -function vnf_faces(vnf) = vnf[1]; - - -// Function: vnf_get_vertex() -// Usage: -// vvnf = vnf_get_vertex(vnf, p); -// Description: -// Finds the index number of the given vertex point `p` in the given VNF structure `vnf`. -// If said point does not already exist in the VNF vertex list, it is added to the returned VNF. -// Returns: `[INDEX, VNF]` where INDEX is the index of the point in the returned VNF's vertex list, -// and VNF is the possibly modified new VNF structure. If `p` is given as a list of points, then -// the returned INDEX will be a list of indices. -// Arguments: -// vnf = The VNF structue to get the point index from. -// p = The point, or list of points to get the index of. -// Example: -// vnf1 = vnf_get_vertex(p=[3,5,8]); // Returns: [0, [[[3,5,8]],[]]] -// vnf2 = vnf_get_vertex(vnf1, p=[3,2,1]); // Returns: [1, [[[3,5,8],[3,2,1]],[]]] -// vnf3 = vnf_get_vertex(vnf2, p=[3,5,8]); // Returns: [0, [[[3,5,8],[3,2,1]],[]]] -// vnf4 = vnf_get_vertex(vnf3, p=[[1,3,2],[3,2,1]]); // Returns: [[1,2], [[[3,5,8],[3,2,1],[1,3,2]],[]]] -function vnf_get_vertex(vnf=EMPTY_VNF, p) = - let( - isvec = is_vector(p), - pts = isvec? [p] : p, - res = set_union(vnf[0], pts, get_indices=true) - ) [ - (isvec? res[0][0] : res[0]), - [ res[1], vnf[1] ] - ]; - - // Section: Constructing VNFs // Function: vnf_vertex_array() @@ -438,6 +381,63 @@ function vnf_merge(vnfs, cleanup=false, eps=EPSILON) = [nverts, nfaces]; +// Function: is_vnf() +// Usage: +// bool = is_vnf(x); +// Description: +// Returns true if the given value looks like a VNF structure. +function is_vnf(x) = + is_list(x) && + len(x)==2 && + is_list(x[0]) && + is_list(x[1]) && + (x[0]==[] || (len(x[0])>=3 && is_vector(x[0][0]))) && + (x[1]==[] || is_vector(x[1][0])); + + +// Function: is_vnf_list() +// Description: Returns true if the given value looks passingly like a list of VNF structures. +function is_vnf_list(x) = is_list(x) && all([for (v=x) is_vnf(v)]); + + +// Function: vnf_vertices() +// Description: Given a VNF structure, returns the list of vertex points. +function vnf_vertices(vnf) = vnf[0]; + + +// Function: vnf_faces() +// Description: Given a VNF structure, returns the list of faces, where each face is a list of indices into the VNF vertex list. +function vnf_faces(vnf) = vnf[1]; + + +// Function: vnf_get_vertex() +// Usage: +// vvnf = vnf_get_vertex(vnf, p); +// Description: +// Finds the index number of the given vertex point `p` in the given VNF structure `vnf`. +// If said point does not already exist in the VNF vertex list, it is added to the returned VNF. +// Returns: `[INDEX, VNF]` where INDEX is the index of the point in the returned VNF's vertex list, +// and VNF is the possibly modified new VNF structure. If `p` is given as a list of points, then +// the returned INDEX will be a list of indices. +// Arguments: +// vnf = The VNF structue to get the point index from. +// p = The point, or list of points to get the index of. +// Example: +// vnf1 = vnf_get_vertex(p=[3,5,8]); // Returns: [0, [[[3,5,8]],[]]] +// vnf2 = vnf_get_vertex(vnf1, p=[3,2,1]); // Returns: [1, [[[3,5,8],[3,2,1]],[]]] +// vnf3 = vnf_get_vertex(vnf2, p=[3,5,8]); // Returns: [0, [[[3,5,8],[3,2,1]],[]]] +// vnf4 = vnf_get_vertex(vnf3, p=[[1,3,2],[3,2,1]]); // Returns: [[1,2], [[[3,5,8],[3,2,1],[1,3,2]],[]]] +function vnf_get_vertex(vnf=EMPTY_VNF, p) = + let( + isvec = is_vector(p), + pts = isvec? [p] : p, + res = set_union(vnf[0], pts, get_indices=true) + ) [ + (isvec? res[0][0] : res[0]), + [ res[1], vnf[1] ] + ]; + + // Section: Altering the VNF Internals @@ -963,6 +963,7 @@ function _split_polygons_at_each_y(polys, ys, _i=0) = // bad edges and vertices, overlaid on a transparent gray polyhedron of the VNF. // . // Currently checks for these problems: +// . // Type | Color | Code | Message // ------- | -------- | ------------ | --------------------------------- // WARNING | Yellow | BIG_FACE | Face has more than 3 vertices, and may confuse CGAL.