From 4fee72f51736de0afe973311987133487ec1cccf Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Mon, 6 Sep 2021 10:48:37 -0400 Subject: [PATCH 1/5] Added section to transforms.scad and new top text. Error trap in offset() for repeated points --- regions.scad | 1 + transforms.scad | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/regions.scad b/regions.scad index 378576c..3355bb3 100644 --- a/regions.scad +++ b/regions.scad @@ -482,6 +482,7 @@ function _offset_chamfer(center, points, delta) = function _shift_segment(segment, d) = + assert(!approx(segment[0],segment[1]),"Path has repeated points") move(d*line_normal(segment),segment); diff --git a/transforms.scad b/transforms.scad index d3bcdfb..2056aae 100644 --- a/transforms.scad +++ b/transforms.scad @@ -1,6 +1,6 @@ ////////////////////////////////////////////////////////////////////// // LibFile: transforms.scad -// Functions and modules for translation, rotation, reflection and skewing. +// Functions and modules that provide shortcuts for translation, rotation, mirror and skew operations. The shortcuts can act on geometry, like the usual OpenSCAD rotate() and translate(). They also work as functions that operate on lists of points in various forms: paths, VNFS and bezier patches. Lastly, the function form of the shortcuts can return a matrix representing the operation the shortcut performs. The rotation and scaling shortcuts accept an optional centerpoint for the rotation or scaling operation. // Includes: // include ////////////////////////////////////////////////////////////////////// @@ -1011,6 +1011,10 @@ function zscale(z=1, p, cp=0) = scale([1,1,z], cp=cp, p=p); +////////////////////////////////////////////////////////////////////// +// Section: Reflection (Mirroring) +////////////////////////////////////////////////////////////////////// + // Function&Module: mirror() // Usage: As Module // mirror(v) ... From 0aad39c7e51a83d447d6decf45748e6de6a9c147 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Mon, 6 Sep 2021 18:07:03 -0400 Subject: [PATCH 2/5] Move affine3d_frame_map to transforms.scad and rename to frame_map. Add module version. Change all calls. --- affine.scad | 58 ------------------------------- coords.scad | 4 +-- paths.scad | 8 ++--- skin.scad | 8 ++--- threading.scad | 2 +- transforms.scad | 92 +++++++++++++++++++++++++++++++++++++++++++++++-- turtle3d.scad | 2 +- 7 files changed, 102 insertions(+), 72 deletions(-) diff --git a/affine.scad b/affine.scad index 0c27e47..f510022 100644 --- a/affine.scad +++ b/affine.scad @@ -882,64 +882,6 @@ function affine3d_rot_from_to(from, to) = ]; -// Function: affine3d_frame_map() -// Usage: -// map = affine3d_frame_map(v1, v2, v3, [reverse=]); -// map = affine3d_frame_map(x=VECTOR1, y=VECTOR2, [reverse=]); -// map = affine3d_frame_map(x=VECTOR1, z=VECTOR2, [reverse=]); -// map = affine3d_frame_map(y=VECTOR1, z=VECTOR2, [reverse=]); -// Topics: Affine, Matrices, Transforms, Rotation -// See Also: rot(), xrot(), yrot(), zrot(), affine2d_zrot() -// Description: -// Returns a transformation that maps one coordinate frame to another. You must specify two or -// three of `x`, `y`, and `z`. The specified axes are mapped to the vectors you supplied. If you -// give two inputs, the third vector is mapped to the appropriate normal to maintain a right hand -// coordinate system. If the vectors you give are orthogonal the result will be a rotation and the -// `reverse` parameter will supply the inverse map, which enables you to map two arbitrary -// coordinate systems to each other by using the canonical coordinate system as an intermediary. -// You cannot use the `reverse` option with non-orthogonal inputs. -// Arguments: -// x = Destination 3D vector for x axis. -// y = Destination 3D vector for y axis. -// z = Destination 3D vector for z axis. -// reverse = reverse direction of the map for orthogonal inputs. Default: false -// Example: -// T = affine3d_frame_map(x=[1,1,0], y=[-1,1,0]); // This map is just a rotation around the z axis -// Example: -// T = affine3d_frame_map(x=[1,0,0], y=[1,1,0]); // This map is not a rotation because x and y aren't orthogonal -// Example: -// // The next map sends [1,1,0] to [0,1,1] and [-1,1,0] to [0,-1,1] -// T = affine3d_frame_map(x=[0,1,1], y=[0,-1,1]) * affine3d_frame_map(x=[1,1,0], y=[-1,1,0],reverse=true); -function affine3d_frame_map(x,y,z, reverse=false) = - assert(num_defined([x,y,z])>=2, "Must define at least two inputs") - let( - xvalid = is_undef(x) || (is_vector(x) && len(x)==3), - yvalid = is_undef(y) || (is_vector(y) && len(y)==3), - zvalid = is_undef(z) || (is_vector(z) && len(z)==3) - ) - assert(xvalid,"Input x must be a length 3 vector") - assert(yvalid,"Input y must be a length 3 vector") - assert(zvalid,"Input z must be a length 3 vector") - let( - x = is_undef(x)? undef : unit(x,RIGHT), - y = is_undef(y)? undef : unit(y,BACK), - z = is_undef(z)? undef : unit(z,UP), - map = is_undef(x)? [cross(y,z), y, z] : - is_undef(y)? [x, cross(z,x), z] : - is_undef(z)? [x, y, cross(x,y)] : - [x, y, z] - ) - reverse? ( - let( - ocheck = ( - approx(map[0]*map[1],0) && - approx(map[0]*map[2],0) && - approx(map[1]*map[2],0) - ) - ) - assert(ocheck, "Inputs must be orthogonal when reverse==true") - [for (r=map) [for (c=r) c, 0], [0,0,0,1]] - ) : [for (r=transpose(map)) [for (c=r) c, 0], [0,0,0,1]]; diff --git a/coords.scad b/coords.scad index 7f4630d..ffd0973 100644 --- a/coords.scad +++ b/coords.scad @@ -224,7 +224,7 @@ function project_plane(plane,p) = y = unit(plane[1]-plane[0]), // y axis goes to point b x = unit(v-(v*y)*y) // x axis ) - affine3d_frame_map(x,y) * move(-plane[0]) + frame_map(x,y) * move(-plane[0]) : is_vector(plane,4) && is_undef(p) ? // no data, plane given in "plane" assert(_valid_plane(plane), "Plane is not valid") let( @@ -280,7 +280,7 @@ function lift_plane(plane, p) = y = unit(plane[1]-plane[0]), // y axis goes to point b x = unit(v-(v*y)*y) // x axis ) - move(plane[0]) * affine3d_frame_map(x,y,reverse=true) + move(plane[0]) * frame_map(x,y,reverse=true) : is_vector(plane,4) && is_undef(p) ? // no data, plane given in "plane" assert(_valid_plane(plane), "Plane is not valid") let( diff --git a/paths.scad b/paths.scad index 2adb204..0eddd19 100644 --- a/paths.scad +++ b/paths.scad @@ -1630,7 +1630,7 @@ module path_spread(path, n, spacing, sp=undef, rotate_children=true, closed=fals if(planar) { rot(from=[0,1],to=cutlist[i][3]) children(); } else { - multmatrix(affine3d_frame_map(x=cutlist[i][2], z=cutlist[i][3])) + frame_map(x=cutlist[i][2], z=cutlist[i][3]) children(); } } else { @@ -1777,9 +1777,9 @@ module path_text(path, text, font, size, thickness=1, lettersize, offset=0, reve ) move(pts[i][0]) - multmatrix(affine3d_frame_map(x=pts[i][2]-adjustment, - z=usetop ? undef : normpts[i], - y=usetop ? toppts[i] : undef)) + frame_map(x=pts[i][2]-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); diff --git a/skin.scad b/skin.scad index b9752aa..ab2fb69 100644 --- a/skin.scad +++ b/skin.scad @@ -1355,7 +1355,7 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi let(rotations = [for( i = 0, ynormal = normal - (normal * tangents[0])*tangents[0], - rotation = affine3d_frame_map(y=ynormal, z=tangents[0]) + rotation = frame_map(y=ynormal, z=tangents[0]) ; i < len(tangents) + (closed?1:0) ; rotation = idepth && _r2>depth, "Screw profile deeper than rod radius"); map_threads = right((_r1 + _r2) / 2) // Shift profile out to thread radius * affine3d_skew(sxz=(_r2-_r1)/l) // Skew correction for tapered threads - * affine3d_frame_map(x=[0,0,1], y=[1,0,0]) // Map profile to 3d, parallel to z axis + * frame_map(x=[0,0,1], y=[1,0,0]) // Map profile to 3d, parallel to z axis * scale(pitch); // scale profile by pitch hig_table = [ [-twist/2-0.0001, 0], diff --git a/transforms.scad b/transforms.scad index 2056aae..67de0e4 100644 --- a/transforms.scad +++ b/transforms.scad @@ -1,6 +1,14 @@ ////////////////////////////////////////////////////////////////////// // LibFile: transforms.scad -// Functions and modules that provide shortcuts for translation, rotation, mirror and skew operations. The shortcuts can act on geometry, like the usual OpenSCAD rotate() and translate(). They also work as functions that operate on lists of points in various forms: paths, VNFS and bezier patches. Lastly, the function form of the shortcuts can return a matrix representing the operation the shortcut performs. The rotation and scaling shortcuts accept an optional centerpoint for the rotation or scaling operation. +// Functions and modules that provide shortcuts for translation, +// rotation and mirror operations. Also provided are skew and frame_map +// which remaps the coordinate axes. The shortcuts can act on +// geometry, like the usual OpenSCAD rotate() and translate(). They +// also work as functions that operate on lists of points in various +// forms: paths, VNFS and bezier patches. Lastly, the function form +// of the shortcuts can return a matrix representing the operation +// the shortcut performs. The rotation and scaling shortcuts accept +// an optional centerpoint for the rotation or scaling operation. // Includes: // include ////////////////////////////////////////////////////////////////////// @@ -771,6 +779,8 @@ module xyzrot(a=0, p, cp) function xyzrot(a=0, p, cp) = rot(a=a, v=[1,1,1], cp=cp, p=p); + + ////////////////////////////////////////////////////////////////////// // Section: Scaling and Mirroring ////////////////////////////////////////////////////////////////////// @@ -1439,9 +1449,87 @@ function yzflip(p, cp=0) = ////////////////////////////////////////////////////////////////////// -// Section: Skewing +// Section: Other Transformations ////////////////////////////////////////////////////////////////////// +// Function&Module: frame_map() +// Usage: As module +// frame_map(v1, v2, v3, [reverse=]) { ... } +// Usage: As function to remap points +// transformed = frame_map(v1, v2, v3, p=points, [reverse=]); +// Usage: As function to return a transformation matrix: +// map = frame_map(v1, v2, v3, [reverse=]); +// map = frame_map(x=VECTOR1, y=VECTOR2, [reverse=]); +// map = frame_map(x=VECTOR1, z=VECTOR2, [reverse=]); +// map = frame_map(y=VECTOR1, z=VECTOR2, [reverse=]); +// Topics: Affine, Matrices, Transforms, Rotation +// See Also: rot(), xrot(), yrot(), zrot(), affine2d_zrot() +// Description: +// Maps one coordinate frame to another. You must specify two or +// three of `x`, `y`, and `z`. The specified axes are mapped to the vectors you supplied, so if you +// specify x=[1,1] then the x axis will be mapped to the line y=x. If you +// give two inputs, the third vector is mapped to the appropriate normal to maintain a right hand +// coordinate system. If the vectors you give are orthogonal the result will be a rotation and the +// `reverse` parameter will supply the inverse map, which enables you to map two arbitrary +// coordinate systems to each other by using the canonical coordinate system as an intermediary. +// You cannot use the `reverse` option with non-orthogonal inputs. Note that only the direction +// of the specified vectors matters: the transformation will not apply scaling, though it can +// skew if your provide non-orthogonal axes. +// Arguments: +// x = Destination 3D vector for x axis. +// y = Destination 3D vector for y axis. +// z = Destination 3D vector for z axis. +// reverse = reverse direction of the map for orthogonal inputs. Default: false +// Example: Remap axes after linear extrusion +// frame_map(x=[0,1,0], y=[0,0,1]) linear_extrude(height=10) square(3); +// Example: This map is just a rotation around the z axis +// mat = frame_map(x=[1,1,0], y=[-1,1,0]); +// Example: This map is not a rotation because x and y aren't orthogonal +// mat = frame_map(x=[1,0,0], y=[1,1,0]); +// Example: This sends [1,1,0] to [0,1,1] and [-1,1,0] to [0,-1,1] +// mat = frame_map(x=[0,1,1], y=[0,-1,1]) * frame_map(x=[1,1,0], y=[-1,1,0],reverse=true); +function frame_map(x,y,z, p, reverse=false) = + is_def(p) + ? apply(frame_map(x,y,z,reverse=reverse), p) + : + assert(num_defined([x,y,z])>=2, "Must define at least two inputs") + let( + xvalid = is_undef(x) || (is_vector(x) && len(x)==3), + yvalid = is_undef(y) || (is_vector(y) && len(y)==3), + zvalid = is_undef(z) || (is_vector(z) && len(z)==3) + ) + assert(xvalid,"Input x must be a length 3 vector") + assert(yvalid,"Input y must be a length 3 vector") + assert(zvalid,"Input z must be a length 3 vector") + let( + x = is_undef(x)? undef : unit(x,RIGHT), + y = is_undef(y)? undef : unit(y,BACK), + z = is_undef(z)? undef : unit(z,UP), + map = is_undef(x)? [cross(y,z), y, z] : + is_undef(y)? [x, cross(z,x), z] : + is_undef(z)? [x, y, cross(x,y)] : + [x, y, z] + ) + reverse? ( + let( + ocheck = ( + approx(map[0]*map[1],0) && + approx(map[0]*map[2],0) && + approx(map[1]*map[2],0) + ) + ) + assert(ocheck, "Inputs must be orthogonal when reverse==true") + [for (r=map) [for (c=r) c, 0], [0,0,0,1]] + ) : [for (r=transpose(map)) [for (c=r) c, 0], [0,0,0,1]]; + + +module frame_map(x,y,z,p,reverse=false) +{ + assert(is_undef(p), "Module form `frame_map()` does not accept p= argument."); + multmatrix(frame_map(x,y,z,reverse=reverse)) + children(); +} + // Function&Module: skew() // Usage: As Module diff --git a/turtle3d.scad b/turtle3d.scad index 200c6e2..0b4c943 100644 --- a/turtle3d.scad +++ b/turtle3d.scad @@ -433,7 +433,7 @@ function turtle3d(commands, state=RIGHT, transforms=false, full_state=false, rep state = is_matrix(state,4,4) ? [[state],[yrot(90)],1,90,0] : is_vector(state,3) ? let( updir = UP - (UP * state) * state / (state*state) ) - [[affine3d_frame_map(x=state, z=approx(norm(updir),0) ? FWD : updir)], [yrot(90)],1, 90, 0] + [[frame_map(x=state, z=approx(norm(updir),0) ? FWD : updir)], [yrot(90)],1, 90, 0] : assert(_turtle3d_state_valid(state), "Supplied state is not valid") state, finalstate = _turtle3d_repeat(commands, state, repeat) From 1763c51a35246b5074db28dbfe2310bab5d66409 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Mon, 6 Sep 2021 18:38:00 -0400 Subject: [PATCH 3/5] fix tests --- tests/test_affine.scad | 6 ------ tests/test_transforms.scad | 5 +++++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/test_affine.scad b/tests/test_affine.scad index 3ccd697..15d6877 100644 --- a/tests/test_affine.scad +++ b/tests/test_affine.scad @@ -216,12 +216,6 @@ test_affine3d_skew_yz(); //////////////////////////// -module test_affine3d_frame_map() { - assert(approx(affine3d_frame_map(x=[1,1,0], y=[-1,1,0]), affine3d_zrot(45))); -} -test_affine3d_frame_map(); - - module test_apply() { assert(approx(apply(affine3d_xrot(90),2*UP),2*FRONT)); assert(approx(apply(affine3d_yrot(90),2*UP),2*RIGHT)); diff --git a/tests/test_transforms.scad b/tests/test_transforms.scad index 21f0ae4..a7e9cb8 100644 --- a/tests/test_transforms.scad +++ b/tests/test_transforms.scad @@ -462,6 +462,11 @@ module test_xyzrot() { } test_xyzrot(); +module test_frame_map() { + assert(approx(frame_map(x=[1,1,0], y=[-1,1,0]), affine3d_zrot(45))); +} +test_frame_map(); + module test_skew() { m = affine3d_skew(sxy=2, sxz=3, syx=4, syz=5, szx=6, szy=7); From c8a1e14f057aeb93f69564862b5a8f62b7af18d5 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Mon, 6 Sep 2021 19:07:18 -0400 Subject: [PATCH 4/5] Expand top text, and reorder some modules for docs --- shapes2d.scad | 276 ++++++++++++++++++++++++++------------------------ 1 file changed, 143 insertions(+), 133 deletions(-) diff --git a/shapes2d.scad b/shapes2d.scad index 724bab9..34133c3 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -1,11 +1,21 @@ ////////////////////////////////////////////////////////////////////// // LibFile: shapes2d.scad -// Common useful 2D shapes. +// This file includes stroke(), which converts a path +// into a geometric object, like drawing with a pen. You +// can make a dashed line or add arrow heads. The turtle() function +// provides a turtle graphics style approach for producing paths. +// You can 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. // Includes: // include ////////////////////////////////////////////////////////////////////// -// Section: 2D Drawing Helpers +// Section: Line Drawing // Module: stroke() // Usage: @@ -1055,8 +1065,7 @@ function oval(r, d, realign=false, circum=false, anchor=CENTER, spin=0) = ) reorient(anchor,spin, two_d=true, r=[rx,ry], p=pts); - -// Section: 2D N-Gons +// Section: Polygons // Function&Module: regular_ngon() // Usage: @@ -1377,10 +1386,6 @@ module octagon(r, d, or, od, ir, id, side, rounding=0, realign=false, align_tip, regular_ngon(n=8, r=r, d=d, or=or, od=od, ir=ir, id=id, side=side, rounding=rounding, realign=realign, align_tip=align_tip, align_side=align_side, anchor=anchor, spin=spin) children(); - -// Section: Other 2D Shapes - - // Function&Module: trapezoid() // Usage: As Module // trapezoid(h, w1, w2, [shift=], [rounding=], [chamfer=], ...); @@ -1486,6 +1491,136 @@ module trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENTER } + +// Function&Module: star() +// Usage: As Module +// star(n, r/or, ir, [realign=], [align_tip=], [align_pit=], ...); +// star(n, r/or, step=, ...); +// Usage: With Attachments +// star(n, r/or, ir, ...) { attachments } +// Usage: As Function +// path = star(n, r/or, ir, [realign=], [align_tip=], [align_pit=], ...); +// path = star(n, r/or, step=, ...); +// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable +// See Also: circle(), oval() +// Description: +// When called as a function, returns the path needed to create a star polygon with N points. +// When called as a module, creates a star polygon with N points. +// Arguments: +// n = The number of stellate tips on the star. +// r/or = The radius to the tips of the star. +// ir = The radius to the inner corners of the star. +// --- +// d/od = The diameter to the tips of the star. +// id = The diameter to the inner corners of the star. +// step = Calculates the radius of the inner star corners by virtually drawing a straight line `step` tips around the star. 2 <= step < n/2 +// realign = If false, a tip is aligned with the Y+ axis. If true, an inner corner is aligned with the Y+ axis. Default: false +// align_tip = If given as a 2D vector, rotates the whole shape so that the first star tip points in that direction. This occurs before spin. +// align_pit = If given as a 2D vector, rotates the whole shape so that the first inner corner is pointed towards that direction. This occurs before spin. +// 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` +// Extra Anchors: +// "tip0" ... "tip4" = Each tip has an anchor, pointing outwards. +// "pit0" ... "pit4" = The inside corner between each tip has an anchor, pointing outwards. +// "midpt0" ... "midpt4" = The center-point between each pair of tips has an anchor, pointing outwards. +// Examples(2D): +// star(n=5, r=50, ir=25); +// star(n=5, r=50, step=2); +// star(n=7, r=50, step=2); +// star(n=7, r=50, step=3); +// Example(2D): Realigned +// star(n=7, r=50, step=3, realign=true); +// Example(2D): Alignment by Tip +// star(n=5, ir=15, or=30, align_tip=BACK+RIGHT) +// attach("tip0", FWD) color("blue") +// stroke([[0,0],[0,7]], endcap2="arrow2"); +// Example(2D): Alignment by Pit +// star(n=5, ir=15, or=30, align_pit=BACK+RIGHT) +// attach("pit0", FWD) color("blue") +// stroke([[0,0],[0,7]], endcap2="arrow2"); +// Example(2D): Called as Function +// stroke(closed=true, star(n=5, r=50, ir=25)); +function star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit, anchor=CENTER, spin=0, _mat, _anchs) = + assert(is_undef(align_tip) || is_vector(align_tip)) + assert(is_undef(align_pit) || is_vector(align_pit)) + assert(is_undef(align_tip) || is_undef(align_pit), "Can only specify one of align_tip and align_pit") + let( + r = get_radius(r1=or, d1=od, r=r, d=d), + count = num_defined([ir,id,step]), + stepOK = is_undef(step) || (step>1 && step1 && step Date: Mon, 6 Sep 2021 19:46:18 -0400 Subject: [PATCH 5/5] Doc error in common.scad for no_module Added no_module and no_function to turtle and stroke respectively --- common.scad | 2 +- shapes2d.scad | 34 +++++++++++++++++++++++----------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/common.scad b/common.scad index c172cbb..29fdd30 100644 --- a/common.scad +++ b/common.scad @@ -566,7 +566,7 @@ function no_function(name) = // Description: // Asserts that the called module exists only as a function. // Example: -// function foo() = no_module(); +// module foo() { no_module(); } module no_module() { assert(false, str("You called ",parent_module(1),"() as a module but it is available only as a function")); } diff --git a/shapes2d.scad b/shapes2d.scad index 34133c3..9791433 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -1,16 +1,17 @@ ////////////////////////////////////////////////////////////////////// // LibFile: shapes2d.scad -// This file includes stroke(), which converts a path -// into a geometric object, like drawing with a pen. You -// can make a dashed line or add arrow heads. The turtle() function -// provides a turtle graphics style approach for producing paths. -// You can 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. +// This file includes stroke(), which converts a path into a +// geometric object, like drawing with a pen. It even works on +// three-dimensional paths. You can make a dashed line or add arrow +// heads. The turtle() function provides a turtle graphics style +// approach for producing paths. You can 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. // Includes: // include ////////////////////////////////////////////////////////////////////// @@ -119,6 +120,16 @@ // Example: 3D Path with Joints and Endcaps // path = [for (i=[0:10:360]) [(i-180)/2,20*cos(3*i),20*sin(3*i)]]; // stroke(path, width=2, joints="dot", endcap1="round", endcap2="arrow2", joint_width=2.0, endcap_width2=3, $fn=18); +function stroke( + path, width=1, closed=false, + endcaps, endcap1, endcap2, joints, plots, + endcap_width, endcap_width1, endcap_width2, joint_width, plot_width, + endcap_length, endcap_length1, endcap_length2, joint_length, plot_length, + endcap_extent, endcap_extent1, endcap_extent2, joint_extent, plot_extent, + endcap_angle, endcap_angle1, endcap_angle2, joint_angle, plot_angle, + trim, trim1, trim2, + convexity=10, hull=true +) = no_function("stroke"); module stroke( path, width=1, closed=false, endcaps, endcap1, endcap2, joints, plots, @@ -753,6 +764,7 @@ function _normal_segment(p1,p2) = // ); // koch=concat(["angle",60,"repeat",3],[concat(koch_unit(3),["left","left"])]); // polygon(turtle(koch)); +module turtle(commands, state=[[[0,0]],[1,0],90,0], full_state=false, repeat=1) {no_module();} function turtle(commands, state=[[[0,0]],[1,0],90,0], full_state=false, repeat=1) = let( state = is_vector(state) ? [[state],[1,0],90,0] : state ) repeat == 1?