diff --git a/attachments.scad b/attachments.scad index 112f173..2c17410 100644 --- a/attachments.scad +++ b/attachments.scad @@ -91,7 +91,7 @@ $tags_hidden = []; // Function: anchorpt() // Usage: -// anchor(name, pos, [dir], [rot]) +// anchor(name, pos, , ) // Description: // Creates a anchor data structure. // Arguments: @@ -105,21 +105,21 @@ function anchorpt(name, pos=[0,0,0], orient=UP, spin=0) = [name, pos, orient, sp // Function: attach_geom() // // Usage: Square/Trapezoid Geometry -// geom = attach_geom(anchor, spin, [orient], two_d, size, [size2], [shift], [cp], [offset], [anchors]); +// geom = attach_geom(anchor, spin, two_d, size, , , , , ); // Usage: Circle/Oval Geometry -// geom = attach_geom(anchor, spin, [orient], two_d, r|d, [cp], [offset], [anchors]); +// geom = attach_geom(anchor, spin, two_d, r|d, , , ); // Usage: 2D Path/Polygon Geometry -// geom = attach_geom(anchor, spin, [orient], two_d, path, [extent], [cp], [offset], [anchors]); +// geom = attach_geom(anchor, spin, two_d, path, , , , ); // Usage: Cubical/Prismoidal Geometry -// geom = attach_geom(anchor, spin, [orient], size, [size2], [shift], [cp], [offset], [anchors]); +// geom = attach_geom(anchor, spin, , size, , , , , ); // Usage: Cylindrical Geometry -// geom = attach_geom(anchor, spin, [orient], r|d, l, [cp], [axis], [offset], [anchors]); +// geom = attach_geom(anchor, spin, , r|d, l, , , , ); // Usage: Conical Geometry -// geom = attach_geom(anchor, spin, [orient], r1|d1, r2|d2, l, [cp], [axis], [offset], [anchors]); +// geom = attach_geom(anchor, spin, , r1|d1, r2|d2, l, , , , ); // Usage: Spheroid/Ovoid Geometry -// geom = attach_geom(anchor, spin, [orient], r|d, [cp], [offset], [anchors]); +// geom = attach_geom(anchor, spin, , r|d, , , ); // Usage: VNF Geometry -// geom = attach_geom(anchor, spin, [orient], vnf, [extent], [cp], [offset], [anchors]); +// geom = attach_geom(anchor, spin, , vnf, , , , ); // // Description: // Given arguments that describe the geometry of an attachable object, returns the internal geometry description. @@ -332,8 +332,8 @@ function attach_geom_size(geom) = ) delt ) : type == "rect"? ( //size, size2 let( - size=geom[1], size2=geom[2], - maxx = max(size.x,size2) + size=geom[1], size2=geom[2], shift=geom[3], + maxx = max(size.x,size2+abs(shift)) ) [maxx, size.y] ) : type == "circle"? ( //r let( r=geom[1] ) @@ -349,7 +349,7 @@ function attach_geom_size(geom) = // Function: attach_transform() // Usage: -// mat = attach_transform(anchor=CENTER, spin=0, orient=UP, geom); +// mat = attach_transform(anchor, spin, orient, geom); // Description: // Returns the affine3d transformation matrix needed to `anchor`, `spin`, and `orient` // the given geometry `geom` shape into position. @@ -557,12 +557,12 @@ function find_anchor(anchor, geom) = mpt = approx(point2d(anchor),[0,0])? [maxx,0,0] : [maxx, avgy, avgz], pos = point3d(cp) + rot(from=RIGHT, to=anchor, p=mpt) ) [anchor, pos, anchor, oang] - ) : type == "rect"? ( //size, size2 + ) : type == "rect"? ( //size, size2, shift let( - size=geom[1], size2=geom[2], + size=geom[1], size2=geom[2], shift=geom[3], u = (anchor.y+1)/2, frpt = [size.x/2*anchor.x, -size.y/2], - bkpt = [size2/2*anchor.x, size.y/2], + bkpt = [size2/2*anchor.x+shift, size.y/2], pos = point2d(cp) + lerp(frpt, bkpt, u) + offset, vec = unit(rot(from=BACK, to=bkpt-frpt, p=anchor),[0,1]) ) [anchor, pos, vec, 0] @@ -629,21 +629,21 @@ function attachment_is_shown(tags) = // Function: reorient() // // Usage: Square/Trapezoid Geometry -// reorient(anchor, spin, [orient], two_d, size, [size2], [shift], [cp], [offset], [anchors], [p]); +// reorient(anchor, spin, , two_d, size, , , , , ,

); // Usage: Circle/Oval Geometry -// reorient(anchor, spin, [orient], two_d, r|d, [cp], [offset], [anchors], [p]); +// reorient(anchor, spin, , two_d, r|d, , , ,

); // Usage: 2D Path/Polygon Geometry -// reorient(anchor, spin, [orient], two_d, path, [extent], [cp], [offset], [anchors], [p]); +// reorient(anchor, spin, , two_d, path, , , , ,

); // Usage: Cubical/Prismoidal Geometry -// reorient(anchor, spin, [orient], size, [size2], [shift], [cp], [offset], [anchors], [p]); +// reorient(anchor, spin, , size, , , , , ,

); // Usage: Cylindrical Geometry -// reorient(anchor, spin, [orient], r|d, l, [offset], [axis], [cp], [anchors], [p]); +// reorient(anchor, spin, , r|d, l, , , , ,

); // Usage: Conical Geometry -// reorient(anchor, spin, [orient], r1|d1, r2|d2, l, [axis], [cp], [offset], [anchors], [p]); +// reorient(anchor, spin, , r1|d1, r2|d2, l, , , , ,

); // Usage: Spheroid/Ovoid Geometry -// reorient(anchor, spin, [orient], r|d, [cp], [offset], [anchors], [p]); +// reorient(anchor, spin, , r|d, , , ,

); // Usage: VNF Geometry -// reorient(anchor, spin, [orient], vnf, [extent], [cp], [offset], [anchors], [p]); +// reorient(anchor, spin, , vnf, , , , ,

); // // Description: // Given anchor, spin, orient, and general geometry info for a managed volume, this calculates @@ -723,21 +723,21 @@ function reorient( // Module: attachable() // // Usage: Square/Trapezoid Geometry -// attachable(anchor, spin, [orient], two_d, size, [size2], [shift], [cp], [offset], [anchors] ... +// attachable(anchor, spin, two_d, size, , , , , ... // Usage: Circle/Oval Geometry -// attachable(anchor, spin, [orient], two_d, r|d, [cp], [offset], [anchors]) ... +// attachable(anchor, spin, two_d, r|d, , , ) ... // Usage: 2D Path/Polygon Geometry -// attachable(anchor, spin, [orient], two_d, path, [extent], [cp], [offset], [anchors] ... +// attachable(anchor, spin, two_d, path, , , , ... // Usage: Cubical/Prismoidal Geometry -// attachable(anchor, spin, [orient], size, [size2], [shift], [cp], [offset], [anchors] ... +// attachable(anchor, spin, , size, , , , , ... // Usage: Cylindrical Geometry -// attachable(anchor, spin, [orient], r|d, l, [axis], [cp], [offset], [anchors]) ... +// attachable(anchor, spin, , r|d, l, , , , ) ... // Usage: Conical Geometry -// attachable(anchor, spin, [orient], r1|d1, r2|d2, l, [axis], [cp], [offset], [anchors]) ... +// attachable(anchor, spin, , r1|d1, r2|d2, l, , , , ) ... // Usage: Spheroid/Ovoid Geometry -// attachable(anchor, spin, [orient], r|d, [cp], [offset], [anchors]) ... +// attachable(anchor, spin, , r|d, , , ) ... // Usage: VNF Geometry -// attachable(anchor, spin, [orient], vnf, [extent], [cp], [offset], [anchors]) ... +// attachable(anchor, spin, , vnf, , , , ) ... // // Description: // Manages the anchoring, spin, orientation, and attachments for a 3D volume or 2D area. @@ -969,8 +969,8 @@ module position(from) // Module: attach() // Usage: -// attach(from, [overlap]) ... -// attach(from, to, [overlap]) ... +// attach(from, ) ... +// attach(from, to, ) ... // Description: // Attaches children to a parent object at an anchor point and orientation. // Attached objects will be overlapped into the parent object by a little bit, @@ -1012,7 +1012,7 @@ module attach(from, to=undef, overlap=undef, norot=false) // Module: face_profile() // Usage: -// face_profile(faces=[], convexity=10, r, d) ... +// face_profile(faces, r, d, ) ... // Description: // Given a 2D edge profile, extrudes it into a mask for all edges and corners bounding each given face. // Arguments: @@ -1032,7 +1032,7 @@ module face_profile(faces=[], r, d, convexity=10) { // Module: edge_profile() // Usage: -// edge_profile([edges], [except], [convexity]) ... +// edge_profile(, , ) ... // Description: // Takes a 2D mask shape and attaches it to the selected edges, with the appropriate orientation // and extruded length to be `diff()`ed away, to give the edge a matching profile. @@ -1082,7 +1082,7 @@ module edge_profile(edges=EDGES_ALL, except=[], convexity=10) { // Module: corner_profile() // Usage: -// corner_profile([corners], [except], [convexity]) ... +// corner_profile(, , ) ... // Description: // Takes a 2D mask shape, rotationally extrudes and converts it into a corner mask, and attaches it // to the selected corners with the appropriate orientation. Tags it as a "mask" to allow it to be @@ -1144,7 +1144,7 @@ module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) { // Module: edge_mask() // Usage: -// edge_mask([edges], [except]) ... +// edge_mask(, ) ... // Description: // Takes a 3D mask shape, and attaches it to the given edges, with the // appropriate orientation to be `diff()`ed away. @@ -1186,7 +1186,7 @@ module edge_mask(edges=EDGES_ALL, except=[]) { // Module: corner_mask() // Usage: -// corner_mask([corners], [except]) ... +// corner_mask(, ) ... // Description: // Takes a 3D mask shape, and attaches it to the given corners, with the appropriate // orientation to be `diff()`ed away. The 3D corner mask shape should be designed to @@ -1305,8 +1305,8 @@ module show(tags="") // Module: diff() // Usage: -// diff(neg, [keep]) ... -// diff(neg, pos, [keep]) ... +// diff(neg, ) ... +// diff(neg, pos, ) ... // Description: // If `neg` is given, takes the union of all children with tags that are in `neg`, and differences // them from the union of all children with tags in `pos`. If `pos` is not given, then all items in @@ -1363,8 +1363,8 @@ module diff(neg, pos=undef, keep=undef) // Module: intersect() // Usage: -// intersect(a, [keep]) ... -// intersect(a, b, [keep]) ... +// intersect(a, ) ... +// intersect(a, b, ) ... // Description: // If `a` is given, takes the union of all children with tags that are in `a`, and `intersection()`s // them with the union of all children with tags in `b`. If `b` is not given, then the union of all diff --git a/debug.scad b/debug.scad index a38a690..96e5715 100644 --- a/debug.scad +++ b/debug.scad @@ -274,7 +274,7 @@ function standard_anchors(two_d=false) = [ // Module: anchor_arrow() // Usage: -// anchor_arrow([s], [color], [flag]); +// anchor_arrow(, , ); // Description: // Show an anchor orientation arrow. // Arguments: @@ -303,7 +303,7 @@ module anchor_arrow(s=10, color=[0.333,0.333,1], flag=true, $tags="anchor-arrow" // Module: anchor_arrow2d() // Usage: -// anchor_arrow2d([s], [color], [flag]); +// anchor_arrow2d(, , ); // Description: // Show an anchor orientation arrow. // Arguments: @@ -312,7 +312,7 @@ module anchor_arrow(s=10, color=[0.333,0.333,1], flag=true, $tags="anchor-arrow" // Example: // anchor_arrow2d(s=20); module anchor_arrow2d(s=15, color=[0.333,0.333,1], $tags="anchor-arrow") { - noop() stroke([[0,0],[0,s]], width=s/10, endcap1="butt", endcap2="arrow2"); + noop() color(color) stroke([[0,0],[0,s]], width=s/10, endcap1="butt", endcap2="arrow2"); } @@ -341,18 +341,28 @@ module show_internal_anchors(opacity=0.2) { // Example(FlatSpin): // cube(50, center=true) show_anchors(); module show_anchors(s=10, std=true, custom=true) { + check = assert($parent_geom != undef) 1; + two_d = attach_geom_2d($parent_geom); if (std) { - for (anchor=standard_anchors()) { - attach(anchor) anchor_arrow(s); + for (anchor=standard_anchors(two_d=two_d)) { + if(two_d) { + attach(anchor) anchor_arrow2d(s); + } else { + attach(anchor) anchor_arrow(s); + } } } if (custom) { for (anchor=select($parent_geom,-1)) { attach(anchor[0]) { - anchor_arrow(s, color="cyan"); - recolor("black") + if(two_d) { + anchor_arrow2d(s, color="cyan"); + } else { + anchor_arrow(s, color="cyan"); + } + color("black") noop($tags="anchor-arrow") { - xrot(90) { + xrot(two_d? 0 : 90) { up(s/10) { linear_extrude(height=0.01, convexity=12, center=true) { text(text=anchor[0], size=s/4, halign="center", valign="center"); diff --git a/scripts/make_tutorials.sh b/scripts/make_tutorials.sh index 10ba8ae..9c0fee4 100755 --- a/scripts/make_tutorials.sh +++ b/scripts/make_tutorials.sh @@ -15,7 +15,7 @@ done if [[ "$FILES" != "" ]]; then PREVIEW_LIBS="$FILES" else - PREVIEW_LIBS="Transforms Distributors Basic_Shapes FractalTree" + PREVIEW_LIBS="Transforms Distributors Shapes2d Shapes3d Paths FractalTree" fi dir="$(basename $PWD)" diff --git a/shapes.scad b/shapes.scad index 5b8c975..53149f6 100644 --- a/shapes.scad +++ b/shapes.scad @@ -1159,6 +1159,14 @@ module torus( // Creates a spheroid 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 spheroid. +// The exact triangulation of this spheroid can be controlled via the `style=` +// argument, where the value can be one of `"orig"`, `"aligned"`, `"stagger"`, +// `"octa"`, or `"icosa"`: +// - `style="orig"` constructs a sphere the same way that the OpenSCAD `sphere()` built-in does. +// - `style="aligned"` constructs a sphere where, if `$fn` is a multiple of 4, it has vertices at all axis maxima and minima. ie: its bounding box is exactly the sphere diameter in length on all three axes. This is the default. +// - `style="stagger"` forms a sphere where all faces are triangular, but the top and bottom poles have thinner triangles. +// - `style="octa"` forms a sphere by subdividing an octahedron (8-sided platonic solid). This makes more uniform faces over the entirety of the sphere, and guarantees the bounding box is the sphere diameter in size on all axes. The effective `$fn` value is quantized to a multiple of 4, though. This is used in constructing rounded corners for various other shapes. +// - `style="icosa"` forms a sphere by subdividing an icosahedron (20-sided platonic solid). This makes even more uniform faces over the entirety of the sphere. The effective `$fn` value is quantized to a multiple of 5, though. // Arguments: // r = Radius of the spheroid. // d = Diameter of the spheroid. @@ -1200,21 +1208,16 @@ module spheroid(r, d, circum=false, style="aligned", anchor=CENTER, spin=0, orie { r = get_radius(r=r, d=d, dflt=1); sides = segs(r); + vsides = ceil(sides/2); attachable(anchor,spin,orient, r=r) { if (style=="orig") { - rotate_extrude(convexity=2,$fn=sides) { - difference() { - oval(r=r, circum=circum, realign=true, $fn=sides); - left(r) square(2*r,center=true); - } - } - } else if (style=="aligned") { - rotate_extrude(convexity=2,$fn=sides) { - difference() { - oval(r=r, circum=circum, $fn=sides); - left(r) square(2*r,center=true); - } - } + merids = [ for (i=[0:1:vsides]) 90-(i+0.5)*180/(vsides+1) ]; + path = [ + let(a = merids[0]) [0, r*sin(a)], + for (a=merids) r * [cos(a), sin(a)], + let(a = select(merids,-1)) [0, r*sin(a)] + ]; + rotate_extrude(convexity=2,$fn=sides) polygon(path); } else { vnf = spheroid(r=r, circum=circum, style=style); vnf_polyhedron(vnf, convexity=2); diff --git a/shapes2d.scad b/shapes2d.scad index b9750da..4fec9a3 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -1250,7 +1250,7 @@ module trapezoid(h, w1, w2, angle, shift=0, anchor=CENTER, spin=0) { w2 = !is_undef(w2)? w2 : w1 - 2*(adj_ang_to_opp(h, angle) + shift); assert(w1>=0 && w2>=0 && h>0, "Degenerate trapezoid geometry."); path = [[w1/2,-h/2], [-w1/2,-h/2], [-w2/2+shift,h/2], [w2/2+shift,h/2]]; - attachable(anchor,spin, two_d=true, size=[w1,h], size2=w2) { + attachable(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift) { polygon(path); children(); } @@ -1325,8 +1325,8 @@ function teardrop2d(r, d, ang=45, cap_h, anchor=CENTER, spin=0) = // Arguments: // r = The radius of the end circles. // d = The diameter of the end circles. -// spread = The distance between the centers of the end circles. -// tangent = The angle in degrees of the tangent point for the joining arcs, measured away from the Y axis. +// spread = The distance between the centers of the end circles. Default: 10 +// tangent = The angle in degrees of the tangent point for the joining arcs, measured away from the Y axis. Default: 30 // 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` // Examples(2D): diff --git a/tutorials/Paths.md b/tutorials/Paths.md new file mode 100644 index 0000000..68963dd --- /dev/null +++ b/tutorials/Paths.md @@ -0,0 +1,504 @@ +# Paths, Polygons and Regions Tutorial + +## Paths +A number of advanced features in BOSL2 rely on paths, which are just ordered lists of points. + +First-off, some terminology: +- A 2D point is a vectors of X and Y axis position values. ie: `[3,4]` or `[7,-3]`. +- A 3D point is a vectors of X, Y and Z axis position values. ie: `[3,4,2]` or `[-7,5,3]`. +- A 2D path is simply a list of two or more 2D points. ie: `[[5,7], [1,-5], [-5,6]]` +- A 3D path is simply a list of two or more 3D points. ie: `[[5,7,-1], [1,-5,3], [-5,6,1]]` +- A polygon is a 2D (or planar 3D) path where the last point is assumed to connect to the first point. +- A region is a list of 2D polygons, where each polygon is XORed against all the others. ie: if one polygon is inside another, it makes a hole in the first polygon. + +### Stroke +A path can be hard to visualize, since it's just a bunch of numbers in the source code. +One way to see the path is to pass it to `polygon()`: + +```openscad-2D + path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; + polygon(path); +``` + +Sometimes, however, it's easier to see just the path itself. For this, you can use the `stroke()` module. +At its most basic, `stroke()` just shows the path's line segments: + +```openscad-2D + path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; + stroke(path); +``` + +You can vary the width of the drawn path with the `width=` argument: + +```openscad-2D + path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; + stroke(path, width=3); +``` + +You can vary the line length along the path by giving a list of widths, one per point: + +```openscad-2D + path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; + stroke(path, width=[3,2,1,2,3]); +``` + +If a path is meant to represent a closed polygon, you can use `closed=true` to show it that way: + +```openscad-2D + path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; + stroke(path, closed=true); +``` + +The ends of the drawn path are normally capped with a "round" endcap, but there are other options: + +```openscad-2D + path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; + stroke(path, endcaps="round"); +``` + +```openscad-2D + path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; + stroke(path, endcaps="butt"); +``` + +```openscad-2D + path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; + stroke(path, endcaps="line"); +``` + +```openscad-2D + path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; + stroke(path, endcaps="tail"); +``` + +```openscad-2D + path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; + stroke(path, endcaps="arrow2"); +``` + +For more standard supported endcap options, see the docs for [`stroke()`](shapes2d.scad#stroke). + +The start and ending endcaps can be specified individually or separately, using `endcap1=` and `endcap2=`: + +```openscad-2D + path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; + stroke(path, endcap2="arrow2"); +``` + +```openscad-2D + path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; + stroke(path, endcap1="butt", endcap2="arrow2"); +``` + +```openscad-2D + path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; + stroke(path, endcap1="tail", endcap2="arrow"); +``` + +The size of the endcaps will be relative to the width of the line where the endcap is to be placed: + +```openscad-2D + path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; + widths = [1, 1.25, 1.5, 1.75, 2]; + stroke(path, width=widths, endcaps="arrow2"); +``` + +If none of the standard endcaps are useful to you, it is possible to design your own, simply by +passing a path to the `endcaps=`, `endcap1=`, or `endcap2=` arguments. You may also need to give +`trim=` to tell it how far back to trim the main line, so it renders nicely. The values in the +endcap polygon, and in the `trim=` argument are relative to the line width. A value of 1 is one +line width size. + +Untrimmed: + +```openscad-2D + path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; + dblarrow = [[0,0], [2,-3], [0.5,-2.3], [2,-4], [0.5,-3.5], [-0.5,-3.5], [-2,-4], [-0.5,-2.3], [-2,-3]]; + stroke(path, endcaps=dblarrow); +``` + +Trimmed: + +```openscad-2D + path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; + dblarrow = [[0,0], [2,-3], [0.5,-2.3], [2,-4], [0.5,-3.5], [-0.5,-3.5], [-2,-4], [-0.5,-2.3], [-2,-3]]; + stroke(path, trim=3.5, endcaps=dblarrow); +``` + +### Standard 2D Shape Polygons +BOSL2 will let you get the perimeter polygon for almost all of the standard 2D shapes simply by calling them like a function: + +```openscad-2D + path = square(40, center=true); + stroke(path, closed=true, endcap2="arrow2"); +``` + +```openscad-2D + path = rect([40,30], rounding=5, center=true); + stroke(path, closed=true, endcap2="arrow2"); +``` + +```openscad-2D + path = trapezoid(w1=40, w2=20, h=30); + stroke(path, closed=true, endcap2="arrow2"); +``` + +```openscad-2D + path = circle(d=50); + stroke(path, closed=true, endcap2="arrow2"); +``` + +```openscad-2D + path = oval(d=[50,30]); + stroke(path, closed=true, endcap2="arrow2"); +``` + +```openscad-2D + path = pentagon(d=50); + stroke(path, closed=true, endcap2="arrow2"); +``` + +```openscad-2D + path = star(n=5, step=2, d=50); + stroke(path, closed=true, endcap2="arrow2"); +``` + +### Arcs +Often, when you are constructing a path, you will want to add an arc. The `arc()` command lets you do that: + +```openscad-2D + path = arc(r=30, angle=120); + stroke(path, endcap2="arrow2"); +``` + +```openscad-2D + path = arc(d=60, angle=120); + stroke(path, endcap2="arrow2"); +``` + +If you give the `N=` argument, you can control exactly how many points the arc is divided into: + +```openscad-2D + path = arc(N=5, r=30, angle=120); + stroke(path, endcap2="arrow2"); +``` + +With the `start=` argument, you can start the arc somewhere other than the X+ axis: + +```openscad-2D + path = arc(start=45, r=30, angle=120); + stroke(path, endcap2="arrow2"); +``` + +Alternatively, you can give starting and ending angles in a list in the `angle=` argument: + +```openscad-2D + path = arc(angle=[120,45], r=30); + stroke(path, endcap2="arrow2"); +``` + +The `cp=` argument lets you center the arc somewhere other than the origin: + +```openscad-2D + path = arc(cp=[10,0], r=30, angle=120); + stroke(path, endcap2="arrow2"); +``` + +The arc can also be given by three points on the arc: + +```openscad-2D + pts = [[-15,10],[0,20],[35,-5]]; + path = arc(points=pts); + stroke(path, endcap2="arrow2"); +``` + + +### Turtle Graphics +Another way you can create a path is using the `turtle()` command. It implements a simple path +description language that is similar to LOGO Turtle Graphics. The concept is that you have a virtial +turtle or cursor walking a path. It can "move" forward or backward, or turn "left" or "right" in +place: + +```openscad-2D + path = turtle([ + "move", 10, + "left", 90, + "move", 20, + "left", 135, + "move", 10*sqrt(2), + "right", 90, + "move", 10*sqrt(2), + "left", 135, + "move", 20 + ]); + stroke(path, endcap2="arrow2"); +``` + +The position and the facing of the turtle/cursor updates after each command. The motion and turning +commands can also have default distances or angles given: + +```openscad-2D + path = turtle([ + "angle",360/6, + "length",10, + "move","turn", + "move","turn", + "move","turn", + "move","turn", + "move" + ]); + stroke(path, endcap2="arrow2"); +``` + +You can use "scale" to relatively scale up the default motion length: + +```openscad-2D + path = turtle([ + "angle",360/6, + "length",10, + "move","turn", + "move","turn", + "scale",2, + "move","turn", + "move","turn", + "scale",0.5, + "move" + ]); + stroke(path, endcap2="arrow2"); +``` + +Sequences of commands can be repeated using the "repeat" command: + +```openscad-2D + path=turtle([ + "angle",360/5, + "length",10, + "repeat",5,["move","turn"] + ]); + stroke(path, endcap2="arrow2"); +``` + +More complicated commands also exist, including those that form arcs: + +```openscad-2D + path = turtle([ + "move", 10, + "left", 90, + "move", 20, + "arcleft", 10, 180, + "move", 20 + ]); + stroke(path, endcap2="arrow2"); +``` + +A comprehensive list of supported turtle commands can be found in the docs for [`turtle()`](shapes2d.scad#turtle). + +### Transforming Paths and Polygons +To translate a path, you can just pass it to the `move()` (or up/down/left/right/fwd/back) function in the `p=` argument: + +```openscad-2D + path = move([-15,-30], p=square(50,center=true)); + stroke(path, closed=true, endcap2="arrow2"); +``` + +```openscad-2D + path = fwd(30, p=square(50,center=true)); + stroke(path, closed=true, endcap2="arrow2"); +``` + +```openscad-2D + path = left(30, p=square(50,center=true)); + stroke(path, closed=true, endcap2="arrow2"); +``` + +To scale a path, you can just pass it to the `scale()` (or [xyz]scale) function in the `p=` argument: + +```openscad-2D + path = scale([1.5,0.75], p=square(50,center=true)); + stroke(path, closed=true, endcap2="arrow2"); +``` + +```openscad-2D + path = xscale(1.5, p=square(50,center=true)); + stroke(path, closed=true, endcap2="arrow2"); +``` + +```openscad-2D + path = yscale(1.5, p=square(50,center=true)); + stroke(path, closed=true, endcap2="arrow2"); +``` + +To rotate a path, just can pass it to the `rot()` (or [xyz]rot) function in the `p=` argument: + +```openscad-2D + path = rot(30, p=square(50,center=true)); + stroke(path, closed=true, endcap2="arrow2"); +``` + +```openscad-2D + path = zrot(30, p=square(50,center=true)); + stroke(path, closed=true, endcap2="arrow2"); +``` + +To mirror a path, just can pass it to the `mirror()` (or [xyz]flip) function in the `p=` argument: + +```openscad-2D + path = mirror([1,1], p=trapezoid(w1=40, w2=10, h=25)); + stroke(path, closed=true, endcap2="arrow2"); +``` + +```openscad-2D + path = xflip(p=trapezoid(w1=40, w2=10, h=25)); + stroke(path, closed=true, endcap2="arrow2"); +``` + +```openscad-2D + path = yflip(p=trapezoid(w1=40, w2=10, h=25)); + stroke(path, closed=true, endcap2="arrow2"); +``` + +You can get raw transformation matrices for various transformations by calling them like a function without a `p=` argument: + +```openscad-2D + mat = move([5,10,0]); + multmatrix(mat) square(50,center=true); +``` + +```openscad-2D + mat = scale([1.5,0.75,1]); + multmatrix(mat) square(50,center=true); +``` + +```openscad-2D + mat = rot(30); + multmatrix(mat) square(50,center=true); +``` + +Raw transformation matrices can be multiplied together to precalculate a compound transformation. For example, to scale a shape, then rotate it, then translate the result, you can do something like: + +```openscad-2D + mat = move([5,10,0]) * rot(30) * scale([1.5,0.75,1]); + multmatrix(mat) square(50,center=true); +``` + +To apply a compound transformation matrix to a path, you can use the `apply()` function: + +```openscad-2D + mat = move([5,10]) * rot(30, planar=true) * scale([1.5,0.75]); + path = square(50,center=true); + tpath = apply(mat, path); + stroke(tpath, endcap2="arrow2"); +``` + + +### Regions +A polygon is good to denote a single closed 2D shape with no holes in it. For more complex 2D +shapes, you will need to use regions. A region is a list of 2D polygons, where each polygon is +XORed against all the others. You can display a region using the `region()` module. + +If you have a region with one polygon fully inside another, it makes a hole: + +```openscad-2D + rgn = [square(50,center=true), circle(d=30)]; + region(rgn); +``` + +If you have a region with multiple polygons that are not contained by any others, they make multiple discontiguous shapes: + +```openscad-2D + rgn = [ + move([-30, 20], p=square(20,center=true)), + move([ 0,-20], p=trapezoid(w1=20, w2=10, h=20)), + move([ 30, 20], p=square(20,center=true)), + ]; + region(rgn); +``` + +Region polygons can be nested abitrarily deep, in multiple discontiguous shapes: + +```openscad-2D + rgn = [ + for (d=[50:-10:10]) left(30, p=circle(d=d)), + for (d=[50:-10:10]) right(30, p=circle(d=d)) + ]; + region(rgn); +``` + +A region with crossing polygons is somewhat poorly formed, but the intersection(s) of the polygons become holes: + +```openscad-2D + rgn = [ + left(15, p=circle(d=50)), + right(15, p=circle(d=50)) + ]; + region(rgn); +``` + +### Boolean Region Geometry +Similarly to how OpenSCAD can perform operations like union/difference/intersection/offset on shape geometry, +the BOSL2 library lets you perform those same operations on regions: + +```openscad-2D + rgn1 = [for (d=[40:-10:10]) circle(d=d)]; + rgn2 = [square([60,12], center=true)]; + rgn = union(rgn1, rgn2); + region(rgn); +``` + +```openscad-2D + rgn1 = [for (d=[40:-10:10]) circle(d=d)]; + rgn2 = [square([60,12], center=true)]; + rgn = difference(rgn1, rgn2); + region(rgn); +``` + +```openscad-2D + rgn1 = [for (d=[40:-10:10]) circle(d=d)]; + rgn2 = [square([60,12], center=true)]; + rgn = intersection(rgn1, rgn2); + region(rgn); +``` + +```openscad-2D + rgn1 = [for (d=[40:-10:10]) circle(d=d)]; + rgn2 = [square([60,12], center=true)]; + rgn = exclusive_or(rgn1, rgn2); + region(rgn); +``` + +```openscad-2D + orig_rgn = [star(n=5, step=2, d=50)]; + rgn = offset(orig_rgn, r=-3, closed=true); + color("blue") region(orig_rgn); + region(rgn); +``` + +You can use regions for several useful things. If you wanted a grid of holes in your object that +form the shape given by a region, you can do that with `grid2d()`: + +```openscad-3D + rgn = [ + circle(d=100), + star(n=5,step=2,d=100,spin=90) + ]; + difference() { + cyl(h=5, d=120); + grid2d(size=[120,120], spacing=[4,4], inside=rgn) cyl(h=10,d=2); + } +``` + +You can also sweep a region through 3-space to make a solid: + +```openscad-3D + $fa=1; $fs=1; + rgn = [ for (d=[50:-10:10]) circle(d=d) ]; + tforms = [ + for (a=[90:-5:0]) xrot(a, cp=[0,-70]), + for (a=[0:5:90]) xrot(a, cp=[0,70]), + move([0,150,-70]) * xrot(90), + ]; + sweep(rgn, tforms, closed=false, caps=true); +``` + + + diff --git a/tutorials/Shapes2d.md b/tutorials/Shapes2d.md new file mode 100644 index 0000000..28b353b --- /dev/null +++ b/tutorials/Shapes2d.md @@ -0,0 +1,553 @@ +# 2D Shapes Tutorial + +## Primitives +There are two built-in 2D primitive shapes that OpenSCAD provides: `square()`, and `circle()`. +The BOSL2 library provides alternative to these shapes so that they support more features, +and more ways to simply reorient them. + + +### 2D Squares +You can still use the built-in `square()` in the familiar ways that OpenSCAD provides: + +```openscad-2D + square(100, center=false); +``` + +```openscad-2D + square(100, center=true); +``` + +```openscad-2D + square([60,40], center=true); +``` + +The BOSL2 library provides an enhanced equivalent to `square()` called `rect()`. +You can use it in the same way you use `square()`, but it also provides +extended functionality. For example, it allows you to round the corners: + +```openscad-2D + rect([60,40], center=true, rounding=10); +``` + +Or chamfer them: + +```openscad-2D + rect([60,40], center=true, chamfer=10); +``` + +You can even specify *which* corners get rounded or chamfered. If you pass a +list of four size numbers to the `rounding=` or `chamfer=` arguments, it will +give each corner its own size. In order, it goes from the back-right (quadrant I) +corner, counter-clockwise around to the back-left (quadrant II) corner, to the +forward-left (quadrant III) corner, to the forward-right (quadrant IV) corner: + +```openscad-2DImgOnly + module text3d(text) color("black") text( + text=text, font="Times", size=10, + halign="center", valign="center" + ); + translate([ 50, 50]) text3d("I"); + translate([-50, 50]) text3d("II"); + translate([-50,-50]) text3d("III"); + translate([ 50,-50]) text3d("IV"); + rect([90,80], center=true); +``` + +If a size is given as `0`, then there is no rounding and/or chamfering for +that quadrant's corner: + +```openscad-2D + rect([60,40], center=true, rounding=[0,5,10,15]); +``` + +```openscad-2D + rect([60,40], center=true, chamfer=[0,5,10,15]); +``` + +You can give both `rounding=` and `chamfer=` arguments to mix rounding and +chamfering, but only if you specify per corner. If you want a rounding in +a corner, specify a 0 chamfer for that corner, and vice versa: + +```openscad-2D + rect([60,40], center=true, rounding=[5,0,10,0], chamfer=[0,5,0,15]); +``` + +#### Anchors and Spin +Another way that `rect()` is enhanced over `square()`, is that you can anchor, +spin and attach it. + +The `anchor=` argument is an alternative to `center=`, which allows more +alignment options. It takes a vector as a value, pointing roughly towards +the side or corner you want to align to the origin. For example, to align +the center of the back edge to the origin, set the anchor to `[0,1]`: + +```openscad-2D + rect([60,40], anchor=[0,1]); +``` + +To align the front right corner to the origin: + +```openscad-2D + rect([60,40], anchor=[1,-1]); +``` + +To center: + +```openscad-2D + rect([60,40], anchor=[0,0]); +``` + +To make it clearer when giving vectors, there are several standard vector +constants defined: + +Constant | Direction | Value +-------- | --------- | ----------- +`LEFT` | X- | `[-1, 0, 0]` +`RIGHT` | X+ | `[ 1, 0, 0]` +`FRONT`/`FORWARD`/`FWD` | Y- | `[ 0,-1, 0]` +`BACK` | Y+ | `[ 0, 1, 0]` +`BOTTOM`/`BOT`/`BTM`/`DOWN` | Z- | `[ 0, 0,-1]` (3D only.) +`TOP`/`UP` | Z+ | `[ 0, 0, 1]` (3D only.) +`CENTER`/`CTR` | Centered | `[ 0, 0, 0]` + +Note that even though these are 3D vectors, you can use most of them, +(except `UP`/`DOWN`, of course) for anchors in 2D shapes: + +```openscad-2D + rect([60,40], anchor=BACK); +``` + +```openscad-2D + rect([60,40], anchor=CENTER); +``` + +You can add vectors together to point to corners: + +```openscad-2D + rect([60,40], anchor=FRONT+RIGHT); +``` + +Finally, the `spin` argument can rotate the shape by a given number of degrees +clockwise: + +```openscad-2D + rect([60,40], anchor=CENTER, spin=30); +``` + +Anchoring or centering is performed before the spin: + +```openscad-2D + rect([60,40], anchor=BACK, spin=30); +``` + +Anchor points double as attachment points, so that you can attach other shapes: + +```openscad-2D + rect([60,40],center=true) + show_anchors(); +``` + +### 2D Circles and Ovals +The built-in `circle()` primitive can be used as expected: + +```openscad-2D + circle(r=50); +``` + +```openscad-2D + circle(d=100); +``` + +```openscad-2D + circle(d=100, $fn=8); +``` + +The BOSL2 library also provides an enhanced equivalent of `circle()` called `oval()`. +You can use it in the same way you use `circle()`, but it also provides extended +functionality. For example, it allows more control over its size and orientation. + +Since a circle in OpenSCAD can only be approximated by a regular polygon with +a number of straight sides, this can lead to size and shape inaccuracies. +To counter this, the `realign=` and `circum=` arguments are also provided. + +The `realign=` argument, if set `true`, rotates the `oval()` by half the angle +between the sides: + +```openscad-2D + oval(d=100, $fn=8, realign=true); +``` + +The `circum=` argument, if true, makes it so that the polygon forming the +`oval()` circumscribes the ideal circle instead of inscribing it. + +Inscribing the ideal circle: + +```openscad-2D + difference() { + circle(d=100, $fn=360); + oval(d=100, $fn=8); + } +``` + +Circumscribing the ideal circle: + +```openscad-2D + difference() { + oval(d=100, $fn=8, circum=true); + circle(d=100, $fn=360); + } +``` + +The `oval()` module, as its name suggests, can be given separate X and Y radii +or diameters. To do this, just give `r=` or `d=` with a list of two radii or +diameters: + +```openscad-2D + oval(r=[30,20]); +``` + +```openscad-2D + oval(d=[60,40]); +``` + +Another way that `oval()` is enhanced over `circle()`, is that you can anchor, +spin and attach it. + +```openscad-2D + oval(r=50, anchor=BACK); +``` + +```openscad-2D + oval(r=50, anchor=FRONT+RIGHT); +``` + +Using spin on a circle may not make initial sense, until you remember that +anchoring is performed before spin: + +```openscad-2D + oval(r=50, anchor=FRONT, spin=-30); +``` + + +### Trapezoids + +OpenSCAD doesn't provide a simple way to make 2D triangles, trapezoids, or parallelograms. +The BOSL2 library can provide all of these shapes with the `trapezoid()` module. + +To make a simple triangle, just make one of the widths zero: + +```openscad-2D + trapezoid(w1=50, w2=0, h=50); +``` + +To make a right triangle, you need to use the `shift=` argument, to shift the back of the trapezoid along the X axis: + +```openscad-2D + trapezoid(w1=50, w2=0, h=50, shift=-25); +``` + +```openscad-2D + trapezoid(w1=50, w2=0, h=50, shift=25); +``` + +```openscad-2D + trapezoid(w1=0, w2=50, h=50, shift=-25); +``` + +```openscad-2D + trapezoid(w1=0, w2=50, h=50, shift=25); +``` + +You can make a trapezoid by specifying non-zero widths for both the front (`w1=`) and back (`w2=`): + +```openscad-2D + trapezoid(w1=30, w2=50, h=50); +``` + +A parallelogram is just a matter of using the same width for front and back, with a shift along the X axis: + +```openscad-2D + trapezoid(w1=50, w2=50, shift=20, h=50); +``` + +A quadrilateral can be made by having unequal, non-zero front (`w1=`) and back (`w2=`) widths, with the back shifted along the X axis: + +```openscad-2D + trapezoid(w1=50, w2=30, shift=20, h=50); +``` + +You can use `anchor=` and `spin=`, just like with other attachable shapes. However, the anchor +points are based on the side angles of the faces, and may not be where you expect them: + +```openscad-2D + trapezoid(w1=30, w2=50, h=50) + show_anchors(); +``` + +### Regular N-Gons + +OpenSCAD lets you make regular N-gons (pentagon, hexagon, etc) by using `circle()` with `$fn`. +While this is concise, it may be less than obvious at first glance: + +```openscad-2D + circle(d=50, $fn=5); +``` + +The BOSL2 library has modules that are named more clearly: + +```openscad-2D + pentagon(d=50); +``` + +```openscad-2D + hexagon(d=50); +``` + +```openscad-2D + octagon(d=50); +``` + +```openscad-2D + regular_ngon(n=7, d=50); +``` + +These modules also provide you with extra functionality. + +They can be sized by side length: + +```openscad-2D + pentagon(side=20); +``` + +They can be sized by circumscribed circle radius/diameter: + +```openscad-2D + pentagon(ir=25); + pentagon(id=50); +``` + +They can be realigned by half a side's angle: + +```openscad-2D + left(30) pentagon(d=50, realign=true); + right(30) pentagon(d=50, realign=false); +``` + +They can be rounded: + +```openscad-2D + pentagon(d=50, rounding=10); +``` + +```openscad-2D + hexagon(d=50, rounding=10); +``` + +They also have somewhat different attachment behavior: + +```openscad-2D + color("green") stroke(circle(d=50), closed=true); + oval(d=50,$fn=5) + attach(LEFT) color("blue") anchor_arrow2d(); +``` + +```openscad-2D + pentagon(d=50) + attach(LEFT) color("blue") anchor_arrow2d(); +``` + +You can use `anchor=` and `spin=`, just like with other attachable shapes. However, the anchor +points are based on where the anchor vector would intersect the side of the N-gon, and may not +be where you expect them: + +```openscad-2D + pentagon(d=50) + show_anchors(custom=false); +``` + +N-gons also have named anchor points for their sides and tips: + +```openscad-2D + pentagon(d=30) + show_anchors(std=false); +``` + + +### Stars + +The BOSL2 library has stars as a basic supported shape. They can have any number of points. +You can specify a star's shape by point count, inner and outer vertex radius/diameters: + +```openscad-2D + star(n=3, id=10, d=50); +``` + +```openscad-2D + star(n=5, id=15, r=25); +``` + +```openscad-2D + star(n=10, id=30, d=50); +``` + +Or you can specify the star shape by point count and number of points to step: + +```openscad-2D + star(n=7, step=2, d=50); +``` + +```openscad-2D + star(n=7, step=3, d=50); +``` + +If the `realign=` argument is given a true value, then the star will be rotated by half a point angle: + +```openscad-2D + left(30) star(n=5, step=2, d=50); + right(30) star(n=5, step=2, d=50, realign=true); +``` + +The `align_tip=` argument can be given a vector so that you can align the first point in a specific direction: + +```openscad-2D + star(n=5, ir=15, or=30, align_tip=BACK+LEFT) + attach("tip0") color("blue") anchor_arrow2d(); +``` + +```openscad-2D + star(n=5, ir=15, or=30, align_tip=BACK+RIGHT) + attach("tip0") color("blue") anchor_arrow2d(); +``` + +Similarly, the first indentation or pit can be oriented towards a specific vector with `align_pit=`: + + +```openscad-2D + star(n=5, ir=15, or=30, align_pit=BACK+LEFT) + attach("pit0") color("blue") anchor_arrow2d(); +``` + +```openscad-2D + star(n=5, ir=15, or=30, align_pit=BACK+RIGHT) + attach("pit0") color("blue") anchor_arrow2d(); +``` + +You can use `anchor=` and `spin=`, just like with other attachable shapes. However, the anchor +points are based on the furthest extents of the shape, and may not be where you expect them: + +```openscad-2D + star(n=5, step=2, d=50) + show_anchors(custom=false); +``` + +Stars also have named anchor points for their pits, tips, and midpoints between tips: + +```openscad-2D + star(n=5, step=2, d=40) + show_anchors(std=false); +``` + + + +### Teardrop2D + +Often when 3D printing, you may want to make a circular hole in a vertical wall. If the hole is +too big, however, the overhang at the top of the hole can cause problems with printing on an +FDM/FFF printer. If you don't want to use support material, you can just use the teardrop shape. +The `teardrop2d()` module will let you make a 2D version of the teardrop shape, so that you can +extrude it later: + +```openscad-2D + teardrop2d(r=20); +``` + +```openscad-2D + teardrop2d(d=50); +``` + +The default overhang angle is 45 degrees, but you can adjust that with the `ang=` argument: + +```openscad-2D + teardrop2d(d=50, ang=30); +``` + +If you prefer to flatten the top of the teardrop, to encourage bridging, you can use the `cap_h=` +argument: + +```openscad-2D + teardrop2d(d=50, cap_h=25); +``` + +```openscad-2D + teardrop2d(d=50, ang=30, cap_h=30); +``` + +You can use `anchor=` and `spin=`, just like with other attachable shapes. However, the anchor +points are based on the furthest extents of the shape, and may not be where you expect them: + +```openscad-2D + teardrop2d(d=50, ang=30, cap_h=30) + show_anchors(); +``` + + +### Glued Circles + +A more unusal shape that BOSL2 provides is Glued Circles. It's basically a pair of circles, +connected by what looks like a gloopy glued miniscus: + +```openscad-2D + glued_circles(d=30, spread=40); +``` + +The `r=`/`d=` arguments can specify the radius or diameter of the two circles: + +```openscad-2D + glued_circles(r=20, spread=45); +``` + +```openscad-2D + glued_circles(d=40, spread=45); +``` + +The `spread=` argument specifies the distance between the centers of the two circles: + +```openscad-2D + glued_circles(d=30, spread=30); +``` + +```openscad-2D + glued_circles(d=30, spread=40); +``` + +The `tangent=` argument gives the angle of the tangent of the meniscus on the two circles: + +```openscad-2D + glued_circles(d=30, spread=30, tangent=45); +``` + +```openscad-2D + glued_circles(d=30, spread=30, tangent=20); +``` + +```openscad-2D + glued_circles(d=30, spread=30, tangent=-20); +``` + +One useful thing you can do is to string a few `glued_circle()`s in a line then extrude them to make a ribbed wall: + +```openscad-3D + $fn=36; s=10; + linear_extrude(height=50,convexity=16,center=true) + xcopies(s*sqrt(2),n=3) + glued_circles(d=s, spread=s*sqrt(2), tangent=45); +``` + +You can use `anchor=` and `spin=`, just like with other attachable shapes. However, the anchor +points are based on the furthest extents of the shape, and may not be where you expect them: + +```openscad-2D + glued_circles(d=40, spread=40, tangent=45) + show_anchors(); +``` + diff --git a/tutorials/Basic_Shapes.md b/tutorials/Shapes3d.md similarity index 55% rename from tutorials/Basic_Shapes.md rename to tutorials/Shapes3d.md index 9f848b5..c65f2c0 100644 --- a/tutorials/Basic_Shapes.md +++ b/tutorials/Shapes3d.md @@ -1,233 +1,13 @@ # Basic Shapes Tutorial ## Primitives -There are 5 built-in primitive shapes that OpenSCAD provides. -`square()`, `circle()`, `cube()`, `cylinder()`, and `sphere()`. -The BOSL2 library extends or provides alternative to these shapes so +There are 3 built-in 3D primitive shapes that OpenSCAD provides: `cube()`, `cylinder()`, +and `sphere()`. The BOSL2 library extends and provides alternative to these shapes so that they support more features, and more ways to simply reorient them. -### 2D Squares -You can still use the built-in `square()` in the familiar ways that OpenSCAD provides: - -```openscad-2D - square(100, center=false); -``` - -```openscad-2D - square(100, center=true); -``` - -```openscad-2D - square([60,40], center=true); -``` - -The BOSL2 library provides an enhanced equivalent to `square()` called `rect()`. -You can use it in the same way you use `square()`, but it also provides -extended functionality. For example, it allows you to round the corners: - -```openscad-2D - rect([60,40], center=true, rounding=10); -``` - -Or chamfer them: - -```openscad-2D - rect([60,40], center=true, chamfer=10); -``` - -You can even specify *which* corners get rounded or chamfered. If you pass a -list of four size numbers to the `rounding=` or `chamfer=` arguments, it will -give each corner its own size. In order, it goes from the back-right (quadrant I) -corner, counter-clockwise around to the back-left (quadrant II) corner, to the -forward-left (quadrant III) corner, to the forward-right (quadrant IV) corner: - -```openscad-2DImgOnly - module text3d(text) color("black") text( - text=text, font="Times", size=10, - halign="center", valign="center" - ); - translate([ 50, 50]) text3d("I"); - translate([-50, 50]) text3d("II"); - translate([-50,-50]) text3d("III"); - translate([ 50,-50]) text3d("IV"); - rect([90,80], center=true); -``` - -If a size is given as `0`, then there is no rounding and/or chamfering for -that quadrant's corner: - -```openscad-2D - rect([60,40], center=true, rounding=[0,5,10,15]); -``` - -```openscad-2D - rect([60,40], center=true, chamfer=[0,5,10,15]); -``` - -You can give both `rounding=` and `chamfer=` arguments to mix rounding and -chamfering, but only if you specify per corner. If you want a rounding in -a corner, specify a 0 chamfer for that corner, and vice versa: - -```openscad-2D - rect([60,40], center=true, rounding=[5,0,10,0], chamfer=[0,5,0,15]); -``` - -#### Anchors and Spin -Another way that `rect()` is enhanced over `square()`, is that you can anchor, -spin and attach it. - -The `anchor=` argument is an alternative to `center=`, which allows more -alignment options. It takes a vector as a value, pointing roughly towards -the side or corner you want to align to the origin. For example, to align -the center of the back edge to the origin, set the anchor to `[0,1]`: - -```openscad-2D - rect([60,40], anchor=[0,1]); -``` - -To align the front right corner to the origin: - -```openscad-2D - rect([60,40], anchor=[1,-1]); -``` - -To center: - -```openscad-2D - rect([60,40], anchor=[0,0]); -``` - -To make it clearer when giving vectors, there are several standard vector -constants defined: - -Constant | Direction | Value --------- | --------- | ----------- -`LEFT` | X- | `[-1, 0, 0]` -`RIGHT` | X+ | `[ 1, 0, 0]` -`FRONT`/`FORWARD`/`FWD` | Y- | `[ 0,-1, 0]` -`BACK` | Y+ | `[ 0, 1, 0]` -`BOTTOM`/`BOT`/`BTM`/`DOWN` | Z- | `[ 0, 0,-1]` (3D only.) -`TOP`/`UP` | Z+ | `[ 0, 0, 1]` (3D only.) -`CENTER`/`CTR` | Centered | `[ 0, 0, 0]` - -Note that even though these are 3D vectors, you can use most of them, -(except `UP`/`DOWN`, of course) for anchors in 2D shapes: - -```openscad-2D - rect([60,40], anchor=BACK); -``` - -```openscad-2D - rect([60,40], anchor=CENTER); -``` - -You can add vectors together to point to corners: - -```openscad-2D - rect([60,40], anchor=FRONT+RIGHT); -``` - -Finally, the `spin` argument can rotate the shape by a given number of degrees -clockwise: - -```openscad-2D - rect([60,40], anchor=CENTER, spin=30); -``` - -Anchoring or centering is performed before the spin: - -```openscad-2D - rect([60,40], anchor=BACK, spin=30); -``` - - -### 2D Circles -The built-in `circle()` primitive can be used as expected: - -```openscad-2D - circle(r=50); -``` - -```openscad-2D - circle(d=100); -``` - -```openscad-2D - circle(d=100, $fn=8); -``` - -The BOSL2 library provides an enhanced equivalent of `circle()` called `oval()`. -You can use it in the same way you use `circle()`, but it also provides -extended functionality. For example, it allows more control over its size and -orientation. - -Since a circle in OpenSCAD can only be approximated by a regular polygon with -a number of straight sides, this can lead to size and shape inaccuracies. -To counter this, the `realign=` and `circum=` arguments are also provided. - -The `realign=` argument, if set `true`, rotates the `oval()` by half the angle -between the sides: - -```openscad-2D - oval(d=100, $fn=8, realign=true); -``` - -The `circum=` argument, if true, makes it so that the polygon forming the -`oval()` circumscribes the ideal circle instead of inscribing it. - -Inscribing the ideal circle: - -```openscad-2D - difference() { - circle(d=100, $fn=360); - oval(d=100, $fn=8); - } -``` - -Circumscribing the ideal circle: - -```openscad-2D - difference() { - oval(d=100, $fn=8, circum=true); - circle(d=100, $fn=360); - } -``` - -The `oval()` module, as its name suggests, can be given separate X and Y radii -or diameters. To do this, just give `r=` or `d=` with a list of two radii or -diameters: - -```openscad-2D - oval(r=[30,20]); -``` - -```openscad-2D - oval(d=[60,40]); -``` - -Another way that `oval()` is enhanced over `circle()`, is that you can anchor, -spin and attach it. - -```openscad-2D - oval(r=50, anchor=BACK); -``` - -```openscad-2D - oval(r=50, anchor=FRONT+RIGHT); -``` - -Using spin on a circle may not make initial sense, until you remember that -anchoring is performed before spin: - -```openscad-2D - oval(r=50, anchor=FRONT, spin=-30); -``` - - ### 3D Cubes -BOSL2 overrides the built-in `cube()` module. It still can be used as you -expect from the built-in: +BOSL2 overrides the built-in `cube()` module. It still can be used as you expect from the built-in: ```openscad-3D cube(100); @@ -243,7 +23,7 @@ expect from the built-in: It is also enhanced to allow you to anchor, spin, orient, and attach it. -You can use `anchor=` similarly to how you use it with `square()` or `rect()`, +You can use `anchor=` similarly to how you use it with `rect()` or `oval()`, except you can also anchor vertically in 3D, allowing anchoring to faces, edges, and corners: @@ -546,11 +326,20 @@ The "stagger" style will stagger the triangulation of the vertical rows: spheroid(d=100, style="stagger", $fn=20); ``` -The "icosa"` style will make for roughly equal-sized triangles for the entire -sphere surface: +The "icosa" style will make for roughly equal-sized triangles for the entire +sphere surface, based on subdividing an icosahedron. This style will round the +effective `$fn` to a multiple of 5 when constructing the spheroid: ```openscad-3D spheroid(d=100, style="icosa", $fn=20); ``` +The "octa" style will also make for roughly equal-sized triangles for the entire +sphere surface, but based on subdividing an octahedron. This is useful in that it +guarantees vertices at the axis extrema. This style will round the effective `$fn` +to a multiple of 4 when constructing the spheroid: + +```openscad-3D + spheroid(d=100, style="octa", $fn=20); +``` diff --git a/version.scad b/version.scad index e533b27..e34c880 100644 --- a/version.scad +++ b/version.scad @@ -8,7 +8,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,482]; +BOSL_VERSION = [2,0,489]; // Section: BOSL Library Version Functions