diff --git a/.openscad_docsgen_rc b/.openscad_docsgen_rc index 9211fa2..f0d8724 100644 --- a/.openscad_docsgen_rc +++ b/.openscad_docsgen_rc @@ -5,10 +5,11 @@ GenerateDocs: Files, TOC, Index, Topics, CheatSheet, Sidebar UsePNGAnimations: Yes IgnoreFiles: affine.scad - foo.scad + metric_screws.scad std.scad bosl1compat.scad builtins.scad + foo.scad tmp_*.scad PrioritizeFiles: constants.scad @@ -73,6 +74,7 @@ DefineSynTags: Path = Can return a Path. PathList = Can return a list of Paths. Region = Can return a Region. + RegList = Can return a list of Regions. Trans = Can transform children. VNF = Can return a VNF. diff --git a/README.md b/README.md index c07d9b0..da36dda 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Requires OpenSCAD 2021.01 or later. - **NOTE:** BOSL2 IS BETA CODE. THE CODE IS STILL BEING REORGANIZED. - **NOTE2:** CODE WRITTEN FOR BOSLv1 PROBABLY WON'T WORK WITH BOSL2! -[![Join the chat at https://gitter.im/BelfrySCAD/BOSL2](https://badges.gitter.im/BelfrySCAD/BOSL2.svg)](https://gitter.im/BelfrySCAD/BOSL2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Join the chat at https://gitter.im/revarbat/BOSL2](https://badges.gitter.im/revarbat/BOSL2.svg)](https://gitter.im/revarbat/BOSL2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ## Documentation diff --git a/attachments.scad b/attachments.scad index c2b202f..3ec8c60 100644 --- a/attachments.scad +++ b/attachments.scad @@ -1,3549 +1,3844 @@ -////////////////////////////////////////////////////////////////////// -// LibFile: attachments.scad -// The modules in this file allows you to attach one object to another by making one object the child of another object. -// You can place the child object in relation to its parent object and control the position and orientation -// relative to the parent. The modifiers allow you to treat children in ways different from simple union, such -// as differencing them from the parent, or changing their color. Attachment only works when the parent and child -// are both written to support attachment. Also included in this file are the tools to make your own "attachable" objects. -// Includes: -// include -// FileGroup: Basic Modeling -// FileSummary: Positioning objects on or relative to other objects. Making your own objects support attachment. -// FileFootnotes: STD=Included in std.scad -////////////////////////////////////////////////////////////////////// - - -// Default values for attachment code. -$tags=undef; // for backward compatibility -$tag = ""; -$tag_prefix = ""; -$overlap = 0; -$color = "default"; -$save_color = undef; // Saved color to revert back for children - -$attach_to = undef; -$attach_anchor = [CENTER, CENTER, UP, 0]; -$attach_norot = false; - -$parent_anchor = BOTTOM; -$parent_spin = 0; -$parent_orient = UP; - -$parent_size = undef; -$parent_geom = undef; - -$tags_shown = "ALL"; -$tags_hidden = []; - -_ANCHOR_TYPES = ["intersect","hull"]; - - -// Section: Terminology and Shortcuts -// This library adds the concept of anchoring, spin and orientation to the `cube()`, `cylinder()` -// and `sphere()` builtins, as well as to most of the shapes provided by this library itself. -// - An anchor is a place on an object which you can align the object to, or attach other objects -// to using `attach()` or `position()`. An anchor has a position, a direction, and a spin. -// The direction and spin are used to orient other objects to match when using `attach()`. -// - Spin is a simple rotation around the Z axis. -// - Orientation is rotating an object so that its top is pointed towards a given vector. -// An object will first be translated to its anchor position, then spun, then oriented. -// For a detailed step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// . -// For describing directions, faces, edges, and corners the library provides a set of shortcuts -// all based on combinations of unit direction vectors. You can use these for anchoring and orienting -// attachable objects. You can also them to specify edge sets for rounding or chamfering cuboids, -// or for placing edge, face and corner masks. -// Subsection: Anchor -// Anchoring is specified with the `anchor` argument in most shape modules. Specifying `anchor` -// when creating an object will translate the object so that the anchor point is at the origin -// (0,0,0). Anchoring always occurs before spin and orientation are applied. -// . -// An anchor can be referred to in one of two ways; as a directional vector, or as a named anchor string. -// . -// When given as a vector, it points, in a general way, towards the face, edge, or corner of the -// object that you want the anchor for, relative to the center of the object. You can simply -// specify a vector like `[0,0,1]` to anchor an object at the Z+ end, but you can also use -// directional constants with names like `TOP`, `BOTTOM`, `LEFT`, `RIGHT` and `BACK` that you can add together -// to specify anchor points. See [specifying directions](attachments.scad#subsection-specifying-directions) -// below for the full list of pre-defined directional constants. -// . -// For example: -// - `[0,0,1]` is the same as `TOP` and refers to the center of the top face. -// - `[-1,0,1]` is the same as `TOP+LEFT`, and refers to the center of the top-left edge. -// - `[1,1,-1]` is the same as `BOTTOM+BACK+RIGHT`, and refers to the bottom-back-right corner. -// . -// When the object is cubical or rectangular in shape the anchors must have zero or one values -// for their components and they refer to the face centers, edge centers, or corners of the object. -// The direction of a face anchor will be perpendicular to the face, pointing outward. The direction of a edge anchor -// will be the average of the anchor directions of the two faces the edge is between. The direction -// of a corner anchor will be the average of the anchor directions of the three faces the corner is -// on. -// . -// When the object is cylindrical, conical, or spherical in nature, the anchors will be located -// around the surface of the cylinder, cone, or sphere, relative to the center. -// You can generally use an arbitrary vector to get an anchor positioned anywhere on the curved -// surface of such an object, and the anchor direction will be the surface normal at the anchor location. -// However, for anchor component pointing toward the flat face should be either -1, 1, or 0, and -// anchors that point diagonally toward one of the flat faces will select a point on the edge. -// . -// For objects in two dimensions, the natural expectation is for TOP and BOTTOM to refer to the Y direction -// of the shape. To support this, if you give an anchor in 2D that has anchor.y=0 then the Z component -// will be mapped to the Y direction. This means you can use TOP and BOTTOM for anchors of 2D objects. -// But remember that TOP and BOTTOM are three dimensional vectors and this is a special interpretation -// for 2d anchoring. -// . -// Some more complex objects, like screws and stepper motors, have named anchors to refer to places -// on the object that are not at one of the standard faces, edges or corners. For example, stepper -// motors have anchors for `"screw1"`, `"screw2"`, etc. to refer to the various screwholes on the -// stepper motor shape. The names, positions, directions, and spins of these anchors are -// specific to the object, and are documented when they exist. -// Subsection: Spin -// Spin is specified with the `spin` argument in most shape modules. Specifying a scalar `spin` -// when creating an object will rotate the object counter-clockwise around the Z axis by the given -// number of degrees. If given as a 3D vector, the object will be rotated around each of the X, Y, Z -// axes by the number of degrees in each component of the vector. Spin is always applied after -// anchoring, and before orientation. Since spin is applied after anchoring it is not what -// you might think of intuitively as spinning the shape. To do that, apply `zrot()` to the shape before anchoring. -// Subsection: Orient -// Orientation is specified with the `orient` argument in most shape modules. Specifying `orient` -// when creating an object will rotate the object such that the top of the object will be pointed -// at the vector direction given in the `orient` argument. Orientation is always applied after -// anchoring and spin. The constants `UP`, `DOWN`, `FRONT`, `BACK`, `LEFT`, and `RIGHT` can be -// added together to form the directional vector for this. ie: `LEFT+BACK` -// Subsection: Specifying Directions -// You can use direction vectors to specify anchors for objects or to specify edges, faces, and -// corners of cubes. You can simply specify these direction vectors numerically, but another -// option is to use named constants for direction vectors. These constants define unit vectors -// for the six axis directions as shown below. -// Figure(3D,Big,VPD=6): Named constants for direction vectors. Some directions have more than one name. -// $fn=12; -// stroke([[0,0,0],RIGHT], endcap2="arrow2", width=.05); -// color("black")right(.05)up(.05)move(RIGHT) text3d("RIGHT",size=.1,h=.01,anchor=LEFT,orient=FRONT); -// stroke([[0,0,0],LEFT], endcap2="arrow2", width=.05); -// color("black")left(.05)up(.05)move(LEFT) text3d("LEFT",size=.1,h=.01,anchor=RIGHT,orient=FRONT); -// stroke([[0,0,0],FRONT], endcap2="arrow2", width=.05); -// color("black") -// left(.1){ -// up(.12)move(FRONT) text3d("FRONT",size=.1,h=.01,anchor=RIGHT,orient=FRONT); -// move(FRONT) text3d("FWD",size=.1,h=.01,anchor=RIGHT,orient=FRONT); -// down(.12)move(FRONT) text3d("FORWARD",size=.1,h=.01,anchor=RIGHT,orient=FRONT); -// } -// stroke([[0,0,0],BACK], endcap2="arrow2", width=.05); -// right(.05) -// color("black")move(BACK) text3d("BACK",size=.1,h=.01,anchor=LEFT,orient=FRONT); -// stroke([[0,0,0],DOWN], endcap2="arrow2", width=.05); -// color("black") -// right(.1){ -// up(.12)move(BOT) text3d("DOWN",size=.1,h=.01,anchor=LEFT,orient=FRONT); -// move(BOT) text3d("BOTTOM",size=.1,h=.01,anchor=LEFT,orient=FRONT); -// down(.12)move(BOT) text3d("BOT",size=.1,h=.01,anchor=LEFT,orient=FRONT); -// } -// stroke([[0,0,0],TOP], endcap2="arrow2", width=.05); -// color("black")left(.05){ -// up(.12)move(TOP) text3d("TOP",size=.1,h=.01,anchor=RIGHT,orient=FRONT); -// move(TOP) text3d("UP",size=.1,h=.01,anchor=RIGHT,orient=FRONT); -// } -// Figure(2D,Big): Named constants for direction vectors in 2D. For anchors the TOP and BOTTOM directions are collapsed into 2D as shown here, but do not try to use TOP or BOTTOM as 2D directions in other situations. -// $fn=12; -// stroke(path2d([[0,0,0],RIGHT]), endcap2="arrow2", width=.05); -// color("black")fwd(.22)left(.05)move(RIGHT) text("RIGHT",size=.1,anchor=RIGHT); -// stroke(path2d([[0,0,0],LEFT]), endcap2="arrow2", width=.05); -// color("black")right(.05)fwd(.22)move(LEFT) text("LEFT",size=.1,anchor=LEFT); -// stroke(path2d([[0,0,0],FRONT]), endcap2="arrow2", width=.05); -// color("black") -// fwd(.2) -// right(.15) -// color("black")move(BACK) { text("BACK",size=.1,anchor=LEFT); back(.14) text("(TOP)", size=.1, anchor=LEFT);} -// color("black") -// left(.15)back(.2+.14)move(FRONT){ -// back(.14) text("FRONT",size=.1,anchor=RIGHT); -// text("FWD",size=.1,anchor=RIGHT); -// fwd(.14) text("FORWARD",size=.1,anchor=RIGHT); -// fwd(.28) text("(BOTTOM)",size=.1,anchor=RIGHT); -// fwd(.14*3) text("(BOT)",size=.1,anchor=RIGHT); -// } -// stroke(path2d([[0,0,0],BACK]), endcap2="arrow2", width=.05); -// Subsection: Specifying Faces -// Modules operating on faces accept a list of faces to describe the faces to operate on. Each -// face is given by a vector that points to that face. Attachments of cuboid objects onto their faces also -// work by choosing an attachment face with a single vector in the same manner. -// Figure(3D,Big,NoScales,VPD=275): The six faces of the cube. Some have faces have more than one name. -// ydistribute(50) { -// xdistribute(35){ -// _show_cube_faces([BACK], botlabel=["BACK"]); -// _show_cube_faces([UP],botlabel=["TOP","UP"]); -// _show_cube_faces([RIGHT],botlabel=["RIGHT"]); -// } -// xdistribute(35){ -// _show_cube_faces([FRONT],toplabel=["FRONT","FWD", "FORWARD"]); -// _show_cube_faces([DOWN],toplabel=["BOTTOM","BOT","DOWN"]); -// _show_cube_faces([LEFT],toplabel=["LEFT"]); -// } -// } -// Subsection: Specifying Edges -// Modules operating on edges use two arguments to describe the edge set they will use: The `edges` argument -// is a list of edge set descriptors to include in the edge set, and the `except` argument is a list of -// edge set descriptors to remove from the edge set. -// The default value for `edges` is `"ALL"`, the set of all edges. -// The default value for `except` is the empty set, meaning no edges are removed. -// If either argument is just a single edge set -// descriptor it can be passed directly rather than in a singleton list. -// Each edge set descriptor must be one of: -// - A vector pointing towards an edge, indicating that single edge. -// - A vector pointing towards a face, indicating all edges surrounding that face. -// - A vector pointing towards a corner, indicating all edges touching that corner. -// - The string `"X"`, indicating all X axis aligned edges. -// - The string `"Y"`, indicating all Y axis aligned edges. -// - The string `"Z"`, indicating all Z axis aligned edges. -// - The string `"ALL"`, indicating all edges. -// - The string `"NONE"`, indicating no edges at all. -// - A 3x4 array, where each entry corresponds to one of the 12 edges and is set to 1 if that edge is included and 0 if the edge is not. The edge ordering is: -// ``` -// [ -// [Y-Z-, Y+Z-, Y-Z+, Y+Z+], -// [X-Z-, X+Z-, X-Z+, X+Z+], -// [X-Y-, X+Y-, X-Y+, X+Y+] -// ] -// ``` -// You can specify edge descriptors directly by giving a vector, or you can use sums of the -// named direction vectors described above. Below we show all of the edge sets you can -// describe with sums of the direction vectors, and then we show some examples of combining -// edge set descriptors. -// Figure(3D,Big,VPD=300,NoScales): Vectors pointing toward an edge select that single edge -// ydistribute(50) { -// xdistribute(30) { -// _show_edges(edges=BOT+RIGHT); -// _show_edges(edges=BOT+BACK); -// _show_edges(edges=BOT+LEFT); -// _show_edges(edges=BOT+FRONT); -// } -// xdistribute(30) { -// _show_edges(edges=FWD+RIGHT); -// _show_edges(edges=BACK+RIGHT); -// _show_edges(edges=BACK+LEFT); -// _show_edges(edges=FWD+LEFT); -// } -// xdistribute(30) { -// _show_edges(edges=TOP+RIGHT); -// _show_edges(edges=TOP+BACK); -// _show_edges(edges=TOP+LEFT); -// _show_edges(edges=TOP+FRONT); -// } -// } -// Figure(3D,Med,VPD=205,NoScales): Vectors pointing toward a face select all edges surrounding that face. -// ydistribute(50) { -// xdistribute(30) { -// _show_edges(edges=LEFT); -// _show_edges(edges=FRONT); -// _show_edges(edges=RIGHT); -// } -// xdistribute(30) { -// _show_edges(edges=TOP); -// _show_edges(edges=BACK); -// _show_edges(edges=BOTTOM); -// } -// } -// Figure(3D,Big,VPD=300,NoScales): Vectors pointing toward a corner select all edges surrounding that corner. -// ydistribute(50) { -// xdistribute(30) { -// _show_edges(edges=FRONT+LEFT+TOP); -// _show_edges(edges=FRONT+RIGHT+TOP); -// _show_edges(edges=FRONT+LEFT+BOT); -// _show_edges(edges=FRONT+RIGHT+BOT); -// } -// xdistribute(30) { -// _show_edges(edges=TOP+LEFT+BACK); -// _show_edges(edges=TOP+RIGHT+BACK); -// _show_edges(edges=BOT+LEFT+BACK); -// _show_edges(edges=BOT+RIGHT+BACK); -// } -// } -// Figure(3D,Med,VPD=205,NoScales): Named Edge Sets -// ydistribute(50) { -// xdistribute(30) { -// _show_edges(edges="X"); -// _show_edges(edges="Y"); -// _show_edges(edges="Z"); -// } -// xdistribute(30) { -// _show_edges(edges="ALL"); -// _show_edges(edges="NONE"); -// } -// } -// Figure(3D,Big,VPD=310,NoScales): Next are some examples showing how you can combine edge descriptors to obtain different edge sets. You can specify the top front edge with a numerical vector or by combining the named direction vectors. If you combine them as a list you get all the edges around the front and top faces. Adding `except` removes an edge. -// xdistribute(43){ -// _show_edges(_edges([0,-1,1]),toplabel=["edges=[0,-1,1]"]); -// _show_edges(_edges(TOP+FRONT),toplabel=["edges=TOP+FRONT"]); -// _show_edges(_edges([TOP,FRONT]),toplabel=["edges=[TOP,FRONT]"]); -// _show_edges(_edges([TOP,FRONT],TOP+FRONT),toplabel=["edges=[TOP,FRONT]","except=TOP+FRONT"]); -// } -// Figure(3D,Big,VPD=310,NoScales): Using `except=BACK` removes the four edges surrounding the back face if they are present in the edge set. In the first example only one edge needs to be removed. In the second example we remove two of the Z-aligned edges. The third example removes all four back edges from the default edge set of all edges. You can explicitly give `edges="ALL"` but it is not necessary, since this is the default. In the fourth example, the edge set of Y-aligned edges contains no back edges, so the `except` parameter has no effect. -// xdistribute(43){ -// _show_edges(_edges(BOT,BACK), toplabel=["edges=BOT","except=BACK"]); -// _show_edges(_edges("Z",BACK), toplabel=["edges=\"Z\"", "except=BACK"]); -// _show_edges(_edges("ALL",BACK), toplabel=["(edges=\"ALL\")", "except=BACK"]); -// _show_edges(_edges("Y",BACK), toplabel=["edges=\"Y\"","except=BACK"]); -// } -// Figure(3D,Big,NoScales,VPD=310): On the left `except` is a list to remove two edges. In the center we show a corner edge set defined by a numerical vector, and at the right we remove that same corner edge set with named direction vectors. -// xdistribute(52){ -// _show_edges(_edges("ALL",[FRONT+RIGHT,FRONT+LEFT]), -// toplabel=["except=[FRONT+RIGHT,"," FRONT+LEFT]"]); -// _show_edges(_edges([1,-1,1]),toplabel=["edges=[1,-1,1]"]); -// _show_edges(_edges([TOP,BOT], TOP+RIGHT+FRONT),toplabel=["edges=[TOP,BOT]","except=TOP+RIGHT+FRONT"]); -// } -// Subsection: Specifying Corners -// Modules operating on corners use two arguments to describe the corner set they will use: The `corners` argument -// is a list of corner set descriptors to include in the corner set, and the `except` argument is a list of -// corner set descriptors to remove from the corner set. -// The default value for `corners` is `"ALL"`, the set of all corners. -// The default value for `except` is the empty set, meaning no corners are removed. -// If either argument is just a single corner set -// descriptor it can be passed directly rather than in a singleton list. -// Each corner set descriptor must be one of: -// - A vector pointing towards a corner, indicating that corner. -// - A vector pointing towards an edge indicating both corners at the ends of that edge. -// - A vector pointing towards a face, indicating all the corners of that face. -// - The string `"ALL"`, indicating all corners. -// - The string `"NONE"`, indicating no corners at all. -// - A length 8 vector where each entry corresponds to a corner and is 1 if the corner is included and 0 if it is excluded. The corner ordering is -// ``` -// [X-Y-Z-, X+Y-Z-, X-Y+Z-, X+Y+Z-, X-Y-Z+, X+Y-Z+, X-Y+Z+, X+Y+Z+] -// ``` -// You can specify corner descriptors directly by giving a vector, or you can use sums of the -// named direction vectors described above. Below we show all of the corner sets you can -// describe with sums of the direction vectors and then we show some examples of combining -// corner set descriptors. -// Figure(3D,Big,NoScales,VPD=300): Vectors pointing toward a corner select that corner. -// ydistribute(55) { -// xdistribute(35) { -// _show_corners(corners=FRONT+LEFT+TOP); -// _show_corners(corners=FRONT+RIGHT+TOP); -// _show_corners(corners=FRONT+LEFT+BOT); -// _show_corners(corners=FRONT+RIGHT+BOT); -// } -// xdistribute(35) { -// _show_corners(corners=TOP+LEFT+BACK); -// _show_corners(corners=TOP+RIGHT+BACK); -// _show_corners(corners=BOT+LEFT+BACK); -// _show_corners(corners=BOT+RIGHT+BACK); -// } -// } -// Figure(3D,Big,NoScales,VPD=340): Vectors pointing toward an edge select the corners and the ends of the edge. -// ydistribute(55) { -// xdistribute(35) { -// _show_corners(corners=BOT+RIGHT); -// _show_corners(corners=BOT+BACK); -// _show_corners(corners=BOT+LEFT); -// _show_corners(corners=BOT+FRONT); -// } -// xdistribute(35) { -// _show_corners(corners=FWD+RIGHT); -// _show_corners(corners=BACK+RIGHT); -// _show_corners(corners=BACK+LEFT); -// _show_corners(corners=FWD+LEFT); -// } -// xdistribute(35) { -// _show_corners(corners=TOP+RIGHT); -// _show_corners(corners=TOP+BACK); -// _show_corners(corners=TOP+LEFT); -// _show_corners(corners=TOP+FRONT); -// } -// } -// Figure(3D,Med,NoScales,VPD=225): Vectors pointing toward a face select the corners of the face. -// ydistribute(55) { -// xdistribute(35) { -// _show_corners(corners=LEFT); -// _show_corners(corners=FRONT); -// _show_corners(corners=RIGHT); -// } -// xdistribute(35) { -// _show_corners(corners=TOP); -// _show_corners(corners=BACK); -// _show_corners(corners=BOTTOM); -// } -// } -// Figure(3D,Med,NoScales,VPD=200): Corners by name -// xdistribute(35) { -// _show_corners(corners="ALL"); -// _show_corners(corners="NONE"); -// } -// Figure(3D,Big,NoScales,VPD=300): Next are some examples showing how you can combine corner descriptors to obtain different corner sets. You can specify corner sets numerically or by adding together named directions. The third example shows a list of two corner specifications, giving all the corners on the front face or the right face. -// xdistribute(52){ -// _show_corners(_corners([1,-1,-1]),toplabel=["corners=[1,-1,-1]"]); -// _show_corners(_corners(BOT+RIGHT+FRONT),toplabel=["corners=BOT+RIGHT+FRONT"]); -// _show_corners(_corners([FRONT,RIGHT]), toplabel=["corners=[FRONT,RIGHT]"]); -// } -// Figure(3D,Big,NoScales,VPD=300): Corners for one edge, two edges, and all the edges except the two on one edge. Note that since the default is all edges, you only need to give the except argument in this case: -// xdistribute(52){ -// _show_corners(_corners(FRONT+TOP), toplabel=["corners=FRONT+TOP"]); -// _show_corners(_corners([FRONT+TOP,BOT+BACK]), toplabel=["corners=[FRONT+TOP,"," BOT+BACK]"]); -// _show_corners(_corners("ALL",FRONT+TOP), toplabel=["(corners=\"ALL\")","except=FRONT+TOP"]); -// } -// Figure(3D,Med,NoScales,VPD=240): The first example shows a single corner removed from the top corners using a numerical vector. The second one shows removing a set of two corner descriptors from the implied set of all corners. -// xdistribute(58){ -// _show_corners(_corners(TOP,[1,1,1]), toplabel=["corners=TOP","except=[1,1,1]"]); -// _show_corners(_corners("ALL",[FRONT+RIGHT+TOP,FRONT+LEFT+BOT]), -// toplabel=["except=[FRONT+RIGHT+TOP,"," FRONT+LEFT+BOT]"]); -// } -// Subsection: Anchoring of Non-Rectangular Objects and Anchor Type (atype) -// We focused above on rectangular objects that have well-defined faces and edges aligned with the coordinate axes. -// Things get difficult when the objects are curved, or even when their edges are not neatly aligned with the coordinate axes. -// In these cases, the library may provide multiple different anchoring schemes, called the anchor types. When a module supports -// multiple anchor types, use the `atype=` parameter to select the anchor type you need. -// . -// First consider the case of a simple rectangle whose corners have been rounded. Where should the anchors lie? -// The default anchor type puts them in the same location as the anchors of an unrounded rectangle, which means that for -// positive rounding radii, they are not even located on the perimeter of the object. -// Figure(2D,Med,NoAxes): Default "box" atype anchors for a rounded {{rect()}} -// rect([100,50], rounding=[10,0,0,-20],chamfer=[0,10,-20,0]) show_anchors(); -// Continues: -// This choice enables you to position the box, or attach things to it, without regard to its rounding or chamfers. If you need to -// anchor onto the roundovers or chamfers then you can use the "perim" anchor type: -// Figure(2D,Med,NoAxes): The "perim" atype for a rounded and chamfered {{rect()}} -// rect([100,50], rounding=[10,0,0,-20],chamfer=[0,10,-20,0],atype="perim") show_anchors(); -// Continues: -// With this anchor type, the anchors are located on the perimeter. For positive roundings they point in the standard anchor direction; -// for negative roundings they are parallel to the base. As noted above, for circles, cylinders, and spheres, the anchor point is -// determined by choosing the point where the anchor vector intersects the shape. On a circle, this results in an anchor whose direction -// matches the user provided anchor vector. But on an ellipse, something else happens: -// Figure(2D,Med,NoAxes): Anchors on an ellipse. The red arrow shows a TOP+RIGHT anchor direction. -// ellipse([70,30]) show_anchors(); -// stroke([[0,0],[45,45]], color="red",endcap2="arrow2"); -// Continues: -// For a TOP+RIGHT anchor direction, the surface normal at the intersection point does not match the anchor direction, -// so the direction of the anchor shown in blue does not match the direction specified, in red. -// Anchors computed this way have anchor type "intersect". When a shape is concave, intersection anchors can produce -// a result buried inside the shape's concavity. Consider the RIGHT anchor of this supershape example: -// Figure(2D,Med,NoAxes): A supershape with "intersect" anchor type: -// supershape(n=150,r=75, m1=4, n1=4.0,n2=16, n3=1.5, a=0.9, b=9,atype="intersect") show_anchors(); -// Continues: -// A different anchor type called "hull" finds anchors that are on the convex hull of the shape. -// Figure(2D,Med,NoAxes): A supershape with "hull" anchor type: -// supershape(n=150,r=55, m1=4, n1=4.0,n2=16, n3=1.5, a=0.9, b=9,atype="hull") show_anchors(); -// Continues: -// Hull anchoring works by creating the line (or plane in 3D) that is normal to the specified anchor direction, and -// finding the point farthest from the center that intersects that line (or plane). -// Figure(2D,Med,NoAxes): Finding the RIGHT and BACK+LEFT "hull" anchors -// supershape(n=128,r=55, m1=4, n1=4.0,n2=16, n3=1.5, a=0.9, b=9,atype="hull") { -// position(RIGHT) color_this("red")rect([1,90],anchor=LEFT); -// attach(RIGHT)anchor_arrow2d(13); -// attach(BACK+LEFT) { -// anchor_arrow2d(13); -// color_this("red")rect([30,1]); -// } -// } -// Continues: -// In the example the RIGHT anchor is found when the normal line (shown in red) is tangent to the shape at two points. -// The anchor is then taken to be the midpoint. The BACK+LEFT anchor occurs with a single tangent point, and the -// anchor point is located at the tangent point. For circles intersection is done to the exact circle, but for other -// shapes these calculations are done on the point lists that defines the shape, so if you change the number of points -// in the list, the precise location of the anchors can change. You can also get surprising results if your point list is badly chosen. -// Figure(2D,Med,NoAxes): Circle anchor in blue. The red anchor is computed to a point list of a circle with 17 segments. -// circle(r=31,$fn=128) attach(TOP)anchor_arrow2d(15); -// region(circle(r=33,$fn=17)) {color("red")attach(TOP)anchor_arrow2d(13);} -// Continues: -// The figure shows a large horizontal offset due to a poor choice of sampling for the circular shape when using the "hull" anchor type. -// The determination of "hull" or "intersect" anchors may depend on the location of the centerpoint used in the computation. -// Some of the modules allow you to change the centerpoint using a `cp=` argument. If you need to change the centerpoint for -// a module that does not provide this option, you can use the generic {{region()}} module, which will let you specify a centerpoint. -// The default center point is the centroid, specified by "centroid". You can also choose "mean", which gives the mean of all -// the data points, or "bbox", which gives the centerpoint of the bounding box for the data. Your last option for centerpoint is to -// choose an arbitrary point that meets your needs. -// Figure(2D,Med,NoAxes): The centerpoint for "intersect" anchors is located at the red dot -// region(supershape(n=128,r=55, m1=4, n1=4.0,n2=16, n3=1.5, a=0.9, b=9),atype="intersect",cp=[0,30]) show_anchors(); -// color("red")back(30)circle(r=2,$fn=16); -// Continues: -// Note that all the anchors for an object have to be determined based on one anchor type and relative to the same centerpoint. -// The supported anchor types for each module appear in the "Anchor Types" section of its entry. - - - - - -// Section: Attachment Positioning - -// Module: position() -// Synopsis: Attaches children to a parent object at an anchor point. -// Topics: Attachments -// See Also: attachable(), attach(), orient() -// Usage: -// PARENT() position(from) CHILDREN; -// Description: -// Attaches children to a parent object at an anchor point. For a step-by-step explanation -// of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// from = The vector, or name of the parent anchor point to attach to. -// Side Effects: -// `$attach_anchor` for each `from=` anchor given, this is set to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. -// `$attach_to` is set to `undef`. -// `$attach_norot` is set to `true`. -// `$align` set to the anchor that will position a child flush to the edge of the parent at specified position. -// Example: -// spheroid(d=20) { -// position(TOP) cyl(l=10, d1=10, d2=5, anchor=BOTTOM); -// position(RIGHT) cyl(l=10, d1=10, d2=5, anchor=BOTTOM); -// position(FRONT) cyl(l=10, d1=10, d2=5, anchor=BOTTOM); -// } -// Example: Child would require anchor of RIGHT+FRONT+BOT if given explicitly. -// cuboid([50,40,15]) -// position(RIGHT+FRONT+TOP) -// color("lightblue")prismoid([10,5],[7,4],height=4, anchor=$align); -// Example: Child requires a different anchor for each position, so explicit specification of the anchor is impossible in this case. -// cuboid([50,40,15]) -// position([RIGHT+TOP,LEFT+TOP]) -// color("lightblue")prismoid([10,5],[7,4],height=4, anchor=$align); -// Example: If you try to spin your child, the spin happens after the position anchor, so the child will not be flush: -// cuboid([50,40,15]) -// position([RIGHT+TOP]) -// color("lightblue")prismoid([10,5],[7,4],height=4, -// anchor=$align, spin=90); -// Example: You can instead spin the attached children using {{orient()}}. In this example, the required anchor is BOT+FWD, which is less obvious. -// cuboid([50,40,15]) -// position(RIGHT+TOP) -// orient(TOP, spin=90) -// color("lightblue")prismoid([10,5],[7,4],height=4, anchor=$align); -// Example: Of course, {{orient()}} can also place children on different sides of the parent. In this case you don't have to figure out that the required anchor is BOT+BACK. -// cuboid([50,40,15]) -// position(RIGHT+TOP) -// orient(RIGHT) -// color("lightblue")prismoid([10,5],[7,4],height=4, anchor=$align); -// Example: You can combine this with {{diff()}} to remove the child. Note that it's more intuitive to shift the child after positioning, relative to the global coordinate system: -// diff() -// cuboid([50,40,15]) -// right(.1)up(.1) -// position(RIGHT+TOP) -// orient(LEFT) -// tag("remove")cuboid([10,5,4], anchor=$align); -module position(from) -{ - req_children($children); - assert($parent_geom != undef, "No object to attach to!"); - anchors = (is_vector(from)||is_string(from))? [from] : from; - two_d = _attach_geom_2d($parent_geom); - for (anchr = anchors) { - anch = _find_anchor(anchr, $parent_geom); - $attach_to = undef; - $attach_anchor = anch; - $attach_norot = true; - $align=two_d && anchr.y!=0 ? [anchr.x,-anchr.y] - :!two_d && anchr.z!=0 ? [anchr.x, anchr.y, -anchr.z] - : -anchr; - translate(anch[1]) children(); - } -} - - -// Module: orient() -// Synopsis: Orients children's tops in the directon of the specified anchor. -// Topics: Attachments -// See Also: attachable(), attach(), orient() -// Usage: -// PARENT() orient(anchor, [spin]) CHILDREN; -// Description: -// Orients children such that their top is tilted in the direction of the specified parent anchor point. -// The `$align` variable can help you anchor the child, aligned with the edges of the parent, based -// on the position specified by a parent {{position()}} module. See {{position()}} for examples using `$align`. -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// anchor = The anchor on the parent which you want to match the orientation of. -// spin = The spin to add to the children. (Overrides anchor spin.) -// Side Effects: -// `$attach_anchor` is set to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for the `anchor=`, if given. -// `$attach_to` is set to `undef`. -// `$attach_norot` is set to `true`. -// `$align` is set to the anchor that will position the child flush on the parent at a designated {{position()}} -// -// Example: When orienting to an anchor, the spin of the anchor may cause confusion: -// prismoid([50,50],[30,30],h=40) { -// position(TOP+RIGHT) -// orient(RIGHT) -// prismoid([30,30],[0,5],h=20,anchor=BOT+LEFT); -// } -// Example: You can override anchor spin with `spin=`. -// prismoid([50,50],[30,30],h=40) { -// position(TOP+RIGHT) -// orient(RIGHT,spin=0) -// prismoid([30,30],[0,5],h=20,anchor=BOT+LEFT); -// } -// Example: Or you can anchor the child from the back -// prismoid([50,50],[30,30],h=40) { -// position(TOP+RIGHT) -// orient(RIGHT) -// prismoid([30,30],[0,5],h=20,anchor=BOT+BACK); -// } -module orient(anchor, spin) { - req_children($children); - check= - assert($parent_geom != undef, "No parent to orient from!") - assert(is_string(anchor) || is_vector(anchor)); - anch = _find_anchor(anchor, $parent_geom); - two_d = _attach_geom_2d($parent_geom); - fromvec = two_d? BACK : UP; - spin = default(spin, anch[3]); - assert(is_finite(spin)); - - $align = is_undef($attach_anchor) ? undef - : two_d ? let(newalign=rot(from=anch[2], to=fromvec, p=zrot(-spin,$attach_anchor[0]))) - [sign(newalign.x), -1] - : let(newalign=rot(spin, from=fromvec, to=anch[2], reverse=true, p=$attach_anchor[0])) - [sign(newalign.x), sign(newalign.y), -1]; - $attach_to = undef; - $attach_anchor = anch; - $attach_norot = true; - if (two_d) - rot(spin)rot(from=fromvec, to=anch[2]) children(); - else - rot(spin, from=fromvec, to=anch[2]) children(); -} - - - - - -// Module: attach() -// Synopsis: Attaches children to a parent object at an anchor point and orientation. -// Topics: Attachments -// See Also: attachable(), position(), face_profile(), edge_profile(), corner_profile() -// Usage: -// PARENT() attach(from, [overlap=], [norot=]) CHILDREN; -// PARENT() attach(from, to, [overlap=], [norot=]) CHILDREN; -// 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, as specified by the `$overlap` -// value (0 by default), or by the overriding `overlap=` argument. This is to prevent OpenSCAD -// from making non-manifold objects. You can define `$overlap=` as an argument in a parent -// module to set the default for all attachments to it. For a step-by-step explanation of -// attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// from = The vector, or name of the parent anchor point to attach to. -// to = Optional name of the child anchor point. If given, orients the child such that the named anchors align together rotationally. -// --- -// overlap = Amount to sink child into the parent. Equivalent to `down(X)` after the attach. This defaults to the value in `$overlap`, which is `0` by default. -// norot = If true, don't rotate children when attaching to the anchor point. Only translate to the anchor point. -// Side Effects: -// `$idx` is set to the index number of each anchor if a list of anchors is given. Otherwise is set to `0`. -// `$attach_anchor` for each `from=` anchor given, this is set to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. -// `$attach_to` is set to the value of the `to=` argument, if given. Otherwise, `undef` -// `$attach_norot` is set to the value of the `norot=` argument. -// Example: -// spheroid(d=20) { -// attach(TOP) down(1.5) cyl(l=11.5, d1=10, d2=5, anchor=BOTTOM); -// attach(RIGHT, BOTTOM) down(1.5) cyl(l=11.5, d1=10, d2=5); -// attach(FRONT, BOTTOM, overlap=1.5) cyl(l=11.5, d1=10, d2=5); -// } -module attach(from, to, overlap, norot=false) -{ - req_children($children); - assert($parent_geom != undef, "No object to attach to!"); - overlap = (overlap!=undef)? overlap : $overlap; - anchors = (is_vector(from)||is_string(from))? [from] : from; - for ($idx = idx(anchors)) { - anchr = anchors[$idx]; - anch = _find_anchor(anchr, $parent_geom); - two_d = _attach_geom_2d($parent_geom); - $attach_to = to; - $attach_anchor = anch; - $attach_norot = norot; - olap = two_d? [0,-overlap,0] : [0,0,-overlap]; - if (norot || (norm(anch[2]-UP)<1e-9 && anch[3]==0)) { - translate(anch[1]) translate(olap) children(); - } else { - fromvec = two_d? BACK : UP; - translate(anch[1]) rot(anch[3],from=fromvec,to=anch[2]) translate(olap) children(); - } - } -} - -// Section: Tagging - -// Module: tag() -// Synopsis: Assigns a tag to an object -// Topics: Attachments -// See Also: force_tag(), recolor(), hide(), show_only(), diff(), intersect() -// Usage: -// PARENT() tag(tag) CHILDREN; -// Description: -// Assigns the specified tag to all of the children. Note that if you want -// to apply a tag to non-tag-aware objects you need to use {{force_tag()}} instead. -// This works by setting the `$tag` variable, but it provides extra error checking and -// handling of scopes. You may set `$tag` directly yourself, but this is not recommended. -// . -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// tag = tag string, which must not contain any spaces. -// Side Effects: -// Sets `$tag` to the tag you specify, possibly with a scope prefix. -// Example(3D): Applies the tag to both cuboids instead of having to repeat `$tag="remove"` for each one. -// diff("remove") -// cuboid(10){ -// position(TOP) cuboid(3); -// tag("remove") -// { -// position(FRONT) cuboid(3); -// position(RIGHT) cuboid(3); -// } -// } -module tag(tag) -{ - req_children($children); - check= - assert(is_string(tag),"tag must be a string") - assert(undef==str_find(tag," "),str("Tag string \"",tag,"\" contains a space, which is not allowed")); - $tag = str($tag_prefix,tag); - children(); -} - - -// Module: force_tag() -// Synopsis: Assigns a tag to a non-attachable object. -// Topics: Attachments -// See Also: tag(), recolor(), hide(), show_only(), diff(), intersect() -// Usage: -// PARENT() force_tag([tag]) CHILDREN; -// Description: -// You use this module when you want to make a non-attachable or non-BOSL2 module respect tags. -// It applies to its children the tag specified (or the tag currently in force if you don't specify a tag), -// making a final determination about whether to show or hide the children. -// This means that tagging in children's children will be ignored. -// This module is specifically provided for operating on children that are not tag aware such as modules -// that don't use {{attachable()}} or built in modules such as -// - `polygon()` -// - `projection()` -// - `polyhedron()` (or use [`vnf_polyhedron()`](vnf.scad#vnf_polyhedron)) -// - `linear_extrude()` (or use [`linear_sweep()`](regions.scad#linear_sweep)) -// - `rotate_extrude()` -// - `surface()` -// - `import()` -// - `difference()` -// - `intersection()` -// - `hull()` -// . -// When you use tag-based modules like {{diff()}} with a non-attachable module, the result may be puzzling. -// Any time a test occurs for display of child() that test will succeed. This means that when diff() checks -// to see if it should show a module it will show it, and when diff() checks to see if it should subtract the module -// it will subtract it. The result will be a hole, possibly with zero-thickness edges or faces. In order to -// get the correct behavior, every non-attachable module needs an invocation of force_tag, even ones -// that are not tagged. -// . -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// tag = tag string, which must not contain any spaces -// Side Effects: -// Sets `$tag` to the tag you specify, possibly with a scope prefix. -// Example(2D): This example produces the full square without subtracting the "remove" item. When you use non-attachable modules with tags, results are unpredictable. -// diff() -// { -// polygon(square(10)); -// move(-[.01,.01])polygon(square(5),$tag="remove"); -// } -// Example(2D): Adding force_tag() fixes the model. Note you need to add it to *every* non-attachable module, even the untagged ones, as shown here. -// diff() -// { -// force_tag() -// polygon(square(10)); -// force_tag("remove") -// move(-[.01,.01])polygon(square(5)); -// } -module force_tag(tag) -{ - req_children($children); - check1=assert(is_undef(tag) || is_string(tag),"tag must be a string"); - $tag = str($tag_prefix,default(tag,$tag)); - assert(undef==str_find($tag," "),str("Tag string \"",$tag,"\" contains a space, which is not allowed")); - if(_is_shown()) - show_all() - children(); -} - - - -// Module: default_tag() -// Synopsis: Sets a default tag for all children. -// Topics: Attachments -// See Also: force_tag(), recolor(), hide(), show_only(), diff(), intersect() -// Usage: -// PARENT() default_tag(tag) CHILDREN; -// Description: -// Sets a default tag for all of the children. This is intended to be used to set a tag for a whole module -// that is then used outside the module, such as setting the tag to "remove" for easy operation with {{diff()}}. -// The default_tag() module sets the `$tag` variable only if it is not already -// set so you can have a module set a default tag of "remove" but that tag can be overridden by a {{tag()}} -// in force from a parent. If you use {{tag()}} it will override any previously -// specified tag from a parent, which can be very confusing to a user trying to change the tag on a module. -// . -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// tag = tag string, which must not contain any spaces. -// Side Effects: -// Sets `$tag` to the tag you specify, possibly with a scope prefix. -// Example(3D): The module thing() is defined with {{tag()}} and the user applied tag of "keep_it" is ignored, leaving the user puzzled. -// module thing() { tag("remove") cuboid(10);} -// diff() -// cuboid(20){ -// position(TOP) thing(); -// position(RIGHT) tag("keep_it") thing(); -// } -// Example(3D): Using default_tag() fixes this problem: the user applied tag does not get overridden by the tag hidden in the module definition. -// module thing() { default_tag("remove") cuboid(10);} -// diff() -// cuboid(20){ -// position(TOP) thing(); -// position(RIGHT) tag("keep_it") thing(); -// } -module default_tag(tag) -{ - if ($tag=="") tag(tag) children(); - else children(); -} - - -// Module: tag_scope() -// Synopsis: Creates a new tag scope. -// See Also: tag(), force_tag(), default_tag() -// Topics: Attachments -// Usage: -// tag_scope([scope]) CHILDREN; -// Description: -// Creates a tag scope with locally altered tag names to avoid tag name conflict with other code. -// This is necessary when writing modules because the module's caller might happen to use the same tags. -// Note that if you directly set the `$tag` variable then tag scoping will not work correctly. -// Side Effects: -// `$tag_prefix` is set to the value of `scope=` if given, otherwise is set to a random string. -// Example: In this example the ring module uses "remove" tags which will conflict with use of the same tags by the parent. -// module ring(r,h,w=1,anchor,spin,orient) -// { -// tag_scope("ringscope") -// attachable(anchor,spin,orient,r=r,h=h){ -// diff() -// cyl(r=r,h=h) -// tag("remove") cyl(r=r-w,h=h+1); -// children(); -// } -// } -// // Calling the module using "remove" tags -// // will conflict with internal tag use in -// // the ring module. -// $fn=32; -// diff(){ -// ring(10,7,w=4); -// tag("remove")ring(8,8); -// tag("remove")diff("rem"){ -// ring(9.5,8,w=1); -// tag("rem")ring(9.5,8,w=.3); -// } -// } -module tag_scope(scope){ - req_children($children); - scope = is_undef(scope) ? rand_str(20) : scope; - assert(is_string(scope), "scope must be a string"); - assert(undef==str_find(scope," "),str("Scope string \"",scope,"\" contains a space, which is not allowed")); - $tag_prefix=scope; - children(); -} - - -// Section: Attachment Modifiers - -// Module: diff() -// Synopsis: Performs a differencing operation using tags rather than hierarchy to control what happens. -// Topics: Attachments -// See Also: tag(), force_tag(), recolor(), show_only(), hide(), tag_diff(), intersect(), tag_intersect() -// Usage: -// diff([remove], [keep]) PARENT() CHILDREN; -// Description: -// Performs a differencing operation using tags to control what happens. This is specifically intended to -// address the situation where you want differences between a parent and child object, something -// that is impossible with the native difference() module. -// The children to diff are grouped into three categories, regardless of nesting level. -// The `remove` argument is a space delimited list of tags specifying objects to -// subtract. The `keep` argument is a similar list of tags giving objects to be kept. -// Objects not matching either the `remove` or `keep` lists form the third category of base objects. -// To produce its output, diff() forms the union of all the base objects and then -// subtracts all the objects with tags in `remove`. Finally it adds in objects listed in `keep`. -// Attachable objects should be tagged using {{tag()}} -// and non-attachable objects with {{force_tag()}}. -// . -// Remember when using tagged operations with that the operations don't happen in hierarchical order, since -// the point of tags is to break the hierarchy. If you tag an object with a keep tag, nothing will be -// subtracted from it, no matter where it appears because kept objects are unioned in at the end. -// If you want a child of an object tagged with a remove tag to stay in the model it may be -// better to give it a tag that is not a remove tag or a keep tag. Such an object *will* be subject to -// subtractions from other remove-tagged objects. -// . -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// remove = String containing space delimited set of tag names of children to difference away. Default: `"remove"` -// keep = String containing space delimited set of tag names of children to keep; that is, to union into the model after differencing is completed. Default: `"keep"` -// Example: Diffing using default tags -// diff() -// cuboid(50) { -// tag("remove") attach(TOP) sphere(d=40); -// tag("keep") attach(CTR) cylinder(h=40, d=10); -// } -// Example: The "hole" items are subtracted from everything else. The other tags can be anything you find convenient. -// diff("hole") -// tag("body")sphere(d=100) { -// tag("pole") zcyl(d=55, h=100); // attach() not needed for center-to-center. -// tag("hole") { -// xcyl(d=55, h=101); -// ycyl(d=55, h=101); -// } -// tag("axle")zcyl(d=15, h=140); -// } -// Example: -// diff(keep="axle") -// sphere(d=100) { -// tag("axle")xcyl(d=40, l=120); -// tag("remove")cuboid([40,120,100]); -// } -// Example: Masking -// diff() -// cube([80,90,100], center=true) { -// edge_mask(FWD) -// rounding_edge_mask(l=max($parent_size)*1.01, r=25); -// } -// Example: Here we subtract the parent object from the child. Because tags propagate to children we need to clear the "remove" tag from the child. -// diff() -// tag("remove")cuboid(10) -// tag("")position(RIGHT+BACK)cyl(r=8,h=9); -// Example(3D,VPR=[104,0,200], VPT=[-0.9,3.03, -0.74], VPD=19,NoAxes,NoScales): A pipe module that subtracts its interior when you call it using diff(). Normally if you union two pipes together, you'll get interfering walls at the intersection, but not here: -// $fn=16; -// // This module must be called by subtracting with "diff" -// module pipe(length, od, id) { -// // Strip the tag the user is using to subtract -// tag("")cylinder(h=length, d=od, center=true); -// // Leave the tag along here, so this one is removed -// cylinder(h=length+.02, d=id, center=true); -// } -// // Draw some intersecting pipes -// diff(){ -// tag("remove"){ -// pipe(length=5, od=2, id=1.9); -// zrot(10)xrot(75) -// pipe(length=5, od=2, id=1.9); -// } -// // The orange bar has its center removed -// color("orange") down(1) xcyl(h=8, d=1); -// // "keep" prevents interior of the blue bar intact -// tag("keep") recolor("blue") up(1) xcyl(h=8, d=1); -// } -// // Objects outside the diff don't have pipe interiors removed -// color("purple") down(2.2) ycyl(h=8, d=0.3); -// Example(3D,NoScales,NoAxes): Nested diff() calls work as expected, but be careful of reusing tag names, even hidden in submodules. -// $fn=32; -// diff("rem1") -// cyl(r=10,h=10){ -// diff("rem2",$tag="rem1"){ -// cyl(r=8,h=11); -// tag("rem2")diff("rem3"){ -// cyl(r=6,h=12); -// tag("rem3")cyl(r=4,h=13); -// } -// } -// } -// Example: This example shows deep nesting, where all the differences cross levels. Unlike the preceding example, each cylinder is positioned relative to its parent. Note that it suffices to use two remove tags, alternating between them at each level. -// $fn=32; -// diff("remA") -// cyl(r=9, h=6) -// tag("remA")diff("remB") -// left(.2)position(RIGHT)cyl(r=8,h=7,anchor=RIGHT) -// tag("remB")diff("remA") -// left(.2)position(LEFT)cyl(r=7,h=7,anchor=LEFT) -// tag("remA")diff("remB") -// left(.2)position(LEFT)cyl(r=6,h=8,anchor=LEFT) -// tag("remB")diff("remA") -// right(.2)position(RIGHT)cyl(r=5,h=9,anchor=RIGHT) -// tag("remA")diff("remB") -// right(.2)position(RIGHT)cyl(r=4,h=10,anchor=RIGHT) -// tag("remB")left(.2)position(LEFT)cyl(r=3,h=11,anchor=LEFT); -// Example(3D,NoAxes,NoScales): When working with Non-Attachables like rotate_extrude() you must apply {{force_tag()}} to every non-attachable object. -// back_half() -// diff("remove") -// cuboid(40) { -// attach(TOP) -// recolor("lightgreen") -// cyl(l=10,d=30); -// position(TOP+RIGHT) -// force_tag("remove") -// xrot(90) -// rotate_extrude() -// right(20) -// circle(5); -// } -// Example: Here is another example where two children are intersected using the native intersection operator, and then tagged with {{force_tag()}}. Note that because the children are at the same level, you don't need to use a tagged operator for their intersection. -// $fn=32; -// diff() -// cuboid(10){ -// force_tag("remove")intersection() -// { -// position(RIGHT) cyl(r=7,h=15); -// position(LEFT) cyl(r=7,h=15); -// } -// tag("keep")cyl(r=1,h=9); -// } -// Example: In this example the children that are subtracted are each at different nesting levels, with a kept object in between. -// $fn=32; -// diff() -// cuboid(10){ -// tag("remove")cyl(r=4,h=11) -// tag("keep")cyl(r=3,h=17) -// tag("remove")position(RIGHT)cyl(r=2,h=18); -// } -// Example: Combining tag operators can be tricky. Here the `diff()` operation keeps two tags, "fullkeep" and "keep". Then {{intersect()}} intersects the "keep" tagged item with everything else, but keeps the "fullkeep" object. -// $fn=32; -// intersect("keep","fullkeep") -// diff(keep="fullkeep keep") -// cuboid(10){ -// tag("remove")cyl(r=4,h=11); -// tag("keep") position(RIGHT)cyl(r=8,h=12); -// tag("fullkeep")cyl(r=1,h=12); -// } -// Example: In this complex example we form an intersection, subtract an object, and keep some objects. Note that for the small cylinders on either side, marking them as "keep" or removing their tag gives the same effect. This is because without a tag they become part of the intersection and the result ends up the same. For the two cylinders at the back, however, the result is different. With "keep" the cylinder on the left appears whole, but without it, the cylinder at the back right is subject to intersection. -// $fn=64; -// diff() -// intersect(keep="remove keep") -// cuboid(10,$thing="cube"){ -// tag("intersect"){ -// position(RIGHT) cyl(r=5.5,h=15) -// tag("")cyl(r=2,h=10); -// position(LEFT) cyl(r=5.54,h=15) -// tag("keep")cyl(r=2,h=10); -// } -// // Untagged it is in the intersection -// tag("") position(BACK+RIGHT) -// cyl(r=2,h=10,anchor=CTR); -// // With keep the full cylinder appears -// tag("keep") position(BACK+LEFT) -// cyl(r=2,h=10,anchor=CTR); -// tag("remove") cyl(r=3,h=15); -// } -module diff(remove="remove", keep="keep") -{ - req_children($children); - assert(is_string(remove),"remove must be a string of tags"); - assert(is_string(keep),"keep must be a string of tags"); - if (_is_shown()) - { - difference() { - hide(str(remove," ",keep)) children(); - show_only(remove) children(); - } - } - show_int(keep)children(); -} - - -// Module: tag_diff() -// Synopsis: Performs a {{diff()}} and then sets a tag on the result. -// Topics: Attachments -// See Also: tag(), force_tag(), recolor(), show_only(), hide(), diff(), intersect(), tag_intersect() -// Usage: -// tag_diff(tag, [remove], [keep]) PARENT() CHILDREN; -// Description: -// Perform a differencing operation in the manner of {{diff()}} using tags to control what happens, -// and then tag the resulting difference object with the specified tag. This forces the specified -// tag to be resolved at the level of the difference operation. In most cases, this is not necessary, -// but if you have kept objects and want to operate on this difference object as a whole object using -// more tag operations, you will probably not get the results you want if you simply use {{tag()}}. -// . -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// tag = Tag string to apply to this difference object -// remove = String containing space delimited set of tag names of children to difference away. Default: `"remove"` -// keep = String containing space delimited set of tag names of children to keep; that is, to union into the model after differencing is completed. Default: `"keep"` -// Side Effects: -// Sets `$tag` to the tag you specify, possibly with a scope prefix. -// Example: In this example we have a difference with a kept object that is then subtracted from a cube, but we don't want the kept object to appear in the final output, so this result is wrong: -// diff("rem"){ -// cuboid([20,10,30],anchor=FRONT); -// tag("rem")diff("remove","keep"){ -// cuboid([10,10,20]); -// tag("remove")cuboid([11,11,5]); -// tag("keep")cuboid([2,2,20]); -// } -// } -// Example: Using tag_diff corrects the problem: -// diff("rem"){ -// cuboid([20,10,30],anchor=FRONT); -// tag_diff("rem","remove","keep"){ -// cuboid([10,10,20]); -// tag("remove")cuboid([11,11,5]); -// tag("keep")cuboid([2,2,20]); -// } -// } -// Example: This concentric cylinder example uses "keep" and produces the wrong result. The kept cylinder gets kept in the final output instead of subtracted. This happens even when we make sure to change the `keep` argument at the top level {{diff()}} call. -// diff("rem","nothing") -// cyl(r=8,h=6) -// tag("rem")diff() -// cyl(r=7,h=7) -// tag("remove")cyl(r=6,h=8) -// tag("keep")cyl(r=5,h=9); -// Example: Changing to tag_diff() causes the kept cylinder to be subtracted, producing the desired result: -// diff("rem") -// cyl(r=8,h=6) -// tag_diff("rem") -// cyl(r=7,h=7) -// tag("remove")cyl(r=6,h=8) -// tag("keep")cyl(r=5,h=9); -module tag_diff(tag,remove="remove", keep="keep") -{ - req_children($children); - assert(is_string(remove),"remove must be a string of tags"); - assert(is_string(keep),"keep must be a string of tags"); - assert(is_string(tag),"tag must be a string"); - assert(undef==str_find(tag," "),str("Tag string \"",tag,"\" contains a space, which is not allowed")); - $tag=str($tag_prefix,tag); - if (_is_shown()) - show_all(){ - difference() { - hide(str(remove," ",keep)) children(); - show_only(remove) children(); - } - show_only(keep)children(); - } -} - - -// Module: intersect() -// Synopsis: Perform an intersection operation on children using tags rather than hierarchy to control what happens. -// Topics: Attachments -// See Also: tag(), force_tag(), recolor(), show_only(), hide(), diff(), tag_diff(), tag_intersect() -// Usage: -// intersect([intersect], [keep]) PARENT() CHILDREN; -// Description: -// Performs an intersection operation on its children, using tags to -// determine what happens. This is specifically intended to address -// the situation where you want intersections involving a parent and -// child object, something that is impossible with the native -// intersection() module. This module treats the children in three -// groups: objects matching the tags listed in `intersect`, objects -// matching tags listed in `keep`, and the remaining objects that -// don't match any of the listed tags. The intersection is computed -// between the union of the `intersect` tagged objects and union of the objects that don't -// match any of the listed tags. Finally the objects listed in `keep` are -// unioned with the result. Attachable objects should be tagged using {{tag()}} -// and non-attachable objects with {{force_tag()}}. -// . -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// intersect = String containing space delimited set of tag names of children to intersect. Default: "intersect" -// keep = String containing space delimited set of tag names of children to keep whole. Default: "keep" -// Example: -// intersect("mask", keep="axle") -// sphere(d=100) { -// tag("mask")cuboid([40,100,100]); -// tag("axle")xcyl(d=40, l=100); -// } -// Example: Combining tag operators can be tricky. Here the {{diff()}} operation keeps two tags, "fullkeep" and "keep". Then `intersect()` intersects the "keep" tagged item with everything else, but keeps the "fullkeep" object. -// $fn=32; -// intersect("keep","fullkeep") -// diff(keep="fullkeep keep") -// cuboid(10){ -// tag("remove")cyl(r=4,h=11); -// tag("keep") position(RIGHT)cyl(r=8,h=12); -// tag("fullkeep")cyl(r=1,h=12); -// } -// Example: In this complex example we form an intersection, subtract an object, and keep some objects. Note that for the small cylinders on either side, marking them as "keep" or removing their tag gives the same effect. This is because without a tag they become part of the intersection and the result ends up the same. For the two cylinders at the back, however, the result is different. With "keep" the cylinder on the left appears whole, but without it, the cylinder at the back right is subject to intersection. -// $fn=64; -// diff() -// intersect(keep="remove keep") -// cuboid(10,$thing="cube"){ -// tag("intersect"){ -// position(RIGHT) cyl(r=5.5,h=15) -// tag("")cyl(r=2,h=10); -// position(LEFT) cyl(r=5.54,h=15) -// tag("keep")cyl(r=2,h=10); -// } -// // Untagged it is in the intersection -// tag("") position(BACK+RIGHT) -// cyl(r=2,h=10,anchor=CTR); -// // With keep the full cylinder appears -// tag("keep") position(BACK+LEFT) -// cyl(r=2,h=10,anchor=CTR); -// tag("remove") cyl(r=3,h=15); -// } -module intersect(intersect="intersect",keep="keep") -{ - assert(is_string(intersect),"intersect must be a string of tags"); - assert(is_string(keep),"keep must be a string of tags"); - intersection(){ - show_only(intersect) children(); - hide(str(intersect," ",keep)) children(); - } - show_int(keep) children(); -} - - -// Module: tag_intersect() -// Synopsis: Performs an {{intersect()}} and then tags the result. -// Topics: Attachments -// See Also: tag(), force_tag(), recolor(), show_only(), hide(), diff(), tag_diff(), intersect() -// Usage: -// tag_intersect(tag, [intersect], [keep]) PARENT() CHILDREN; -// Description: -// Perform an intersection operation in the manner of {{intersect()}} using tags to control what happens, -// and then tag the resulting difference object with the specified tag. This forces the specified -// tag to be resolved at the level of the intersect operation. In most cases, this is not necessary, -// but if you have kept objects and want to operate on this difference object as a whole object using -// more tag operations, you will probably not get the results you want if you simply use {{tag()}}. -// . -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// tag = Tag to set for the intersection -// intersect = String containing space delimited set of tag names of children to intersect. Default: "intersect" -// keep = String containing space delimited set of tag names of children to keep whole. Default: "keep" -// Side Effects: -// Sets `$tag` to the tag you specify, possibly with a scope prefix. -// Example: Without `tag_intersect()` the kept object is not included in the difference. -// $fn=32; -// diff() -// cuboid([20,15,9]) -// tag("remove")intersect() -// cuboid(10){ -// tag("intersect")position(RIGHT) cyl(r=7,h=10); -// tag("keep")position(LEFT)cyl(r=4,h=10); -// } -// Example: Using tag_intersect corrects the problem. -// $fn=32; -// diff() -// cuboid([20,15,9]) -// tag_intersect("remove") -// cuboid(10){ -// tag("intersect")position(RIGHT) cyl(r=7,h=10); -// tag("keep")position(LEFT)cyl(r=4,h=10); -// } -module tag_intersect(tag,intersect="intersect",keep="keep") -{ - assert(is_string(intersect),"intersect must be a string of tags"); - assert(is_string(keep),"keep must be a string of tags"); - assert(is_string(tag),"tag must be a string"); - assert(undef==str_find(tag," "),str("Tag string \"",tag,"\" contains a space, which is not allowed")); - $tag=str($tag_prefix,tag); - if (_is_shown()) - show_all(){ - intersection(){ - show_only(intersect) children(); - hide(str(intersect," ",keep)) children(); - } - show_only(keep) children(); - } -} - - -// Module: conv_hull() -// Synopsis: Performs a hull operation on the children using tags to determine what happens. -// Topics: Attachments -// See Also: tag(), recolor(), show_only(), hide(), diff(), intersect() -// Usage: -// conv_hull([keep]) CHILDREN; -// Description: -// Performs a hull operation on the children using tags to determine what happens. The items -// not tagged with the `keep` tags are combined into a convex hull, and the children tagged with the keep tags -// are unioned with the result. -// . -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// keep = String containing space delimited set of tag names of children to keep out of the hull. Default: "keep" -// Example: -// conv_hull("keep") -// sphere(d=100, $fn=64) { -// cuboid([40,90,90]); -// tag("keep")xcyl(d=40, l=120); -// } -// Example: difference combined with hull where all objects are relative to each other. -// $fn=32; -// diff() -// conv_hull("remove") -// cuboid(10) -// position(RIGHT+BACK)cyl(r=4,h=10) -// tag("remove")cyl(r=2,h=12); -module conv_hull(keep="keep") -{ - req_children($children); - assert(is_string(keep),"keep must be a string of tags"); - if (_is_shown()) - hull() hide(keep) children(); - show_int(keep) children(); -} - - -// Module: tag_conv_hull() -// Synopsis: Performs a {{conv_hull()}} and then sets a tag on the result. -// Topics: Attachments -// See Also: tag(), recolor(), show_only(), hide(), diff(), intersect() -// Usage: -// tag_conv_hull(tag, [keep]) CHILDREN; -// Description: -// Perform a convex hull operation in the manner of {{conv_hull()}} using tags to control what happens, -// and then tag the resulting hull object with the specified tag. This forces the specified -// tag to be resolved at the level of the hull operation. In most cases, this is not necessary, -// but if you have kept objects and want to operate on the hull object as a whole object using -// more tag operations, you will probably not get the results you want if you simply use {{tag()}}. -// . -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// keep = String containing space delimited set of tag names of children to keep out of the hull. Default: "keep" -// Side Effects: -// Sets `$tag` to the tag you specify, possibly with a scope prefix. -// Example: With a regular tag, the kept object is not handled as desired: -// diff(){ -// cuboid([30,30,9]) -// tag("remove")conv_hull("remove") -// cuboid(10,anchor=LEFT+FRONT){ -// position(RIGHT+BACK)cyl(r=4,h=10); -// tag("keep")position(FRONT+LEFT)cyl(r=4,h=10); -// } -// } -// Example: Using `tag_conv_hull()` fixes the problem: -// diff(){ -// cuboid([30,30,9]) -// tag_conv_hull("remove") -// cuboid(10,anchor=LEFT+FRONT){ -// position(RIGHT+BACK)cyl(r=4,h=10); -// tag("keep")position(FRONT+LEFT)cyl(r=4,h=10); -// } -// } -module tag_conv_hull(tag,keep="keep") -{ - req_children($children); - assert(is_string(keep),"keep must be a string of tags"); - assert(is_string(tag),"tag must be a string"); - assert(undef==str_find(tag," "),str("Tag string \"",tag,"\" contains a space, which is not allowed")); - $tag=str($tag_prefix,tag); - if (_is_shown()) - show_all(){ - hull() hide(keep) children(); - show_only(keep) children(); - } -} - - -// Module: hide() -// Synopsis: Hides attachable children with the given tags. -// Topics: Attachments -// See Also: tag(), recolor(), show_only(), show_all(), show_int(), diff(), intersect() -// Usage: -// hide(tags) CHILDREN; -// Description: -// Hides all attachable children with the given tags, which you supply as a space separated string. Previously hidden objects remain hidden, so hiding is cumulative, unlike `show_only()`. -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Side Effects: -// Sets `$tags_hidden` to include the tags you specify. -// Example: Hides part of the model. -// hide("A") -// tag("main") cube(50, anchor=CENTER, $tag="Main") { -// tag("A")attach(LEFT, BOTTOM) cylinder(d=30, h=30); -// tag("B")attach(RIGHT, BOTTOM) cylinder(d=30, h=30); -// } -// Example: Use an invisible parent to position children. Note that children must be retagged because they inherit the parent tag. -// $fn=16; -// hide("hidden") -// tag("hidden")cuboid(10) -// tag("visible") { -// position(RIGHT) cyl(r=1,h=12); -// position(LEFT) cyl(r=1,h=12); -// } -module hide(tags) -{ - req_children($children); - dummy=assert(is_string(tags), "tags must be a string"); - taglist = [for(s=str_split(tags," ",keep_nulls=false)) str($tag_prefix,s)]; - $tags_hidden = concat($tags_hidden,taglist); - children(); -} - - -// Module: show_only() -// Synopsis: Show only the children with the listed tags. -// See Also: tag(), recolor(), show_all(), show_int(), diff(), intersect() -// Topics: Attachments -// Usage: -// show_only(tags) CHILDREN; -// Description: -// Show only the children with the listed tags, which you sply as a space separated string. Only unhidden objects will be shown, so if an object is hidden either before or after the `show_only()` call then it will remain hidden. This overrides any previous `show_only()` calls. Unlike `hide()`, calls to `show_only()` are not cumulative. -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Side Effects: -// Sets `$tags_shown` to the tag you specify. -// Example: Display the attachments but not the parent -// show_only("visible") -// cube(50, anchor=CENTER) -// tag("visible"){ -// attach(LEFT, BOTTOM) cylinder(d=30, h=30); -// attach(RIGHT, BOTTOM) cylinder(d=30, h=30); -// } -module show_only(tags) -{ - req_children($children); - dummy=assert(is_string(tags), str("tags must be a string",tags)); - taglist = [for(s=str_split(tags," ",keep_nulls=false)) str($tag_prefix,s)]; - $tags_shown = taglist; - children(); -} - -// Module: show_all() -// Synopsis: Shows all children and clears tags. -// See Also: tag(), recolor(), show_only(), show_int(), diff(), intersect() -// Topics: Attachments -// Usage; -// show_all() CHILDREN; -// Description: -// Shows all children. Clears the list of hidden tags and shown tags so that all child objects will be -// fully displayed. -// Side Effects: -// Sets `$tags_shown="ALL"` -// Sets `$tags_hidden=[]` -module show_all() -{ - req_children($children); - $tags_shown="ALL"; - $tags_hidden=[]; - children(); -} - - -// Module: show_int() -// Synopsis: Shows children with the listed tags which were already shown in the parent context. -// See Also: tag(), recolor(), show_only(), show_all(), show_int(), diff(), intersect() -// Topics: Attachments -// Usage: -// show_int(tags) CHILDREN; -// Description: -// Show only the children with the listed tags which were already shown in the parent context. -// This intersects the current show list with the list of tags you provide. -// Arguments: -// tags = list of tags to show -// Side Effects: -// Sets `$tags_shown` -module show_int(tags) -{ - req_children($children); - dummy=assert(is_string(tags), str("tags must be a string",tags)); - taglist = [for(s=str_split(tags," ",keep_nulls=false)) str($tag_prefix,s)]; - $tags_shown = $tags_shown == "ALL" ? taglist : set_intersection($tags_shown,taglist); - children(); -} - - -// Section: Mask Attachment - - -// Module: face_mask() -// Synopsis: Ataches a 3d mask shape to the given faces of the parent. -// Topics: Attachments, Masking -// See Also: attachable(), position(), attach(), edge_mask(), corner_mask(), face_profile(), edge_profile(), corner_profile() -// Usage: -// PARENT() face_mask(faces) CHILDREN; -// Description: -// Takes a 3D mask shape, and attaches it to the given faces, with the appropriate orientation to be -// differenced away. The mask shape should be vertically oriented (Z-aligned) with the bottom half -// (Z-) shaped to be diffed away from the face of parent attachable shape. If no tag is set then -// `face_mask()` sets the tag for children to "remove" so that it will work with the default {{diff()}} tag. -// For details on specifying the faces to mask see [Specifying Faces](attachments.scad#subsection-specifying-faces). -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// edges = Faces to mask. See [Specifying Faces](attachments.scad#subsection-specifying-faces) for information on specifying faces. Default: All faces -// Side Effects: -// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. -// `$idx` is set to the index number of each face in the list of faces given. -// `$attach_anchor` is set for each face given, to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. -// Example: -// diff() -// cylinder(r=30, h=60) -// face_mask(TOP) { -// rounding_cylinder_mask(r=30,rounding=5); -// cuboid([5,61,10]); -// } -// Example: Using `$idx` -// diff() -// cylinder(r=30, h=60) -// face_mask([TOP, BOT]) -// zrot(45*$idx) zrot_copies([0,90]) cuboid([5,61,10]); -module face_mask(faces=[LEFT,RIGHT,FRONT,BACK,BOT,TOP]) { - req_children($children); - faces = is_vector(faces)? [faces] : faces; - assert(all([for (face=faces) is_vector(face) && sum([for (x=face) x!=0? 1 : 0])==1]), "Vector in faces doesn't point at a face."); - assert($parent_geom != undef, "No object to attach to!"); - attach(faces) { - if ($tag=="") tag("remove") children(); - else children(); - } -} - - -// Module: edge_mask() -// Synopsis: Attaches a 3D mask shape to the given edges of the parent. -// Topics: Attachments, Masking -// See Also: attachable(), position(), attach(), face_mask(), corner_mask(), face_profile(), edge_profile(), corner_profile() -// Usage: -// PARENT() edge_mask([edges], [except]) CHILDREN; -// Description: -// Takes a 3D mask shape, and attaches it to the given edges, with the appropriate orientation to be -// differenced away. The mask shape should be vertically oriented (Z-aligned) with the back-right -// quadrant (X+Y+) shaped to be diffed away from the edge of parent attachable shape. If no tag is set -// then `edge_mask` sets the tag for children to "remove" so that it will work with the default {{diff()}} tag. -// For details on specifying the edges to mask see [Specifying Edges](attachments.scad#subsection-specifying-edges). -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Figure: A Typical Edge Rounding Mask -// module roundit(l,r) difference() { -// translate([-1,-1,-l/2]) -// cube([r+1,r+1,l]); -// translate([r,r]) -// cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4)); -// } -// roundit(l=30,r=10); -// Arguments: -// edges = Edges to mask. See [Specifying Edges](attachments.scad#subsection-specifying-edges). Default: All edges. -// except = Edges to explicitly NOT mask. See [Specifying Edges](attachments.scad#subsection-specifying-edges). Default: No edges. -// Side Effects: -// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. -// Side Effects: -// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. -// `$idx` is set to the index number of each edge. -// `$attach_anchor` is set for each edge given, to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. -// Example: -// diff() -// cube([50,60,70],center=true) -// edge_mask([TOP,"Z"],except=[BACK,TOP+LEFT]) -// rounding_edge_mask(l=71,r=10); -module edge_mask(edges=EDGES_ALL, except=[]) { - req_children($children); - assert($parent_geom != undef, "No object to attach to!"); - edges = _edges(edges, except=except); - vecs = [ - for (i = [0:3], axis=[0:2]) - if (edges[axis][i]>0) - EDGE_OFFSETS[axis][i] - ]; - for ($idx = idx(vecs)) { - vec = vecs[$idx]; - vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0); - dummy=assert(vcount == 2, "Not an edge vector!"); - anch = _find_anchor(vec, $parent_geom); - $attach_to = undef; - $attach_anchor = anch; - $attach_norot = true; - rotang = - vec.z<0? [90,0,180+v_theta(vec)] : - vec.z==0 && sign(vec.x)==sign(vec.y)? 135+v_theta(vec) : - vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+v_theta(vec)] : - [-90,0,180+v_theta(vec)]; - translate(anch[1]) rot(rotang) - if ($tag=="") tag("remove") children(); - else children(); - } -} - - -// Module: corner_mask() -// Synopsis: Attaches a 3d mask shape to the given corners of the parent. -// Topics: Attachments, Masking -// See Also: attachable(), position(), attach(), face_mask(), edge_mask(), face_profile(), edge_profile(), corner_profile() -// Usage: -// PARENT() corner_mask([corners], [except]) CHILDREN; -// Description: -// Takes a 3D mask shape, and attaches it to the specified corners, with the appropriate orientation to -// be differenced away. The 3D corner mask shape should be designed to mask away the X+Y+Z+ octant. If no tag is set -// then `corner_mask` sets the tag for children to "remove" so that it will work with the default {{diff()}} tag. -// See [Specifying Corners](attachments.scad#subsection-specifying-corners) for information on how to specify corner sets. -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// corners = Corners to mask. See [Specifying Corners](attachments.scad#subsection-specifying-corners). Default: All corners. -// except = Corners to explicitly NOT mask. See [Specifying Corners](attachments.scad#subsection-specifying-corners). Default: No corners. -// Side Effects: -// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. -// `$idx` is set to the index number of each corner. -// `$attach_anchor` is set for each corner given, to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. -// Example: -// diff() -// cube(100, center=true) -// corner_mask([TOP,FRONT],LEFT+FRONT+TOP) -// difference() { -// translate(-0.01*[1,1,1]) cube(20); -// translate([20,20,20]) sphere(r=20); -// } -module corner_mask(corners=CORNERS_ALL, except=[]) { - req_children($children); - assert($parent_geom != undef, "No object to attach to!"); - corners = _corners(corners, except=except); - vecs = [for (i = [0:7]) if (corners[i]>0) CORNER_OFFSETS[i]]; - for ($idx = idx(vecs)) { - vec = vecs[$idx]; - vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0); - dummy=assert(vcount == 3, "Not an edge vector!"); - anch = _find_anchor(vec, $parent_geom); - $attach_to = undef; - $attach_anchor = anch; - $attach_norot = true; - rotang = vec.z<0? - [ 0,0,180+v_theta(vec)-45] : - [180,0,-90+v_theta(vec)-45]; - translate(anch[1]) rot(rotang) - if ($tag=="") tag("remove") children(); - else children(); - } -} - - -// Module: face_profile() -// Synopsis: Extrudes a 2D edge profile into a mask for all edges and corners of the given faces on the parent. -// Topics: Attachments, Masking -// See Also: attachable(), position(), attach(), edge_profile(), corner_profile(), face_mask(), edge_mask(), corner_mask() -// Usage: -// PARENT() face_profile(faces, r|d=, [convexity=]) CHILDREN; -// Description: -// Given a 2D edge profile, extrudes it into a mask for all edges and corners bounding each given face. If no tag is set -// then `face_profile` sets the tag for children to "remove" so that it will work with the default {{diff()}} tag. -// See [Specifying Faces](attachments.scad#subsection-specifying-faces) for information on specifying faces. -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// faces = Faces to mask edges and corners of. -// r = Radius of corner mask. -// --- -// d = Diameter of corner mask. -// excess = Excess length to extrude the profile to make edge masks. Default: 0.01 -// convexity = Max number of times a line could intersect the perimeter of the mask shape. Default: 10 -// Side Effects: -// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. -// `$idx` is set to the index number of each face. -// `$attach_anchor` is set for each edge or corner given, to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. -// `$profile_type` is set to `"edge"` or `"corner"`, depending on what is being masked. -// Example: -// diff() -// cube([50,60,70],center=true) -// face_profile(TOP,r=10) -// mask2d_roundover(r=10); -module face_profile(faces=[], r, d, excess=0.01, convexity=10) { - req_children($children); - faces = is_vector(faces)? [faces] : faces; - assert(all([for (face=faces) is_vector(face) && sum([for (x=face) x!=0? 1 : 0])==1]), "Vector in faces doesn't point at a face."); - r = get_radius(r=r, d=d, dflt=undef); - assert(is_num(r) && r>=0); - edge_profile(faces, excess=excess) children(); - corner_profile(faces, convexity=convexity, r=r) children(); -} - - -// Module: edge_profile() -// Synopsis: Extrudes a 2d edge profile into a mask on the given edges of the parent. -// Topics: Attachments, Masking -// See Also: attachable(), position(), attach(), face_profile(), corner_profile(), edge_mask(), face_mask(), corner_mask() -// Usage: -// PARENT() edge_profile([edges], [except], [convexity]) CHILDREN; -// 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. If no tag is set -// then `edge_profile` sets the tag for children to "remove" so that it will work with the default {{diff()}} tag. -// For details on specifying the edges to mask see [Specifying Edges](attachments.scad#subsection-specifying-edges). -// For a step-by-step -// explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// edges = Edges to mask. See [Specifying Edges](attachments.scad#subsection-specifying-edges). Default: All edges. -// except = Edges to explicitly NOT mask. See [Specifying Edges](attachments.scad#subsection-specifying-edges). Default: No edges. -// excess = Excess length to extrude the profile to make edge masks. Default: 0.01 -// convexity = Max number of times a line could intersect the perimeter of the mask shape. Default: 10 -// Side Effects: -// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. -// `$idx` is set to the index number of each edge. -// `$attach_anchor` is set for each edge given, to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. -// `$profile_type` is set to `"edge"`. -// Example: -// diff() -// cube([50,60,70],center=true) -// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) -// mask2d_roundover(r=10, inset=2); -module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) { - req_children($children); - assert($parent_geom != undef, "No object to attach to!"); - edges = _edges(edges, except=except); - vecs = [ - for (i = [0:3], axis=[0:2]) - if (edges[axis][i]>0) - EDGE_OFFSETS[axis][i] - ]; - for ($idx = idx(vecs)) { - vec = vecs[$idx]; - vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0); - dummy=assert(vcount == 2, "Not an edge vector!"); - anch = _find_anchor(vec, $parent_geom); - $attach_to = undef; - $attach_anchor = anch; - $attach_norot = true; - $profile_type = "edge"; - psize = point3d($parent_size); - length = [for (i=[0:2]) if(!vec[i]) psize[i]][0] + excess; - rotang = - vec.z<0? [90,0,180+v_theta(vec)] : - vec.z==0 && sign(vec.x)==sign(vec.y)? 135+v_theta(vec) : - vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+v_theta(vec)] : - [-90,0,180+v_theta(vec)]; - translate(anch[1]) { - rot(rotang) { - linear_extrude(height=length, center=true, convexity=convexity) { - if ($tag=="") tag("remove") children(); - else children(); - } - } - } - } -} - -// Module: corner_profile() -// Synopsis: Rotationally extrudes a 2d edge profile into corner mask on the given corners of the parent. -// Topics: Attachments, Masking -// See Also: attachable(), position(), attach(), face_profile(), edge_profile(), corner_mask(), face_mask(), edge_mask() -// Usage: -// PARENT() corner_profile([corners], [except], [r=|d=], [convexity=]) CHILDREN; -// 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. If no tag is set then `corner_profile()` -// sets the tag for children to "remove" so that it will work with the default {{diff()}} tag. -// See [Specifying Corners](attachments.scad#subsection-specifying-corners) for information on how to specify corner sets. -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// corners = Corners to mask. See [Specifying Corners](attachments.scad#subsection-specifying-corners). Default: All corners. -// except = Corners to explicitly NOT mask. See [Specifying Corners](attachments.scad#subsection-specifying-corners). Default: No corners. -// --- -// r = Radius of corner mask. -// d = Diameter of corner mask. -// convexity = Max number of times a line could intersect the perimeter of the mask shape. Default: 10 -// Side Effects: -// Tags the children with "remove" (and hence sets $tag) if no tag is already set. -// `$idx` is set to the index number of each corner. -// `$attach_anchor` is set for each corner given, to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. -// `$profile_type` is set to `"corner"`. -// Example: -// diff() -// cuboid([50,60,70],rounding=10,edges="Z",anchor=CENTER) { -// corner_profile(TOP,r=10) -// mask2d_teardrop(r=10, angle=40); -// } -module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) { - assert($parent_geom != undef, "No object to attach to!"); - r = max(0.01, get_radius(r=r, d=d, dflt=undef)); - assert(is_num(r)); - corners = _corners(corners, except=except); - vecs = [for (i = [0:7]) if (corners[i]>0) CORNER_OFFSETS[i]]; - for ($idx = idx(vecs)) { - vec = vecs[$idx]; - vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0); - dummy=assert(vcount == 3, "Not an edge vector!"); - anch = _find_anchor(vec, $parent_geom); - $attach_to = undef; - $attach_anchor = anch; - $attach_norot = true; - $profile_type = "corner"; - rotang = vec.z<0? - [ 0,0,180+v_theta(vec)-45] : - [180,0,-90+v_theta(vec)-45]; - $tag = $tag=="" ? str($tag_prefix,"remove") : $tag; - translate(anch[1]) { - rot(rotang) { - down(0.01) { - linear_extrude(height=r+0.01, center=false) { - difference() { - translate(-[0.01,0.01]) square(r); - translate([r,r]) circle(r=r*0.999); - } - } - } - translate([r,r]) zrot(180) { - rotate_extrude(angle=90, convexity=convexity) { - right(r) xflip() { - children(); - } - } - } - } - } - } -} - - -// Section: Making your objects attachable - - -// Module: attachable() -// Synopsis: Manages the anchoring, spin, orientation, and attachments for an object. -// Topics: Attachments -// See Also: reorient() -// Usage: Square/Trapezoid Geometry -// attachable(anchor, spin, two_d=true, size=, [size2=], [shift=], [override=], ...) {OBJECT; children();} -// Usage: Circle/Oval Geometry -// attachable(anchor, spin, two_d=true, r=|d=, ...) {OBJECT; children();} -// Usage: 2D Path/Polygon Geometry -// attachable(anchor, spin, two_d=true, path=, [extent=], ...) {OBJECT; children();} -// Usage: 2D Region Geometry -// attachable(anchor, spin, two_d=true, region=, [extent=], ...) {OBJECT; children();} -// Usage: Cubical/Prismoidal Geometry -// attachable(anchor, spin, [orient], size=, [size2=], [shift=], ...) {OBJECT; children();} -// Usage: Cylindrical Geometry -// attachable(anchor, spin, [orient], r=|d=, l=, [axis=], ...) {OBJECT; children();} -// Usage: Conical Geometry -// attachable(anchor, spin, [orient], r1=|d1=, r2=|d2=, l=, [axis=], ...) {OBJECT; children();} -// Usage: Spheroid/Ovoid Geometry -// attachable(anchor, spin, [orient], r=|d=, ...) {OBJECT; children();} -// Usage: Extruded Path/Polygon Geometry -// attachable(anchor, spin, path=, l=|h=, [extent=], ...) {OBJECT; children();} -// Usage: Extruded Region Geometry -// attachable(anchor, spin, region=, l=|h=, [extent=], ...) {OBJECT; children();} -// Usage: VNF Geometry -// attachable(anchor, spin, [orient], vnf=, [extent=], ...) {OBJECT; children();} -// Usage: Pre-Specified Geometry -// attachable(anchor, spin, [orient], geom=) {OBJECT; children();} -// -// Description: -// Manages the anchoring, spin, orientation, and attachments for OBJECT, located in a 3D volume or 2D area. -// A managed 3D volume is assumed to be vertically (Z-axis) oriented, and centered. -// A managed 2D area is just assumed to be centered. The shape to be managed is given -// as the first child to this module, and the second child should be given as `children()`. -// For example, to manage a conical shape: -// ```openscad -// attachable(anchor, spin, orient, r1=r1, r2=r2, l=h) { -// cyl(r1=r1, r2=r2, l=h); -// children(); -// } -// ``` -// . -// If this is *not* run as a child of `attach()` with the `to` argument -// given, then the following transformations are performed in order: -// * Translates so the `anchor` point is at the origin (0,0,0). -// * Rotates around the Z axis by `spin` degrees counter-clockwise. -// * Rotates so the top of the part points towards the vector `orient`. -// . -// If this is called as a child of `attach(from,to)`, then the info -// for the anchor points referred to by `from` and `to` are fetched, -// which will include position, direction, and spin. With that info, -// the following transformations are performed: -// * Translates this part so it's anchor position matches the parent's anchor position. -// * Rotates this part so it's anchor direction vector exactly opposes the parent's anchor direction vector. -// * Rotates this part so it's anchor spin matches the parent's anchor spin. -// . -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// -// Arguments: -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` -// --- -// size = If given as a 3D vector, contains the XY size of the bottom of the cuboidal/prismoidal volume, and the Z height. If given as a 2D vector, contains the front X width of the rectangular/trapezoidal shape, and the Y length. -// size2 = If given as a 2D vector, contains the XY size of the top of the prismoidal volume. If given as a number, contains the back width of the trapezoidal shape. -// shift = If given as a 2D vector, shifts the top of the prismoidal or conical shape by the given amount. If given as a number, shifts the back of the trapezoidal shape right by that amount. Default: No shift. -// r = Radius of the cylindrical/conical volume. Can be a scalar, or a list of sizes per axis. -// d = Diameter of the cylindrical/conical volume. Can be a scalar, or a list of sizes per axis. -// r1 = Radius of the bottom of the conical volume. Can be a scalar, or a list of sizes per axis. -// r2 = Radius of the top of the conical volume. Can be a scalar, or a list of sizes per axis. -// d1 = Diameter of the bottom of the conical volume. Can be a scalar, a list of sizes per axis. -// d2 = Diameter of the top of the conical volume. Can be a scalar, a list of sizes per axis. -// l/h = Length of the cylindrical, conical, or extruded path volume along axis. -// vnf = The [VNF](vnf.scad) of the volume. -// path = The path to generate a polygon from. -// region = The region to generate a shape from. -// extent = If true, calculate anchors by extents, rather than intersection, for VNFs and paths. Default: true. -// cp = If given, specifies the centerpoint of the volume. Default: `[0,0,0]` -// offset = If given, offsets the perimeter of the volume around the centerpoint. -// anchors = If given as a list of anchor points, allows named anchor points. -// two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D) -// axis = The vector pointing along the axis of a geometry. Default: UP -// override = Function that takes an anchor and returns a pair `[position,direction]` to use for that anchor to override the normal one. You can also supply a lookup table that is a list of `[anchor, [position, direction]]` entries. If the direction/position that is returned is undef then the default will be used. -// geom = If given, uses the pre-defined (via {{attach_geom()}} geometry. -// -// Side Effects: -// `$parent_anchor` is set to the parent object's `anchor` value. -// `$parent_spin` is set to the parent object's `spin` value. -// `$parent_orient` is set to the parent object's `orient` value. -// `$parent_geom` is set to the parent object's `geom` value. -// `$parent_size` is set to the parent object's cubical `[X,Y,Z]` volume size. -// `$color` is used to set the color of the object -// `$save_color` is used to revert color to the parent's color -// -// Example(NORENDER): Cubical Shape -// attachable(anchor, spin, orient, size=size) { -// cube(size, center=true); -// children(); -// } -// -// Example(NORENDER): Prismoidal Shape -// attachable( -// anchor, spin, orient, -// size=point3d(botsize,h), -// size2=topsize, -// shift=shift -// ) { -// prismoid(botsize, topsize, h=h, shift=shift); -// children(); -// } -// -// Example(NORENDER): Cylindrical Shape, Z-Axis Aligned -// attachable(anchor, spin, orient, r=r, l=h) { -// cyl(r=r, l=h); -// children(); -// } -// -// Example(NORENDER): Cylindrical Shape, Y-Axis Aligned -// attachable(anchor, spin, orient, r=r, l=h, axis=BACK) { -// cyl(r=r, l=h); -// children(); -// } -// -// Example(NORENDER): Cylindrical Shape, X-Axis Aligned -// attachable(anchor, spin, orient, r=r, l=h, axis=RIGHT) { -// cyl(r=r, l=h); -// children(); -// } -// -// Example(NORENDER): Conical Shape, Z-Axis Aligned -// attachable(anchor, spin, orient, r1=r1, r2=r2, l=h) { -// cyl(r1=r1, r2=r2, l=h); -// children(); -// } -// -// Example(NORENDER): Conical Shape, Y-Axis Aligned -// attachable(anchor, spin, orient, r1=r1, r2=r2, l=h, axis=BACK) { -// cyl(r1=r1, r2=r2, l=h); -// children(); -// } -// -// Example(NORENDER): Conical Shape, X-Axis Aligned -// attachable(anchor, spin, orient, r1=r1, r2=r2, l=h, axis=RIGHT) { -// cyl(r1=r1, r2=r2, l=h); -// children(); -// } -// -// Example(NORENDER): Spherical Shape -// attachable(anchor, spin, orient, r=r) { -// sphere(r=r); -// children(); -// } -// -// Example(NORENDER): Extruded Polygon Shape, by Extents -// attachable(anchor, spin, orient, path=path, l=length) { -// linear_extrude(height=length, center=true) -// polygon(path); -// children(); -// } -// -// Example(NORENDER): Extruded Polygon Shape, by Intersection -// attachable(anchor, spin, orient, path=path, l=length, extent=false) { -// linear_extrude(height=length, center=true) -// polygon(path); -// children(); -// } -// -// Example(NORENDER): Arbitrary VNF Shape, by Extents -// attachable(anchor, spin, orient, vnf=vnf) { -// vnf_polyhedron(vnf); -// children(); -// } -// -// Example(NORENDER): Arbitrary VNF Shape, by Intersection -// attachable(anchor, spin, orient, vnf=vnf, extent=false) { -// vnf_polyhedron(vnf); -// children(); -// } -// -// Example(NORENDER): 2D Rectangular Shape -// attachable(anchor, spin, orient, two_d=true, size=size) { -// square(size, center=true); -// children(); -// } -// -// Example(NORENDER): 2D Trapezoidal Shape -// attachable( -// anchor, spin, orient, -// two_d=true, -// size=[x1,y], -// size2=x2, -// shift=shift -// ) { -// trapezoid(w1=x1, w2=x2, h=y, shift=shift); -// children(); -// } -// -// Example(NORENDER): 2D Circular Shape -// attachable(anchor, spin, orient, two_d=true, r=r) { -// circle(r=r); -// children(); -// } -// -// Example(NORENDER): Arbitrary 2D Polygon Shape, by Extents -// attachable(anchor, spin, orient, two_d=true, path=path) { -// polygon(path); -// children(); -// } -// -// Example(NORENDER): Arbitrary 2D Polygon Shape, by Intersection -// attachable(anchor, spin, orient, two_d=true, path=path, extent=false) { -// polygon(path); -// children(); -// } -// -// Example(NORENDER): Using Pre-defined Geometry -// geom = atype=="perim"? attach_geom(two_d=true, path=path, extent=false) : -// atype=="extents"? attach_geom(two_d=true, path=path, extent=true) : -// atype=="circle"? attach_geom(two_d=true, r=r) : -// assert(false, "Bad atype"); -// attachable(anchor, spin, orient, geom=geom) { -// polygon(path); -// children(); -// } -// -// Example: An object can be designed to attach as negative space using {{diff()}}, but if you want an object to include both positive and negative space then you need to call attachable() twice, because tags inside the attachable() call don't work as expected. This example shows how you can call attachable twice to create an object with positive and negative space. Note, however, that children in the negative space are differenced away: the highlighted little cube does not survive into the final model. -// module thing(anchor,spin,orient) { -// tag("remove") attachable(size=[15,15,15],anchor=anchor,spin=spin,orient=orient){ -// cuboid([10,10,16]); -// union(){} // dummy children -// } -// attachable(size=[15,15,15], anchor=anchor, spin=spin, orient=orient){ -// cuboid([15,15,15]); -// children(); -// } -// } -// diff() -// cube([19,10,19]) -// attach([FRONT],overlap=-4) -// thing(anchor=TOP) -// # attach(TOP) cuboid(2,anchor=TOP); -// Example: Here is an example where the "keep" tag allows children to appear in the negative space. That tag is also needed for this module to produce the desired output. As above, the tag must be applied outside the attachable() call. -// module thing(anchor = CENTER, spin = 0, orient = UP) { -// tag("remove") attachable(anchor, spin, orient, d1=0,d2=95,h=33) { -// cylinder(h = 33.1, d1 = 0, d2 = 95, anchor=CENTER); -// union(){} // dummy children -// } -// tag("keep") attachable(anchor, spin, orient,d1=0,d2=95,h=33) { -// cylinder(h = 33, d = 10,anchor=CENTER); -// children(); -// } -// } -// diff() -// cube(100) -// attach([FRONT,TOP],overlap=-4) -// thing(anchor=TOP) -// tube(ir=12,h=10); -module attachable( - anchor, spin, orient, - size, size2, shift, - r,r1,r2, d,d1,d2, l,h, - vnf, path, region, - extent=true, - cp=[0,0,0], - offset=[0,0,0], - anchors=[], - two_d=false, - axis=UP,override, - geom -) { - dummy1 = - assert($children==2, "attachable() expects exactly two children; the shape to manage, and the union of all attachment candidates.") - 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); - region = !is_undef(region)? region : - !is_undef(path)? [path] : - undef; - geom = is_def(geom)? geom : - attach_geom( - size=size, size2=size2, shift=shift, - r=r, r1=r1, r2=r2, h=h, - d=d, d1=d1, d2=d2, l=l, - vnf=vnf, region=region, extent=extent, - cp=cp, offset=offset, anchors=anchors, - two_d=two_d, axis=axis, override=override - ); - m = _attach_transform(anchor,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; - $align=undef; - if (_is_shown()) - _color($color) children(0); - if (is_def($save_color)) { - $color=$save_color; - $save_color=undef; - children(1); - } - else children(1); - } -} - -// Function: reorient() -// Synopsis: Calculates the transformation matrix needed to reorient an object. -// Topics: Attachments -// See Also: reorient(), attachable() -// Usage: Square/Trapezoid Geometry -// mat = reorient(anchor, spin, [orient], two_d=true, size=, [size2=], [shift=], ...); -// pts = reorient(anchor, spin, [orient], two_d=true, size=, [size2=], [shift=], p=, ...); -// Usage: Circle/Oval Geometry -// mat = reorient(anchor, spin, [orient], two_d=true, r=|d=, ...); -// pts = reorient(anchor, spin, [orient], two_d=true, r=|d=, p=, ...); -// Usage: 2D Path/Polygon Geometry -// mat = reorient(anchor, spin, [orient], two_d=true, path=, [extent=], ...); -// pts = reorient(anchor, spin, [orient], two_d=true, path=, [extent=], p=, ...); -// Usage: 2D Region/Polygon Geometry -// mat = reorient(anchor, spin, [orient], two_d=true, region=, [extent=], ...); -// pts = reorient(anchor, spin, [orient], two_d=true, region=, [extent=], p=, ...); -// Usage: Cubical/Prismoidal Geometry -// mat = reorient(anchor, spin, [orient], size=, [size2=], [shift=], ...); -// pts = reorient(anchor, spin, [orient], size=, [size2=], [shift=], p=, ...); -// Usage: Cylindrical Geometry -// mat = reorient(anchor, spin, [orient], r=|d=, l=, [axis=], ...); -// pts = reorient(anchor, spin, [orient], r=|d=, l=, [axis=], p=, ...); -// Usage: Conical Geometry -// mat = reorient(anchor, spin, [orient], r1=|d1=, r2=|d2=, l=, [axis=], ...); -// pts = reorient(anchor, spin, [orient], r1=|d1=, r2=|d2=, l=, [axis=], p=, ...); -// Usage: Spheroid/Ovoid Geometry -// mat = reorient(anchor, spin, [orient], r|d=, ...); -// pts = reorient(anchor, spin, [orient], r|d=, p=, ...); -// Usage: Extruded Path/Polygon Geometry -// mat = reorient(anchor, spin, [orient], path=, l=|h=, [extent=], ...); -// pts = reorient(anchor, spin, [orient], path=, l=|h=, [extent=], p=, ...); -// Usage: Extruded Region Geometry -// mat = reorient(anchor, spin, [orient], region=, l=|h=, [extent=], ...); -// pts = reorient(anchor, spin, [orient], region=, l=|h=, [extent=], p=, ...); -// Usage: VNF Geometry -// mat = reorient(anchor, spin, [orient], vnf, [extent], ...); -// pts = reorient(anchor, spin, [orient], vnf, [extent], p=, ...); -// -// Description: -// Given anchor, spin, orient, and general geometry info for a managed volume, this calculates -// the transformation matrix needed to be applied to the contents of that volume. A managed 3D -// volume is assumed to be vertically (Z-axis) oriented, and centered. A managed 2D area is just -// assumed to be centered. -// . -// If `p` is not given, then the transformation matrix will be returned. -// If `p` contains a VNF, a new VNF will be returned with the vertices transformed by the matrix. -// If `p` contains a path, a new path will be returned with the vertices transformed by the matrix. -// If `p` contains a point, a new point will be returned, transformed by the matrix. -// . -// If `$attach_to` is not defined, then the following transformations are performed in order: -// * Translates so the `anchor` point is at the origin (0,0,0). -// * Rotates around the Z axis by `spin` degrees counter-clockwise. -// * Rotates so the top of the part points towards the vector `orient`. -// . -// If `$attach_to` is defined, as a consequence of `attach(from,to)`, then -// the following transformations are performed in order: -// * Translates this part so it's anchor position matches the parent's anchor position. -// * Rotates this part so it's anchor direction vector exactly opposes the parent's anchor direction vector. -// * Rotates this part so it's anchor spin matches the parent's anchor spin. -// . -// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). -// -// Arguments: -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` -// --- -// size = If given as a 3D vector, contains the XY size of the bottom of the cuboidal/prismoidal volume, and the Z height. If given as a 2D vector, contains the front X width of the rectangular/trapezoidal shape, and the Y length. -// size2 = If given as a 2D vector, contains the XY size of the top of the prismoidal volume. If given as a number, contains the back width of the trapezoidal shape. -// shift = If given as a 2D vector, shifts the top of the prismoidal or conical shape by the given amount. If given as a number, shifts the back of the trapezoidal shape right by that amount. Default: No shift. -// r = Radius of the cylindrical/conical volume. Can be a scalar, or a list of sizes per axis. -// d = Diameter of the cylindrical/conical volume. Can be a scalar, or a list of sizes per axis. -// r1 = Radius of the bottom of the conical volume. Can be a scalar, or a list of sizes per axis. -// r2 = Radius of the top of the conical volume. Can be a scalar, or a list of sizes per axis. -// d1 = Diameter of the bottom of the conical volume. Can be a scalar, a list of sizes per axis. -// d2 = Diameter of the top of the conical volume. Can be a scalar, a list of sizes per axis. -// l/h = Length of the cylindrical, conical, or extruded path volume along axis. -// vnf = The [VNF](vnf.scad) of the volume. -// path = The path to generate a polygon from. -// region = The region to generate a shape from. -// extent = If true, calculate anchors by extents, rather than intersection. Default: false. -// cp = If given, specifies the centerpoint of the volume. Default: `[0,0,0]` -// offset = If given, offsets the perimeter of the volume around the centerpoint. -// anchors = If given as a list of anchor points, allows named anchor points. -// two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D) -// axis = The vector pointing along the axis of a geometry. Default: UP -// p = The VNF, path, or point to transform. -function reorient( - anchor, spin, orient, - size, size2, shift, - r,r1,r2, d,d1,d2, l,h, - vnf, path, region, - extent=true, - offset=[0,0,0], - cp=[0,0,0], - anchors=[], - two_d=false, - axis=UP, override, - geom, - p=undef -) = - 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)) - let( - anchor = default(anchor, CENTER), - spin = default(spin, 0), - orient = default(orient, UP), - region = !is_undef(region)? region : - !is_undef(path)? [path] : - undef, - geom = is_def(geom)? geom : - attach_geom( - size=size, size2=size2, shift=shift, - r=r, r1=r1, r2=r2, h=h, - d=d, d1=d1, d2=d2, l=l, - vnf=vnf, region=region, extent=extent, - cp=cp, offset=offset, anchors=anchors, - two_d=two_d, axis=axis, override=override - ), - $attach_to = undef - ) _attach_transform(anchor,spin,orient,geom,p); - - -// Function: named_anchor() -// Synopsis: Creates an anchro data structure. -// Topics: Attachments -// See Also: reorient(), attachable() -// Usage: -// a = named_anchor(name, pos, [orient], [spin]); -// Description: -// Creates an anchor data structure. For a step-by-step explanation of attachments, -// see the [Attachments Tutorial](Tutorial-Attachments). -// Arguments: -// name = The string name of the anchor. Lowercase. Words separated by single dashes. No spaces. -// pos = The [X,Y,Z] position of the anchor. -// orient = A vector pointing in the direction parts should project from the anchor position. Default: UP -// spin = If needed, the angle to rotate the part around the direction vector. Default: 0 -function named_anchor(name, pos, orient=UP, spin=0) = [name, pos, orient, spin]; - - -// Function: attach_geom() -// Synopsis: Returns the internal geometry description of an attachable object. -// Topics: Attachments -// See Also: reorient(), attachable() -// Usage: Null/Point Geometry -// geom = attach_geom(...); -// Usage: Square/Trapezoid Geometry -// geom = attach_geom(two_d=true, size=, [size2=], [shift=], ...); -// Usage: Circle/Oval Geometry -// geom = attach_geom(two_d=true, r=|d=, ...); -// Usage: 2D Path/Polygon/Region Geometry -// geom = attach_geom(two_d=true, region=, [extent=], ...); -// Usage: Cubical/Prismoidal Geometry -// geom = attach_geom(size=, [size2=], [shift=], ...); -// Usage: Cylindrical Geometry -// geom = attach_geom(r=|d=, l=|h=, [axis=], ...); -// Usage: Conical Geometry -// geom = attach_geom(r1|d1=, r2=|d2=, l=, [axis=], ...); -// Usage: Spheroid/Ovoid Geometry -// geom = attach_geom(r=|d=, ...); -// Usage: Extruded 2D Path/Polygon/Region Geometry -// geom = attach_geom(region=, l=|h=, [extent=], [shift=], [scale=], [twist=], ...); -// Usage: VNF Geometry -// geom = attach_geom(vnf=, [extent=], ...); -// -// Description: -// Given arguments that describe the geometry of an attachable object, returns the internal geometry description. -// This will probably not not ever need to be called by the end user. -// -// Arguments: -// --- -// size = If given as a 3D vector, contains the XY size of the bottom of the cuboidal/prismoidal volume, and the Z height. If given as a 2D vector, contains the front X width of the rectangular/trapezoidal shape, and the Y length. -// size2 = If given as a 2D vector, contains the XY size of the top of the prismoidal volume. If given as a number, contains the back width of the trapezoidal shape. -// shift = If given as a 2D vector, shifts the top of the prismoidal or conical shape by the given amount. If given as a number, shifts the back of the trapezoidal shape right by that amount. Default: No shift. -// scale = If given as number or a 2D vector, scales the top of the shape, relative to the bottom. Default: `[1,1]` -// twist = If given as number, rotates the top of the shape by the given number of degrees clockwise, relative to the bottom. Default: `0` -// r = Radius of the cylindrical/conical volume. Can be a scalar, or a list of sizes per axis. -// d = Diameter of the cylindrical/conical volume. Can be a scalar, or a list of sizes per axis. -// r1 = Radius of the bottom of the conical volume. Can be a scalar, or a list of sizes per axis. -// r2 = Radius of the top of the conical volume. Can be a scalar, or a list of sizes per axis. -// d1 = Diameter of the bottom of the conical volume. Can be a scalar, a list of sizes per axis. -// d2 = Diameter of the top of the conical volume. Can be a scalar, a list of sizes per axis. -// l/h = Length of the cylindrical, conical or extruded region volume along axis. -// vnf = The [VNF](vnf.scad) of the volume. -// region = The region to generate a shape from. -// extent = If true, calculate anchors by extents, rather than intersection. Default: true. -// cp = If given, specifies the centerpoint of the volume. Default: `[0,0,0]` -// offset = If given, offsets the perimeter of the volume around the centerpoint. -// anchors = If given as a list of anchor points, allows named anchor points. -// two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D) -// axis = The vector pointing along the axis of a geometry. Default: UP -// override = Function that takes an anchor and returns a pair `[position,direction]` to use for that anchor to override the normal one. You can also supply a lookup table that is a list of `[anchor, [position, direction]]` entries. If the direction/position that is returned is undef then the default will be used. -// -// Example(NORENDER): Null/Point Shape -// geom = attach_geom(); -// -// Example(NORENDER): Cubical Shape -// geom = attach_geom(size=size); -// -// Example(NORENDER): Prismoidal Shape -// geom = attach_geom( -// size=point3d(botsize,h), -// size2=topsize, shift=shift -// ); -// -// Example(NORENDER): Cylindrical Shape, Z-Axis Aligned -// geom = attach_geom(r=r, h=h); -// -// Example(NORENDER): Cylindrical Shape, Y-Axis Aligned -// geom = attach_geom(r=r, h=h, axis=BACK); -// -// Example(NORENDER): Cylindrical Shape, X-Axis Aligned -// geom = attach_geom(r=r, h=h, axis=RIGHT); -// -// Example(NORENDER): Conical Shape, Z-Axis Aligned -// geom = attach_geom(r1=r1, r2=r2, h=h); -// -// Example(NORENDER): Conical Shape, Y-Axis Aligned -// geom = attach_geom(r1=r1, r2=r2, h=h, axis=BACK); -// -// Example(NORENDER): Conical Shape, X-Axis Aligned -// geom = attach_geom(r1=r1, r2=r2, h=h, axis=RIGHT); -// -// Example(NORENDER): Spherical Shape -// geom = attach_geom(r=r); -// -// Example(NORENDER): Ovoid Shape -// geom = attach_geom(r=[r_x, r_y, r_z]); -// -// Example(NORENDER): Arbitrary VNF Shape, Anchored by Extents -// geom = attach_geom(vnf=vnf); -// -// Example(NORENDER): Arbitrary VNF Shape, Anchored by Intersection -// geom = attach_geom(vnf=vnf, extent=false); -// -// Example(NORENDER): 2D Rectangular Shape -// geom = attach_geom(two_d=true, size=size); -// -// Example(NORENDER): 2D Trapezoidal Shape -// geom = attach_geom(two_d=true, size=[x1,y], size2=x2, shift=shift, override=override); -// -// Example(NORENDER): 2D Circular Shape -// geom = attach_geom(two_d=true, r=r); -// -// Example(NORENDER): 2D Oval Shape -// geom = attach_geom(two_d=true, r=[r_x, r_y]); -// -// Example(NORENDER): Arbitrary 2D Region Shape, Anchored by Extents -// geom = attach_geom(two_d=true, region=region); -// -// Example(NORENDER): Arbitrary 2D Region Shape, Anchored by Intersection -// geom = attach_geom(two_d=true, region=region, extent=false); -// -// Example(NORENDER): Extruded Region, Anchored by Extents -// geom = attach_geom(region=region, l=height); -// -// Example(NORENDER): Extruded Region, Anchored by Intersection -// geom = attach_geom(region=region, l=length, extent=false); -// - -function _local_struct_val(struct, key)= - assert(is_def(key),"key is missing") - let(ind = search([key],struct)[0]) - ind == [] ? undef : struct[ind][1]; - - -function attach_geom( - size, size2, - shift, scale, twist, - r,r1,r2, d,d1,d2, l,h, - vnf, region, - extent=true, - cp=[0,0,0], - offset=[0,0,0], - anchors=[], - two_d=false, - axis=UP, override -) = - assert(is_bool(extent)) - assert(is_vector(cp) || is_string(cp)) - assert(is_vector(offset)) - assert(is_list(anchors)) - assert(is_bool(two_d)) - assert(is_vector(axis)) - !is_undef(size)? ( - two_d? ( - let( - size2 = default(size2, size.x), - shift = default(shift, 0), - over_f = is_undef(override) ? function(anchor) [undef,undef] - : is_func(override) ? override - : function(anchor) _local_struct_val(override,anchor) - ) - assert(is_vector(size,2)) - assert(is_num(size2)) - assert(is_num(shift)) - ["trapezoid", point2d(size), size2, shift, over_f, cp, offset, anchors] - ) : ( - let( - size2 = default(size2, point2d(size)), - shift = default(shift, [0,0]) - ) - assert(is_vector(size,3)) - assert(is_vector(size2,2)) - assert(is_vector(shift,2)) - ["prismoid", size, size2, shift, axis, cp, offset, anchors] - ) - ) : !is_undef(vnf)? ( - assert(is_vnf(vnf)) - assert(two_d == false) - extent? ["vnf_extent", vnf, cp, offset, anchors] : - ["vnf_isect", vnf, cp, offset, anchors] - ) : !is_undef(region)? ( - assert(is_region(region),2) - let( l = default(l, h) ) - two_d==true - ? assert(is_undef(l)) - extent==true - ? ["rgn_extent", region, cp, offset, anchors] - : ["rgn_isect", region, cp, offset, anchors] - : assert(is_finite(l)) - let( - shift = default(shift, [0,0]), - scale = is_num(scale)? [scale,scale] : default(scale, [1,1]), - twist = default(twist, 0) - ) - assert(is_vector(shift,2)) - assert(is_vector(scale,2)) - assert(is_num(twist)) - extent==true - ? ["extrusion_extent", region, l, twist, scale, shift, cp, offset, anchors] - : ["extrusion_isect", region, l, twist, scale, shift, cp, offset, anchors] - ) : - let( - r1 = get_radius(r1=r1,d1=d1,r=r,d=d,dflt=undef) - ) - !is_undef(r1)? ( - let( l = default(l, h) ) - !is_undef(l)? ( - let( - shift = default(shift, [0,0]), - r2 = get_radius(r1=r2,d1=d2,r=r,d=d,dflt=undef) - ) - assert(is_num(r1) || is_vector(r1,2)) - assert(is_num(r2) || is_vector(r2,2)) - assert(is_num(l)) - assert(is_vector(shift,2)) - ["conoid", r1, r2, l, shift, axis, cp, offset, anchors] - ) : ( - two_d? ( - assert(is_num(r1) || is_vector(r1,2)) - ["ellipse", r1, cp, offset, anchors] - ) : ( - assert(is_num(r1) || is_vector(r1,3)) - ["spheroid", r1, cp, offset, anchors] - ) - ) - ) : - ["point", cp, offset, anchors]; - - - - - - -////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Attachment internal functions - - -/// Internal Function: _attach_geom_2d() -/// Topics: Attachments -/// See Also: reorient(), attachable() -/// Usage: -/// bool = _attach_geom_2d(geom); -/// Description: -/// Returns true if the given attachment geometry description is for a 2D shape. -function _attach_geom_2d(geom) = - let( type = geom[0] ) - type == "trapezoid" || type == "ellipse" || - type == "rgn_isect" || type == "rgn_extent"; - - -/// Internal Function: _attach_geom_size() -/// Usage: -/// bounds = _attach_geom_size(geom); -/// Topics: Attachments -/// See Also: reorient(), attachable() -/// Description: -/// Returns the `[X,Y,Z]` bounding size for the given attachment geometry description. -function _attach_geom_size(geom) = - let( type = geom[0] ) - type == "point"? [0,0,0] : - type == "prismoid"? ( //size, size2, shift, axis - let( - size=geom[1], size2=geom[2], shift=point2d(geom[3]), - maxx = max(size.x,size2.x), - maxy = max(size.y,size2.y), - z = size.z - ) [maxx, maxy, z] - ) : type == "conoid"? ( //r1, r2, l, shift - let( - r1=geom[1], r2=geom[2], l=geom[3], - shift=point2d(geom[4]), axis=point3d(geom[5]), - rx1 = default(r1[0],r1), - ry1 = default(r1[1],r1), - rx2 = default(r2[0],r2), - ry2 = default(r2[1],r2), - maxxr = max(rx1,rx2), - maxyr = max(ry1,ry2) - ) - approx(axis,UP)? [2*maxxr,2*maxyr,l] : - approx(axis,RIGHT)? [l,2*maxyr,2*maxxr] : - approx(axis,BACK)? [2*maxxr,l,2*maxyr] : - [2*maxxr, 2*maxyr, l] - ) : type == "spheroid"? ( //r - let( r=geom[1] ) - is_num(r)? [2,2,2]*r : v_mul([2,2,2],point3d(r)) - ) : type == "vnf_extent" || type=="vnf_isect"? ( //vnf - let( - vnf = geom[1] - ) vnf==EMPTY_VNF? [0,0,0] : - let( - mm = pointlist_bounds(geom[1][0]), - delt = mm[1]-mm[0] - ) delt - ) : type == "extrusion_isect" || type == "extrusion_extent"? ( //path, l - let( - mm = pointlist_bounds(flatten(geom[1])), - delt = mm[1]-mm[0] - ) [delt.x, delt.y, geom[2]] - ) : type == "trapezoid"? ( //size, size2 - let( - size=geom[1], size2=geom[2], shift=geom[3], - maxx = max(size.x,size2+abs(shift)) - ) [maxx, size.y] - ) : type == "ellipse"? ( //r - let( r=geom[1] ) - is_num(r)? [2,2]*r : v_mul([2,2],point2d(r)) - ) : type == "rgn_isect" || type == "rgn_extent"? ( //path - let( - mm = pointlist_bounds(flatten(geom[1])), - delt = mm[1]-mm[0] - ) [delt.x, delt.y] - ) : - assert(false, "Unknown attachment geometry type."); - - -/// Internal Function: _attach_transform() -/// Usage: To Get a Transformation Matrix -/// mat = _attach_transform(anchor, spin, orient, geom); -/// Usage: To Transform Points, Paths, Patches, or VNFs -/// new_p = _attach_transform(anchor, spin, orient, geom, p); -/// Topics: Attachments -/// See Also: reorient(), attachable() -/// Description: -/// Returns the affine3d transformation matrix needed to `anchor`, `spin`, and `orient` -/// the given geometry `geom` shape into position. -/// Arguments: -/// anchor = Anchor point to translate to the origin `[0,0,0]`. See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` -/// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` -/// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` -/// geom = The geometry description of the shape. -/// p = If given as a VNF, path, or point, applies the affine3d transformation matrix to it and returns the result. -function _attach_transform(anchor, spin, orient, geom, p) = - 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)) - let( - anchor = default(anchor, CENTER), - spin = default(spin, 0), - orient = default(orient, UP), - two_d = _attach_geom_2d(geom), - m = ($attach_to != undef)? ( - let( - anch = _find_anchor($attach_to, geom), - pos = anch[1] - ) two_d? ( - assert(two_d && is_num(spin)) - affine3d_zrot(spin) * - rot(to=FWD, from=point3d(anch[2])) * - affine3d_translate(point3d(-pos)) - ) : ( - assert(is_num(spin) || is_vector(spin,3)) - let( - ang = vector_angle(anch[2], DOWN), - axis = vector_axis(anch[2], DOWN), - ang2 = (anch[2]==UP || anch[2]==DOWN)? 0 : 180-anch[3], - axis2 = rot(p=axis,[0,0,ang2]) - ) - affine3d_rot_by_axis(axis2,ang) * ( - is_num(spin)? affine3d_zrot(ang2+spin) : ( - affine3d_zrot(spin.z) * - affine3d_yrot(spin.y) * - affine3d_xrot(spin.x) * - affine3d_zrot(ang2) - ) - ) * affine3d_translate(point3d(-pos)) - ) - ) : ( - let( - pos = _find_anchor(anchor, geom)[1] - ) two_d? ( - assert(two_d && is_num(spin)) - affine3d_zrot(spin) * - affine3d_translate(point3d(-pos)) - ) : ( - assert(is_num(spin) || is_vector(spin,3)) - let( - axis = vector_axis(UP,orient), - ang = vector_angle(UP,orient) - ) - affine3d_rot_by_axis(axis,ang) * ( - is_num(spin)? affine3d_zrot(spin) : ( - affine3d_zrot(spin.z) * - affine3d_yrot(spin.y) * - affine3d_xrot(spin.x) - ) - ) * affine3d_translate(point3d(-pos)) - ) - ) - ) is_undef(p)? m : - is_vnf(p)? [(p==EMPTY_VNF? p : apply(m, p[0])), p[1]] : - apply(m, p); - - -function _get_cp(geom) = - let(cp=select(geom,-3)) - is_vector(cp) ? cp - : let( - type = in_list(geom[0],["vnf_extent","vnf_isect"]) ? "vnf" - : in_list(geom[0],["rgn_extent","rgn_isect"]) ? "path" - : in_list(geom[0],["extrusion_extent","extrusion_isect"]) ? "xpath" - : "other" - ) - assert(type!="other", "Invalid cp value") - cp=="centroid" ? ( - type=="vnf" && (len(geom[1][0])==0 || len(geom[1][1])==0) ? [0,0,0] : - [each centroid(geom[1]), if (type=="xpath") 0] - ) - : let(points = type=="vnf"?geom[1][0]:flatten(force_region(geom[1]))) - cp=="mean" ? [each mean(points), if (type=="xpath") 0] - : cp=="box" ?[each mean(pointlist_bounds(points)), if (type=="xpath") 0] - : assert(false,"Invalid cp specification"); - - -function _get_cp(geom) = - let(cp=select(geom,-3)) - is_vector(cp) ? cp - : let( - is_vnf = in_list(geom[0],["vnf_extent","vnf_isect"]) - ) - cp == "centroid" ? ( - is_vnf && len(geom[1][1])==0 - ? [0,0,0] - : centroid(geom[1]) - ) - : let(points = is_vnf?geom[1][0]:flatten(force_region(geom[1]))) - cp=="mean" ? mean(points) - : cp=="box" ? mean(pointlist_bounds(points)) - : assert(false,"Invalid cp specification"); - - - -function _force_anchor_2d(anchor) = - assert(anchor.y==0 || anchor.z==0, "Anchor for a 2D shape cannot be fully 3D. It must have either Y or Z component equal to zero.") - anchor.y==0 ? [anchor.x,anchor.z] : point2d(anchor); - - -/// Internal Function: _find_anchor() -/// Usage: -/// anchorinfo = _find_anchor(anchor, geom); -/// Topics: Attachments -/// See Also: reorient(), attachable() -/// Description: -/// Calculates the anchor data for the given `anchor` vector or name, in the given attachment -/// geometry. Returns `[ANCHOR, POS, VEC, ANG]` where `ANCHOR` is the requested anchorname -/// or vector, `POS` is the anchor position, `VEC` is the direction vector of the anchor, and -/// `ANG` is the angle to align with around the rotation axis of th anchor direction vector. -/// Arguments: -/// anchor = Vector or named anchor string. -/// geom = The geometry description of the shape. -function _find_anchor(anchor, geom) = - is_string(anchor)? ( - anchor=="origin"? [anchor, CENTER, UP, 0] - : let( - anchors = last(geom), - found = search([anchor], anchors, num_returns_per_match=1)[0] - ) - assert(found!=[], str("Unknown anchor: ",anchor)) - anchors[found] - ) : - let( - cp = _get_cp(geom), - offset_raw = select(geom,-2), - offset = [for (i=[0:2]) anchor[i]==0? 0 : offset_raw[i]], // prevents bad centering. - type = geom[0] - ) - assert(is_vector(anchor),str("Invalid anchor: anchor=",anchor)) - let( - anchor = point3d(anchor), - oang = ( - approx(point2d(anchor), [0,0])? 0 : - atan2(anchor.y, anchor.x)+90 - ) - ) - type == "prismoid"? ( //size, size2, shift, axis - let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[]) - assert(all_comps_good, "All components of an anchor for a cuboid/prismoid must be -1, 0, or 1") - let( - size=geom[1], size2=geom[2], - shift=point2d(geom[3]), axis=point3d(geom[4]), - anch = rot(from=axis, to=UP, p=anchor), - offset = rot(from=axis, to=UP, p=offset), - h = size.z, - u = (anch.z + 1) / 2, // u is one of 0, 0.5, or 1 - axy = point2d(anch), - bot = point3d(v_mul(point2d(size )/2, axy), -h/2), - top = point3d(v_mul(point2d(size2)/2, axy) + shift, h/2), - pos = point3d(cp) + lerp(bot,top,u) + offset, - vecs = anchor==CENTER? [UP] - : [ - if (anch.x!=0) unit(rot(from=UP, to=[(top-bot).x,0,h], p=[axy.x,0,0]), UP), - if (anch.y!=0) unit(rot(from=UP, to=[0,(top-bot).y,h], p=[0,axy.y,0]), UP), - if (anch.z!=0) unit([0,0,anch.z],UP) - ], - vec2 = anchor==CENTER? UP - : len(vecs)==1? unit(vecs[0],UP) - : len(vecs)==2? vector_bisect(vecs[0],vecs[1]) - : let( - v1 = vector_bisect(vecs[0],vecs[2]), - v2 = vector_bisect(vecs[1],vecs[2]), - p1 = plane_from_normal(yrot(90,p=v1)), - p2 = plane_from_normal(xrot(-90,p=v2)), - line = plane_intersection(p1,p2), - v3 = unit(line[1]-line[0],UP) * anch.z - ) - unit(v3,UP), - vec = rot(from=UP, to=axis, p=vec2), - pos2 = rot(from=UP, to=axis, p=pos) - ) [anchor, pos2, vec, oang] - ) : type == "conoid"? ( //r1, r2, l, shift - assert(anchor.z == sign(anchor.z), "The Z component of an anchor for a cylinder/cone must be -1, 0, or 1") - let( - rr1=geom[1], rr2=geom[2], l=geom[3], - shift=point2d(geom[4]), axis=point3d(geom[5]), - r1 = is_num(rr1)? [rr1,rr1] : point2d(rr1), - r2 = is_num(rr2)? [rr2,rr2] : point2d(rr2), - anch = rot(from=axis, to=UP, p=anchor), - offset = rot(from=axis, to=UP, p=offset), - u = (anch.z+1)/2, - axy = unit(point2d(anch),[0,0]), - bot = point3d(v_mul(r1,axy), -l/2), - top = point3d(v_mul(r2,axy)+shift, l/2), - pos = point3d(cp) + lerp(bot,top,u) + offset, - sidevec = rot(from=UP, to=top==bot?UP:top-bot, p=point3d(axy)), - vvec = anch==CENTER? UP : unit([0,0,anch.z],UP), - vec = anch==CENTER? CENTER : - approx(axy,[0,0])? unit(anch,UP) : - approx(anch.z,0)? sidevec : - unit((sidevec+vvec)/2,UP), - pos2 = rot(from=UP, to=axis, p=pos), - vec2 = anch==CENTER? UP : rot(from=UP, to=axis, p=vec) - ) [anchor, pos2, vec2, oang] - ) : type == "point"? ( - let( - anchor = unit(point3d(anchor),CENTER), - pos = point3d(cp) + point3d(offset), - vec = unit(anchor,UP) - ) [anchor, pos, vec, oang] - ) : type == "spheroid"? ( //r - let( - rr = geom[1], - r = is_num(rr)? [rr,rr,rr] : point3d(rr), - anchor = unit(point3d(anchor),CENTER), - pos = point3d(cp) + v_mul(r,anchor) + point3d(offset), - vec = unit(v_mul(r,anchor),UP) - ) [anchor, pos, vec, oang] - ) : type == "vnf_isect"? ( //vnf - let( vnf=geom[1] ) - approx(anchor,CTR)? [anchor, [0,0,0], UP, 0] : - vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor), 0] : - let( - eps = 1/2048, - points = vnf[0], - faces = vnf[1], - rpts = apply(rot(from=anchor, to=RIGHT) * move(-cp), points), - hits = [ - for (face = faces) - let( - verts = select(rpts, face), - ys = column(verts,1), - zs = column(verts,2) - ) - if (max(ys) >= -eps && max(zs) >= -eps && - min(ys) <= eps && min(zs) <= eps) - let( - poly = select(points, face), - isect = polygon_line_intersection(poly, [cp,cp+anchor], eps=eps), - ptlist = is_undef(isect) ? [] : - is_vector(isect) ? [isect] - : flatten(isect), // parallel to a face - n = len(ptlist)>0 ? polygon_normal(poly) : undef - ) - for(pt=ptlist) [anchor * (pt-cp), n, pt] - ] - ) - assert(len(hits)>0, "Anchor vector does not intersect with the shape. Attachment failed.") - let( - furthest = max_index(column(hits,0)), - dist = hits[furthest][0], - pos = hits[furthest][2], - hitnorms = [for (hit = hits) if (approx(hit[0],dist,eps=eps)) hit[1]], - unorms = [ - for (i = idx(hitnorms)) - let( - thisnorm = hitnorms[i], - isdup = [ - for (j = [i+1:1:len(hitnorms)-1]) - if (approx(thisnorm, hitnorms[j])) 1 - ] != [] - ) - if (!isdup) thisnorm - ], - n = unit(sum(unorms)), - oang = approx(point2d(n), [0,0])? 0 : atan2(n.y, n.x) + 90 - ) - [anchor, pos, n, oang] - ) : type == "vnf_extent"? ( //vnf - let( vnf=geom[1] ) - approx(anchor,CTR)? [anchor, [0,0,0], UP, 0] : - vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor,UP), 0] : - let( - rpts = apply(rot(from=anchor, to=RIGHT) * move(point3d(-cp)), vnf[0]), - maxx = max(column(rpts,0)), - idxs = [for (i = idx(rpts)) if (approx(rpts[i].x, maxx)) i], - avep = sum(select(rpts,idxs))/len(idxs), - mpt = approx(point2d(anchor),[0,0])? [maxx,0,0] : avep, - pos = point3d(cp) + rot(from=RIGHT, to=anchor, p=mpt) - ) [anchor, pos, anchor, oang] - ) : type == "trapezoid"? ( //size, size2, shift, override - let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[]) - assert(all_comps_good, "All components of an anchor for a rectangle/trapezoid must be -1, 0, or 1") - let( - anchor=_force_anchor_2d(anchor), - size=geom[1], size2=geom[2], shift=geom[3], - u = (anchor.y+1)/2, // 0<=u<=1 - frpt = [size.x/2*anchor.x, -size.y/2], - bkpt = [size2/2*anchor.x+shift, size.y/2], - override = geom[4](anchor), - pos = default(override[0],point2d(cp) + lerp(frpt, bkpt, u) + point2d(offset)), - svec = point3d(line_normal(bkpt,frpt)*anchor.x), - vec = is_def(override[1]) ? override[1] - : anchor.y == 0? ( anchor.x == 0? BACK : svec ) - : anchor.x == 0? [0,anchor.y,0] - : unit((svec + [0,anchor.y,0]) / 2, [0,anchor.y,0]) - ) [anchor, pos, vec, 0] - ) : type == "ellipse"? ( //r - let( - anchor = unit(_force_anchor_2d(anchor),[0,0]), - r = force_list(geom[1],2), - pos = approx(anchor.x,0) ? [0,sign(anchor.y)*r.y] - : let( - m = anchor.y/anchor.x, - px = sign(anchor.x) * sqrt(1/(1/sqr(r.x) + m*m/sqr(r.y))) - ) - [px,m*px], - vec = unit([r.y/r.x*pos.x, r.x/r.y*pos.y],BACK) - ) [anchor, point2d(cp+offset)+pos, vec, 0] - ) : type == "rgn_isect"? ( //region - let( - anchor = _force_anchor_2d(anchor), - rgn = force_region(move(-point2d(cp), p=geom[1])) - ) - approx(anchor,[0,0])? [anchor, [0,0,0], BACK, 0] : - let( - isects = [ - for (path=rgn, t=triplet(path,true)) let( - seg1 = [t[0],t[1]], - seg2 = [t[1],t[2]], - isect = line_intersection([[0,0],anchor], seg1, RAY, SEGMENT), - n = is_undef(isect)? [0,1] : - !approx(isect, t[1])? line_normal(seg1) : - unit((line_normal(seg1)+line_normal(seg2))/2,[0,1]), - n2 = vector_angle(anchor,n)>90? -n : n - ) - if(!is_undef(isect) && !approx(isect,t[0])) [norm(isect), isect, n2] - ] - ) - assert(len(isects)>0, "Anchor vector does not intersect with the shape. Attachment failed.") - let( - maxidx = max_index(column(isects,0)), - isect = isects[maxidx], - pos = point2d(cp) + isect[1], - vec = unit(isect[2],[0,1]) - ) [anchor, pos, vec, 0] - ) : type == "rgn_extent"? ( //region - let( anchor = _force_anchor_2d(anchor) ) - approx(anchor,[0,0])? [anchor, [0,0,0], BACK, 0] : - let( - rgn = force_region(geom[1]), - rpts = rot(from=anchor, to=RIGHT, p=flatten(rgn)), - maxx = max(column(rpts,0)), - ys = [for (pt=rpts) if (approx(pt.x, maxx)) pt.y], - midy = (min(ys)+max(ys))/2, - pos = rot(from=RIGHT, to=anchor, p=[maxx,midy]) - ) [anchor, pos, unit(anchor,BACK), 0] - ) : type=="extrusion_extent" || type=="extrusion_isect" ? ( // extruded region - assert(in_list(anchor.z,[-1,0,1]), "The Z component of an anchor for an extruded 2D shape must be -1, 0, or 1.") - let( - anchor_xy = point2d(anchor), - rgn = geom[1], - L = geom[2], - twist = geom[3], - scale = geom[4], - shift = geom[5], - u = (anchor.z + 1) / 2, - shmat = move(lerp([0,0], shift, u)), - scmat = scale(lerp([1,1], scale, u)), - twmat = zrot(lerp(0, -twist, u)), - mat = shmat * scmat * twmat - ) - approx(anchor_xy,[0,0]) ? [anchor, apply(mat, point3d(cp,anchor.z*L/2)), unit(anchor, UP), oang] : - let( - newrgn = apply(mat, rgn), - newgeom = attach_geom(two_d=true, region=newrgn, extent=type=="extrusion_extent", cp=cp), - result2d = _find_anchor(anchor_xy, newgeom), - pos = point3d(result2d[1], anchor.z*L/2), - vec = unit(point3d(result2d[2], anchor.z),UP), - oang = atan2(vec.y,vec.x) + 90 - ) - [anchor, pos, vec, oang] - ) : - assert(false, "Unknown attachment geometry type."); - - -/// Internal Function: _is_shown() -/// Usage: -/// bool = _is_shown(); -/// Topics: Attachments -/// See Also: reorient(), attachable() -/// Description: -/// Returns true if objects should currently be shown based on the tag settings. -function _is_shown() = - assert(is_list($tags_shown) || $tags_shown=="ALL") - assert(is_list($tags_hidden)) - let( - dummy=is_undef($tags) ? 0 : echo("Use tag() instead of $tags for specifying an object's tag."), - $tag = default($tag,$tags) - ) - assert(is_string($tag), str("Tag value (",$tag,") is not a string")) - assert(undef==str_find($tag," "),str("Tag string \"",$tag,"\" contains a space, which is not allowed")) - let( - shown = $tags_shown=="ALL" || in_list($tag,$tags_shown), - hidden = in_list($tag, $tags_hidden) - ) - shown && !hidden; - - -// Section: Visualizing Anchors - -/// Internal Function: _standard_anchors() -/// Usage: -/// anchs = _standard_anchors([two_d]); -/// Description: -/// Return the vectors for all standard anchors. -/// Arguments: -/// two_d = If true, returns only the anchors where the Z component is 0. Default: false -function _standard_anchors(two_d=false) = [ - for ( - zv = [ - if (!two_d) TOP, - CENTER, - if (!two_d) BOTTOM - ], - yv = [FRONT, CENTER, BACK], - xv = [LEFT, CENTER, RIGHT] - ) xv+yv+zv -]; - - - -// Module: show_anchors() -// Synopsis: Shows anchors for the parent object. -// SynTags: Geom -// Topics: Attachments -// See Also: expose_anchors(), anchor_arrow(), anchor_arrow2d(), frame_ref() -// Usage: -// PARENT() show_anchors([s], [std=], [custom=]); -// Description: -// Show all standard anchors for the parent object. -// Arguments: -// s = Length of anchor arrows. -// --- -// std = If true show standard anchors. Default: true -// custom = If true show named anchors. Default: true -// Example(FlatSpin,VPD=333): -// cube(50, center=true) show_anchors(); -module show_anchors(s=10, std=true, custom=true) { - check = assert($parent_geom != undef); - two_d = _attach_geom_2d($parent_geom); - if (std) { - 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=last($parent_geom)) { - attach(anchor[0]) { - if(two_d) { - anchor_arrow2d(s, color="cyan"); - } else { - anchor_arrow(s, color="cyan"); - } - color("black") - tag("anchor-arrow") { - xrot(two_d? 0 : 90) { - back(s/3) { - yrot_copies(n=2) - up(two_d? 0.51 : s/30) { - linear_extrude(height=0.01, convexity=12, center=true) { - text(text=anchor[0], size=s/4, halign="center", valign="center", font="Helvetica", $fn=36); - } - } - } - } - } - color([1, 1, 1, 1]) - tag("anchor-arrow") { - xrot(two_d? 0 : 90) { - back(s/3) { - cube([s/4.5*len(anchor[0]), s/3, 0.01], center=true); - } - } - } - } - } - } - children(); -} - - -// Module: anchor_arrow() -// Synopsis: Shows a 3d anchor orientation arrow. -// SynTags: Geom -// Topics: Attachments -// See Also: anchor_arrow2d(), show_anchors(), expose_anchors(), frame_ref() -// Usage: -// anchor_arrow([s], [color], [flag], [anchor=], [orient=], [spin=]) [ATTACHMENTS]; -// Description: -// Show an anchor orientation arrow. By default, tagged with the name "anchor-arrow". -// Arguments: -// s = Length of the arrows. Default: `10` -// color = Color of the arrow. Default: `[0.333, 0.333, 1]` -// flag = If true, draw the orientation flag on the arrowhead. Default: true -// --- -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Example: -// anchor_arrow(s=20); -module anchor_arrow(s=10, color=[0.333,0.333,1], flag=true, $tag="anchor-arrow", $fn=12, anchor=BOT, spin=0, orient=UP) { - attachable(anchor,spin,orient, r=s/6, l=s) { - down(s/2) - recolor("gray") spheroid(d=s/6) { - attach(CENTER,BOT) recolor(color) cyl(h=s*2/3, d=s/15) { - attach(TOP,BOT) cyl(h=s/3, d1=s/5, d2=0) { - if(flag) { - position(BOT) - recolor([1,0.5,0.5]) - cuboid([s/100, s/6, s/4], anchor=FRONT+BOT); - } - } - } - } - children(); - } -} - - - -// Module: anchor_arrow2d() -// Synopsis: Shows a 2d anchor orientation arrow. -// SynTags: Geom -// Topics: Attachments -// See Also: anchor_arrow(), show_anchors(), expose_anchors(), frame_ref() -// Usage: -// anchor_arrow2d([s], [color], [flag]); -// Description: -// Show an anchor orientation arrow. -// Arguments: -// s = Length of the arrows. -// color = Color of the arrow. -// Example: -// anchor_arrow2d(s=20); -module anchor_arrow2d(s=15, color=[0.333,0.333,1], $tag="anchor-arrow") { - color(color) stroke([[0,0],[0,s]], width=s/10, endcap1="butt", endcap2="arrow2"); -} - - - -// Module: expose_anchors() -// Synopsis: Used to show a transparent object with solid color anchor arrows. -// Topics: Attachments -// See Also: anchor_arrow2d(), show_anchors(), show_anchors(), frame_ref() -// Usage: -// expose_anchors(opacity) {child1() show_anchors(); child2() show_anchors(); ...} -// Description: -// Used in combination with show_anchors() to display an object in transparent gray with its anchors in solid color. -// Children will appear transparent and any anchor arrows drawn with will appear in solid color. -// Arguments: -// opacity = The opacity of the children. 0.0 is invisible, 1.0 is opaque. Default: 0.2 -// Example(FlatSpin,VPD=333): -// expose_anchors() cube(50, center=true) show_anchors(); -module expose_anchors(opacity=0.2) { - show_only("anchor-arrow") - children(); - hide("anchor-arrow") - color(is_undef($color) || $color=="default" ? [0,0,0] : - is_string($color) ? $color - : point3d($color), - opacity) - children(); -} - - - -// Module: frame_ref() -// Synopsis: Shows axis orientation arrows. -// SynTags: Geom -// Topics: Attachments -// See Also: anchor_arrow(), anchor_arrow2d(), show_anchors(), expose_anchors() -// Usage: -// frame_ref(s, opacity); -// Description: -// Displays X,Y,Z axis arrows in red, green, and blue respectively. -// Arguments: -// s = Length of the arrows. -// opacity = The opacity of the arrows. 0.0 is invisible, 1.0 is opaque. Default: 1.0 -// Examples: -// frame_ref(25); -// frame_ref(30, opacity=0.5); -module frame_ref(s=15, opacity=1) { - cube(0.01, center=true) { - attach([1,0,0]) anchor_arrow(s=s, flag=false, color=[1.0, 0.3, 0.3, opacity]); - attach([0,1,0]) anchor_arrow(s=s, flag=false, color=[0.3, 1.0, 0.3, opacity]); - attach([0,0,1]) anchor_arrow(s=s, flag=false, color=[0.3, 0.3, 1.0, opacity]); - children(); - } -} - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////////////////// -/// -/// Code after this is internal code for managing edge and corner sets and for displaying -/// edge and corners in the docs -/// - -module _edges_text3d(txt,size=3) { - if (is_list(txt)) { - for (i=idx(txt)) { - down((i-len(txt)/2+0.5)*size*1.5) { - _edges_text3d(txt[i], size=size); - } - } - } else { - xrot(90) color("#000") - linear_extrude(height=0.1) { - text(text=txt, size=size, halign="center", valign="center"); - } - } -} - - -function _edges_vec_txt(x) = is_string(x)? str("\"", x, "\"") : - assert(is_string(x) || is_vector(x,3), str(x)) - let( - lst = concat( - x.z>0? ["TOP"] : x.z<0? ["BOT"] : [], - x.y>0? ["BACK"] : x.y<0? ["FWD"] : [], - x.x>0? ["RIGHT"] : x.x<0? ["LEFT"] : [] - ), - out = [ - for (i = idx(lst)) - i>0? str("+",lst[i]) : lst[i] - ] - ) out; - - -function _edges_text(edges) = - is_string(edges) ? [str("\"",edges,"\"")] : - edges==EDGES_NONE ? ["EDGES_NONE"] : - edges==EDGES_ALL ? ["EDGES_ALL"] : - _is_edge_array(edges) ? [""] : - is_vector(edges,3) ? _edges_vec_txt(edges) : - is_list(edges) ? let( - lst = [for (x=edges) each _edges_text(x)], - out = [ - for (i=idx(lst)) - str( - (i==0? "[" : ""), - lst[i], - (i0? 1 : 0]]; - - - - -/// Internal Function: _edges() -/// Topics: Edges -/// Usage: -/// edgs = _edges(v); -/// edgs = _edges(v, except); -/// -/// Description: -/// Takes a list of edge set descriptors, and returns a normalized edges array -/// that represents all those given edges. -/// Arguments: -/// v = The edge set to include. -/// except = The edge set to specifically exclude, even if they are in `v`. -/// -/// See Also: EDGES_NONE, EDGES_ALL -/// -function _edges(v, except=[]) = - v==[] ? EDGES_NONE : - (is_string(v) || is_vector(v) || _is_edge_array(v))? _edges([v], except=except) : - (is_string(except) || is_vector(except) || _is_edge_array(except))? _edges(v, except=[except]) : - except==[]? _normalize_edges(sum([for (x=v) _edge_set(x)])) : - _normalize_edges( - _normalize_edges(sum([for (x=v) _edge_set(x)])) - - sum([for (x=except) _edge_set(x)]) - ); - - -/// Internal Module: _show_edges() -/// Topics: Edges, Debugging -/// Usage: -/// _show_edges(edges, [size=], [text=], [txtsize=]); -/// Description: -/// Draws a semi-transparent cube with the given edges highlighted in red. -/// Arguments: -/// edges = The edges to highlight. -/// size = The scalar size of the cube. -/// text = The text to show on the front of the cube. -/// txtsize = The size of the text. -/// See Also: _edges(), EDGES_NONE, EDGES_ALL -/// Example: -/// _show_edges(size=30, edges=["X","Y"]); -module _show_edges(edges="ALL", size=20, text, txtsize=3,toplabel) { - edge_set = _edges(edges); - text = !is_undef(text) ? text : _edges_text(edges); - color("red") { - for (axis=[0:2], i=[0:3]) { - if (edge_set[axis][i] > 0) { - translate(EDGE_OFFSETS[axis][i]*size/2) { - if (axis==0) xcyl(h=size, d=2); - if (axis==1) ycyl(h=size, d=2); - if (axis==2) zcyl(h=size, d=2); - } - } - } - } - fwd(size/2) _edges_text3d(text, size=txtsize); - color("yellow",0.7) cuboid(size=size); - vpr = [55,0,25]; - color("black") - if (is_def(toplabel)) - for(h=idx(toplabel)) up(21+6*h)rot(vpr) text3d(select(toplabel,-h-1),size=3.3,h=0.1,orient=UP,anchor=FRONT); -} - - - - -/// Internal Constant: CORNERS_NONE -/// Topics: Corners -/// Description: -/// The set of no corners. -/// Figure(3D): -/// _show_corners(corners="NONE"); -/// See Also: CORNERS_ALL, corners() -CORNERS_NONE = [0,0,0,0,0,0,0,0]; // No corners. - - -/// Internal Constant: CORNERS_ALL -/// Topics: Corners -/// Description: -/// The set of all corners. -/// Figure(3D): -/// _show_corners(corners="ALL"); -/// See Also: CORNERS_NONE, _corners() -CORNERS_ALL = [1,1,1,1,1,1,1,1]; - - -/// Internal Constant: CORNER_OFFSETS -/// Topics: Corners -/// Description: -/// The vectors pointing to each corner of a unit sized cube. -/// Each item in a corner array will have a corresponding vector in this array. -/// See Also: CORNERS_NONE, CORNERS_ALL, _corners() -CORNER_OFFSETS = [ - [-1,-1,-1], [ 1,-1,-1], [-1, 1,-1], [ 1, 1,-1], - [-1,-1, 1], [ 1,-1, 1], [-1, 1, 1], [ 1, 1, 1] -]; - - - - -/// Internal Function: _is_corner_array() -/// Topics: Corners, Type Checking -/// Usage: -/// bool = _is_corner_array(x) -/// Description: -/// Returns true if the given value has the form of a corner array. -/// See Also: CORNERS_NONE, CORNERS_ALL, _corners() -function _is_corner_array(x) = is_vector(x) && len(x)==8 && all([for (xx=x) xx==1||xx==0]); - - -/// Internal Function: _normalize_corners() -/// Topics: Corners -/// Usage: -/// corns = _normalize_corners(v); -/// Description: -/// Normalizes all values in a corner array to be `1`, if it was originally greater than `0`, -/// or `0`, if it was originally less than or equal to `0`. -/// See Also: CORNERS_NONE, CORNERS_ALL, _corners() -function _normalize_corners(v) = [for (x=v) x>0? 1 : 0]; - - -function _corner_set(v) = - _is_corner_array(v)? v : [ - for (i=[0:7]) let( - v2 = CORNER_OFFSETS[i] - ) ( - is_string(v)? ( - v=="ALL"? true : // Return all corners. - v=="NONE"? false : // Return no corners. - let(valid_values = ["ALL", "NONE"]) - assert( - in_list(v, valid_values), - str(v, " must be a vector, corner array, or one of ", valid_values) - ) v - ) : - all([for (i=[0:2]) !v[i] || (v[i]==v2[i])]) - )? 1 : 0 -]; - - -/// Function: _corners() -/// Topics: Corners -/// Usage: -/// corns = _corners(v); -/// corns = _corners(v, except); -/// Description: -/// Takes a list of corner set descriptors, and returns a normalized corners array -/// that represents all those given corners. If the `except` argument is given -/// a list of corner set descriptors, then all those corners will be removed -/// from the returned corners array. If either argument only has a single corner -/// set descriptor, you do not have to pass it in a list. -function _corners(v, except=[]) = - v==[] ? CORNERS_NONE : - (is_string(v) || is_vector(v) || _is_corner_array(v))? _corners([v], except=except) : - (is_string(except) || is_vector(except) || _is_corner_array(except))? _corners(v, except=[except]) : - except==[]? _normalize_corners(sum([for (x=v) _corner_set(x)])) : - let( - a = _normalize_corners(sum([for (x=v) _corner_set(x)])), - b = _normalize_corners(sum([for (x=except) _corner_set(x)])) - ) _normalize_corners(a - b); - - -/// Internal Function: _corner_edges() -/// Topics: Corners -/// Description: -/// Returns [XCOUNT,YCOUNT,ZCOUNT] where each is the count of edges aligned with that -/// axis that are in the edge set and touch the given corner. -/// Arguments: -/// edges = Standard edges array. -/// v = Vector pointing to the corner to count edge intersections at. -/// See Also: CORNERS_NONE, CORNERS_ALL, _corners() -function _corner_edges(edges, v) = - let(u = (v+[1,1,1])/2) [edges[0][u.y+u.z*2], edges[1][u.x+u.z*2], edges[2][u.x+u.y*2]]; - - -/// InternalFunction: _corner_edge_count() -/// Topics: Corners -/// Description: -/// Counts how many given edges intersect at a specific corner. -/// Arguments: -/// edges = Standard edges array. -/// v = Vector pointing to the corner to count edge intersections at. -/// See Also: CORNERS_NONE, CORNERS_ALL, _corners() -function _corner_edge_count(edges, v) = - let(u = (v+[1,1,1])/2) edges[0][u.y+u.z*2] + edges[1][u.x+u.z*2] + edges[2][u.x+u.y*2]; - - -function _corners_text(corners) = - is_string(corners) ? [str("\"",corners,"\"")] : - corners==CORNERS_NONE ? ["CORNERS_NONE"] : - corners==CORNERS_ALL ? ["CORNERS_ALL"] : - _is_corner_array(corners) ? [""] : - is_vector(corners,3) ? _edges_vec_txt(corners) : - is_list(corners) ? let( - lst = [for (x=corners) each _corners_text(x)], - out = [ - for (i=idx(lst)) - str( - (i==0? "[" : ""), - lst[i], - (i0) - translate(CORNER_OFFSETS[i]*size/2) - color("red") sphere(d=2, $fn=16); - fwd(size/2) _edges_text3d(text, size=txtsize); - color("yellow",0.7) cuboid(size=size); - vpr = [55,0,25]; - color("black") - if (is_def(toplabel)) - for(h=idx(toplabel)) up(21+6*h)rot(vpr) text3d(select(toplabel,-h-1),size=3.3,h=.1,orient=UP,anchor=FRONT); -} - -module _show_cube_faces(faces, size=20, toplabel,botlabel) { - color("red") - for(f=faces){ - move(f*size/2) rot(from=UP,to=f) - cuboid([size,size,.1]); - } - vpr = [55,0,25]; - color("black"){ - if (is_def(toplabel)) - for(h=idx(toplabel)) up(21+6*h)rot(vpr) text3d(select(toplabel,-h-1),size=3.3,h=.1,orient=UP,anchor=FRONT); - if (is_def(botlabel)) - for(h=idx(botlabel)) down(26+6*h)rot(vpr) text3d(botlabel[h],size=3.3,h=.1,orient=UP,anchor=FRONT); - } - color("yellow",0.7) cuboid(size=size); -} - -// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +////////////////////////////////////////////////////////////////////// +// LibFile: attachments.scad +// The modules in this file allows you to attach one object to another by making one object the child of another object. +// You can place the child object in relation to its parent object and control the position and orientation +// relative to the parent. The modifiers allow you to treat children in ways different from simple union, such +// as differencing them from the parent, or changing their color. Attachment only works when the parent and child +// are both written to support attachment. Also included in this file are the tools to make your own "attachable" objects. +// Includes: +// include +// FileGroup: Basic Modeling +// FileSummary: Positioning objects on or relative to other objects. Making your own objects support attachment. +// FileFootnotes: STD=Included in std.scad +////////////////////////////////////////////////////////////////////// + + +// Default values for attachment code. +$tags=undef; // for backward compatibility +$tag = ""; +$tag_prefix = ""; +$overlap = 0; +$color = "default"; +$save_color = undef; // Saved color to revert back for children + +$attach_to = undef; +$attach_anchor = [CENTER, CENTER, UP, 0]; +$attach_norot = false; + +$parent_anchor = BOTTOM; +$parent_spin = 0; +$parent_orient = UP; + +$parent_size = undef; +$parent_geom = undef; + +$tags_shown = "ALL"; +$tags_hidden = []; + +_ANCHOR_TYPES = ["intersect","hull"]; + + +// Section: Terminology and Shortcuts +// This library adds the concept of anchoring, spin and orientation to the `cube()`, `cylinder()` +// and `sphere()` builtins, as well as to most of the shapes provided by this library itself. +// - An anchor is a place on an object which you can align the object to, or attach other objects +// to using `attach()` or `position()`. An anchor has a position, a direction, and a spin. +// The direction and spin are used to orient other objects to match when using `attach()`. +// - Spin is a simple rotation around the Z axis. +// - Orientation is rotating an object so that its top is pointed towards a given vector. +// An object will first be translated to its anchor position, then spun, then oriented. +// For a detailed step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// . +// For describing directions, faces, edges, and corners the library provides a set of shortcuts +// all based on combinations of unit direction vectors. You can use these for anchoring and orienting +// attachable objects. You can also them to specify edge sets for rounding or chamfering cuboids, +// or for placing edge, face and corner masks. +// Subsection: Anchor +// Anchoring is specified with the `anchor` argument in most shape modules. Specifying `anchor` +// when creating an object will translate the object so that the anchor point is at the origin +// (0,0,0). Anchoring always occurs before spin and orientation are applied. +// . +// An anchor can be referred to in one of two ways; as a directional vector, or as a named anchor string. +// . +// When given as a vector, it points, in a general way, towards the face, edge, or corner of the +// object that you want the anchor for, relative to the center of the object. You can simply +// specify a vector like `[0,0,1]` to anchor an object at the Z+ end, but you can also use +// directional constants with names like `TOP`, `BOTTOM`, `LEFT`, `RIGHT` and `BACK` that you can add together +// to specify anchor points. See [specifying directions](attachments.scad#subsection-specifying-directions) +// below for the full list of pre-defined directional constants. +// . +// For example: +// - `[0,0,1]` is the same as `TOP` and refers to the center of the top face. +// - `[-1,0,1]` is the same as `TOP+LEFT`, and refers to the center of the top-left edge. +// - `[1,1,-1]` is the same as `BOTTOM+BACK+RIGHT`, and refers to the bottom-back-right corner. +// . +// When the object is cubical or rectangular in shape the anchors must have zero or one values +// for their components and they refer to the face centers, edge centers, or corners of the object. +// The direction of a face anchor will be perpendicular to the face, pointing outward. The direction of a edge anchor +// will be the average of the anchor directions of the two faces the edge is between. The direction +// of a corner anchor will be the average of the anchor directions of the three faces the corner is +// on. +// . +// When the object is cylindrical, conical, or spherical in nature, the anchors will be located +// around the surface of the cylinder, cone, or sphere, relative to the center. +// You can generally use an arbitrary vector to get an anchor positioned anywhere on the curved +// surface of such an object, and the anchor direction will be the surface normal at the anchor location. +// However, for anchor component pointing toward the flat face should be either -1, 1, or 0, and +// anchors that point diagonally toward one of the flat faces will select a point on the edge. +// . +// For objects in two dimensions, the natural expectation is for TOP and BOTTOM to refer to the Y direction +// of the shape. To support this, if you give an anchor in 2D that has anchor.y=0 then the Z component +// will be mapped to the Y direction. This means you can use TOP and BOTTOM for anchors of 2D objects. +// But remember that TOP and BOTTOM are three dimensional vectors and this is a special interpretation +// for 2d anchoring. +// . +// Some more complex objects, like screws and stepper motors, have named anchors to refer to places +// on the object that are not at one of the standard faces, edges or corners. For example, stepper +// motors have anchors for `"screw1"`, `"screw2"`, etc. to refer to the various screwholes on the +// stepper motor shape. The names, positions, directions, and spins of these anchors are +// specific to the object, and are documented when they exist. +// Subsection: Spin +// Spin is specified with the `spin` argument in most shape modules. Specifying a scalar `spin` +// when creating an object will rotate the object counter-clockwise around the Z axis by the given +// number of degrees. If given as a 3D vector, the object will be rotated around each of the X, Y, Z +// axes by the number of degrees in each component of the vector. Spin is always applied after +// anchoring, and before orientation. Since spin is applied after anchoring it is not what +// you might think of intuitively as spinning the shape. To do that, apply `zrot()` to the shape before anchoring. +// Subsection: Orient +// Orientation is specified with the `orient` argument in most shape modules. Specifying `orient` +// when creating an object will rotate the object such that the top of the object will be pointed +// at the vector direction given in the `orient` argument. Orientation is always applied after +// anchoring and spin. The constants `UP`, `DOWN`, `FRONT`, `BACK`, `LEFT`, and `RIGHT` can be +// added together to form the directional vector for this. ie: `LEFT+BACK` +// Subsection: Specifying Directions +// You can use direction vectors to specify anchors for objects or to specify edges, faces, and +// corners of cubes. You can simply specify these direction vectors numerically, but another +// option is to use named constants for direction vectors. These constants define unit vectors +// for the six axis directions as shown below. +// Figure(3D,Big,VPD=6): Named constants for direction vectors. Some directions have more than one name. +// $fn=12; +// stroke([[0,0,0],RIGHT], endcap2="arrow2", width=.05); +// color("black")right(.05)up(.05)move(RIGHT) text3d("RIGHT",size=.1,h=.01,anchor=LEFT,orient=FRONT); +// stroke([[0,0,0],LEFT], endcap2="arrow2", width=.05); +// color("black")left(.05)up(.05)move(LEFT) text3d("LEFT",size=.1,h=.01,anchor=RIGHT,orient=FRONT); +// stroke([[0,0,0],FRONT], endcap2="arrow2", width=.05); +// color("black") +// left(.1){ +// up(.12)move(FRONT) text3d("FRONT",size=.1,h=.01,anchor=RIGHT,orient=FRONT); +// move(FRONT) text3d("FWD",size=.1,h=.01,anchor=RIGHT,orient=FRONT); +// down(.12)move(FRONT) text3d("FORWARD",size=.1,h=.01,anchor=RIGHT,orient=FRONT); +// } +// stroke([[0,0,0],BACK], endcap2="arrow2", width=.05); +// right(.05) +// color("black")move(BACK) text3d("BACK",size=.1,h=.01,anchor=LEFT,orient=FRONT); +// stroke([[0,0,0],DOWN], endcap2="arrow2", width=.05); +// color("black") +// right(.1){ +// up(.12)move(BOT) text3d("DOWN",size=.1,h=.01,anchor=LEFT,orient=FRONT); +// move(BOT) text3d("BOTTOM",size=.1,h=.01,anchor=LEFT,orient=FRONT); +// down(.12)move(BOT) text3d("BOT",size=.1,h=.01,anchor=LEFT,orient=FRONT); +// } +// stroke([[0,0,0],TOP], endcap2="arrow2", width=.05); +// color("black")left(.05){ +// up(.12)move(TOP) text3d("TOP",size=.1,h=.01,anchor=RIGHT,orient=FRONT); +// move(TOP) text3d("UP",size=.1,h=.01,anchor=RIGHT,orient=FRONT); +// } +// Figure(2D,Big): Named constants for direction vectors in 2D. For anchors the TOP and BOTTOM directions are collapsed into 2D as shown here, but do not try to use TOP or BOTTOM as 2D directions in other situations. +// $fn=12; +// stroke(path2d([[0,0,0],RIGHT]), endcap2="arrow2", width=.05); +// color("black")fwd(.22)left(.05)move(RIGHT) text("RIGHT",size=.1,anchor=RIGHT); +// stroke(path2d([[0,0,0],LEFT]), endcap2="arrow2", width=.05); +// color("black")right(.05)fwd(.22)move(LEFT) text("LEFT",size=.1,anchor=LEFT); +// stroke(path2d([[0,0,0],FRONT]), endcap2="arrow2", width=.05); +// color("black") +// fwd(.2) +// right(.15) +// color("black")move(BACK) { text("BACK",size=.1,anchor=LEFT); back(.14) text("(TOP)", size=.1, anchor=LEFT);} +// color("black") +// left(.15)back(.2+.14)move(FRONT){ +// back(.14) text("FRONT",size=.1,anchor=RIGHT); +// text("FWD",size=.1,anchor=RIGHT); +// fwd(.14) text("FORWARD",size=.1,anchor=RIGHT); +// fwd(.28) text("(BOTTOM)",size=.1,anchor=RIGHT); +// fwd(.14*3) text("(BOT)",size=.1,anchor=RIGHT); +// } +// stroke(path2d([[0,0,0],BACK]), endcap2="arrow2", width=.05); +// Subsection: Specifying Faces +// Modules operating on faces accept a list of faces to describe the faces to operate on. Each +// face is given by a vector that points to that face. Attachments of cuboid objects onto their faces also +// work by choosing an attachment face with a single vector in the same manner. +// Figure(3D,Big,NoScales,VPD=275): The six faces of the cube. Some have faces have more than one name. +// ydistribute(50) { +// xdistribute(35){ +// _show_cube_faces([BACK], botlabel=["BACK"]); +// _show_cube_faces([UP],botlabel=["TOP","UP"]); +// _show_cube_faces([RIGHT],botlabel=["RIGHT"]); +// } +// xdistribute(35){ +// _show_cube_faces([FRONT],toplabel=["FRONT","FWD", "FORWARD"]); +// _show_cube_faces([DOWN],toplabel=["BOTTOM","BOT","DOWN"]); +// _show_cube_faces([LEFT],toplabel=["LEFT"]); +// } +// } +// Subsection: Specifying Edges +// Modules operating on edges use two arguments to describe the edge set they will use: The `edges` argument +// is a list of edge set descriptors to include in the edge set, and the `except` argument is a list of +// edge set descriptors to remove from the edge set. +// The default value for `edges` is `"ALL"`, the set of all edges. +// The default value for `except` is the empty set, meaning no edges are removed. +// If either argument is just a single edge set +// descriptor it can be passed directly rather than in a singleton list. +// Each edge set descriptor must be one of: +// - A vector pointing towards an edge, indicating that single edge. +// - A vector pointing towards a face, indicating all edges surrounding that face. +// - A vector pointing towards a corner, indicating all edges touching that corner. +// - The string `"X"`, indicating all X axis aligned edges. +// - The string `"Y"`, indicating all Y axis aligned edges. +// - The string `"Z"`, indicating all Z axis aligned edges. +// - The string `"ALL"`, indicating all edges. +// - The string `"NONE"`, indicating no edges at all. +// - A 3x4 array, where each entry corresponds to one of the 12 edges and is set to 1 if that edge is included and 0 if the edge is not. The edge ordering is: +// ``` +// [ +// [Y-Z-, Y+Z-, Y-Z+, Y+Z+], +// [X-Z-, X+Z-, X-Z+, X+Z+], +// [X-Y-, X+Y-, X-Y+, X+Y+] +// ] +// ``` +// You can specify edge descriptors directly by giving a vector, or you can use sums of the +// named direction vectors described above. Below we show all of the edge sets you can +// describe with sums of the direction vectors, and then we show some examples of combining +// edge set descriptors. +// Figure(3D,Big,VPD=300,NoScales): Vectors pointing toward an edge select that single edge +// ydistribute(50) { +// xdistribute(30) { +// _show_edges(edges=BOT+RIGHT); +// _show_edges(edges=BOT+BACK); +// _show_edges(edges=BOT+LEFT); +// _show_edges(edges=BOT+FRONT); +// } +// xdistribute(30) { +// _show_edges(edges=FWD+RIGHT); +// _show_edges(edges=BACK+RIGHT); +// _show_edges(edges=BACK+LEFT); +// _show_edges(edges=FWD+LEFT); +// } +// xdistribute(30) { +// _show_edges(edges=TOP+RIGHT); +// _show_edges(edges=TOP+BACK); +// _show_edges(edges=TOP+LEFT); +// _show_edges(edges=TOP+FRONT); +// } +// } +// Figure(3D,Med,VPD=205,NoScales): Vectors pointing toward a face select all edges surrounding that face. +// ydistribute(50) { +// xdistribute(30) { +// _show_edges(edges=LEFT); +// _show_edges(edges=FRONT); +// _show_edges(edges=RIGHT); +// } +// xdistribute(30) { +// _show_edges(edges=TOP); +// _show_edges(edges=BACK); +// _show_edges(edges=BOTTOM); +// } +// } +// Figure(3D,Big,VPD=300,NoScales): Vectors pointing toward a corner select all edges surrounding that corner. +// ydistribute(50) { +// xdistribute(30) { +// _show_edges(edges=FRONT+LEFT+TOP); +// _show_edges(edges=FRONT+RIGHT+TOP); +// _show_edges(edges=FRONT+LEFT+BOT); +// _show_edges(edges=FRONT+RIGHT+BOT); +// } +// xdistribute(30) { +// _show_edges(edges=TOP+LEFT+BACK); +// _show_edges(edges=TOP+RIGHT+BACK); +// _show_edges(edges=BOT+LEFT+BACK); +// _show_edges(edges=BOT+RIGHT+BACK); +// } +// } +// Figure(3D,Med,VPD=205,NoScales): Named Edge Sets +// ydistribute(50) { +// xdistribute(30) { +// _show_edges(edges="X"); +// _show_edges(edges="Y"); +// _show_edges(edges="Z"); +// } +// xdistribute(30) { +// _show_edges(edges="ALL"); +// _show_edges(edges="NONE"); +// } +// } +// Figure(3D,Big,VPD=310,NoScales): Next are some examples showing how you can combine edge descriptors to obtain different edge sets. You can specify the top front edge with a numerical vector or by combining the named direction vectors. If you combine them as a list you get all the edges around the front and top faces. Adding `except` removes an edge. +// xdistribute(43){ +// _show_edges(_edges([0,-1,1]),toplabel=["edges=[0,-1,1]"]); +// _show_edges(_edges(TOP+FRONT),toplabel=["edges=TOP+FRONT"]); +// _show_edges(_edges([TOP,FRONT]),toplabel=["edges=[TOP,FRONT]"]); +// _show_edges(_edges([TOP,FRONT],TOP+FRONT),toplabel=["edges=[TOP,FRONT]","except=TOP+FRONT"]); +// } +// Figure(3D,Big,VPD=310,NoScales): Using `except=BACK` removes the four edges surrounding the back face if they are present in the edge set. In the first example only one edge needs to be removed. In the second example we remove two of the Z-aligned edges. The third example removes all four back edges from the default edge set of all edges. You can explicitly give `edges="ALL"` but it is not necessary, since this is the default. In the fourth example, the edge set of Y-aligned edges contains no back edges, so the `except` parameter has no effect. +// xdistribute(43){ +// _show_edges(_edges(BOT,BACK), toplabel=["edges=BOT","except=BACK"]); +// _show_edges(_edges("Z",BACK), toplabel=["edges=\"Z\"", "except=BACK"]); +// _show_edges(_edges("ALL",BACK), toplabel=["(edges=\"ALL\")", "except=BACK"]); +// _show_edges(_edges("Y",BACK), toplabel=["edges=\"Y\"","except=BACK"]); +// } +// Figure(3D,Big,NoScales,VPD=310): On the left `except` is a list to remove two edges. In the center we show a corner edge set defined by a numerical vector, and at the right we remove that same corner edge set with named direction vectors. +// xdistribute(52){ +// _show_edges(_edges("ALL",[FRONT+RIGHT,FRONT+LEFT]), +// toplabel=["except=[FRONT+RIGHT,"," FRONT+LEFT]"]); +// _show_edges(_edges([1,-1,1]),toplabel=["edges=[1,-1,1]"]); +// _show_edges(_edges([TOP,BOT], TOP+RIGHT+FRONT),toplabel=["edges=[TOP,BOT]","except=TOP+RIGHT+FRONT"]); +// } +// Subsection: Specifying Corners +// Modules operating on corners use two arguments to describe the corner set they will use: The `corners` argument +// is a list of corner set descriptors to include in the corner set, and the `except` argument is a list of +// corner set descriptors to remove from the corner set. +// The default value for `corners` is `"ALL"`, the set of all corners. +// The default value for `except` is the empty set, meaning no corners are removed. +// If either argument is just a single corner set +// descriptor it can be passed directly rather than in a singleton list. +// Each corner set descriptor must be one of: +// - A vector pointing towards a corner, indicating that corner. +// - A vector pointing towards an edge indicating both corners at the ends of that edge. +// - A vector pointing towards a face, indicating all the corners of that face. +// - The string `"ALL"`, indicating all corners. +// - The string `"NONE"`, indicating no corners at all. +// - A length 8 vector where each entry corresponds to a corner and is 1 if the corner is included and 0 if it is excluded. The corner ordering is +// ``` +// [X-Y-Z-, X+Y-Z-, X-Y+Z-, X+Y+Z-, X-Y-Z+, X+Y-Z+, X-Y+Z+, X+Y+Z+] +// ``` +// You can specify corner descriptors directly by giving a vector, or you can use sums of the +// named direction vectors described above. Below we show all of the corner sets you can +// describe with sums of the direction vectors and then we show some examples of combining +// corner set descriptors. +// Figure(3D,Big,NoScales,VPD=300): Vectors pointing toward a corner select that corner. +// ydistribute(55) { +// xdistribute(35) { +// _show_corners(corners=FRONT+LEFT+TOP); +// _show_corners(corners=FRONT+RIGHT+TOP); +// _show_corners(corners=FRONT+LEFT+BOT); +// _show_corners(corners=FRONT+RIGHT+BOT); +// } +// xdistribute(35) { +// _show_corners(corners=TOP+LEFT+BACK); +// _show_corners(corners=TOP+RIGHT+BACK); +// _show_corners(corners=BOT+LEFT+BACK); +// _show_corners(corners=BOT+RIGHT+BACK); +// } +// } +// Figure(3D,Big,NoScales,VPD=340): Vectors pointing toward an edge select the corners and the ends of the edge. +// ydistribute(55) { +// xdistribute(35) { +// _show_corners(corners=BOT+RIGHT); +// _show_corners(corners=BOT+BACK); +// _show_corners(corners=BOT+LEFT); +// _show_corners(corners=BOT+FRONT); +// } +// xdistribute(35) { +// _show_corners(corners=FWD+RIGHT); +// _show_corners(corners=BACK+RIGHT); +// _show_corners(corners=BACK+LEFT); +// _show_corners(corners=FWD+LEFT); +// } +// xdistribute(35) { +// _show_corners(corners=TOP+RIGHT); +// _show_corners(corners=TOP+BACK); +// _show_corners(corners=TOP+LEFT); +// _show_corners(corners=TOP+FRONT); +// } +// } +// Figure(3D,Med,NoScales,VPD=225): Vectors pointing toward a face select the corners of the face. +// ydistribute(55) { +// xdistribute(35) { +// _show_corners(corners=LEFT); +// _show_corners(corners=FRONT); +// _show_corners(corners=RIGHT); +// } +// xdistribute(35) { +// _show_corners(corners=TOP); +// _show_corners(corners=BACK); +// _show_corners(corners=BOTTOM); +// } +// } +// Figure(3D,Med,NoScales,VPD=200): Corners by name +// xdistribute(35) { +// _show_corners(corners="ALL"); +// _show_corners(corners="NONE"); +// } +// Figure(3D,Big,NoScales,VPD=300): Next are some examples showing how you can combine corner descriptors to obtain different corner sets. You can specify corner sets numerically or by adding together named directions. The third example shows a list of two corner specifications, giving all the corners on the front face or the right face. +// xdistribute(52){ +// _show_corners(_corners([1,-1,-1]),toplabel=["corners=[1,-1,-1]"]); +// _show_corners(_corners(BOT+RIGHT+FRONT),toplabel=["corners=BOT+RIGHT+FRONT"]); +// _show_corners(_corners([FRONT,RIGHT]), toplabel=["corners=[FRONT,RIGHT]"]); +// } +// Figure(3D,Big,NoScales,VPD=300): Corners for one edge, two edges, and all the edges except the two on one edge. Note that since the default is all edges, you only need to give the except argument in this case: +// xdistribute(52){ +// _show_corners(_corners(FRONT+TOP), toplabel=["corners=FRONT+TOP"]); +// _show_corners(_corners([FRONT+TOP,BOT+BACK]), toplabel=["corners=[FRONT+TOP,"," BOT+BACK]"]); +// _show_corners(_corners("ALL",FRONT+TOP), toplabel=["(corners=\"ALL\")","except=FRONT+TOP"]); +// } +// Figure(3D,Med,NoScales,VPD=240): The first example shows a single corner removed from the top corners using a numerical vector. The second one shows removing a set of two corner descriptors from the implied set of all corners. +// xdistribute(58){ +// _show_corners(_corners(TOP,[1,1,1]), toplabel=["corners=TOP","except=[1,1,1]"]); +// _show_corners(_corners("ALL",[FRONT+RIGHT+TOP,FRONT+LEFT+BOT]), +// toplabel=["except=[FRONT+RIGHT+TOP,"," FRONT+LEFT+BOT]"]); +// } +// Subsection: Anchoring of Non-Rectangular Objects and Anchor Type (atype) +// We focused above on rectangular objects that have well-defined faces and edges aligned with the coordinate axes. +// Things get difficult when the objects are curved, or even when their edges are not neatly aligned with the coordinate axes. +// In these cases, the library may provide multiple different anchoring schemes, called the anchor types. When a module supports +// multiple anchor types, use the `atype=` parameter to select the anchor type you need. +// . +// First consider the case of a simple rectangle whose corners have been rounded. Where should the anchors lie? +// The default anchor type puts them in the same location as the anchors of an unrounded rectangle, which means that for +// positive rounding radii, they are not even located on the perimeter of the object. +// Figure(2D,Med,NoAxes): Default "box" atype anchors for a rounded {{rect()}} +// rect([100,50], rounding=[10,0,0,-20],chamfer=[0,10,-20,0]) show_anchors(); +// Continues: +// This choice enables you to position the box, or attach things to it, without regard to its rounding or chamfers. If you need to +// anchor onto the roundovers or chamfers then you can use the "perim" anchor type: +// Figure(2D,Med,NoAxes): The "perim" atype for a rounded and chamfered {{rect()}} +// rect([100,50], rounding=[10,0,0,-20],chamfer=[0,10,-20,0],atype="perim") show_anchors(); +// Continues: +// With this anchor type, the anchors are located on the perimeter. For positive roundings they point in the standard anchor direction; +// for negative roundings they are parallel to the base. As noted above, for circles, cylinders, and spheres, the anchor point is +// determined by choosing the point where the anchor vector intersects the shape. On a circle, this results in an anchor whose direction +// matches the user provided anchor vector. But on an ellipse, something else happens: +// Figure(2D,Med,NoAxes): Anchors on an ellipse. The red arrow shows a TOP+RIGHT anchor direction. +// ellipse([70,30]) show_anchors(); +// stroke([[0,0],[45,45]], color="red",endcap2="arrow2"); +// Continues: +// For a TOP+RIGHT anchor direction, the surface normal at the intersection point does not match the anchor direction, +// so the direction of the anchor shown in blue does not match the direction specified, in red. +// Anchors computed this way have anchor type "intersect". When a shape is concave, intersection anchors can produce +// a result buried inside the shape's concavity. Consider the RIGHT anchor of this supershape example: +// Figure(2D,Med,NoAxes): A supershape with "intersect" anchor type: +// supershape(n=150,r=75, m1=4, n1=4.0,n2=16, n3=1.5, a=0.9, b=9,atype="intersect") show_anchors(); +// Continues: +// A different anchor type called "hull" finds anchors that are on the convex hull of the shape. +// Figure(2D,Med,NoAxes): A supershape with "hull" anchor type: +// supershape(n=150,r=55, m1=4, n1=4.0,n2=16, n3=1.5, a=0.9, b=9,atype="hull") show_anchors(); +// Continues: +// Hull anchoring works by creating the line (or plane in 3D) that is normal to the specified anchor direction, and +// finding the point farthest from the center that intersects that line (or plane). +// Figure(2D,Med,NoAxes): Finding the RIGHT and BACK+LEFT "hull" anchors +// supershape(n=128,r=55, m1=4, n1=4.0,n2=16, n3=1.5, a=0.9, b=9,atype="hull") { +// position(RIGHT) color_this("red")rect([1,90],anchor=LEFT); +// attach(RIGHT)anchor_arrow2d(13); +// attach(BACK+LEFT) { +// anchor_arrow2d(13); +// color_this("red")rect([30,1]); +// } +// } +// Continues: +// In the example the RIGHT anchor is found when the normal line (shown in red) is tangent to the shape at two points. +// The anchor is then taken to be the midpoint. The BACK+LEFT anchor occurs with a single tangent point, and the +// anchor point is located at the tangent point. For circles intersection is done to the exact circle, but for other +// shapes these calculations are done on the point lists that defines the shape, so if you change the number of points +// in the list, the precise location of the anchors can change. You can also get surprising results if your point list is badly chosen. +// Figure(2D,Med,NoAxes): Circle anchor in blue. The red anchor is computed to a point list of a circle with 17 segments. +// circle(r=31,$fn=128) attach(TOP)anchor_arrow2d(15); +// region(circle(r=33,$fn=17)) {color("red")attach(TOP)anchor_arrow2d(13);} +// Continues: +// The figure shows a large horizontal offset due to a poor choice of sampling for the circular shape when using the "hull" anchor type. +// The determination of "hull" or "intersect" anchors may depend on the location of the centerpoint used in the computation. +// Some of the modules allow you to change the centerpoint using a `cp=` argument. If you need to change the centerpoint for +// a module that does not provide this option, you can use the generic {{region()}} module, which will let you specify a centerpoint. +// The default center point is the centroid, specified by "centroid". You can also choose "mean", which gives the mean of all +// the data points, or "bbox", which gives the centerpoint of the bounding box for the data. Your last option for centerpoint is to +// choose an arbitrary point that meets your needs. +// Figure(2D,Med,NoAxes): The centerpoint for "intersect" anchors is located at the red dot +// region(supershape(n=128,r=55, m1=4, n1=4.0,n2=16, n3=1.5, a=0.9, b=9),atype="intersect",cp=[0,30]) show_anchors(); +// color("red")back(30)circle(r=2,$fn=16); +// Continues: +// Note that all the anchors for an object have to be determined based on one anchor type and relative to the same centerpoint. +// The supported anchor types for each module appear in the "Anchor Types" section of its entry. + + + + + +// Section: Attachment Positioning + +// Module: position() +// Synopsis: Attaches children to a parent object at an anchor point. +// SynTags: Trans +// Topics: Attachments +// See Also: attachable(), attach(), orient() +// Usage: +// PARENT() position(from) CHILDREN; +// Description: +// Attaches children to a parent object at an anchor point. For a step-by-step explanation +// of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// from = The vector, or name of the parent anchor point to attach to. +// from = The vector, or name of the parent anchor point to attach to. +// Side Effects: +// `$attach_anchor` for each `from=` anchor given, this is set to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. +// `$attach_to` is set to `undef`. +// `$attach_norot` is set to `true`. +// `$align` set to the anchor that will position a child flush to the edge of the parent at specified position. +// Example: +// spheroid(d=20) { +// position(TOP) cyl(l=10, d1=10, d2=5, anchor=BOTTOM); +// position(RIGHT) cyl(l=10, d1=10, d2=5, anchor=BOTTOM); +// position(FRONT) cyl(l=10, d1=10, d2=5, anchor=BOTTOM); +// } +// Example: Child would require anchor of RIGHT+FRONT+BOT if given explicitly. +// cuboid([50,40,15]) +// position(RIGHT+FRONT+TOP) +// color("lightblue")prismoid([10,5],[7,4],height=4, anchor=$align); +// Example: Child requires a different anchor for each position, so explicit specification of the anchor is impossible in this case. +// cuboid([50,40,15]) +// position([RIGHT+TOP,LEFT+TOP]) +// color("lightblue")prismoid([10,5],[7,4],height=4, anchor=$align); +// Example: If you try to spin your child, the spin happens after the position anchor, so the child will not be flush: +// cuboid([50,40,15]) +// position([RIGHT+TOP]) +// color("lightblue")prismoid([10,5],[7,4],height=4, +// anchor=$align, spin=90); +// Example: You can instead spin the attached children using {{orient()}}. In this example, the required anchor is BOT+FWD, which is less obvious. +// cuboid([50,40,15]) +// position(RIGHT+TOP) +// orient(TOP, spin=90) +// color("lightblue")prismoid([10,5],[7,4],height=4, anchor=$align); +// Example: Of course, {{orient()}} can also place children on different sides of the parent. In this case you don't have to figure out that the required anchor is BOT+BACK. +// cuboid([50,40,15]) +// position(RIGHT+TOP) +// orient(RIGHT) +// color("lightblue")prismoid([10,5],[7,4],height=4, anchor=$align); +// Example: You can combine this with {{diff()}} to remove the child. Note that it's more intuitive to shift the child after positioning, relative to the global coordinate system: +// diff() +// cuboid([50,40,15]) +// right(.1)up(.1) +// position(RIGHT+TOP) +// orient(LEFT) +// tag("remove")cuboid([10,5,4], anchor=$align); +module position(from) +{ + req_children($children); + assert($parent_geom != undef, "No object to attach to!"); + anchors = (is_vector(from)||is_string(from))? [from] : from; + two_d = _attach_geom_2d($parent_geom); + for (anchr = anchors) { + anch = _find_anchor(anchr, $parent_geom); + $attach_to = undef; + $attach_anchor = anch; + $attach_norot = true; + $align=two_d && anchr.y!=0 ? [anchr.x,-anchr.y] + :!two_d && anchr.z!=0 ? [anchr.x, anchr.y, -anchr.z] + : -anchr; + translate(anch[1]) children(); + } +} + + +// Module: orient() +// Synopsis: Orients children's tops in the directon of the specified anchor. +// SynTags: Trans +// Topics: Attachments +// See Also: attachable(), attach(), orient() +// Usage: +// PARENT() orient(anchor, [spin]) CHILDREN; +// Description: +// Orients children such that their top is tilted in the direction of the specified parent anchor point. +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// anchor = The anchor on the parent which you want to match the orientation of. +// spin = The spin to add to the children. (Overrides anchor spin.) +// Side Effects: +// `$attach_anchor` is set to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for the `anchor=`, if given. +// `$attach_to` is set to `undef`. +// `$attach_norot` is set to `true`. +// `$align` is set to the anchor that will position the child flush on the parent at a designated {{position()}} +// +// Example: When orienting to an anchor, the spin of the anchor may cause confusion: +// prismoid([50,50],[30,30],h=40) { +// position(TOP+RIGHT) +// orient(RIGHT) +// prismoid([30,30],[0,5],h=20,anchor=BOT+LEFT); +// } +// Example: You can override anchor spin with `spin=`. +// prismoid([50,50],[30,30],h=40) { +// position(TOP+RIGHT) +// orient(RIGHT,spin=0) +// prismoid([30,30],[0,5],h=20,anchor=BOT+LEFT); +// } +// Example: Or you can anchor the child from the back +// prismoid([50,50],[30,30],h=40) { +// position(TOP+RIGHT) +// orient(RIGHT) +// prismoid([30,30],[0,5],h=20,anchor=BOT+BACK); +// } +module orient(anchor, spin) { + req_children($children); + check= + assert($parent_geom != undef, "No parent to orient from!") + assert(is_string(anchor) || is_vector(anchor)); + anch = _find_anchor(anchor, $parent_geom); + two_d = _attach_geom_2d($parent_geom); + fromvec = two_d? BACK : UP; + spin = default(spin, anch[3]); + assert(is_finite(spin)); + + $align = is_undef($attach_anchor) ? undef + : two_d ? let(newalign=rot(from=anch[2], to=fromvec, p=zrot(-spin,$attach_anchor[0]))) + [sign(newalign.x), -1] + : let(newalign=rot(spin, from=fromvec, to=anch[2], reverse=true, p=$attach_anchor[0])) + [sign(newalign.x), sign(newalign.y), -1]; + $attach_to = undef; + $attach_anchor = anch; + $attach_norot = true; + if (two_d) + rot(spin)rot(from=fromvec, to=anch[2]) children(); + else + rot(spin, from=fromvec, to=anch[2]) children(); +} + + + + +// Module: attach() +// Synopsis: Attaches children to a parent object at an anchor point and orientation. +// SynTags: Trans +// Topics: Attachments +// See Also: attachable(), position(), face_profile(), edge_profile(), corner_profile() +// Usage: +// PARENT() attach(from, [overlap=], [norot=]) CHILDREN; +// PARENT() attach(from, to, [overlap=], [norot=]) CHILDREN; +// 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, as specified by the `$overlap` +// value (0 by default), or by the overriding `overlap=` argument. This is to prevent OpenSCAD +// from making non-manifold objects. You can define `$overlap=` as an argument in a parent +// module to set the default for all attachments to it. For a step-by-step explanation of +// attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// from = The vector, or name of the parent anchor point to attach to. +// to = Optional name of the child anchor point. If given, orients the child such that the named anchors align together rotationally. +// --- +// overlap = Amount to sink child into the parent. Equivalent to `down(X)` after the attach. This defaults to the value in `$overlap`, which is `0` by default. +// norot = If true, don't rotate children when attaching to the anchor point. Only translate to the anchor point. +// Side Effects: +// `$idx` is set to the index number of each anchor if a list of anchors is given. Otherwise is set to `0`. +// `$attach_anchor` for each `from=` anchor given, this is set to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. +// `$attach_to` is set to the value of the `to=` argument, if given. Otherwise, `undef` +// `$attach_norot` is set to the value of the `norot=` argument. +// Example: +// spheroid(d=20) { +// attach(TOP) down(1.5) cyl(l=11.5, d1=10, d2=5, anchor=BOTTOM); +// attach(RIGHT, BOTTOM) down(1.5) cyl(l=11.5, d1=10, d2=5); +// attach(FRONT, BOTTOM, overlap=1.5) cyl(l=11.5, d1=10, d2=5); +// } +module attach(from, to, overlap, norot=false) +{ + req_children($children); + assert($parent_geom != undef, "No object to attach to!"); + overlap = (overlap!=undef)? overlap : $overlap; + anchors = (is_vector(from)||is_string(from))? [from] : from; + for ($idx = idx(anchors)) { + anchr = anchors[$idx]; + anch = _find_anchor(anchr, $parent_geom); + two_d = _attach_geom_2d($parent_geom); + $attach_to = to; + $attach_anchor = anch; + $attach_norot = norot; + olap = two_d? [0,-overlap,0] : [0,0,-overlap]; + if (norot || (norm(anch[2]-UP)<1e-9 && anch[3]==0)) { + translate(anch[1]) translate(olap) children(); + } else { + fromvec = two_d? BACK : UP; + translate(anch[1]) rot(anch[3],from=fromvec,to=anch[2]) translate(olap) children(); + } + } +} + +// Section: Tagging + +// Module: tag() +// Synopsis: Assigns a tag to an object +// Topics: Attachments +// See Also: force_tag(), recolor(), hide(), show_only(), diff(), intersect() +// Usage: +// PARENT() tag(tag) CHILDREN; +// Description: +// Assigns the specified tag to all of the children. Note that if you want +// to apply a tag to non-tag-aware objects you need to use {{force_tag()}} instead. +// This works by setting the `$tag` variable, but it provides extra error checking and +// handling of scopes. You may set `$tag` directly yourself, but this is not recommended. +// . +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// tag = tag string, which must not contain any spaces. +// Side Effects: +// Sets `$tag` to the tag you specify, possibly with a scope prefix. +// Example(3D): Applies the tag to both cuboids instead of having to repeat `$tag="remove"` for each one. +// diff("remove") +// cuboid(10){ +// position(TOP) cuboid(3); +// tag("remove") +// { +// position(FRONT) cuboid(3); +// position(RIGHT) cuboid(3); +// } +// } +module tag(tag) +{ + req_children($children); + check= + assert(is_string(tag),"tag must be a string") + assert(undef==str_find(tag," "),str("Tag string \"",tag,"\" contains a space, which is not allowed")); + $tag = str($tag_prefix,tag); + children(); +} + + +// Module: force_tag() +// Synopsis: Assigns a tag to a non-attachable object. +// Topics: Attachments +// See Also: tag(), recolor(), hide(), show_only(), diff(), intersect() +// Usage: +// PARENT() force_tag([tag]) CHILDREN; +// Description: +// You use this module when you want to make a non-attachable or non-BOSL2 module respect tags. +// It applies to its children the tag specified (or the tag currently in force if you don't specify a tag), +// making a final determination about whether to show or hide the children. +// This means that tagging in children's children will be ignored. +// This module is specifically provided for operating on children that are not tag aware such as modules +// that don't use {{attachable()}} or built in modules such as +// - `polygon()` +// - `projection()` +// - `polyhedron()` (or use [`vnf_polyhedron()`](vnf.scad#vnf_polyhedron)) +// - `linear_extrude()` (or use [`linear_sweep()`](regions.scad#linear_sweep)) +// - `rotate_extrude()` +// - `surface()` +// - `import()` +// - `difference()` +// - `intersection()` +// - `hull()` +// . +// When you use tag-based modules like {{diff()}} with a non-attachable module, the result may be puzzling. +// Any time a test occurs for display of child() that test will succeed. This means that when diff() checks +// to see if it should show a module it will show it, and when diff() checks to see if it should subtract the module +// it will subtract it. The result will be a hole, possibly with zero-thickness edges or faces. In order to +// get the correct behavior, every non-attachable module needs an invocation of force_tag, even ones +// that are not tagged. +// . +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// tag = tag string, which must not contain any spaces +// Side Effects: +// Sets `$tag` to the tag you specify, possibly with a scope prefix. +// Example(2D): This example produces the full square without subtracting the "remove" item. When you use non-attachable modules with tags, results are unpredictable. +// diff() +// { +// polygon(square(10)); +// move(-[.01,.01])polygon(square(5),$tag="remove"); +// } +// Example(2D): Adding force_tag() fixes the model. Note you need to add it to *every* non-attachable module, even the untagged ones, as shown here. +// diff() +// { +// force_tag() +// polygon(square(10)); +// force_tag("remove") +// move(-[.01,.01])polygon(square(5)); +// } +module force_tag(tag) +{ + req_children($children); + check1=assert(is_undef(tag) || is_string(tag),"tag must be a string"); + $tag = str($tag_prefix,default(tag,$tag)); + assert(undef==str_find($tag," "),str("Tag string \"",$tag,"\" contains a space, which is not allowed")); + if(_is_shown()) + show_all() + children(); +} + + + +// Module: default_tag() +// Synopsis: Sets a default tag for all children. +// Topics: Attachments +// See Also: force_tag(), recolor(), hide(), show_only(), diff(), intersect() +// Usage: +// PARENT() default_tag(tag) CHILDREN; +// Description: +// Sets a default tag for all of the children. This is intended to be used to set a tag for a whole module +// that is then used outside the module, such as setting the tag to "remove" for easy operation with {{diff()}}. +// The default_tag() module sets the `$tag` variable only if it is not already +// set so you can have a module set a default tag of "remove" but that tag can be overridden by a {{tag()}} +// in force from a parent. If you use {{tag()}} it will override any previously +// specified tag from a parent, which can be very confusing to a user trying to change the tag on a module. +// . +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// tag = tag string, which must not contain any spaces. +// Side Effects: +// Sets `$tag` to the tag you specify, possibly with a scope prefix. +// Example(3D): The module thing() is defined with {{tag()}} and the user applied tag of "keep_it" is ignored, leaving the user puzzled. +// module thing() { tag("remove") cuboid(10);} +// diff() +// cuboid(20){ +// position(TOP) thing(); +// position(RIGHT) tag("keep_it") thing(); +// } +// Example(3D): Using default_tag() fixes this problem: the user applied tag does not get overridden by the tag hidden in the module definition. +// module thing() { default_tag("remove") cuboid(10);} +// diff() +// cuboid(20){ +// position(TOP) thing(); +// position(RIGHT) tag("keep_it") thing(); +// } +module default_tag(tag) +{ + if ($tag=="") tag(tag) children(); + else children(); +} + + +// Module: tag_scope() +// Synopsis: Creates a new tag scope. +// See Also: tag(), force_tag(), default_tag() +// Topics: Attachments +// Usage: +// tag_scope([scope]) CHILDREN; +// Description: +// Creates a tag scope with locally altered tag names to avoid tag name conflict with other code. +// This is necessary when writing modules because the module's caller might happen to use the same tags. +// Note that if you directly set the `$tag` variable then tag scoping will not work correctly. +// Side Effects: +// `$tag_prefix` is set to the value of `scope=` if given, otherwise is set to a random string. +// Example: In this example the ring module uses "remove" tags which will conflict with use of the same tags by the parent. +// module ring(r,h,w=1,anchor,spin,orient) +// { +// tag_scope("ringscope") +// attachable(anchor,spin,orient,r=r,h=h){ +// diff() +// cyl(r=r,h=h) +// tag("remove") cyl(r=r-w,h=h+1); +// children(); +// } +// } +// // Calling the module using "remove" tags +// // will conflict with internal tag use in +// // the ring module. +// $fn=32; +// diff(){ +// ring(10,7,w=4); +// tag("remove")ring(8,8); +// tag("remove")diff("rem"){ +// ring(9.5,8,w=1); +// tag("rem")ring(9.5,8,w=.3); +// } +// } +module tag_scope(scope){ + req_children($children); + scope = is_undef(scope) ? rand_str(20) : scope; + assert(is_string(scope), "scope must be a string"); + assert(undef==str_find(scope," "),str("Scope string \"",scope,"\" contains a space, which is not allowed")); + $tag_prefix=scope; + children(); +} + + +// Section: Attachment Modifiers + +// Module: diff() +// Synopsis: Performs a differencing operation using tags rather than hierarchy to control what happens. +// Topics: Attachments +// See Also: tag(), force_tag(), recolor(), show_only(), hide(), tag_diff(), intersect(), tag_intersect() +// Usage: +// diff([remove], [keep]) PARENT() CHILDREN; +// Description: +// Performs a differencing operation using tags to control what happens. This is specifically intended to +// address the situation where you want differences between a parent and child object, something +// that is impossible with the native difference() module. +// The children to diff are grouped into three categories, regardless of nesting level. +// The `remove` argument is a space delimited list of tags specifying objects to +// subtract. The `keep` argument is a similar list of tags giving objects to be kept. +// Objects not matching either the `remove` or `keep` lists form the third category of base objects. +// To produce its output, diff() forms the union of all the base objects and then +// subtracts all the objects with tags in `remove`. Finally it adds in objects listed in `keep`. +// Attachable objects should be tagged using {{tag()}} +// and non-attachable objects with {{force_tag()}}. +// . +// Remember when using tagged operations with that the operations don't happen in hierarchical order, since +// the point of tags is to break the hierarchy. If you tag an object with a keep tag, nothing will be +// subtracted from it, no matter where it appears because kept objects are unioned in at the end. +// If you want a child of an object tagged with a remove tag to stay in the model it may be +// better to give it a tag that is not a remove tag or a keep tag. Such an object *will* be subject to +// subtractions from other remove-tagged objects. +// . +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// remove = String containing space delimited set of tag names of children to difference away. Default: `"remove"` +// keep = String containing space delimited set of tag names of children to keep; that is, to union into the model after differencing is completed. Default: `"keep"` +// Example: Diffing using default tags +// diff() +// cuboid(50) { +// tag("remove") attach(TOP) sphere(d=40); +// tag("keep") attach(CTR) cylinder(h=40, d=10); +// } +// Example: The "hole" items are subtracted from everything else. The other tags can be anything you find convenient. +// diff("hole") +// tag("body")sphere(d=100) { +// tag("pole") zcyl(d=55, h=100); // attach() not needed for center-to-center. +// tag("hole") { +// xcyl(d=55, h=101); +// ycyl(d=55, h=101); +// } +// tag("axle")zcyl(d=15, h=140); +// } +// Example: +// diff(keep="axle") +// sphere(d=100) { +// tag("axle")xcyl(d=40, l=120); +// tag("remove")cuboid([40,120,100]); +// } +// Example: Masking +// diff() +// cube([80,90,100], center=true) { +// edge_mask(FWD) +// rounding_edge_mask(l=max($parent_size)*1.01, r=25); +// } +// Example: Here we subtract the parent object from the child. Because tags propagate to children we need to clear the "remove" tag from the child. +// diff() +// tag("remove")cuboid(10) +// tag("")position(RIGHT+BACK)cyl(r=8,h=9); +// Example(3D,VPR=[104,0,200], VPT=[-0.9,3.03, -0.74], VPD=19,NoAxes,NoScales): A pipe module that subtracts its interior when you call it using diff(). Normally if you union two pipes together, you'll get interfering walls at the intersection, but not here: +// $fn=16; +// // This module must be called by subtracting with "diff" +// module pipe(length, od, id) { +// // Strip the tag the user is using to subtract +// tag("")cylinder(h=length, d=od, center=true); +// // Leave the tag along here, so this one is removed +// cylinder(h=length+.02, d=id, center=true); +// } +// // Draw some intersecting pipes +// diff(){ +// tag("remove"){ +// pipe(length=5, od=2, id=1.9); +// zrot(10)xrot(75) +// pipe(length=5, od=2, id=1.9); +// } +// // The orange bar has its center removed +// color("orange") down(1) xcyl(h=8, d=1); +// // "keep" prevents interior of the blue bar intact +// tag("keep") recolor("blue") up(1) xcyl(h=8, d=1); +// } +// // Objects outside the diff don't have pipe interiors removed +// color("purple") down(2.2) ycyl(h=8, d=0.3); +// Example(3D,NoScales,NoAxes): Nested diff() calls work as expected, but be careful of reusing tag names, even hidden in submodules. +// $fn=32; +// diff("rem1") +// cyl(r=10,h=10){ +// diff("rem2",$tag="rem1"){ +// cyl(r=8,h=11); +// tag("rem2")diff("rem3"){ +// cyl(r=6,h=12); +// tag("rem3")cyl(r=4,h=13); +// } +// } +// } +// Example: This example shows deep nesting, where all the differences cross levels. Unlike the preceding example, each cylinder is positioned relative to its parent. Note that it suffices to use two remove tags, alternating between them at each level. +// $fn=32; +// diff("remA") +// cyl(r=9, h=6) +// tag("remA")diff("remB") +// left(.2)position(RIGHT)cyl(r=8,h=7,anchor=RIGHT) +// tag("remB")diff("remA") +// left(.2)position(LEFT)cyl(r=7,h=7,anchor=LEFT) +// tag("remA")diff("remB") +// left(.2)position(LEFT)cyl(r=6,h=8,anchor=LEFT) +// tag("remB")diff("remA") +// right(.2)position(RIGHT)cyl(r=5,h=9,anchor=RIGHT) +// tag("remA")diff("remB") +// right(.2)position(RIGHT)cyl(r=4,h=10,anchor=RIGHT) +// tag("remB")left(.2)position(LEFT)cyl(r=3,h=11,anchor=LEFT); +// Example(3D,NoAxes,NoScales): When working with Non-Attachables like rotate_extrude() you must apply {{force_tag()}} to every non-attachable object. +// back_half() +// diff("remove") +// cuboid(40) { +// attach(TOP) +// recolor("lightgreen") +// cyl(l=10,d=30); +// position(TOP+RIGHT) +// force_tag("remove") +// xrot(90) +// rotate_extrude() +// right(20) +// circle(5); +// } +// Example: Here is another example where two children are intersected using the native intersection operator, and then tagged with {{force_tag()}}. Note that because the children are at the same level, you don't need to use a tagged operator for their intersection. +// $fn=32; +// diff() +// cuboid(10){ +// force_tag("remove")intersection() +// { +// position(RIGHT) cyl(r=7,h=15); +// position(LEFT) cyl(r=7,h=15); +// } +// tag("keep")cyl(r=1,h=9); +// } +// Example: In this example the children that are subtracted are each at different nesting levels, with a kept object in between. +// $fn=32; +// diff() +// cuboid(10){ +// tag("remove")cyl(r=4,h=11) +// tag("keep")cyl(r=3,h=17) +// tag("remove")position(RIGHT)cyl(r=2,h=18); +// } +// Example: Combining tag operators can be tricky. Here the `diff()` operation keeps two tags, "fullkeep" and "keep". Then {{intersect()}} intersects the "keep" tagged item with everything else, but keeps the "fullkeep" object. +// $fn=32; +// intersect("keep","fullkeep") +// diff(keep="fullkeep keep") +// cuboid(10){ +// tag("remove")cyl(r=4,h=11); +// tag("keep") position(RIGHT)cyl(r=8,h=12); +// tag("fullkeep")cyl(r=1,h=12); +// } +// Example: In this complex example we form an intersection, subtract an object, and keep some objects. Note that for the small cylinders on either side, marking them as "keep" or removing their tag gives the same effect. This is because without a tag they become part of the intersection and the result ends up the same. For the two cylinders at the back, however, the result is different. With "keep" the cylinder on the left appears whole, but without it, the cylinder at the back right is subject to intersection. +// $fn=64; +// diff() +// intersect(keep="remove keep") +// cuboid(10,$thing="cube"){ +// tag("intersect"){ +// position(RIGHT) cyl(r=5.5,h=15) +// tag("")cyl(r=2,h=10); +// position(LEFT) cyl(r=5.54,h=15) +// tag("keep")cyl(r=2,h=10); +// } +// // Untagged it is in the intersection +// tag("") position(BACK+RIGHT) +// cyl(r=2,h=10,anchor=CTR); +// // With keep the full cylinder appears +// tag("keep") position(BACK+LEFT) +// cyl(r=2,h=10,anchor=CTR); +// tag("remove") cyl(r=3,h=15); +// } +module diff(remove="remove", keep="keep") +{ + req_children($children); + assert(is_string(remove),"remove must be a string of tags"); + assert(is_string(keep),"keep must be a string of tags"); + if (_is_shown()) + { + difference() { + hide(str(remove," ",keep)) children(); + show_only(remove) children(); + } + } + show_int(keep)children(); +} + + +// Module: tag_diff() +// Synopsis: Performs a {{diff()}} and then sets a tag on the result. +// Topics: Attachments +// See Also: tag(), force_tag(), recolor(), show_only(), hide(), diff(), intersect(), tag_intersect() +// Usage: +// tag_diff(tag, [remove], [keep]) PARENT() CHILDREN; +// Description: +// Perform a differencing operation in the manner of {{diff()}} using tags to control what happens, +// and then tag the resulting difference object with the specified tag. This forces the specified +// tag to be resolved at the level of the difference operation. In most cases, this is not necessary, +// but if you have kept objects and want to operate on this difference object as a whole object using +// more tag operations, you will probably not get the results you want if you simply use {{tag()}}. +// . +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// tag = Tag string to apply to this difference object +// remove = String containing space delimited set of tag names of children to difference away. Default: `"remove"` +// keep = String containing space delimited set of tag names of children to keep; that is, to union into the model after differencing is completed. Default: `"keep"` +// Side Effects: +// Sets `$tag` to the tag you specify, possibly with a scope prefix. +// Example: In this example we have a difference with a kept object that is then subtracted from a cube, but we don't want the kept object to appear in the final output, so this result is wrong: +// diff("rem"){ +// cuboid([20,10,30],anchor=FRONT); +// tag("rem")diff("remove","keep"){ +// cuboid([10,10,20]); +// tag("remove")cuboid([11,11,5]); +// tag("keep")cuboid([2,2,20]); +// } +// } +// Example: Using tag_diff corrects the problem: +// diff("rem"){ +// cuboid([20,10,30],anchor=FRONT); +// tag_diff("rem","remove","keep"){ +// cuboid([10,10,20]); +// tag("remove")cuboid([11,11,5]); +// tag("keep")cuboid([2,2,20]); +// } +// } +// Example: This concentric cylinder example uses "keep" and produces the wrong result. The kept cylinder gets kept in the final output instead of subtracted. This happens even when we make sure to change the `keep` argument at the top level {{diff()}} call. +// diff("rem","nothing") +// cyl(r=8,h=6) +// tag("rem")diff() +// cyl(r=7,h=7) +// tag("remove")cyl(r=6,h=8) +// tag("keep")cyl(r=5,h=9); +// Example: Changing to tag_diff() causes the kept cylinder to be subtracted, producing the desired result: +// diff("rem") +// cyl(r=8,h=6) +// tag_diff("rem") +// cyl(r=7,h=7) +// tag("remove")cyl(r=6,h=8) +// tag("keep")cyl(r=5,h=9); +module tag_diff(tag,remove="remove", keep="keep") +{ + req_children($children); + assert(is_string(remove),"remove must be a string of tags"); + assert(is_string(keep),"keep must be a string of tags"); + assert(is_string(tag),"tag must be a string"); + assert(undef==str_find(tag," "),str("Tag string \"",tag,"\" contains a space, which is not allowed")); + $tag=str($tag_prefix,tag); + if (_is_shown()) + show_all(){ + difference() { + hide(str(remove," ",keep)) children(); + show_only(remove) children(); + } + show_only(keep)children(); + } +} + + +// Module: intersect() +// Synopsis: Perform an intersection operation on children using tags rather than hierarchy to control what happens. +// Topics: Attachments +// See Also: tag(), force_tag(), recolor(), show_only(), hide(), diff(), tag_diff(), tag_intersect() +// Usage: +// intersect([intersect], [keep]) PARENT() CHILDREN; +// Description: +// Performs an intersection operation on its children, using tags to +// determine what happens. This is specifically intended to address +// the situation where you want intersections involving a parent and +// child object, something that is impossible with the native +// intersection() module. This module treats the children in three +// groups: objects matching the tags listed in `intersect`, objects +// matching tags listed in `keep`, and the remaining objects that +// don't match any of the listed tags. The intersection is computed +// between the union of the `intersect` tagged objects and union of the objects that don't +// match any of the listed tags. Finally the objects listed in `keep` are +// unioned with the result. Attachable objects should be tagged using {{tag()}} +// and non-attachable objects with {{force_tag()}}. +// . +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// intersect = String containing space delimited set of tag names of children to intersect. Default: "intersect" +// keep = String containing space delimited set of tag names of children to keep whole. Default: "keep" +// Example: +// intersect("mask", keep="axle") +// sphere(d=100) { +// tag("mask")cuboid([40,100,100]); +// tag("axle")xcyl(d=40, l=100); +// } +// Example: Combining tag operators can be tricky. Here the {{diff()}} operation keeps two tags, "fullkeep" and "keep". Then `intersect()` intersects the "keep" tagged item with everything else, but keeps the "fullkeep" object. +// $fn=32; +// intersect("keep","fullkeep") +// diff(keep="fullkeep keep") +// cuboid(10){ +// tag("remove")cyl(r=4,h=11); +// tag("keep") position(RIGHT)cyl(r=8,h=12); +// tag("fullkeep")cyl(r=1,h=12); +// } +// Example: In this complex example we form an intersection, subtract an object, and keep some objects. Note that for the small cylinders on either side, marking them as "keep" or removing their tag gives the same effect. This is because without a tag they become part of the intersection and the result ends up the same. For the two cylinders at the back, however, the result is different. With "keep" the cylinder on the left appears whole, but without it, the cylinder at the back right is subject to intersection. +// $fn=64; +// diff() +// intersect(keep="remove keep") +// cuboid(10,$thing="cube"){ +// tag("intersect"){ +// position(RIGHT) cyl(r=5.5,h=15) +// tag("")cyl(r=2,h=10); +// position(LEFT) cyl(r=5.54,h=15) +// tag("keep")cyl(r=2,h=10); +// } +// // Untagged it is in the intersection +// tag("") position(BACK+RIGHT) +// cyl(r=2,h=10,anchor=CTR); +// // With keep the full cylinder appears +// tag("keep") position(BACK+LEFT) +// cyl(r=2,h=10,anchor=CTR); +// tag("remove") cyl(r=3,h=15); +// } +module intersect(intersect="intersect",keep="keep") +{ + assert(is_string(intersect),"intersect must be a string of tags"); + assert(is_string(keep),"keep must be a string of tags"); + intersection(){ + show_only(intersect) children(); + hide(str(intersect," ",keep)) children(); + } + show_int(keep) children(); +} + + +// Module: tag_intersect() +// Synopsis: Performs an {{intersect()}} and then tags the result. +// Topics: Attachments +// See Also: tag(), force_tag(), recolor(), show_only(), hide(), diff(), tag_diff(), intersect() +// Usage: +// tag_intersect(tag, [intersect], [keep]) PARENT() CHILDREN; +// Description: +// Perform an intersection operation in the manner of {{intersect()}} using tags to control what happens, +// and then tag the resulting difference object with the specified tag. This forces the specified +// tag to be resolved at the level of the intersect operation. In most cases, this is not necessary, +// but if you have kept objects and want to operate on this difference object as a whole object using +// more tag operations, you will probably not get the results you want if you simply use {{tag()}}. +// . +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// tag = Tag to set for the intersection +// intersect = String containing space delimited set of tag names of children to intersect. Default: "intersect" +// keep = String containing space delimited set of tag names of children to keep whole. Default: "keep" +// Side Effects: +// Sets `$tag` to the tag you specify, possibly with a scope prefix. +// Example: Without `tag_intersect()` the kept object is not included in the difference. +// $fn=32; +// diff() +// cuboid([20,15,9]) +// tag("remove")intersect() +// cuboid(10){ +// tag("intersect")position(RIGHT) cyl(r=7,h=10); +// tag("keep")position(LEFT)cyl(r=4,h=10); +// } +// Example: Using tag_intersect corrects the problem. +// $fn=32; +// diff() +// cuboid([20,15,9]) +// tag_intersect("remove") +// cuboid(10){ +// tag("intersect")position(RIGHT) cyl(r=7,h=10); +// tag("keep")position(LEFT)cyl(r=4,h=10); +// } +module tag_intersect(tag,intersect="intersect",keep="keep") +{ + assert(is_string(intersect),"intersect must be a string of tags"); + assert(is_string(keep),"keep must be a string of tags"); + assert(is_string(tag),"tag must be a string"); + assert(undef==str_find(tag," "),str("Tag string \"",tag,"\" contains a space, which is not allowed")); + $tag=str($tag_prefix,tag); + if (_is_shown()) + show_all(){ + intersection(){ + show_only(intersect) children(); + hide(str(intersect," ",keep)) children(); + } + show_only(keep) children(); + } +} + + +// Module: conv_hull() +// Synopsis: Performs a hull operation on the children using tags to determine what happens. +// Topics: Attachments +// See Also: tag(), recolor(), show_only(), hide(), diff(), intersect() +// Usage: +// conv_hull([keep]) CHILDREN; +// Description: +// Performs a hull operation on the children using tags to determine what happens. The items +// not tagged with the `keep` tags are combined into a convex hull, and the children tagged with the keep tags +// are unioned with the result. +// . +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// keep = String containing space delimited set of tag names of children to keep out of the hull. Default: "keep" +// Example: +// conv_hull("keep") +// sphere(d=100, $fn=64) { +// cuboid([40,90,90]); +// tag("keep")xcyl(d=40, l=120); +// } +// Example: difference combined with hull where all objects are relative to each other. +// $fn=32; +// diff() +// conv_hull("remove") +// cuboid(10) +// position(RIGHT+BACK)cyl(r=4,h=10) +// tag("remove")cyl(r=2,h=12); +module conv_hull(keep="keep") +{ + req_children($children); + assert(is_string(keep),"keep must be a string of tags"); + if (_is_shown()) + hull() hide(keep) children(); + show_int(keep) children(); +} + + +// Module: tag_conv_hull() +// Synopsis: Performs a {{conv_hull()}} and then sets a tag on the result. +// Topics: Attachments +// See Also: tag(), recolor(), show_only(), hide(), diff(), intersect() +// Usage: +// tag_conv_hull(tag, [keep]) CHILDREN; +// Description: +// Perform a convex hull operation in the manner of {{conv_hull()}} using tags to control what happens, +// and then tag the resulting hull object with the specified tag. This forces the specified +// tag to be resolved at the level of the hull operation. In most cases, this is not necessary, +// but if you have kept objects and want to operate on the hull object as a whole object using +// more tag operations, you will probably not get the results you want if you simply use {{tag()}}. +// . +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// keep = String containing space delimited set of tag names of children to keep out of the hull. Default: "keep" +// Side Effects: +// Sets `$tag` to the tag you specify, possibly with a scope prefix. +// Example: With a regular tag, the kept object is not handled as desired: +// diff(){ +// cuboid([30,30,9]) +// tag("remove")conv_hull("remove") +// cuboid(10,anchor=LEFT+FRONT){ +// position(RIGHT+BACK)cyl(r=4,h=10); +// tag("keep")position(FRONT+LEFT)cyl(r=4,h=10); +// } +// } +// Example: Using `tag_conv_hull()` fixes the problem: +// diff(){ +// cuboid([30,30,9]) +// tag_conv_hull("remove") +// cuboid(10,anchor=LEFT+FRONT){ +// position(RIGHT+BACK)cyl(r=4,h=10); +// tag("keep")position(FRONT+LEFT)cyl(r=4,h=10); +// } +// } +module tag_conv_hull(tag,keep="keep") +{ + req_children($children); + assert(is_string(keep),"keep must be a string of tags"); + assert(is_string(tag),"tag must be a string"); + assert(undef==str_find(tag," "),str("Tag string \"",tag,"\" contains a space, which is not allowed")); + $tag=str($tag_prefix,tag); + if (_is_shown()) + show_all(){ + hull() hide(keep) children(); + show_only(keep) children(); + } +} + + +// Module: hide() +// Synopsis: Hides attachable children with the given tags. +// Topics: Attachments +// See Also: tag(), recolor(), show_only(), show_all(), show_int(), diff(), intersect() +// Usage: +// hide(tags) CHILDREN; +// Description: +// Hides all attachable children with the given tags, which you supply as a space separated string. Previously hidden objects remain hidden, so hiding is cumulative, unlike `show_only()`. +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Side Effects: +// Sets `$tags_hidden` to include the tags you specify. +// Example: Hides part of the model. +// hide("A") +// tag("main") cube(50, anchor=CENTER, $tag="Main") { +// tag("A")attach(LEFT, BOTTOM) cylinder(d=30, h=30); +// tag("B")attach(RIGHT, BOTTOM) cylinder(d=30, h=30); +// } +// Example: Use an invisible parent to position children. Note that children must be retagged because they inherit the parent tag. +// $fn=16; +// hide("hidden") +// tag("hidden")cuboid(10) +// tag("visible") { +// position(RIGHT) cyl(r=1,h=12); +// position(LEFT) cyl(r=1,h=12); +// } +module hide(tags) +{ + req_children($children); + dummy=assert(is_string(tags), "tags must be a string"); + taglist = [for(s=str_split(tags," ",keep_nulls=false)) str($tag_prefix,s)]; + $tags_hidden = concat($tags_hidden,taglist); + children(); +} + + +// Module: show_only() +// Synopsis: Show only the children with the listed tags. +// See Also: tag(), recolor(), show_all(), show_int(), diff(), intersect() +// Topics: Attachments +// Usage: +// show_only(tags) CHILDREN; +// Description: +// Show only the children with the listed tags, which you sply as a space separated string. Only unhidden objects will be shown, so if an object is hidden either before or after the `show_only()` call then it will remain hidden. This overrides any previous `show_only()` calls. Unlike `hide()`, calls to `show_only()` are not cumulative. +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Side Effects: +// Sets `$tags_shown` to the tag you specify. +// Example: Display the attachments but not the parent +// show_only("visible") +// cube(50, anchor=CENTER) +// tag("visible"){ +// attach(LEFT, BOTTOM) cylinder(d=30, h=30); +// attach(RIGHT, BOTTOM) cylinder(d=30, h=30); +// } +module show_only(tags) +{ + req_children($children); + dummy=assert(is_string(tags), str("tags must be a string",tags)); + taglist = [for(s=str_split(tags," ",keep_nulls=false)) str($tag_prefix,s)]; + $tags_shown = taglist; + children(); +} + +// Module: show_all() +// Synopsis: Shows all children and clears tags. +// See Also: tag(), recolor(), show_only(), show_int(), diff(), intersect() +// Topics: Attachments +// Usage; +// show_all() CHILDREN; +// Description: +// Shows all children. Clears the list of hidden tags and shown tags so that all child objects will be +// fully displayed. +// Side Effects: +// Sets `$tags_shown="ALL"` +// Sets `$tags_hidden=[]` +module show_all() +{ + req_children($children); + $tags_shown="ALL"; + $tags_hidden=[]; + children(); +} + + +// Module: show_int() +// Synopsis: Shows children with the listed tags which were already shown in the parent context. +// See Also: tag(), recolor(), show_only(), show_all(), show_int(), diff(), intersect() +// Topics: Attachments +// Usage: +// show_int(tags) CHILDREN; +// Description: +// Show only the children with the listed tags which were already shown in the parent context. +// This intersects the current show list with the list of tags you provide. +// Arguments: +// tags = list of tags to show +// Side Effects: +// Sets `$tags_shown` +module show_int(tags) +{ + req_children($children); + dummy=assert(is_string(tags), str("tags must be a string",tags)); + taglist = [for(s=str_split(tags," ",keep_nulls=false)) str($tag_prefix,s)]; + $tags_shown = $tags_shown == "ALL" ? taglist : set_intersection($tags_shown,taglist); + children(); +} + + +// Section: Mask Attachment + + +// Module: face_mask() +// Synopsis: Ataches a 3d mask shape to the given faces of the parent. +// SynTags: Trans +// Topics: Attachments, Masking +// See Also: attachable(), position(), attach(), edge_mask(), corner_mask(), face_profile(), edge_profile(), corner_profile() +// Usage: +// PARENT() face_mask(faces) CHILDREN; +// Description: +// Takes a 3D mask shape, and attaches it to the given faces, with the appropriate orientation to be +// differenced away. The mask shape should be vertically oriented (Z-aligned) with the bottom half +// (Z-) shaped to be diffed away from the face of parent attachable shape. If no tag is set then +// `face_mask()` sets the tag for children to "remove" so that it will work with the default {{diff()}} tag. +// For details on specifying the faces to mask see [Specifying Faces](attachments.scad#subsection-specifying-faces). +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// edges = Faces to mask. See [Specifying Faces](attachments.scad#subsection-specifying-faces) for information on specifying faces. Default: All faces +// Side Effects: +// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. +// `$idx` is set to the index number of each face in the list of faces given. +// `$attach_anchor` is set for each face given, to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. +// Example: +// diff() +// cylinder(r=30, h=60) +// face_mask(TOP) { +// rounding_cylinder_mask(r=30,rounding=5); +// cuboid([5,61,10]); +// } +// Example: Using `$idx` +// diff() +// cylinder(r=30, h=60) +// face_mask([TOP, BOT]) +// zrot(45*$idx) zrot_copies([0,90]) cuboid([5,61,10]); +module face_mask(faces=[LEFT,RIGHT,FRONT,BACK,BOT,TOP]) { + req_children($children); + faces = is_vector(faces)? [faces] : faces; + assert(all([for (face=faces) is_vector(face) && sum([for (x=face) x!=0? 1 : 0])==1]), "Vector in faces doesn't point at a face."); + assert($parent_geom != undef, "No object to attach to!"); + attach(faces) { + if ($tag=="") tag("remove") children(); + else children(); + } +} + + +// Module: edge_mask() +// Synopsis: Attaches a 3D mask shape to the given edges of the parent. +// SynTags: Trans +// Topics: Attachments, Masking +// See Also: attachable(), position(), attach(), face_mask(), corner_mask(), face_profile(), edge_profile(), corner_profile() +// Usage: +// PARENT() edge_mask([edges], [except]) CHILDREN; +// Description: +// Takes a 3D mask shape, and attaches it to the given edges, with the appropriate orientation to be +// differenced away. The mask shape should be vertically oriented (Z-aligned) with the back-right +// quadrant (X+Y+) shaped to be diffed away from the edge of parent attachable shape. If no tag is set +// then `edge_mask` sets the tag for children to "remove" so that it will work with the default {{diff()}} tag. +// For details on specifying the edges to mask see [Specifying Edges](attachments.scad#subsection-specifying-edges). +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Figure: A Typical Edge Rounding Mask +// module roundit(l,r) difference() { +// translate([-1,-1,-l/2]) +// cube([r+1,r+1,l]); +// translate([r,r]) +// cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4)); +// } +// roundit(l=30,r=10); +// Arguments: +// edges = Edges to mask. See [Specifying Edges](attachments.scad#subsection-specifying-edges). Default: All edges. +// except = Edges to explicitly NOT mask. See [Specifying Edges](attachments.scad#subsection-specifying-edges). Default: No edges. +// Side Effects: +// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. +// Side Effects: +// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. +// `$idx` is set to the index number of each edge. +// `$attach_anchor` is set for each edge given, to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. +// Example: +// diff() +// cube([50,60,70],center=true) +// edge_mask([TOP,"Z"],except=[BACK,TOP+LEFT]) +// rounding_edge_mask(l=71,r=10); +module edge_mask(edges=EDGES_ALL, except=[]) { + req_children($children); + assert($parent_geom != undef, "No object to attach to!"); + edges = _edges(edges, except=except); + vecs = [ + for (i = [0:3], axis=[0:2]) + if (edges[axis][i]>0) + EDGE_OFFSETS[axis][i] + ]; + for ($idx = idx(vecs)) { + vec = vecs[$idx]; + vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0); + dummy=assert(vcount == 2, "Not an edge vector!"); + anch = _find_anchor(vec, $parent_geom); + $attach_to = undef; + $attach_anchor = anch; + $attach_norot = true; + rotang = + vec.z<0? [90,0,180+v_theta(vec)] : + vec.z==0 && sign(vec.x)==sign(vec.y)? 135+v_theta(vec) : + vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+v_theta(vec)] : + [-90,0,180+v_theta(vec)]; + translate(anch[1]) rot(rotang) + if ($tag=="") tag("remove") children(); + else children(); + } +} + + +// Module: corner_mask() +// Synopsis: Attaches a 3d mask shape to the given corners of the parent. +// SynTags: Trans +// Topics: Attachments, Masking +// See Also: attachable(), position(), attach(), face_mask(), edge_mask(), face_profile(), edge_profile(), corner_profile() +// Usage: +// PARENT() corner_mask([corners], [except]) CHILDREN; +// Description: +// Takes a 3D mask shape, and attaches it to the specified corners, with the appropriate orientation to +// be differenced away. The 3D corner mask shape should be designed to mask away the X+Y+Z+ octant. If no tag is set +// then `corner_mask` sets the tag for children to "remove" so that it will work with the default {{diff()}} tag. +// See [Specifying Corners](attachments.scad#subsection-specifying-corners) for information on how to specify corner sets. +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// corners = Corners to mask. See [Specifying Corners](attachments.scad#subsection-specifying-corners). Default: All corners. +// except = Corners to explicitly NOT mask. See [Specifying Corners](attachments.scad#subsection-specifying-corners). Default: No corners. +// Side Effects: +// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. +// `$idx` is set to the index number of each corner. +// `$attach_anchor` is set for each corner given, to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. +// Example: +// diff() +// cube(100, center=true) +// corner_mask([TOP,FRONT],LEFT+FRONT+TOP) +// difference() { +// translate(-0.01*[1,1,1]) cube(20); +// translate([20,20,20]) sphere(r=20); +// } +module corner_mask(corners=CORNERS_ALL, except=[]) { + req_children($children); + assert($parent_geom != undef, "No object to attach to!"); + corners = _corners(corners, except=except); + vecs = [for (i = [0:7]) if (corners[i]>0) CORNER_OFFSETS[i]]; + for ($idx = idx(vecs)) { + vec = vecs[$idx]; + vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0); + dummy=assert(vcount == 3, "Not an edge vector!"); + anch = _find_anchor(vec, $parent_geom); + $attach_to = undef; + $attach_anchor = anch; + $attach_norot = true; + rotang = vec.z<0? + [ 0,0,180+v_theta(vec)-45] : + [180,0,-90+v_theta(vec)-45]; + translate(anch[1]) rot(rotang) + if ($tag=="") tag("remove") children(); + else children(); + } +} + + +// Module: face_profile() +// Synopsis: Extrudes a 2D edge profile into a mask for all edges and corners of the given faces on the parent. +// SynTags: Geom +// Topics: Attachments, Masking +// See Also: attachable(), position(), attach(), edge_profile(), corner_profile(), face_mask(), edge_mask(), corner_mask() +// Usage: +// PARENT() face_profile(faces, r|d=, [convexity=]) CHILDREN; +// Description: +// Given a 2D edge profile, extrudes it into a mask for all edges and corners bounding each given face. If no tag is set +// then `face_profile` sets the tag for children to "remove" so that it will work with the default {{diff()}} tag. +// See [Specifying Faces](attachments.scad#subsection-specifying-faces) for information on specifying faces. +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// faces = Faces to mask edges and corners of. +// r = Radius of corner mask. +// --- +// d = Diameter of corner mask. +// excess = Excess length to extrude the profile to make edge masks. Default: 0.01 +// convexity = Max number of times a line could intersect the perimeter of the mask shape. Default: 10 +// Side Effects: +// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. +// `$idx` is set to the index number of each face. +// `$attach_anchor` is set for each edge or corner given, to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. +// `$profile_type` is set to `"edge"` or `"corner"`, depending on what is being masked. +// Example: +// diff() +// cube([50,60,70],center=true) +// face_profile(TOP,r=10) +// mask2d_roundover(r=10); +module face_profile(faces=[], r, d, excess=0.01, convexity=10) { + req_children($children); + faces = is_vector(faces)? [faces] : faces; + assert(all([for (face=faces) is_vector(face) && sum([for (x=face) x!=0? 1 : 0])==1]), "Vector in faces doesn't point at a face."); + r = get_radius(r=r, d=d, dflt=undef); + assert(is_num(r) && r>=0); + edge_profile(faces, excess=excess) children(); + corner_profile(faces, convexity=convexity, r=r) children(); +} + + +// Module: edge_profile() +// Synopsis: Extrudes a 2d edge profile into a mask on the given edges of the parent. +// SynTags: Geom +// Topics: Attachments, Masking +// See Also: attachable(), position(), attach(), face_profile(), edge_profile_asym(), corner_profile(), edge_mask(), face_mask(), corner_mask() +// Usage: +// PARENT() edge_profile([edges], [except], [convexity]) CHILDREN; +// 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. If no tag is set +// then `edge_profile` sets the tag for children to "remove" so that it will work with the default {{diff()}} tag. +// For details on specifying the edges to mask see [Specifying Edges](attachments.scad#subsection-specifying-edges). +// For a step-by-step +// explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// edges = Edges to mask. See [Specifying Edges](attachments.scad#subsection-specifying-edges). Default: All edges. +// except = Edges to explicitly NOT mask. See [Specifying Edges](attachments.scad#subsection-specifying-edges). Default: No edges. +// excess = Excess length to extrude the profile to make edge masks. Default: 0.01 +// convexity = Max number of times a line could intersect the perimeter of the mask shape. Default: 10 +// Side Effects: +// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. +// `$idx` is set to the index number of each edge. +// `$attach_anchor` is set for each edge given, to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. +// `$profile_type` is set to `"edge"`. +// Example: +// diff() +// cube([50,60,70],center=true) +// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) +// mask2d_roundover(r=10, inset=2); + +module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) { + req_children($children); + check1 = assert($parent_geom != undef, "No object to attach to!"); + edges = _edges(edges, except=except); + vecs = [ + for (i = [0:3], axis=[0:2]) + if (edges[axis][i]>0) + EDGE_OFFSETS[axis][i] + ]; + all_vecs_are_edges = all([for (vec = vecs) sum(v_abs(vec))==2]); + check2 = assert(all_vecs_are_edges, "All vectors must be edges."); + for ($idx = idx(vecs)) { + vec = vecs[$idx]; + anch = _find_anchor(vec, $parent_geom); + $attach_to = undef; + $attach_anchor = anch; + $attach_norot = true; + $profile_type = "edge"; + psize = point3d($parent_size); + length = [for (i=[0:2]) if(!vec[i]) psize[i]][0] + excess; + rotang = + vec.z<0? [90,0,180+v_theta(vec)] : + vec.z==0 && sign(vec.x)==sign(vec.y)? 135+v_theta(vec) : + vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+v_theta(vec)] : + [-90,0,180+v_theta(vec)]; + translate(anch[1]) { + rot(rotang) { + linear_extrude(height=length, center=true, convexity=convexity) { + if ($tag=="") tag("remove") children(); + else children(); + } + } + } + } +} + + +// Module: edge_profile_asym() +// Synopsis: Extrudes an asymmetric 2D profile into a mask on the given edges and corners of the parent. +// SynTags: Geom +// Topics: Attachments, Masking +// See Also: attachable(), position(), attach(), face_profile(), edge_profile(), corner_profile(), edge_mask(), face_mask(), corner_mask() +// Usage: +// PARENT() edge_profile([edges], [except=], [convexity=], [flip=], [corner_type=]) CHILDREN; +// Description: +// Takes an asymmetric 2D mask shape and attaches it to the selected edges and corners, with the appropriate +// orientation and extruded length to be `diff()`ed away, to give the edges and corners a matching profile. +// If no tag is set then `edge_profile_asym()` sets the tag for children to "remove" so that it will work +// with the default {{diff()}} tag. For details on specifying the edges to mask see [Specifying Edges](attachments.scad#subsection-specifying-edges). +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Profile orientation will be made consistent for all connected edges and corners. This prohibits having three +// edges meeting at any one corner. You can intert the orientations of all edges with `flip=true`. +// Arguments: +// edges = Edges to mask. See [Specifying Edges](attachments.scad#subsection-specifying-edges). Default: All edges. +// except = Edges to explicitly NOT mask. See [Specifying Edges](attachments.scad#subsection-specifying-edges). Default: No edges. +// excess = Excess length to extrude the profile to make edge masks. Default: 0.01 +// convexity = Max number of times a line could intersect the perimeter of the mask shape. Default: 10 +// flip = If true, reverses the orientation of any external profile parts at each edge. Default false +// corner_type = Specifies how exterior corners should be formed. Must be one of `"none"`, `"chamfer"`, `"round"`, or `"sharp"`. Default: `"none"` +// Side Effects: +// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set. +// `$idx` is set to the index number of each edge. +// `$attach_anchor` is set for each edge given, to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. +// `$profile_type` is set to `"edge"`. +// Example: +// ogee = [ +// "xstep",1, "ystep",1, // Starting shoulder. +// "fillet",5, "round",5, // S-curve. +// "ystep",1, "xstep",1 // Ending shoulder. +// ]; +// diff() +// cuboid(50) { +// edge_profile_asym(FRONT) +// mask2d_ogee(ogee); +// } +// Example: Flipped +// ogee = [ +// "xstep",1, "ystep",1, // Starting shoulder. +// "fillet",5, "round",5, // S-curve. +// "ystep",1, "xstep",1 // Ending shoulder. +// ]; +// diff() +// cuboid(50) { +// edge_profile_asym(FRONT, flip=true) +// mask2d_ogee(ogee); +// } +// Example: Negative Chamfering +// cuboid(50) { +// edge_profile_asym(FWD, flip=false) +// xflip() mask2d_chamfer(10); +// edge_profile_asym(BACK, flip=true, corner_type="sharp") +// xflip() mask2d_chamfer(10); +// } +// Example: Negative Roundings +// cuboid(50) { +// edge_profile_asym(FWD, flip=false) +// xflip() mask2d_roundover(10); +// edge_profile_asym(BACK, flip=true, corner_type="round") +// xflip() mask2d_roundover(10); +// } +// Example: Cornerless +// cuboid(50) { +// edge_profile_asym( +// "ALL", except=[TOP+FWD+RIGHT, BOT+BACK+LEFT] +// ) xflip() mask2d_roundover(10); +// } +// Example: More complicated edge sets +// cuboid(50) { +// edge_profile_asym( +// "ALL", except=[TOP+FWD+RIGHT, BOT+BACK+LEFT], +// corner_type="chamfer" +// ) xflip() mask2d_roundover(10); +// } + +module edge_profile_asym(edges=EDGES_ALL, except=[], excess=0.01, convexity=10, flip=false, corner_type="none") { + function _corner_orientation(pos,pvec) = + let( + j = [for (i=[0:2]) if (pvec[i]) i][0], + T = + (pos.x>0? xflip() : ident(4)) * + (pos.y>0? yflip() : ident(4)) * + (pos.z>0? zflip() : ident(4)) * + rot(-120*(2-j), v=[1,1,1]) + ) T; + + function _default_edge_orientation(edge) = + edge.z < 0? [[-edge.x,-edge.y,0], UP] : + edge.z > 0? [[-edge.x,-edge.y,0], DOWN] : + edge.y < 0? [[-edge.x,0,0], BACK] : + [[-edge.x,0,0], FWD] ; + + function _edge_transition_needs_flip(from,to) = + let( + flip_edges = [ + [BOT+FWD, [FWD+LEFT, FWD+RIGHT]], + [BOT+BACK, [BACK+LEFT, BACK+RIGHT]], + [BOT+LEFT, []], + [BOT+RIGHT, []], + [TOP+FWD, [FWD+LEFT, FWD+RIGHT]], + [TOP+BACK, [BACK+LEFT, BACK+RIGHT]], + [TOP+LEFT, []], + [TOP+RIGHT, []], + [FWD+LEFT, [TOP+FWD, BOT+FWD]], + [FWD+RIGHT, [TOP+FWD, BOT+FWD]], + [BACK+LEFT, [TOP+BACK, BOT+BACK]], + [BACK+RIGHT, [TOP+BACK, BOT+BACK]], + ], + i = search([from], flip_edges, num_returns_per_match=1)[0], + check = assert(i!=[], "Bad edge vector.") + ) in_list(to,flip_edges[i][1]); + + function _edge_corner_numbers(vec) = + let( + v2 = [for (i=idx(vec)) vec[i]? (vec[i]+1)/2*pow(2,i) : 0], + off = v2.x + v2.y + v2.z, + xs = [0, if (!vec.x) 1], + ys = [0, if (!vec.y) 2], + zs = [0, if (!vec.z) 4] + ) [for (x=xs, y=ys, z=zs) x+y+z + off]; + + function _gather_contiguous_edges(edge_corners) = + let( + no_tri_corners = all([for(cn = [0:7]) len([for (ec=edge_corners) if(in_list(cn,ec[1])) 1])<3]), + check = assert(no_tri_corners, "Cannot have three edges that meet at the same corner.") + ) + _gather_contiguous_edges_r( + [for (i=idx(edge_corners)) if(i) edge_corners[i]], + edge_corners[0][1], + [edge_corners[0][0]], []); + + function _gather_contiguous_edges_r(edge_corners, ecns, curr, out) = + len(edge_corners)==0? [each out, curr] : + let( + i1 = [ + for (i = idx(edge_corners)) + if (in_list(ecns[0], edge_corners[i][1])) + i + ], + i2 = [ + for (i = idx(edge_corners)) + if (in_list(ecns[1], edge_corners[i][1])) + i + ] + ) !i1 && !i2? _gather_contiguous_edges_r( + [for (i=idx(edge_corners)) if(i) edge_corners[i]], + edge_corners[0][1], + [edge_corners[0][0]], + [each out, curr] + ) : let( + nu_curr = [ + if (i1) edge_corners[i1[0]][0], + each curr, + if (i2) edge_corners[i2[0]][0], + ], + nu_ecns = [ + if (!i1) ecns[0] else [ + for (ecn = edge_corners[i1[0]][1]) + if (ecn != ecns[0]) ecn + ][0], + if (!i2) ecns[1] else [ + for (ecn = edge_corners[i2[0]][1]) + if (ecn != ecns[1]) ecn + ][0], + ], + rem = [ + for (i = idx(edge_corners)) + if (i != i1[0] && i != i2[0]) + edge_corners[i] + ] + ) + _gather_contiguous_edges_r(rem, nu_ecns, nu_curr, out); + + function _edge_transition_inversions(edge_string) = + let( + // boolean cumulative sum + bcs = function(list, i=0, inv=false, out=[]) + i>=len(list)? out : + let( nu_inv = list[i]? !inv : inv ) + bcs(list, i+1, nu_inv, [each out, nu_inv]), + inverts = bcs([ + false, + for(i = idx(edge_string)) if (i) + _edge_transition_needs_flip( + edge_string[i-1], + edge_string[i] + ) + ]), + boti = [for(i = idx(edge_string)) if (edge_string[i].z<0) i], + topi = [for(i = idx(edge_string)) if (edge_string[i].z>0) i], + lfti = [for(i = idx(edge_string)) if (edge_string[i].x<0) i], + rgti = [for(i = idx(edge_string)) if (edge_string[i].x>0) i], + idx = [for (m = [boti, topi, lfti, rgti]) if(m) m[0]][0], + rinverts = inverts[idx] == false? inverts : [for (x = inverts) !x] + ) rinverts; + + function _is_closed_edge_loop(edge_string) = + let( + e1 = edge_string[0], + e2 = last(edge_string) + ) + len([for (i=[0:2]) if (abs(e1[i])==1 && e1[i]==e2[i]) 1]) == 1 && + len([for (i=[0:2]) if (e1[i]==0 && abs(e2[i])==1) 1]) == 1 && + len([for (i=[0:2]) if (e2[i]==0 && abs(e1[i])==1) 1]) == 1; + + function _edge_pair_perp_vec(e1,e2) = + [for (i=[0:2]) if (abs(e1[i])==1 && e1[i]==e2[i]) -e1[i] else 0]; + + req_children($children); + check1 = assert($parent_geom != undef, "No object to attach to!") + assert(in_list(corner_type, ["none", "round", "chamfer", "sharp"])) + assert(is_bool(flip)); + edges = _edges(edges, except=except); + vecs = [ + for (i = [0:3], axis=[0:2]) + if (edges[axis][i]>0) + EDGE_OFFSETS[axis][i] + ]; + all_vecs_are_edges = all([for (vec = vecs) sum(v_abs(vec))==2]); + check2 = assert(all_vecs_are_edges, "All vectors must be edges."); + edge_corners = [for (vec = vecs) [vec, _edge_corner_numbers(vec)]]; + edge_strings = _gather_contiguous_edges(edge_corners); + for (edge_string = edge_strings) { + inverts = _edge_transition_inversions(edge_string); + flipverts = [for (x = inverts) flip? !x : x]; + vecpairs = [ + for (i = idx(edge_string)) + let (p = _default_edge_orientation(edge_string[i])) + flipverts[i]? [p.y,p.x] : p + ]; + is_loop = _is_closed_edge_loop(edge_string); + for (i = idx(edge_string)) { + if (corner_type!="none" && (i || is_loop)) { + e1 = select(edge_string,i-1); + e2 = select(edge_string,i); + vp1 = select(vecpairs,i-1); + vp2 = select(vecpairs,i); + pvec = _edge_pair_perp_vec(e1,e2); + pos = [for (i=[0:2]) e1[i]? e1[i] : e2[i]]; + if (vp1.y == vp2.y) { + default_tag("remove") + position(pos) { + mirT = _corner_orientation(pos, pvec); + multmatrix(mirT) { + if (corner_type=="chamfer") { + fn = $fn; + rotate_extrude(angle=90, $fn=4) + right_half(planar=true, $fn=fn) + children(); + rotate_extrude(angle=90, $fn=4) + left_half(planar=true, $fn=fn) + children(); + } else if (corner_type=="round") { + rotate_extrude(angle=90) + right_half(planar=true) + children(); + rotate_extrude(angle=90) + left_half(planar=true) + children(); + } else { //corner_type == "sharp" + intersection() { + rot([90,0, 0]) linear_extrude(height=100,center=true,convexity=convexity) children(); + rot([90,0,90]) linear_extrude(height=100,center=true,convexity=convexity) children(); + } + } + } + } + } + } + } + for (i = idx(edge_string)) { + edge_profile(edge_string[i], excess=excess, convexity=convexity) { + if (flipverts[i]) { + mirror([-1,1]) children(); + } else { + children(); + } + } + } + } +} + + +// Module: corner_profile() +// Synopsis: Rotationally extrudes a 2d edge profile into corner mask on the given corners of the parent. +// SynTags: Geom +// Topics: Attachments, Masking +// See Also: attachable(), position(), attach(), face_profile(), edge_profile(), corner_mask(), face_mask(), edge_mask() +// Usage: +// PARENT() corner_profile([corners], [except], [r=|d=], [convexity=]) CHILDREN; +// 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. If no tag is set then `corner_profile()` +// sets the tag for children to "remove" so that it will work with the default {{diff()}} tag. +// See [Specifying Corners](attachments.scad#subsection-specifying-corners) for information on how to specify corner sets. +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// corners = Corners to mask. See [Specifying Corners](attachments.scad#subsection-specifying-corners). Default: All corners. +// except = Corners to explicitly NOT mask. See [Specifying Corners](attachments.scad#subsection-specifying-corners). Default: No corners. +// --- +// r = Radius of corner mask. +// d = Diameter of corner mask. +// convexity = Max number of times a line could intersect the perimeter of the mask shape. Default: 10 +// Side Effects: +// Tags the children with "remove" (and hence sets $tag) if no tag is already set. +// `$idx` is set to the index number of each corner. +// `$attach_anchor` is set for each corner given, to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. +// `$profile_type` is set to `"corner"`. +// Example: +// diff() +// cuboid([50,60,70],rounding=10,edges="Z",anchor=CENTER) { +// corner_profile(TOP,r=10) +// mask2d_teardrop(r=10, angle=40); +// } +module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) { + check1 = assert($parent_geom != undef, "No object to attach to!"); + r = max(0.01, get_radius(r=r, d=d, dflt=undef)); + check2 = assert(is_num(r), "Bad r/d argument."); + corners = _corners(corners, except=except); + vecs = [for (i = [0:7]) if (corners[i]>0) CORNER_OFFSETS[i]]; + all_vecs_are_corners = all([for (vec = vecs) sum(v_abs(vec))==3]); + check3 = assert(all_vecs_are_corners, "All vectors must be corners."); + for ($idx = idx(vecs)) { + vec = vecs[$idx]; + anch = _find_anchor(vec, $parent_geom); + $attach_to = undef; + $attach_anchor = anch; + $attach_norot = true; + $profile_type = "corner"; + rotang = vec.z<0? + [ 0,0,180+v_theta(vec)-45] : + [180,0,-90+v_theta(vec)-45]; + $tag = $tag=="" ? str($tag_prefix,"remove") : $tag; + translate(anch[1]) { + rot(rotang) { + down(0.01) { + linear_extrude(height=r+0.01, center=false) { + difference() { + translate(-[0.01,0.01]) square(r); + translate([r,r]) circle(r=r*0.999); + } + } + } + translate([r,r]) zrot(180) { + rotate_extrude(angle=90, convexity=convexity) { + right(r) xflip() { + children(); + } + } + } + } + } + } +} + + +// Section: Making your objects attachable + + +// Module: attachable() +// Synopsis: Manages the anchoring, spin, orientation, and attachments for an object. +// Topics: Attachments +// See Also: reorient() +// Usage: Square/Trapezoid Geometry +// attachable(anchor, spin, two_d=true, size=, [size2=], [shift=], [override=], ...) {OBJECT; children();} +// Usage: Circle/Oval Geometry +// attachable(anchor, spin, two_d=true, r=|d=, ...) {OBJECT; children();} +// Usage: 2D Path/Polygon Geometry +// attachable(anchor, spin, two_d=true, path=, [extent=], ...) {OBJECT; children();} +// Usage: 2D Region Geometry +// attachable(anchor, spin, two_d=true, region=, [extent=], ...) {OBJECT; children();} +// Usage: Cubical/Prismoidal Geometry +// attachable(anchor, spin, [orient], size=, [size2=], [shift=], ...) {OBJECT; children();} +// Usage: Cylindrical Geometry +// attachable(anchor, spin, [orient], r=|d=, l=, [axis=], ...) {OBJECT; children();} +// Usage: Conical Geometry +// attachable(anchor, spin, [orient], r1=|d1=, r2=|d2=, l=, [axis=], ...) {OBJECT; children();} +// Usage: Spheroid/Ovoid Geometry +// attachable(anchor, spin, [orient], r=|d=, ...) {OBJECT; children();} +// Usage: Extruded Path/Polygon Geometry +// attachable(anchor, spin, path=, l=|h=, [extent=], ...) {OBJECT; children();} +// Usage: Extruded Region Geometry +// attachable(anchor, spin, region=, l=|h=, [extent=], ...) {OBJECT; children();} +// Usage: VNF Geometry +// attachable(anchor, spin, [orient], vnf=, [extent=], ...) {OBJECT; children();} +// Usage: Pre-Specified Geometry +// attachable(anchor, spin, [orient], geom=) {OBJECT; children();} +// +// Description: +// Manages the anchoring, spin, orientation, and attachments for OBJECT, located in a 3D volume or 2D area. +// A managed 3D volume is assumed to be vertically (Z-axis) oriented, and centered. +// A managed 2D area is just assumed to be centered. The shape to be managed is given +// as the first child to this module, and the second child should be given as `children()`. +// For example, to manage a conical shape: +// ```openscad +// attachable(anchor, spin, orient, r1=r1, r2=r2, l=h) { +// cyl(r1=r1, r2=r2, l=h); +// children(); +// } +// ``` +// . +// If this is *not* run as a child of `attach()` with the `to` argument +// given, then the following transformations are performed in order: +// * Translates so the `anchor` point is at the origin (0,0,0). +// * Rotates around the Z axis by `spin` degrees counter-clockwise. +// * Rotates so the top of the part points towards the vector `orient`. +// . +// If this is called as a child of `attach(from,to)`, then the info +// for the anchor points referred to by `from` and `to` are fetched, +// which will include position, direction, and spin. With that info, +// the following transformations are performed: +// * Translates this part so it's anchor position matches the parent's anchor position. +// * Rotates this part so it's anchor direction vector exactly opposes the parent's anchor direction vector. +// * Rotates this part so it's anchor spin matches the parent's anchor spin. +// . +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// +// Arguments: +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` +// --- +// size = If given as a 3D vector, contains the XY size of the bottom of the cuboidal/prismoidal volume, and the Z height. If given as a 2D vector, contains the front X width of the rectangular/trapezoidal shape, and the Y length. +// size2 = If given as a 2D vector, contains the XY size of the top of the prismoidal volume. If given as a number, contains the back width of the trapezoidal shape. +// shift = If given as a 2D vector, shifts the top of the prismoidal or conical shape by the given amount. If given as a number, shifts the back of the trapezoidal shape right by that amount. Default: No shift. +// r = Radius of the cylindrical/conical volume. Can be a scalar, or a list of sizes per axis. +// d = Diameter of the cylindrical/conical volume. Can be a scalar, or a list of sizes per axis. +// r1 = Radius of the bottom of the conical volume. Can be a scalar, or a list of sizes per axis. +// r2 = Radius of the top of the conical volume. Can be a scalar, or a list of sizes per axis. +// d1 = Diameter of the bottom of the conical volume. Can be a scalar, a list of sizes per axis. +// d2 = Diameter of the top of the conical volume. Can be a scalar, a list of sizes per axis. +// l/h = Length of the cylindrical, conical, or extruded path volume along axis. +// vnf = The [VNF](vnf.scad) of the volume. +// path = The path to generate a polygon from. +// region = The region to generate a shape from. +// extent = If true, calculate anchors by extents, rather than intersection, for VNFs and paths. Default: true. +// cp = If given, specifies the centerpoint of the volume. Default: `[0,0,0]` +// offset = If given, offsets the perimeter of the volume around the centerpoint. +// anchors = If given as a list of anchor points, allows named anchor points. +// two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D) +// axis = The vector pointing along the axis of a geometry. Default: UP +// override = Function that takes an anchor and returns a pair `[position,direction]` to use for that anchor to override the normal one. You can also supply a lookup table that is a list of `[anchor, [position, direction]]` entries. If the direction/position that is returned is undef then the default will be used. +// geom = If given, uses the pre-defined (via {{attach_geom()}} geometry. +// +// Side Effects: +// `$parent_anchor` is set to the parent object's `anchor` value. +// `$parent_spin` is set to the parent object's `spin` value. +// `$parent_orient` is set to the parent object's `orient` value. +// `$parent_geom` is set to the parent object's `geom` value. +// `$parent_size` is set to the parent object's cubical `[X,Y,Z]` volume size. +// `$color` is used to set the color of the object +// `$save_color` is used to revert color to the parent's color +// +// Example(NORENDER): Cubical Shape +// attachable(anchor, spin, orient, size=size) { +// cube(size, center=true); +// children(); +// } +// +// Example(NORENDER): Prismoidal Shape +// attachable( +// anchor, spin, orient, +// size=point3d(botsize,h), +// size2=topsize, +// shift=shift +// ) { +// prismoid(botsize, topsize, h=h, shift=shift); +// children(); +// } +// +// Example(NORENDER): Cylindrical Shape, Z-Axis Aligned +// attachable(anchor, spin, orient, r=r, l=h) { +// cyl(r=r, l=h); +// children(); +// } +// +// Example(NORENDER): Cylindrical Shape, Y-Axis Aligned +// attachable(anchor, spin, orient, r=r, l=h, axis=BACK) { +// cyl(r=r, l=h); +// children(); +// } +// +// Example(NORENDER): Cylindrical Shape, X-Axis Aligned +// attachable(anchor, spin, orient, r=r, l=h, axis=RIGHT) { +// cyl(r=r, l=h); +// children(); +// } +// +// Example(NORENDER): Conical Shape, Z-Axis Aligned +// attachable(anchor, spin, orient, r1=r1, r2=r2, l=h) { +// cyl(r1=r1, r2=r2, l=h); +// children(); +// } +// +// Example(NORENDER): Conical Shape, Y-Axis Aligned +// attachable(anchor, spin, orient, r1=r1, r2=r2, l=h, axis=BACK) { +// cyl(r1=r1, r2=r2, l=h); +// children(); +// } +// +// Example(NORENDER): Conical Shape, X-Axis Aligned +// attachable(anchor, spin, orient, r1=r1, r2=r2, l=h, axis=RIGHT) { +// cyl(r1=r1, r2=r2, l=h); +// children(); +// } +// +// Example(NORENDER): Spherical Shape +// attachable(anchor, spin, orient, r=r) { +// sphere(r=r); +// children(); +// } +// +// Example(NORENDER): Extruded Polygon Shape, by Extents +// attachable(anchor, spin, orient, path=path, l=length) { +// linear_extrude(height=length, center=true) +// polygon(path); +// children(); +// } +// +// Example(NORENDER): Extruded Polygon Shape, by Intersection +// attachable(anchor, spin, orient, path=path, l=length, extent=false) { +// linear_extrude(height=length, center=true) +// polygon(path); +// children(); +// } +// +// Example(NORENDER): Arbitrary VNF Shape, by Extents +// attachable(anchor, spin, orient, vnf=vnf) { +// vnf_polyhedron(vnf); +// children(); +// } +// +// Example(NORENDER): Arbitrary VNF Shape, by Intersection +// attachable(anchor, spin, orient, vnf=vnf, extent=false) { +// vnf_polyhedron(vnf); +// children(); +// } +// +// Example(NORENDER): 2D Rectangular Shape +// attachable(anchor, spin, orient, two_d=true, size=size) { +// square(size, center=true); +// children(); +// } +// +// Example(NORENDER): 2D Trapezoidal Shape +// attachable( +// anchor, spin, orient, +// two_d=true, +// size=[x1,y], +// size2=x2, +// shift=shift +// ) { +// trapezoid(w1=x1, w2=x2, h=y, shift=shift); +// children(); +// } +// +// Example(NORENDER): 2D Circular Shape +// attachable(anchor, spin, orient, two_d=true, r=r) { +// circle(r=r); +// children(); +// } +// +// Example(NORENDER): Arbitrary 2D Polygon Shape, by Extents +// attachable(anchor, spin, orient, two_d=true, path=path) { +// polygon(path); +// children(); +// } +// +// Example(NORENDER): Arbitrary 2D Polygon Shape, by Intersection +// attachable(anchor, spin, orient, two_d=true, path=path, extent=false) { +// polygon(path); +// children(); +// } +// +// Example(NORENDER): Using Pre-defined Geometry +// geom = atype=="perim"? attach_geom(two_d=true, path=path, extent=false) : +// atype=="extents"? attach_geom(two_d=true, path=path, extent=true) : +// atype=="circle"? attach_geom(two_d=true, r=r) : +// assert(false, "Bad atype"); +// attachable(anchor, spin, orient, geom=geom) { +// polygon(path); +// children(); +// } +// +// Example: An object can be designed to attach as negative space using {{diff()}}, but if you want an object to include both positive and negative space then you need to call attachable() twice, because tags inside the attachable() call don't work as expected. This example shows how you can call attachable twice to create an object with positive and negative space. Note, however, that children in the negative space are differenced away: the highlighted little cube does not survive into the final model. +// module thing(anchor,spin,orient) { +// tag("remove") attachable(size=[15,15,15],anchor=anchor,spin=spin,orient=orient){ +// cuboid([10,10,16]); +// union(){} // dummy children +// } +// attachable(size=[15,15,15], anchor=anchor, spin=spin, orient=orient){ +// cuboid([15,15,15]); +// children(); +// } +// } +// diff() +// cube([19,10,19]) +// attach([FRONT],overlap=-4) +// thing(anchor=TOP) +// # attach(TOP) cuboid(2,anchor=TOP); +// Example: Here is an example where the "keep" tag allows children to appear in the negative space. That tag is also needed for this module to produce the desired output. As above, the tag must be applied outside the attachable() call. +// module thing(anchor = CENTER, spin = 0, orient = UP) { +// tag("remove") attachable(anchor, spin, orient, d1=0,d2=95,h=33) { +// cylinder(h = 33.1, d1 = 0, d2 = 95, anchor=CENTER); +// union(){} // dummy children +// } +// tag("keep") attachable(anchor, spin, orient,d1=0,d2=95,h=33) { +// cylinder(h = 33, d = 10,anchor=CENTER); +// children(); +// } +// } +// diff() +// cube(100) +// attach([FRONT,TOP],overlap=-4) +// thing(anchor=TOP) +// tube(ir=12,h=10); +module attachable( + anchor, spin, orient, + size, size2, shift, + r,r1,r2, d,d1,d2, l,h, + vnf, path, region, + extent=true, + cp=[0,0,0], + offset=[0,0,0], + anchors=[], + two_d=false, + axis=UP,override, + geom +) { + dummy1 = + assert($children==2, "attachable() expects exactly two children; the shape to manage, and the union of all attachment candidates.") + 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); + region = !is_undef(region)? region : + !is_undef(path)? [path] : + undef; + geom = is_def(geom)? geom : + attach_geom( + size=size, size2=size2, shift=shift, + r=r, r1=r1, r2=r2, h=h, + d=d, d1=d1, d2=d2, l=l, + vnf=vnf, region=region, extent=extent, + cp=cp, offset=offset, anchors=anchors, + two_d=two_d, axis=axis, override=override + ); + m = _attach_transform(anchor,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; + $align=undef; + if (_is_shown()) + _color($color) children(0); + if (is_def($save_color)) { + $color=$save_color; + $save_color=undef; + children(1); + } + else children(1); + } +} + +// Function: reorient() +// Synopsis: Calculates the transformation matrix needed to reorient an object. +// SynTags: Trans, Path, VNF +// Topics: Attachments +// See Also: reorient(), attachable() +// Usage: Square/Trapezoid Geometry +// mat = reorient(anchor, spin, [orient], two_d=true, size=, [size2=], [shift=], ...); +// pts = reorient(anchor, spin, [orient], two_d=true, size=, [size2=], [shift=], p=, ...); +// Usage: Circle/Oval Geometry +// mat = reorient(anchor, spin, [orient], two_d=true, r=|d=, ...); +// pts = reorient(anchor, spin, [orient], two_d=true, r=|d=, p=, ...); +// Usage: 2D Path/Polygon Geometry +// mat = reorient(anchor, spin, [orient], two_d=true, path=, [extent=], ...); +// pts = reorient(anchor, spin, [orient], two_d=true, path=, [extent=], p=, ...); +// Usage: 2D Region/Polygon Geometry +// mat = reorient(anchor, spin, [orient], two_d=true, region=, [extent=], ...); +// pts = reorient(anchor, spin, [orient], two_d=true, region=, [extent=], p=, ...); +// Usage: Cubical/Prismoidal Geometry +// mat = reorient(anchor, spin, [orient], size=, [size2=], [shift=], ...); +// vnf = reorient(anchor, spin, [orient], size=, [size2=], [shift=], p=, ...); +// Usage: Cylindrical Geometry +// mat = reorient(anchor, spin, [orient], r=|d=, l=, [axis=], ...); +// vnf = reorient(anchor, spin, [orient], r=|d=, l=, [axis=], p=, ...); +// Usage: Conical Geometry +// mat = reorient(anchor, spin, [orient], r1=|d1=, r2=|d2=, l=, [axis=], ...); +// vnf = reorient(anchor, spin, [orient], r1=|d1=, r2=|d2=, l=, [axis=], p=, ...); +// Usage: Spheroid/Ovoid Geometry +// mat = reorient(anchor, spin, [orient], r|d=, ...); +// vnf = reorient(anchor, spin, [orient], r|d=, p=, ...); +// Usage: Extruded Path/Polygon Geometry +// mat = reorient(anchor, spin, [orient], path=, l=|h=, [extent=], ...); +// vnf = reorient(anchor, spin, [orient], path=, l=|h=, [extent=], p=, ...); +// Usage: Extruded Region Geometry +// mat = reorient(anchor, spin, [orient], region=, l=|h=, [extent=], ...); +// vnf = reorient(anchor, spin, [orient], region=, l=|h=, [extent=], p=, ...); +// Usage: VNF Geometry +// mat = reorient(anchor, spin, [orient], vnf, [extent], ...); +// vnf = reorient(anchor, spin, [orient], vnf, [extent], p=, ...); +// +// Description: +// Given anchor, spin, orient, and general geometry info for a managed volume, this calculates +// the transformation matrix needed to be applied to the contents of that volume. A managed 3D +// volume is assumed to be vertically (Z-axis) oriented, and centered. A managed 2D area is just +// assumed to be centered. +// . +// If `p` is not given, then the transformation matrix will be returned. +// If `p` contains a VNF, a new VNF will be returned with the vertices transformed by the matrix. +// If `p` contains a path, a new path will be returned with the vertices transformed by the matrix. +// If `p` contains a point, a new point will be returned, transformed by the matrix. +// . +// If `$attach_to` is not defined, then the following transformations are performed in order: +// * Translates so the `anchor` point is at the origin (0,0,0). +// * Rotates around the Z axis by `spin` degrees counter-clockwise. +// * Rotates so the top of the part points towards the vector `orient`. +// . +// If `$attach_to` is defined, as a consequence of `attach(from,to)`, then +// the following transformations are performed in order: +// * Translates this part so it's anchor position matches the parent's anchor position. +// * Rotates this part so it's anchor direction vector exactly opposes the parent's anchor direction vector. +// * Rotates this part so it's anchor spin matches the parent's anchor spin. +// . +// For a step-by-step explanation of attachments, see the [Attachments Tutorial](Tutorial-Attachments). +// +// Arguments: +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` +// --- +// size = If given as a 3D vector, contains the XY size of the bottom of the cuboidal/prismoidal volume, and the Z height. If given as a 2D vector, contains the front X width of the rectangular/trapezoidal shape, and the Y length. +// size2 = If given as a 2D vector, contains the XY size of the top of the prismoidal volume. If given as a number, contains the back width of the trapezoidal shape. +// shift = If given as a 2D vector, shifts the top of the prismoidal or conical shape by the given amount. If given as a number, shifts the back of the trapezoidal shape right by that amount. Default: No shift. +// r = Radius of the cylindrical/conical volume. Can be a scalar, or a list of sizes per axis. +// d = Diameter of the cylindrical/conical volume. Can be a scalar, or a list of sizes per axis. +// r1 = Radius of the bottom of the conical volume. Can be a scalar, or a list of sizes per axis. +// r2 = Radius of the top of the conical volume. Can be a scalar, or a list of sizes per axis. +// d1 = Diameter of the bottom of the conical volume. Can be a scalar, a list of sizes per axis. +// d2 = Diameter of the top of the conical volume. Can be a scalar, a list of sizes per axis. +// l/h = Length of the cylindrical, conical, or extruded path volume along axis. +// vnf = The [VNF](vnf.scad) of the volume. +// path = The path to generate a polygon from. +// region = The region to generate a shape from. +// extent = If true, calculate anchors by extents, rather than intersection. Default: false. +// cp = If given, specifies the centerpoint of the volume. Default: `[0,0,0]` +// offset = If given, offsets the perimeter of the volume around the centerpoint. +// anchors = If given as a list of anchor points, allows named anchor points. +// two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D) +// axis = The vector pointing along the axis of a geometry. Default: UP +// p = The VNF, path, or point to transform. +function reorient( + anchor, spin, orient, + size, size2, shift, + r,r1,r2, d,d1,d2, l,h, + vnf, path, region, + extent=true, + offset=[0,0,0], + cp=[0,0,0], + anchors=[], + two_d=false, + axis=UP, override, + geom, + p=undef +) = + 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)) + let( + anchor = default(anchor, CENTER), + spin = default(spin, 0), + orient = default(orient, UP), + region = !is_undef(region)? region : + !is_undef(path)? [path] : + undef, + geom = is_def(geom)? geom : + attach_geom( + size=size, size2=size2, shift=shift, + r=r, r1=r1, r2=r2, h=h, + d=d, d1=d1, d2=d2, l=l, + vnf=vnf, region=region, extent=extent, + cp=cp, offset=offset, anchors=anchors, + two_d=two_d, axis=axis, override=override + ), + $attach_to = undef + ) _attach_transform(anchor,spin,orient,geom,p); + + +// Function: named_anchor() +// Synopsis: Creates an anchro data structure. +// Topics: Attachments +// See Also: reorient(), attachable() +// Usage: +// a = named_anchor(name, pos, [orient], [spin]); +// Description: +// Creates an anchor data structure. For a step-by-step explanation of attachments, +// see the [Attachments Tutorial](Tutorial-Attachments). +// Arguments: +// name = The string name of the anchor. Lowercase. Words separated by single dashes. No spaces. +// pos = The [X,Y,Z] position of the anchor. +// orient = A vector pointing in the direction parts should project from the anchor position. Default: UP +// spin = If needed, the angle to rotate the part around the direction vector. Default: 0 +function named_anchor(name, pos, orient=UP, spin=0) = [name, pos, orient, spin]; + + +// Function: attach_geom() +// Synopsis: Returns the internal geometry description of an attachable object. +// Topics: Attachments +// See Also: reorient(), attachable() +// Usage: Null/Point Geometry +// geom = attach_geom(...); +// Usage: Square/Trapezoid Geometry +// geom = attach_geom(two_d=true, size=, [size2=], [shift=], ...); +// Usage: Circle/Oval Geometry +// geom = attach_geom(two_d=true, r=|d=, ...); +// Usage: 2D Path/Polygon/Region Geometry +// geom = attach_geom(two_d=true, region=, [extent=], ...); +// Usage: Cubical/Prismoidal Geometry +// geom = attach_geom(size=, [size2=], [shift=], ...); +// Usage: Cylindrical Geometry +// geom = attach_geom(r=|d=, l=|h=, [axis=], ...); +// Usage: Conical Geometry +// geom = attach_geom(r1|d1=, r2=|d2=, l=, [axis=], ...); +// Usage: Spheroid/Ovoid Geometry +// geom = attach_geom(r=|d=, ...); +// Usage: Extruded 2D Path/Polygon/Region Geometry +// geom = attach_geom(region=, l=|h=, [extent=], [shift=], [scale=], [twist=], ...); +// Usage: VNF Geometry +// geom = attach_geom(vnf=, [extent=], ...); +// +// Description: +// Given arguments that describe the geometry of an attachable object, returns the internal geometry description. +// This will probably not not ever need to be called by the end user. +// +// Arguments: +// --- +// size = If given as a 3D vector, contains the XY size of the bottom of the cuboidal/prismoidal volume, and the Z height. If given as a 2D vector, contains the front X width of the rectangular/trapezoidal shape, and the Y length. +// size2 = If given as a 2D vector, contains the XY size of the top of the prismoidal volume. If given as a number, contains the back width of the trapezoidal shape. +// shift = If given as a 2D vector, shifts the top of the prismoidal or conical shape by the given amount. If given as a number, shifts the back of the trapezoidal shape right by that amount. Default: No shift. +// scale = If given as number or a 2D vector, scales the top of the shape, relative to the bottom. Default: `[1,1]` +// twist = If given as number, rotates the top of the shape by the given number of degrees clockwise, relative to the bottom. Default: `0` +// r = Radius of the cylindrical/conical volume. Can be a scalar, or a list of sizes per axis. +// d = Diameter of the cylindrical/conical volume. Can be a scalar, or a list of sizes per axis. +// r1 = Radius of the bottom of the conical volume. Can be a scalar, or a list of sizes per axis. +// r2 = Radius of the top of the conical volume. Can be a scalar, or a list of sizes per axis. +// d1 = Diameter of the bottom of the conical volume. Can be a scalar, a list of sizes per axis. +// d2 = Diameter of the top of the conical volume. Can be a scalar, a list of sizes per axis. +// l/h = Length of the cylindrical, conical or extruded region volume along axis. +// vnf = The [VNF](vnf.scad) of the volume. +// region = The region to generate a shape from. +// extent = If true, calculate anchors by extents, rather than intersection. Default: true. +// cp = If given, specifies the centerpoint of the volume. Default: `[0,0,0]` +// offset = If given, offsets the perimeter of the volume around the centerpoint. +// anchors = If given as a list of anchor points, allows named anchor points. +// two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D) +// axis = The vector pointing along the axis of a geometry. Default: UP +// override = Function that takes an anchor and returns a pair `[position,direction]` to use for that anchor to override the normal one. You can also supply a lookup table that is a list of `[anchor, [position, direction]]` entries. If the direction/position that is returned is undef then the default will be used. +// +// Example(NORENDER): Null/Point Shape +// geom = attach_geom(); +// +// Example(NORENDER): Cubical Shape +// geom = attach_geom(size=size); +// +// Example(NORENDER): Prismoidal Shape +// geom = attach_geom( +// size=point3d(botsize,h), +// size2=topsize, shift=shift +// ); +// +// Example(NORENDER): Cylindrical Shape, Z-Axis Aligned +// geom = attach_geom(r=r, h=h); +// +// Example(NORENDER): Cylindrical Shape, Y-Axis Aligned +// geom = attach_geom(r=r, h=h, axis=BACK); +// +// Example(NORENDER): Cylindrical Shape, X-Axis Aligned +// geom = attach_geom(r=r, h=h, axis=RIGHT); +// +// Example(NORENDER): Conical Shape, Z-Axis Aligned +// geom = attach_geom(r1=r1, r2=r2, h=h); +// +// Example(NORENDER): Conical Shape, Y-Axis Aligned +// geom = attach_geom(r1=r1, r2=r2, h=h, axis=BACK); +// +// Example(NORENDER): Conical Shape, X-Axis Aligned +// geom = attach_geom(r1=r1, r2=r2, h=h, axis=RIGHT); +// +// Example(NORENDER): Spherical Shape +// geom = attach_geom(r=r); +// +// Example(NORENDER): Ovoid Shape +// geom = attach_geom(r=[r_x, r_y, r_z]); +// +// Example(NORENDER): Arbitrary VNF Shape, Anchored by Extents +// geom = attach_geom(vnf=vnf); +// +// Example(NORENDER): Arbitrary VNF Shape, Anchored by Intersection +// geom = attach_geom(vnf=vnf, extent=false); +// +// Example(NORENDER): 2D Rectangular Shape +// geom = attach_geom(two_d=true, size=size); +// +// Example(NORENDER): 2D Trapezoidal Shape +// geom = attach_geom(two_d=true, size=[x1,y], size2=x2, shift=shift, override=override); +// +// Example(NORENDER): 2D Circular Shape +// geom = attach_geom(two_d=true, r=r); +// +// Example(NORENDER): 2D Oval Shape +// geom = attach_geom(two_d=true, r=[r_x, r_y]); +// +// Example(NORENDER): Arbitrary 2D Region Shape, Anchored by Extents +// geom = attach_geom(two_d=true, region=region); +// +// Example(NORENDER): Arbitrary 2D Region Shape, Anchored by Intersection +// geom = attach_geom(two_d=true, region=region, extent=false); +// +// Example(NORENDER): Extruded Region, Anchored by Extents +// geom = attach_geom(region=region, l=height); +// +// Example(NORENDER): Extruded Region, Anchored by Intersection +// geom = attach_geom(region=region, l=length, extent=false); +// + +function _local_struct_val(struct, key)= + assert(is_def(key),"key is missing") + let(ind = search([key],struct)[0]) + ind == [] ? undef : struct[ind][1]; + + +function attach_geom( + size, size2, + shift, scale, twist, + r,r1,r2, d,d1,d2, l,h, + vnf, region, + extent=true, + cp=[0,0,0], + offset=[0,0,0], + anchors=[], + two_d=false, + axis=UP, override +) = + assert(is_bool(extent)) + assert(is_vector(cp) || is_string(cp)) + assert(is_vector(offset)) + assert(is_list(anchors)) + assert(is_bool(two_d)) + assert(is_vector(axis)) + !is_undef(size)? ( + two_d? ( + let( + size2 = default(size2, size.x), + shift = default(shift, 0), + over_f = is_undef(override) ? function(anchor) [undef,undef] + : is_func(override) ? override + : function(anchor) _local_struct_val(override,anchor) + ) + assert(is_vector(size,2)) + assert(is_num(size2)) + assert(is_num(shift)) + ["trapezoid", point2d(size), size2, shift, over_f, cp, offset, anchors] + ) : ( + let( + size2 = default(size2, point2d(size)), + shift = default(shift, [0,0]) + ) + assert(is_vector(size,3)) + assert(is_vector(size2,2)) + assert(is_vector(shift,2)) + ["prismoid", size, size2, shift, axis, cp, offset, anchors] + ) + ) : !is_undef(vnf)? ( + assert(is_vnf(vnf)) + assert(two_d == false) + extent? ["vnf_extent", vnf, cp, offset, anchors] : + ["vnf_isect", vnf, cp, offset, anchors] + ) : !is_undef(region)? ( + assert(is_region(region),2) + let( l = default(l, h) ) + two_d==true + ? assert(is_undef(l)) + extent==true + ? ["rgn_extent", region, cp, offset, anchors] + : ["rgn_isect", region, cp, offset, anchors] + : assert(is_finite(l)) + let( + shift = default(shift, [0,0]), + scale = is_num(scale)? [scale,scale] : default(scale, [1,1]), + twist = default(twist, 0) + ) + assert(is_vector(shift,2)) + assert(is_vector(scale,2)) + assert(is_num(twist)) + extent==true + ? ["extrusion_extent", region, l, twist, scale, shift, cp, offset, anchors] + : ["extrusion_isect", region, l, twist, scale, shift, cp, offset, anchors] + ) : + let( + r1 = get_radius(r1=r1,d1=d1,r=r,d=d,dflt=undef) + ) + !is_undef(r1)? ( + let( l = default(l, h) ) + !is_undef(l)? ( + let( + shift = default(shift, [0,0]), + r2 = get_radius(r1=r2,d1=d2,r=r,d=d,dflt=undef) + ) + assert(is_num(r1) || is_vector(r1,2)) + assert(is_num(r2) || is_vector(r2,2)) + assert(is_num(l)) + assert(is_vector(shift,2)) + ["conoid", r1, r2, l, shift, axis, cp, offset, anchors] + ) : ( + two_d? ( + assert(is_num(r1) || is_vector(r1,2)) + ["ellipse", r1, cp, offset, anchors] + ) : ( + assert(is_num(r1) || is_vector(r1,3)) + ["spheroid", r1, cp, offset, anchors] + ) + ) + ) : + ["point", cp, offset, anchors]; + + + + + + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Attachment internal functions + + +/// Internal Function: _attach_geom_2d() +/// Topics: Attachments +/// See Also: reorient(), attachable() +/// Usage: +/// bool = _attach_geom_2d(geom); +/// Description: +/// Returns true if the given attachment geometry description is for a 2D shape. +function _attach_geom_2d(geom) = + let( type = geom[0] ) + type == "trapezoid" || type == "ellipse" || + type == "rgn_isect" || type == "rgn_extent"; + + +/// Internal Function: _attach_geom_size() +/// Usage: +/// bounds = _attach_geom_size(geom); +/// Topics: Attachments +/// See Also: reorient(), attachable() +/// Description: +/// Returns the `[X,Y,Z]` bounding size for the given attachment geometry description. +function _attach_geom_size(geom) = + let( type = geom[0] ) + type == "point"? [0,0,0] : + type == "prismoid"? ( //size, size2, shift, axis + let( + size=geom[1], size2=geom[2], shift=point2d(geom[3]), + maxx = max(size.x,size2.x), + maxy = max(size.y,size2.y), + z = size.z + ) [maxx, maxy, z] + ) : type == "conoid"? ( //r1, r2, l, shift + let( + r1=geom[1], r2=geom[2], l=geom[3], + shift=point2d(geom[4]), axis=point3d(geom[5]), + rx1 = default(r1[0],r1), + ry1 = default(r1[1],r1), + rx2 = default(r2[0],r2), + ry2 = default(r2[1],r2), + maxxr = max(rx1,rx2), + maxyr = max(ry1,ry2) + ) + approx(axis,UP)? [2*maxxr,2*maxyr,l] : + approx(axis,RIGHT)? [l,2*maxyr,2*maxxr] : + approx(axis,BACK)? [2*maxxr,l,2*maxyr] : + [2*maxxr, 2*maxyr, l] + ) : type == "spheroid"? ( //r + let( r=geom[1] ) + is_num(r)? [2,2,2]*r : v_mul([2,2,2],point3d(r)) + ) : type == "vnf_extent" || type=="vnf_isect"? ( //vnf + let( + vnf = geom[1] + ) vnf==EMPTY_VNF? [0,0,0] : + let( + mm = pointlist_bounds(geom[1][0]), + delt = mm[1]-mm[0] + ) delt + ) : type == "extrusion_isect" || type == "extrusion_extent"? ( //path, l + let( + mm = pointlist_bounds(flatten(geom[1])), + delt = mm[1]-mm[0] + ) [delt.x, delt.y, geom[2]] + ) : type == "trapezoid"? ( //size, size2 + let( + size=geom[1], size2=geom[2], shift=geom[3], + maxx = max(size.x,size2+abs(shift)) + ) [maxx, size.y] + ) : type == "ellipse"? ( //r + let( r=geom[1] ) + is_num(r)? [2,2]*r : v_mul([2,2],point2d(r)) + ) : type == "rgn_isect" || type == "rgn_extent"? ( //path + let( + mm = pointlist_bounds(flatten(geom[1])), + delt = mm[1]-mm[0] + ) [delt.x, delt.y] + ) : + assert(false, "Unknown attachment geometry type."); + + +/// Internal Function: _attach_transform() +/// Usage: To Get a Transformation Matrix +/// mat = _attach_transform(anchor, spin, orient, geom); +/// Usage: To Transform Points, Paths, Patches, or VNFs +/// new_p = _attach_transform(anchor, spin, orient, geom, p); +/// Topics: Attachments +/// See Also: reorient(), attachable() +/// Description: +/// Returns the affine3d transformation matrix needed to `anchor`, `spin`, and `orient` +/// the given geometry `geom` shape into position. +/// Arguments: +/// anchor = Anchor point to translate to the origin `[0,0,0]`. See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` +/// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` +/// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` +/// geom = The geometry description of the shape. +/// p = If given as a VNF, path, or point, applies the affine3d transformation matrix to it and returns the result. +function _attach_transform(anchor, spin, orient, geom, p) = + 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)) + let( + anchor = default(anchor, CENTER), + spin = default(spin, 0), + orient = default(orient, UP), + two_d = _attach_geom_2d(geom), + m = ($attach_to != undef)? ( + let( + anch = _find_anchor($attach_to, geom), + pos = anch[1] + ) two_d? ( + assert(two_d && is_num(spin)) + affine3d_zrot(spin) * + rot(to=FWD, from=point3d(anch[2])) * + affine3d_translate(point3d(-pos)) + ) : ( + assert(is_num(spin) || is_vector(spin,3)) + let( + ang = vector_angle(anch[2], DOWN), + axis = vector_axis(anch[2], DOWN), + ang2 = (anch[2]==UP || anch[2]==DOWN)? 0 : 180-anch[3], + axis2 = rot(p=axis,[0,0,ang2]) + ) + affine3d_rot_by_axis(axis2,ang) * ( + is_num(spin)? affine3d_zrot(ang2+spin) : ( + affine3d_zrot(spin.z) * + affine3d_yrot(spin.y) * + affine3d_xrot(spin.x) * + affine3d_zrot(ang2) + ) + ) * affine3d_translate(point3d(-pos)) + ) + ) : ( + let( + pos = _find_anchor(anchor, geom)[1] + ) two_d? ( + assert(two_d && is_num(spin)) + affine3d_zrot(spin) * + affine3d_translate(point3d(-pos)) + ) : ( + assert(is_num(spin) || is_vector(spin,3)) + let( + axis = vector_axis(UP,orient), + ang = vector_angle(UP,orient) + ) + affine3d_rot_by_axis(axis,ang) * ( + is_num(spin)? affine3d_zrot(spin) : ( + affine3d_zrot(spin.z) * + affine3d_yrot(spin.y) * + affine3d_xrot(spin.x) + ) + ) * affine3d_translate(point3d(-pos)) + ) + ) + ) is_undef(p)? m : + is_vnf(p)? [(p==EMPTY_VNF? p : apply(m, p[0])), p[1]] : + apply(m, p); + + +function _get_cp(geom) = + let(cp=select(geom,-3)) + is_vector(cp) ? cp + : let( + type = in_list(geom[0],["vnf_extent","vnf_isect"]) ? "vnf" + : in_list(geom[0],["rgn_extent","rgn_isect"]) ? "path" + : in_list(geom[0],["extrusion_extent","extrusion_isect"]) ? "xpath" + : "other" + ) + assert(type!="other", "Invalid cp value") + cp=="centroid" ? ( + type=="vnf" && (len(geom[1][0])==0 || len(geom[1][1])==0) ? [0,0,0] : + [each centroid(geom[1]), if (type=="xpath") 0] + ) + : let(points = type=="vnf"?geom[1][0]:flatten(force_region(geom[1]))) + cp=="mean" ? [each mean(points), if (type=="xpath") 0] + : cp=="box" ?[each mean(pointlist_bounds(points)), if (type=="xpath") 0] + : assert(false,"Invalid cp specification"); + + +function _get_cp(geom) = + let(cp=select(geom,-3)) + is_vector(cp) ? cp + : let( + is_vnf = in_list(geom[0],["vnf_extent","vnf_isect"]) + ) + cp == "centroid" ? ( + is_vnf && len(geom[1][1])==0 + ? [0,0,0] + : centroid(geom[1]) + ) + : let(points = is_vnf?geom[1][0]:flatten(force_region(geom[1]))) + cp=="mean" ? mean(points) + : cp=="box" ? mean(pointlist_bounds(points)) + : assert(false,"Invalid cp specification"); + + + +function _force_anchor_2d(anchor) = + assert(anchor.y==0 || anchor.z==0, "Anchor for a 2D shape cannot be fully 3D. It must have either Y or Z component equal to zero.") + anchor.y==0 ? [anchor.x,anchor.z] : point2d(anchor); + + +/// Internal Function: _find_anchor() +/// Usage: +/// anchorinfo = _find_anchor(anchor, geom); +/// Topics: Attachments +/// See Also: reorient(), attachable() +/// Description: +/// Calculates the anchor data for the given `anchor` vector or name, in the given attachment +/// geometry. Returns `[ANCHOR, POS, VEC, ANG]` where `ANCHOR` is the requested anchorname +/// or vector, `POS` is the anchor position, `VEC` is the direction vector of the anchor, and +/// `ANG` is the angle to align with around the rotation axis of th anchor direction vector. +/// Arguments: +/// anchor = Vector or named anchor string. +/// geom = The geometry description of the shape. +function _find_anchor(anchor, geom) = + is_string(anchor)? ( + anchor=="origin"? [anchor, CENTER, UP, 0] + : let( + anchors = last(geom), + found = search([anchor], anchors, num_returns_per_match=1)[0] + ) + assert(found!=[], str("Unknown anchor: ",anchor)) + anchors[found] + ) : + let( + cp = _get_cp(geom), + offset_raw = select(geom,-2), + offset = [for (i=[0:2]) anchor[i]==0? 0 : offset_raw[i]], // prevents bad centering. + type = geom[0] + ) + assert(is_vector(anchor),str("Invalid anchor: anchor=",anchor)) + let( + anchor = point3d(anchor), + oang = ( + approx(point2d(anchor), [0,0])? 0 : + atan2(anchor.y, anchor.x)+90 + ) + ) + type == "prismoid"? ( //size, size2, shift, axis + let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[]) + assert(all_comps_good, "All components of an anchor for a cuboid/prismoid must be -1, 0, or 1") + let( + size=geom[1], size2=geom[2], + shift=point2d(geom[3]), axis=point3d(geom[4]), + anch = rot(from=axis, to=UP, p=anchor), + offset = rot(from=axis, to=UP, p=offset), + h = size.z, + u = (anch.z + 1) / 2, // u is one of 0, 0.5, or 1 + axy = point2d(anch), + bot = point3d(v_mul(point2d(size )/2, axy), -h/2), + top = point3d(v_mul(point2d(size2)/2, axy) + shift, h/2), + pos = point3d(cp) + lerp(bot,top,u) + offset, + vecs = anchor==CENTER? [UP] + : [ + if (anch.x!=0) unit(rot(from=UP, to=[(top-bot).x,0,h], p=[axy.x,0,0]), UP), + if (anch.y!=0) unit(rot(from=UP, to=[0,(top-bot).y,h], p=[0,axy.y,0]), UP), + if (anch.z!=0) unit([0,0,anch.z],UP) + ], + vec2 = anchor==CENTER? UP + : len(vecs)==1? unit(vecs[0],UP) + : len(vecs)==2? vector_bisect(vecs[0],vecs[1]) + : let( + v1 = vector_bisect(vecs[0],vecs[2]), + v2 = vector_bisect(vecs[1],vecs[2]), + p1 = plane_from_normal(yrot(90,p=v1)), + p2 = plane_from_normal(xrot(-90,p=v2)), + line = plane_intersection(p1,p2), + v3 = unit(line[1]-line[0],UP) * anch.z + ) + unit(v3,UP), + vec = rot(from=UP, to=axis, p=vec2), + pos2 = rot(from=UP, to=axis, p=pos) + ) [anchor, pos2, vec, oang] + ) : type == "conoid"? ( //r1, r2, l, shift + assert(anchor.z == sign(anchor.z), "The Z component of an anchor for a cylinder/cone must be -1, 0, or 1") + let( + rr1=geom[1], rr2=geom[2], l=geom[3], + shift=point2d(geom[4]), axis=point3d(geom[5]), + r1 = is_num(rr1)? [rr1,rr1] : point2d(rr1), + r2 = is_num(rr2)? [rr2,rr2] : point2d(rr2), + anch = rot(from=axis, to=UP, p=anchor), + offset = rot(from=axis, to=UP, p=offset), + u = (anch.z+1)/2, + axy = unit(point2d(anch),[0,0]), + bot = point3d(v_mul(r1,axy), -l/2), + top = point3d(v_mul(r2,axy)+shift, l/2), + pos = point3d(cp) + lerp(bot,top,u) + offset, + sidevec = rot(from=UP, to=top==bot?UP:top-bot, p=point3d(axy)), + vvec = anch==CENTER? UP : unit([0,0,anch.z],UP), + vec = anch==CENTER? CENTER : + approx(axy,[0,0])? unit(anch,UP) : + approx(anch.z,0)? sidevec : + unit((sidevec+vvec)/2,UP), + pos2 = rot(from=UP, to=axis, p=pos), + vec2 = anch==CENTER? UP : rot(from=UP, to=axis, p=vec) + ) [anchor, pos2, vec2, oang] + ) : type == "point"? ( + let( + anchor = unit(point3d(anchor),CENTER), + pos = point3d(cp) + point3d(offset), + vec = unit(anchor,UP) + ) [anchor, pos, vec, oang] + ) : type == "spheroid"? ( //r + let( + rr = geom[1], + r = is_num(rr)? [rr,rr,rr] : point3d(rr), + anchor = unit(point3d(anchor),CENTER), + pos = point3d(cp) + v_mul(r,anchor) + point3d(offset), + vec = unit(v_mul(r,anchor),UP) + ) [anchor, pos, vec, oang] + ) : type == "vnf_isect"? ( //vnf + let( vnf=geom[1] ) + approx(anchor,CTR)? [anchor, [0,0,0], UP, 0] : + vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor), 0] : + let( + eps = 1/2048, + points = vnf[0], + faces = vnf[1], + rpts = apply(rot(from=anchor, to=RIGHT) * move(-cp), points), + hits = [ + for (face = faces) + let( + verts = select(rpts, face), + ys = column(verts,1), + zs = column(verts,2) + ) + if (max(ys) >= -eps && max(zs) >= -eps && + min(ys) <= eps && min(zs) <= eps) + let( + poly = select(points, face), + isect = polygon_line_intersection(poly, [cp,cp+anchor], eps=eps), + ptlist = is_undef(isect) ? [] : + is_vector(isect) ? [isect] + : flatten(isect), // parallel to a face + n = len(ptlist)>0 ? polygon_normal(poly) : undef + ) + for(pt=ptlist) [anchor * (pt-cp), n, pt] + ] + ) + assert(len(hits)>0, "Anchor vector does not intersect with the shape. Attachment failed.") + let( + furthest = max_index(column(hits,0)), + dist = hits[furthest][0], + pos = hits[furthest][2], + hitnorms = [for (hit = hits) if (approx(hit[0],dist,eps=eps)) hit[1]], + unorms = [ + for (i = idx(hitnorms)) + let( + thisnorm = hitnorms[i], + isdup = [ + for (j = [i+1:1:len(hitnorms)-1]) + if (approx(thisnorm, hitnorms[j])) 1 + ] != [] + ) + if (!isdup) thisnorm + ], + n = unit(sum(unorms)), + oang = approx(point2d(n), [0,0])? 0 : atan2(n.y, n.x) + 90 + ) + [anchor, pos, n, oang] + ) : type == "vnf_extent"? ( //vnf + let( vnf=geom[1] ) + approx(anchor,CTR)? [anchor, [0,0,0], UP, 0] : + vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor,UP), 0] : + let( + rpts = apply(rot(from=anchor, to=RIGHT) * move(point3d(-cp)), vnf[0]), + maxx = max(column(rpts,0)), + idxs = [for (i = idx(rpts)) if (approx(rpts[i].x, maxx)) i], + avep = sum(select(rpts,idxs))/len(idxs), + mpt = approx(point2d(anchor),[0,0])? [maxx,0,0] : avep, + pos = point3d(cp) + rot(from=RIGHT, to=anchor, p=mpt) + ) [anchor, pos, anchor, oang] + ) : type == "trapezoid"? ( //size, size2, shift, override + let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[]) + assert(all_comps_good, "All components of an anchor for a rectangle/trapezoid must be -1, 0, or 1") + let( + anchor=_force_anchor_2d(anchor), + size=geom[1], size2=geom[2], shift=geom[3], + u = (anchor.y+1)/2, // 0<=u<=1 + frpt = [size.x/2*anchor.x, -size.y/2], + bkpt = [size2/2*anchor.x+shift, size.y/2], + override = geom[4](anchor), + pos = default(override[0],point2d(cp) + lerp(frpt, bkpt, u) + point2d(offset)), + svec = point3d(line_normal(bkpt,frpt)*anchor.x), + vec = is_def(override[1]) ? override[1] + : anchor.y == 0? ( anchor.x == 0? BACK : svec ) + : anchor.x == 0? [0,anchor.y,0] + : unit((svec + [0,anchor.y,0]) / 2, [0,anchor.y,0]) + ) [anchor, pos, vec, 0] + ) : type == "ellipse"? ( //r + let( + anchor = unit(_force_anchor_2d(anchor),[0,0]), + r = force_list(geom[1],2), + pos = approx(anchor.x,0) ? [0,sign(anchor.y)*r.y] + : let( + m = anchor.y/anchor.x, + px = sign(anchor.x) * sqrt(1/(1/sqr(r.x) + m*m/sqr(r.y))) + ) + [px,m*px], + vec = unit([r.y/r.x*pos.x, r.x/r.y*pos.y],BACK) + ) [anchor, point2d(cp+offset)+pos, vec, 0] + ) : type == "rgn_isect"? ( //region + let( + anchor = _force_anchor_2d(anchor), + rgn = force_region(move(-point2d(cp), p=geom[1])) + ) + approx(anchor,[0,0])? [anchor, [0,0,0], BACK, 0] : + let( + isects = [ + for (path=rgn, t=triplet(path,true)) let( + seg1 = [t[0],t[1]], + seg2 = [t[1],t[2]], + isect = line_intersection([[0,0],anchor], seg1, RAY, SEGMENT), + n = is_undef(isect)? [0,1] : + !approx(isect, t[1])? line_normal(seg1) : + unit((line_normal(seg1)+line_normal(seg2))/2,[0,1]), + n2 = vector_angle(anchor,n)>90? -n : n + ) + if(!is_undef(isect) && !approx(isect,t[0])) [norm(isect), isect, n2] + ] + ) + assert(len(isects)>0, "Anchor vector does not intersect with the shape. Attachment failed.") + let( + maxidx = max_index(column(isects,0)), + isect = isects[maxidx], + pos = point2d(cp) + isect[1], + vec = unit(isect[2],[0,1]) + ) [anchor, pos, vec, 0] + ) : type == "rgn_extent"? ( //region + let( anchor = _force_anchor_2d(anchor) ) + approx(anchor,[0,0])? [anchor, [0,0,0], BACK, 0] : + let( + rgn = force_region(geom[1]), + rpts = rot(from=anchor, to=RIGHT, p=flatten(rgn)), + maxx = max(column(rpts,0)), + ys = [for (pt=rpts) if (approx(pt.x, maxx)) pt.y], + midy = (min(ys)+max(ys))/2, + pos = rot(from=RIGHT, to=anchor, p=[maxx,midy]) + ) [anchor, pos, unit(anchor,BACK), 0] + ) : type=="extrusion_extent" || type=="extrusion_isect" ? ( // extruded region + assert(in_list(anchor.z,[-1,0,1]), "The Z component of an anchor for an extruded 2D shape must be -1, 0, or 1.") + let( + anchor_xy = point2d(anchor), + rgn = geom[1], + L = geom[2], + twist = geom[3], + scale = geom[4], + shift = geom[5], + u = (anchor.z + 1) / 2, + shmat = move(lerp([0,0], shift, u)), + scmat = scale(lerp([1,1], scale, u)), + twmat = zrot(lerp(0, -twist, u)), + mat = shmat * scmat * twmat + ) + approx(anchor_xy,[0,0]) ? [anchor, apply(mat, point3d(cp,anchor.z*L/2)), unit(anchor, UP), oang] : + let( + newrgn = apply(mat, rgn), + newgeom = attach_geom(two_d=true, region=newrgn, extent=type=="extrusion_extent", cp=cp), + result2d = _find_anchor(anchor_xy, newgeom), + pos = point3d(result2d[1], anchor.z*L/2), + vec = unit(point3d(result2d[2], anchor.z),UP), + oang = atan2(vec.y,vec.x) + 90 + ) + [anchor, pos, vec, oang] + ) : + assert(false, "Unknown attachment geometry type."); + + +/// Internal Function: _is_shown() +/// Usage: +/// bool = _is_shown(); +/// Topics: Attachments +/// See Also: reorient(), attachable() +/// Description: +/// Returns true if objects should currently be shown based on the tag settings. +function _is_shown() = + assert(is_list($tags_shown) || $tags_shown=="ALL") + assert(is_list($tags_hidden)) + let( + dummy=is_undef($tags) ? 0 : echo("Use tag() instead of $tags for specifying an object's tag."), + $tag = default($tag,$tags) + ) + assert(is_string($tag), str("Tag value (",$tag,") is not a string")) + assert(undef==str_find($tag," "),str("Tag string \"",$tag,"\" contains a space, which is not allowed")) + let( + shown = $tags_shown=="ALL" || in_list($tag,$tags_shown), + hidden = in_list($tag, $tags_hidden) + ) + shown && !hidden; + + +// Section: Visualizing Anchors + +/// Internal Function: _standard_anchors() +/// Usage: +/// anchs = _standard_anchors([two_d]); +/// Description: +/// Return the vectors for all standard anchors. +/// Arguments: +/// two_d = If true, returns only the anchors where the Z component is 0. Default: false +function _standard_anchors(two_d=false) = [ + for ( + zv = [ + if (!two_d) TOP, + CENTER, + if (!two_d) BOTTOM + ], + yv = [FRONT, CENTER, BACK], + xv = [LEFT, CENTER, RIGHT] + ) xv+yv+zv +]; + + + +// Module: show_anchors() +// Synopsis: Shows anchors for the parent object. +// SynTags: Geom +// Topics: Attachments +// See Also: expose_anchors(), anchor_arrow(), anchor_arrow2d(), frame_ref() +// Usage: +// PARENT() show_anchors([s], [std=], [custom=]); +// Description: +// Show all standard anchors for the parent object. +// Arguments: +// s = Length of anchor arrows. +// --- +// std = If true show standard anchors. Default: true +// custom = If true show named anchors. Default: true +// Example(FlatSpin,VPD=333): +// cube(50, center=true) show_anchors(); +module show_anchors(s=10, std=true, custom=true) { + check = assert($parent_geom != undef); + two_d = _attach_geom_2d($parent_geom); + if (std) { + 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=last($parent_geom)) { + attach(anchor[0]) { + if(two_d) { + anchor_arrow2d(s, color="cyan"); + } else { + anchor_arrow(s, color="cyan"); + } + color("black") + tag("anchor-arrow") { + xrot(two_d? 0 : 90) { + back(s/3) { + yrot_copies(n=2) + up(two_d? 0.51 : s/30) { + linear_extrude(height=0.01, convexity=12, center=true) { + text(text=anchor[0], size=s/4, halign="center", valign="center", font="Helvetica", $fn=36); + } + } + } + } + } + color([1, 1, 1, 1]) + tag("anchor-arrow") { + xrot(two_d? 0 : 90) { + back(s/3) { + cube([s/4.5*len(anchor[0]), s/3, 0.01], center=true); + } + } + } + } + } + } + children(); +} + + +// Module: anchor_arrow() +// Synopsis: Shows a 3d anchor orientation arrow. +// SynTags: Geom +// Topics: Attachments +// See Also: anchor_arrow2d(), show_anchors(), expose_anchors(), frame_ref() +// Usage: +// anchor_arrow([s], [color], [flag], [anchor=], [orient=], [spin=]) [ATTACHMENTS]; +// Description: +// Show an anchor orientation arrow. By default, tagged with the name "anchor-arrow". +// Arguments: +// s = Length of the arrows. Default: `10` +// color = Color of the arrow. Default: `[0.333, 0.333, 1]` +// flag = If true, draw the orientation flag on the arrowhead. Default: true +// --- +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` +// Example: +// anchor_arrow(s=20); +module anchor_arrow(s=10, color=[0.333,0.333,1], flag=true, $tag="anchor-arrow", $fn=12, anchor=BOT, spin=0, orient=UP) { + attachable(anchor,spin,orient, r=s/6, l=s) { + down(s/2) + recolor("gray") spheroid(d=s/6) { + attach(CENTER,BOT) recolor(color) cyl(h=s*2/3, d=s/15) { + attach(TOP,BOT) cyl(h=s/3, d1=s/5, d2=0) { + if(flag) { + position(BOT) + recolor([1,0.5,0.5]) + cuboid([s/100, s/6, s/4], anchor=FRONT+BOT); + } + } + } + } + children(); + } +} + + + +// Module: anchor_arrow2d() +// Synopsis: Shows a 2d anchor orientation arrow. +// SynTags: Geom +// Topics: Attachments +// See Also: anchor_arrow(), show_anchors(), expose_anchors(), frame_ref() +// Usage: +// anchor_arrow2d([s], [color], [flag]); +// Description: +// Show an anchor orientation arrow. +// Arguments: +// s = Length of the arrows. +// color = Color of the arrow. +// Example: +// anchor_arrow2d(s=20); +module anchor_arrow2d(s=15, color=[0.333,0.333,1], $tag="anchor-arrow") { + color(color) stroke([[0,0],[0,s]], width=s/10, endcap1="butt", endcap2="arrow2"); +} + + + +// Module: expose_anchors() +// Synopsis: Used to show a transparent object with solid color anchor arrows. +// Topics: Attachments +// See Also: anchor_arrow2d(), show_anchors(), show_anchors(), frame_ref() +// Usage: +// expose_anchors(opacity) {child1() show_anchors(); child2() show_anchors(); ...} +// Description: +// Used in combination with show_anchors() to display an object in transparent gray with its anchors in solid color. +// Children will appear transparent and any anchor arrows drawn with will appear in solid color. +// Arguments: +// opacity = The opacity of the children. 0.0 is invisible, 1.0 is opaque. Default: 0.2 +// Example(FlatSpin,VPD=333): +// expose_anchors() cube(50, center=true) show_anchors(); +module expose_anchors(opacity=0.2) { + show_only("anchor-arrow") + children(); + hide("anchor-arrow") + color(is_undef($color) || $color=="default" ? [0,0,0] : + is_string($color) ? $color + : point3d($color), + opacity) + children(); +} + + + +// Module: frame_ref() +// Synopsis: Shows axis orientation arrows. +// SynTags: Geom +// Topics: Attachments +// See Also: anchor_arrow(), anchor_arrow2d(), show_anchors(), expose_anchors() +// Usage: +// frame_ref(s, opacity); +// Description: +// Displays X,Y,Z axis arrows in red, green, and blue respectively. +// Arguments: +// s = Length of the arrows. +// opacity = The opacity of the arrows. 0.0 is invisible, 1.0 is opaque. Default: 1.0 +// Examples: +// frame_ref(25); +// frame_ref(30, opacity=0.5); +module frame_ref(s=15, opacity=1) { + cube(0.01, center=true) { + attach([1,0,0]) anchor_arrow(s=s, flag=false, color=[1.0, 0.3, 0.3, opacity]); + attach([0,1,0]) anchor_arrow(s=s, flag=false, color=[0.3, 1.0, 0.3, opacity]); + attach([0,0,1]) anchor_arrow(s=s, flag=false, color=[0.3, 0.3, 1.0, opacity]); + children(); + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// Code after this is internal code for managing edge and corner sets and for displaying +/// edge and corners in the docs +/// + +module _edges_text3d(txt,size=3) { + if (is_list(txt)) { + for (i=idx(txt)) { + down((i-len(txt)/2+0.5)*size*1.5) { + _edges_text3d(txt[i], size=size); + } + } + } else { + xrot(90) color("#000") + linear_extrude(height=0.1) { + text(text=txt, size=size, halign="center", valign="center"); + } + } +} + + +function _edges_vec_txt(x) = is_string(x)? str("\"", x, "\"") : + assert(is_string(x) || is_vector(x,3), str(x)) + let( + lst = concat( + x.z>0? ["TOP"] : x.z<0? ["BOT"] : [], + x.y>0? ["BACK"] : x.y<0? ["FWD"] : [], + x.x>0? ["RIGHT"] : x.x<0? ["LEFT"] : [] + ), + out = [ + for (i = idx(lst)) + i>0? str("+",lst[i]) : lst[i] + ] + ) out; + + +function _edges_text(edges) = + is_string(edges) ? [str("\"",edges,"\"")] : + edges==EDGES_NONE ? ["EDGES_NONE"] : + edges==EDGES_ALL ? ["EDGES_ALL"] : + _is_edge_array(edges) ? [""] : + is_vector(edges,3) ? _edges_vec_txt(edges) : + is_list(edges) ? let( + lst = [for (x=edges) each _edges_text(x)], + out = [ + for (i=idx(lst)) + str( + (i==0? "[" : ""), + lst[i], + (i0? 1 : 0]]; + + + + +/// Internal Function: _edges() +/// Topics: Edges +/// Usage: +/// edgs = _edges(v); +/// edgs = _edges(v, except); +/// +/// Description: +/// Takes a list of edge set descriptors, and returns a normalized edges array +/// that represents all those given edges. +/// Arguments: +/// v = The edge set to include. +/// except = The edge set to specifically exclude, even if they are in `v`. +/// +/// See Also: EDGES_NONE, EDGES_ALL +/// +function _edges(v, except=[]) = + v==[] ? EDGES_NONE : + (is_string(v) || is_vector(v) || _is_edge_array(v))? _edges([v], except=except) : + (is_string(except) || is_vector(except) || _is_edge_array(except))? _edges(v, except=[except]) : + except==[]? _normalize_edges(sum([for (x=v) _edge_set(x)])) : + _normalize_edges( + _normalize_edges(sum([for (x=v) _edge_set(x)])) - + sum([for (x=except) _edge_set(x)]) + ); + + +/// Internal Module: _show_edges() +/// Topics: Edges, Debugging +/// Usage: +/// _show_edges(edges, [size=], [text=], [txtsize=]); +/// Description: +/// Draws a semi-transparent cube with the given edges highlighted in red. +/// Arguments: +/// edges = The edges to highlight. +/// size = The scalar size of the cube. +/// text = The text to show on the front of the cube. +/// txtsize = The size of the text. +/// See Also: _edges(), EDGES_NONE, EDGES_ALL +/// Example: +/// _show_edges(size=30, edges=["X","Y"]); +module _show_edges(edges="ALL", size=20, text, txtsize=3,toplabel) { + edge_set = _edges(edges); + text = !is_undef(text) ? text : _edges_text(edges); + color("red") { + for (axis=[0:2], i=[0:3]) { + if (edge_set[axis][i] > 0) { + translate(EDGE_OFFSETS[axis][i]*size/2) { + if (axis==0) xcyl(h=size, d=2); + if (axis==1) ycyl(h=size, d=2); + if (axis==2) zcyl(h=size, d=2); + } + } + } + } + fwd(size/2) _edges_text3d(text, size=txtsize); + color("yellow",0.7) cuboid(size=size); + vpr = [55,0,25]; + color("black") + if (is_def(toplabel)) + for(h=idx(toplabel)) up(21+6*h)rot(vpr) text3d(select(toplabel,-h-1),size=3.3,h=0.1,orient=UP,anchor=FRONT); +} + + + + +/// Internal Constant: CORNERS_NONE +/// Topics: Corners +/// Description: +/// The set of no corners. +/// Figure(3D): +/// _show_corners(corners="NONE"); +/// See Also: CORNERS_ALL, corners() +CORNERS_NONE = [0,0,0,0,0,0,0,0]; // No corners. + + +/// Internal Constant: CORNERS_ALL +/// Topics: Corners +/// Description: +/// The set of all corners. +/// Figure(3D): +/// _show_corners(corners="ALL"); +/// See Also: CORNERS_NONE, _corners() +CORNERS_ALL = [1,1,1,1,1,1,1,1]; + + +/// Internal Constant: CORNER_OFFSETS +/// Topics: Corners +/// Description: +/// The vectors pointing to each corner of a unit sized cube. +/// Each item in a corner array will have a corresponding vector in this array. +/// See Also: CORNERS_NONE, CORNERS_ALL, _corners() +CORNER_OFFSETS = [ + [-1,-1,-1], [ 1,-1,-1], [-1, 1,-1], [ 1, 1,-1], + [-1,-1, 1], [ 1,-1, 1], [-1, 1, 1], [ 1, 1, 1] +]; + + + + +/// Internal Function: _is_corner_array() +/// Topics: Corners, Type Checking +/// Usage: +/// bool = _is_corner_array(x) +/// Description: +/// Returns true if the given value has the form of a corner array. +/// See Also: CORNERS_NONE, CORNERS_ALL, _corners() +function _is_corner_array(x) = is_vector(x) && len(x)==8 && all([for (xx=x) xx==1||xx==0]); + + +/// Internal Function: _normalize_corners() +/// Topics: Corners +/// Usage: +/// corns = _normalize_corners(v); +/// Description: +/// Normalizes all values in a corner array to be `1`, if it was originally greater than `0`, +/// or `0`, if it was originally less than or equal to `0`. +/// See Also: CORNERS_NONE, CORNERS_ALL, _corners() +function _normalize_corners(v) = [for (x=v) x>0? 1 : 0]; + + +function _corner_set(v) = + _is_corner_array(v)? v : [ + for (i=[0:7]) let( + v2 = CORNER_OFFSETS[i] + ) ( + is_string(v)? ( + v=="ALL"? true : // Return all corners. + v=="NONE"? false : // Return no corners. + let(valid_values = ["ALL", "NONE"]) + assert( + in_list(v, valid_values), + str(v, " must be a vector, corner array, or one of ", valid_values) + ) v + ) : + all([for (i=[0:2]) !v[i] || (v[i]==v2[i])]) + )? 1 : 0 +]; + + +/// Function: _corners() +/// Topics: Corners +/// Usage: +/// corns = _corners(v); +/// corns = _corners(v, except); +/// Description: +/// Takes a list of corner set descriptors, and returns a normalized corners array +/// that represents all those given corners. If the `except` argument is given +/// a list of corner set descriptors, then all those corners will be removed +/// from the returned corners array. If either argument only has a single corner +/// set descriptor, you do not have to pass it in a list. +function _corners(v, except=[]) = + v==[] ? CORNERS_NONE : + (is_string(v) || is_vector(v) || _is_corner_array(v))? _corners([v], except=except) : + (is_string(except) || is_vector(except) || _is_corner_array(except))? _corners(v, except=[except]) : + except==[]? _normalize_corners(sum([for (x=v) _corner_set(x)])) : + let( + a = _normalize_corners(sum([for (x=v) _corner_set(x)])), + b = _normalize_corners(sum([for (x=except) _corner_set(x)])) + ) _normalize_corners(a - b); + + +/// Internal Function: _corner_edges() +/// Topics: Corners +/// Description: +/// Returns [XCOUNT,YCOUNT,ZCOUNT] where each is the count of edges aligned with that +/// axis that are in the edge set and touch the given corner. +/// Arguments: +/// edges = Standard edges array. +/// v = Vector pointing to the corner to count edge intersections at. +/// See Also: CORNERS_NONE, CORNERS_ALL, _corners() +function _corner_edges(edges, v) = + let(u = (v+[1,1,1])/2) [edges[0][u.y+u.z*2], edges[1][u.x+u.z*2], edges[2][u.x+u.y*2]]; + + +/// InternalFunction: _corner_edge_count() +/// Topics: Corners +/// Description: +/// Counts how many given edges intersect at a specific corner. +/// Arguments: +/// edges = Standard edges array. +/// v = Vector pointing to the corner to count edge intersections at. +/// See Also: CORNERS_NONE, CORNERS_ALL, _corners() +function _corner_edge_count(edges, v) = + let(u = (v+[1,1,1])/2) edges[0][u.y+u.z*2] + edges[1][u.x+u.z*2] + edges[2][u.x+u.y*2]; + + +function _corners_text(corners) = + is_string(corners) ? [str("\"",corners,"\"")] : + corners==CORNERS_NONE ? ["CORNERS_NONE"] : + corners==CORNERS_ALL ? ["CORNERS_ALL"] : + _is_corner_array(corners) ? [""] : + is_vector(corners,3) ? _edges_vec_txt(corners) : + is_list(corners) ? let( + lst = [for (x=corners) each _corners_text(x)], + out = [ + for (i=idx(lst)) + str( + (i==0? "[" : ""), + lst[i], + (i0) + translate(CORNER_OFFSETS[i]*size/2) + color("red") sphere(d=2, $fn=16); + fwd(size/2) _edges_text3d(text, size=txtsize); + color("yellow",0.7) cuboid(size=size); + vpr = [55,0,25]; + color("black") + if (is_def(toplabel)) + for(h=idx(toplabel)) up(21+6*h)rot(vpr) text3d(select(toplabel,-h-1),size=3.3,h=.1,orient=UP,anchor=FRONT); +} + +module _show_cube_faces(faces, size=20, toplabel,botlabel) { + color("red") + for(f=faces){ + move(f*size/2) rot(from=UP,to=f) + cuboid([size,size,.1]); + } + vpr = [55,0,25]; + color("black"){ + if (is_def(toplabel)) + for(h=idx(toplabel)) up(21+6*h)rot(vpr) text3d(select(toplabel,-h-1),size=3.3,h=.1,orient=UP,anchor=FRONT); + if (is_def(botlabel)) + for(h=idx(botlabel)) down(26+6*h)rot(vpr) text3d(botlabel[h],size=3.3,h=.1,orient=UP,anchor=FRONT); + } + color("yellow",0.7) cuboid(size=size); +} + +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/comparisons.scad b/comparisons.scad index 1687f12..2d090da 100644 --- a/comparisons.scad +++ b/comparisons.scad @@ -241,7 +241,7 @@ function are_ends_equal(list, eps=EPSILON) = // Usage: // bool = is_increasing(list, [strict]); // Description: -// Returns true if the list is (non-strictly) increasing, or strictly increasing if strict is set to true. +// Returns true if the list is (non-strictly) increasing, or strictly increasing if `strict=true`. // The list can be a list of any items that OpenSCAD can compare, or it can be a string which will be // evaluated character by character. // Arguments: @@ -266,7 +266,7 @@ function is_increasing(list,strict=false) = // Usage: // bool = is_decreasing(list, [strict]); // Description: -// Returns true if the list is (non-strictly) decreasing, or strictly decreasing if strict is set to true. +// Returns true if the list is (non-strictly) decreasing, or strictly decreasing if `strict=true`. // The list can be a list of any items that OpenSCAD can compare, or it can be a string which will be // evaluated character by character. // Arguments: @@ -532,7 +532,7 @@ function close_path(list,eps=EPSILON) = list_wrap(list,eps); // Function: list_unwrap() -// Synopsis: Removes the last item of a list if it's first and last values are equal. +// Synopsis: Removes the last item of a list if its first and last values are equal. // Topics: List Handling, Paths // See Also: list_wrap(), deduplicate() // Usage: diff --git a/constants.scad b/constants.scad index a93f9c9..466dfcc 100644 --- a/constants.scad +++ b/constants.scad @@ -130,6 +130,7 @@ INCH = 25.4; // Constant: IDENT // Synopsis: A constant containing the 3D identity transformation matrix. +// SynTags: Mat // Topics: Constants, Affine, Matrices, Transforms // See Also: ident() // Description: Identity transformation matrix for three-dimensional transforms. Equal to `ident(4)`. diff --git a/coords.scad b/coords.scad index 272e102..46b7577 100644 --- a/coords.scad +++ b/coords.scad @@ -12,11 +12,11 @@ // Section: Coordinate Manipulation // Function: point2d() -// Usage: -// pt = point2d(p, [fill]); +// Synopsis: Convert a vector to 2D. // Topics: Coordinates, Points // See Also: path2d(), point3d(), path3d() -// Synopsis: Convert a vector to 2d. +// Usage: +// pt = point2d(p, [fill]); // Description: // Returns a 2D vector/point from a 2D or 3D vector. If given a 3D point, removes the Z coordinate. // Arguments: @@ -26,11 +26,12 @@ function point2d(p, fill=0) = assert(is_list(p)) [for (i=[0:1]) (p[i]==undef)? f // Function: path2d() -// Usage: -// pts = path2d(points); +// Synopsis: Convert a path to 2D. +// SynTags: Path // Topics: Coordinates, Points, Paths // See Also: point2d(), point3d(), path3d() -// Synopsis: Convert a path to 2d. +// Usage: +// pts = path2d(points); // Description: // Returns a list of 2D vectors/points from a list of 2D, 3D or higher dimensional vectors/points. // Removes the extra coordinates from higher dimensional points. The input must be a path, where @@ -45,11 +46,11 @@ function path2d(points) = // Function: point3d() -// Usage: -// pt = point3d(p, [fill]); +// Synopsis: Convert a vector to 3D. // Topics: Coordinates, Points // See Also: path2d(), point2d(), path3d() -// Synopsis: Convert a vector to 3d. +// Usage: +// pt = point3d(p, [fill]); // Description: // Returns a 3D vector/point from a 2D or 3D vector. // Arguments: @@ -61,11 +62,12 @@ function point3d(p, fill=0) = // Function: path3d() +// Synopsis: Convert a path to 3D. +// SynTags: Path +// Topics: Coordinates, Points, Paths +// See Also: point2d(), path2d(), point3d() // Usage: // pts = path3d(points, [fill]); -// Topics: Coordinates, Points, Paths -// Synopsis: Convert a path to 3d. -// See Also: point2d(), path2d(), point3d() // Description: // Returns a list of 3D vectors/points from a list of 2D or higher dimensional vectors/points // by removing extra coordinates or adding the z coordinate. @@ -86,11 +88,11 @@ function path3d(points, fill=0) = // Function: point4d() +// Synopsis: Convert a vector to 4d. +// Topics: Coordinates, Points +// See Also: point2d(), path2d(), point3d(), path3d(), path4d() // Usage: // pt = point4d(p, [fill]); -// Topics: Coordinates, Points -// Synopsis: Convert a vector to 4d. -// See Also: point2d(), path2d(), point3d(), path3d(), path4d() // Description: // Returns a 4D vector/point from a 2D or 3D vector. // Arguments: @@ -101,11 +103,12 @@ function point4d(p, fill=0) = assert(is_list(p)) // Function: path4d() -// Usage: -// pt = path4d(points, [fill]); +// Synopsis: Convert a path to 4d. +// SynTags: Path // Topics: Coordinates, Points, Paths // See Also: point2d(), path2d(), point3d(), path3d(), point4d() -// Synopsis: Convert a path to 4d. +// Usage: +// pt = path4d(points, [fill]); // Description: // Returns a list of 4D vectors/points from a list of 2D or 3D vectors/points. // Arguments: @@ -134,13 +137,14 @@ function path4d(points, fill=0) = // Section: Coordinate Systems // Function: polar_to_xy() +// Synopsis: Convert 2D polar coordinates to cartesian coordinates. +// SynTags: Path +// Topics: Coordinates, Points, Paths +// See Also: xy_to_polar(), xyz_to_cylindrical(), cylindrical_to_xyz(), xyz_to_spherical(), spherical_to_xyz() // Usage: // pt = polar_to_xy(r, theta); // pt = polar_to_xy([R, THETA]); // pts = polar_to_xy([[R,THETA], [R,THETA], ...]); -// Topics: Coordinates, Points, Paths -// Synopsis: Convert 2d polar coordinates to cartesian coordinates. -// See Also: xy_to_polar(), xyz_to_cylindrical(), cylindrical_to_xyz(), xyz_to_spherical(), spherical_to_xyz() // Description: // Called with two arguments, converts the `r` and `theta` 2D polar coordinate into an `[X,Y]` cartesian coordinate. // Called with one `[R,THETA]` vector argument, converts the 2D polar coordinate into an `[X,Y]` cartesian coordinate. @@ -172,13 +176,13 @@ function polar_to_xy(r,theta) = // Function: xy_to_polar() +// Synopsis: Convert 2D cartesian coordinates to polar coordinates (radius and angle) +// Topics: Coordinates, Points, Paths +// See Also: polar_to_xy(), xyz_to_cylindrical(), cylindrical_to_xyz(), xyz_to_spherical(), spherical_to_xyz() // Usage: // r_theta = xy_to_polar(x,y); // r_theta = xy_to_polar([X,Y]); // r_thetas = xy_to_polar([[X,Y], [X,Y], ...]); -// Topics: Coordinates, Points, Paths -// Synopsis: Convert 2d cartesian coordinates to polar coordinates (radius and angle) -// See Also: polar_to_xy(), xyz_to_cylindrical(), cylindrical_to_xyz(), xyz_to_spherical(), spherical_to_xyz() // Description: // Called with two arguments, converts the `x` and `y` 2D cartesian coordinate into a `[RADIUS,THETA]` polar coordinate. // Called with one `[X,Y]` vector argument, converts the 2D cartesian coordinate into a `[RADIUS,THETA]` polar coordinate. @@ -209,16 +213,19 @@ function xy_to_polar(x, y) = // Function: project_plane() +// Synopsis: Project a set of points onto a specified plane, returning 2D points. +// SynTags: Path +// Topics: Coordinates, Points, Paths +// See Also: lift_plane() // Usage: // xy = project_plane(plane, p); // Usage: To get a transform matrix // M = project_plane(plane) -// Synopsis: Project a set of points onto a specified plane, returning 2d points. // Description: -// Maps the provided 3d point(s) from 3D coordinates to a 2d coordinate system defined by `plane`. Points that are not +// Maps the provided 3D point(s) from 3D coordinates to a 2D coordinate system defined by `plane`. Points that are not // on the specified plane will be projected orthogonally onto the plane. This coordinate system is useful if you need -// to perform 2d operations on a coplanar set of data. After those operations are done you can return the data -// to 3d with `lift_plane()`. You could also use this to force approximately coplanar data to be exactly coplanar. +// to perform 2D operations on a coplanar set of data. After those operations are done you can return the data +// to 3D with `lift_plane()`. You could also use this to force approximately coplanar data to be exactly coplanar. // The parameter p can be a point, path, region, bezier patch or VNF. // The plane can be specified as // - A list of three points. The planar coordinate system will have [0,0] at plane[0], and plane[1] will lie on the Y+ axis. @@ -227,7 +234,6 @@ function xy_to_polar(x, y) = // . // If you omit the point specification then `project_plane()` returns a rotation matrix that maps the specified plane to the XY plane. // Note that if you apply this transformation to data lying on the plane it will produce 3D points with the Z coordinate of zero. -// Topics: Coordinates, Points, Paths // Arguments: // plane = plane specification or point list defining the plane // p = 3D point, path, region, VNF or bezier patch to project @@ -271,7 +277,7 @@ function project_plane(plane,p) = is_vnf(p) ? [project_plane(plane,p[0]), p[1]] : is_list(p) && is_list(p[0]) && is_vector(p[0][0],3) ? // bezier patch or region [for(plist=p) project_plane(plane,plist)] - : assert(is_vector(p,3) || is_path(p,3),str("Data must be a 3d point, path, region, vnf or bezier patch",p)) + : assert(is_vector(p,3) || is_path(p,3),str("Data must be a 3D point, path, region, vnf or bezier patch",p)) is_matrix(plane,3,3) ? assert(!is_collinear(plane),"Points defining the plane must not be collinear") let( @@ -285,13 +291,14 @@ function project_plane(plane,p) = // Function: lift_plane() +// Synopsis: Map a list of 2D points onto a plane in 3D. +// SynTags: Path +// Topics: Coordinates, Points, Paths +// See Also: project_plane() // Usage: // xyz = lift_plane(plane, p); // Usage: to get transform matrix // M = lift_plane(plane); -// Synopsis: Map a list of 2d points onto a plane in 3d. -// Topics: Coordinates, Points, Paths -// See Also: project_plane() // Description: // Converts the given 2D point on the plane to 3D coordinates of the specified plane. // The parameter p can be a point, path, region, bezier patch or VNF. @@ -327,7 +334,7 @@ function lift_plane(plane, p) = : is_vnf(p) ? [lift_plane(plane,p[0]), p[1]] : is_list(p) && is_list(p[0]) && is_vector(p[0][0],3) ? // bezier patch or region [for(plist=p) lift_plane(plane,plist)] - : assert(is_vector(p,2) || is_path(p,2),"Data must be a 2d point, path, region, vnf or bezier patch") + : assert(is_vector(p,2) || is_path(p,2),"Data must be a 2D point, path, region, vnf or bezier patch") is_matrix(plane,3,3) ? let( v = plane[2]-plane[0], @@ -338,13 +345,14 @@ function lift_plane(plane, p) = // Function: cylindrical_to_xyz() +// Synopsis: Convert cylindrical coordinates to cartesian coordinates. +// SynTags: Path +// Topics: Coordinates, Points, Paths +// See Also: xyz_to_cylindrical(), xy_to_polar(), polar_to_xy(), xyz_to_spherical(), spherical_to_xyz() // Usage: // pt = cylindrical_to_xyz(r, theta, z); // pt = cylindrical_to_xyz([RADIUS,THETA,Z]); // pts = cylindrical_to_xyz([[RADIUS,THETA,Z], [RADIUS,THETA,Z], ...]); -// Topics: Coordinates, Points, Paths -// See Also: xyz_to_cylindrical(), xy_to_polar(), polar_to_xy(), xyz_to_spherical(), spherical_to_xyz() -// Synopsis: Convert cylindrical coordinates to cartesian coordinates. // Description: // Called with three arguments, converts the `r`, `theta`, and 'z' 3D cylindrical coordinate into an `[X,Y,Z]` cartesian coordinate. // Called with one `[RADIUS,THETA,Z]` vector argument, converts the 3D cylindrical coordinate into an `[X,Y,Z]` cartesian coordinate. @@ -368,13 +376,13 @@ function cylindrical_to_xyz(r,theta,z) = // Function: xyz_to_cylindrical() +// Synopsis: Convert 3D cartesian coordinates to cylindrical coordinates. +// Topics: Coordinates, Points, Paths +// See Also: cylindrical_to_xyz(), xy_to_polar(), polar_to_xy(), xyz_to_spherical(), spherical_to_xyz() // Usage: // rtz = xyz_to_cylindrical(x,y,z); // rtz = xyz_to_cylindrical([X,Y,Z]); // rtzs = xyz_to_cylindrical([[X,Y,Z], [X,Y,Z], ...]); -// Topics: Coordinates, Points, Paths -// Synopsis: Convert 3d cartesian coordinates to cylindrical coordinates. -// See Also: cylindrical_to_xyz(), xy_to_polar(), polar_to_xy(), xyz_to_spherical(), spherical_to_xyz() // Description: // Called with three arguments, converts the `x`, `y`, and `z` 3D cartesian coordinate into a `[RADIUS,THETA,Z]` cylindrical coordinate. // Called with one `[X,Y,Z]` vector argument, converts the 3D cartesian coordinate into a `[RADIUS,THETA,Z]` cylindrical coordinate. @@ -399,6 +407,10 @@ function xyz_to_cylindrical(x,y,z) = // Function: spherical_to_xyz() +// Synopsis: Convert spherical coordinates to 3D cartesian coordinates. +// SynTags: Path +// Topics: Coordinates, Points, Paths +// See Also: cylindrical_to_xyz(), xyz_to_spherical(), xyz_to_cylindrical(), altaz_to_xyz(), xyz_to_altaz() // Usage: // pt = spherical_to_xyz(r, theta, phi); // pt = spherical_to_xyz([RADIUS,THETA,PHI]); @@ -408,9 +420,6 @@ function xyz_to_cylindrical(x,y,z) = // Called with one `[RADIUS,THETA,PHI]` vector argument, converts the 3D spherical coordinate into an `[X,Y,Z]` cartesian coordinate. // Called with a list of `[RADIUS,THETA,PHI]` vector arguments, converts each 3D spherical coordinate into `[X,Y,Z]` cartesian coordinates. // Theta is the angle counter-clockwise of X+ on the XY plane. Phi is the angle down from the Z+ pole. -// Synopsis: Convert spherical coordinates to 3d cartesian coordinates. -// Topics: Coordinates, Points, Paths -// See Also: cylindrical_to_xyz(), xyz_to_spherical(), xyz_to_cylindrical(), altaz_to_xyz(), xyz_to_altaz() // Arguments: // r = distance from origin. // theta = angle in degrees, counter-clockwise of X+ on the XY plane. @@ -435,7 +444,7 @@ function spherical_to_xyz(r,theta,phi) = // r_theta_phi = xyz_to_spherical([X,Y,Z]) // r_theta_phis = xyz_to_spherical([[X,Y,Z], [X,Y,Z], ...]) // Topics: Coordinates, Points, Paths -// Synopsis: Convert 3d cartesian coordinates to spherical coordinates. +// Synopsis: Convert 3D cartesian coordinates to spherical coordinates. // See Also: cylindrical_to_xyz(), spherical_to_xyz(), xyz_to_cylindrical(), altaz_to_xyz(), xyz_to_altaz() // Description: // Called with three arguments, converts the `x`, `y`, and `z` 3D cartesian coordinate into a `[RADIUS,THETA,PHI]` spherical coordinate. @@ -461,13 +470,14 @@ function xyz_to_spherical(x,y,z) = // Function: altaz_to_xyz() +// Synopsis: Convert altitude/azimuth/range to 3D cartesian coordinates. +// SynTags: Path +// Topics: Coordinates, Points, Paths +// See Also: cylindrical_to_xyz(), xyz_to_spherical(), spherical_to_xyz(), xyz_to_cylindrical(), xyz_to_altaz() // Usage: // pt = altaz_to_xyz(alt, az, r); // pt = altaz_to_xyz([ALT,AZ,R]); // pts = altaz_to_xyz([[ALT,AZ,R], [ALT,AZ,R], ...]); -// Topics: Coordinates, Points, Paths -// See Also: cylindrical_to_xyz(), xyz_to_spherical(), spherical_to_xyz(), xyz_to_cylindrical(), xyz_to_altaz() -// Synopsis: Convert altitude/azimuth/range to 3d cartesian coordinates. // Description: // Convert altitude/azimuth/range coordinates to 3D cartesian coordinates. // Called with three arguments, converts the `alt`, `az`, and 'r' 3D altitude-azimuth coordinate into an `[X,Y,Z]` cartesian coordinate. @@ -493,13 +503,13 @@ function altaz_to_xyz(alt,az,r) = // Function: xyz_to_altaz() +// Synopsis: Convert 3D cartesian coordinates to [altitude,azimuth,range]. +// Topics: Coordinates, Points, Paths +// See Also: cylindrical_to_xyz(), xyz_to_spherical(), spherical_to_xyz(), xyz_to_cylindrical(), altaz_to_xyz() // Usage: // alt_az_r = xyz_to_altaz(x,y,z); // alt_az_r = xyz_to_altaz([X,Y,Z]); // alt_az_rs = xyz_to_altaz([[X,Y,Z], [X,Y,Z], ...]); -// Topics: Coordinates, Points, Paths -// See Also: cylindrical_to_xyz(), xyz_to_spherical(), spherical_to_xyz(), xyz_to_cylindrical(), altaz_to_xyz() -// Synopsis: Convert 3d cartesian coordinates to [altitude,azimuth,range]. // Description: // Converts 3D cartesian coordinates to altitude/azimuth/range coordinates. // Called with three arguments, converts the `x`, `y`, and `z` 3D cartesian coordinate into an `[ALTITUDE,AZIMUTH,RANGE]` coordinate. diff --git a/cubetruss.scad b/cubetruss.scad index e0e16b9..822a41c 100644 --- a/cubetruss.scad +++ b/cubetruss.scad @@ -18,6 +18,7 @@ $cubetruss_clip_thickness = 1.6; // Module: cubetruss() // Synopsis: Creates a multi-cube straight cubetruss shape. +// SynTags: Geom // Topics: Trusses, CubeTruss, FDM Optimized, Parts // See Also: cubetruss_segment(), cubetruss_support(), cubetruss(), cubetruss_corner() // Usage: @@ -35,7 +36,6 @@ $cubetruss_clip_thickness = 1.6; // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Topics: Attachable, Trusses // Examples: // cubetruss(extents=3); // cubetruss(extents=3, clips=FRONT); @@ -91,6 +91,7 @@ module cubetruss(extents=6, clips=[], bracing, size, strut, clipthick, anchor=CE // Module: cubetruss_corner() // Synopsis: Creates a multi-cube corner cubetruss shape. +// SynTags: Geom // Topics: Trusses, CubeTruss, FDM Optimized, Parts // See Also: cubetruss_segment(), cubetruss_support(), cubetruss(), cubetruss_corner() // Usage: @@ -108,7 +109,6 @@ module cubetruss(extents=6, clips=[], bracing, size, strut, clipthick, anchor=CE // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Topics: Attachable, Trusses // Examples: // cubetruss_corner(extents=2); // cubetruss_corner(extents=2, h=2); @@ -167,6 +167,7 @@ module cubetruss_corner(h=1, extents=[1,1,0,0,1], bracing, size, strut, clipthic // Module: cubetruss_support() // Synopsis: Creates a cubetruss support structure shape. +// SynTags: Geom // Topics: Trusses, CubeTruss, FDM Optimized, Parts // See Also: cubetruss_segment(), cubetruss_support(), cubetruss(), cubetruss_corner() // Usage: @@ -181,7 +182,6 @@ module cubetruss_corner(h=1, extents=[1,1,0,0,1], bracing, size, strut, clipthic // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Topics: Attachable, Trusses // Example(VPT=[0,0,0],VPD=150): // cubetruss_support(); // Example(VPT=[0,0,0],VPD=200): @@ -234,6 +234,7 @@ module cubetruss_support(size, strut, extents=1, anchor=CENTER, spin=0, orient=U // Module: cubetruss_foot() // Synopsis: Creates a foot that can connect two cubetrusses. +// SynTags: Geom // Topics: Trusses, CubeTruss, FDM Optimized, Parts // See Also: cubetruss_segment(), cubetruss_support(), cubetruss(), cubetruss_corner() // Usage: @@ -250,7 +251,6 @@ module cubetruss_support(size, strut, extents=1, anchor=CENTER, spin=0, orient=U // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Topics: Attachable, Trusses // Examples: // cubetruss_foot(w=1); // cubetruss_foot(w=3); @@ -314,6 +314,7 @@ module cubetruss_foot(w=1, size, strut, clipthick, anchor=CENTER, spin=0, orient // Module: cubetruss_joiner() // Synopsis: Creates a joiner that can connect two cubetrusses end-to-end. +// SynTags: Geom // Topics: Trusses, CubeTruss, FDM Optimized, Parts // See Also: cubetruss_segment(), cubetruss_support(), cubetruss(), cubetruss_corner() // Usage: @@ -331,7 +332,6 @@ module cubetruss_foot(w=1, size, strut, clipthick, anchor=CENTER, spin=0, orient // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Topics: Attachable, Trusses // Examples: // cubetruss_joiner(w=1, vert=false); // cubetruss_joiner(w=1, vert=true); @@ -385,6 +385,7 @@ module cubetruss_joiner(w=1, vert=true, size, strut, clipthick, anchor=CENTER, s // Module: cubetruss_uclip() // Synopsis: Creates a joiner that can connect two cubetrusses end-to-end. +// SynTags: Geom // Topics: Trusses, CubeTruss, FDM Optimized, Parts // See Also: cubetruss_segment(), cubetruss_support(), cubetruss(), cubetruss_corner() // Usage: @@ -401,7 +402,6 @@ module cubetruss_joiner(w=1, vert=true, size, strut, clipthick, anchor=CENTER, s // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Topics: Attachable, Trusses // Examples: // cubetruss_uclip(dual=false); // cubetruss_uclip(dual=true); @@ -437,6 +437,7 @@ module cubetruss_uclip(dual=true, size, strut, clipthick, anchor=CENTER, spin=0, // Module: cubetruss_segment() // Synopsis: Creates a single cubetruss cube. +// SynTags: Geom // Topics: Trusses, CubeTruss, FDM Optimized, Parts // See Also: cubetruss_segment(), cubetruss_support(), cubetruss(), cubetruss_corner() // Usage: @@ -451,7 +452,6 @@ module cubetruss_uclip(dual=true, size, strut, clipthick, anchor=CENTER, spin=0, // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Topics: Attachable, Trusses // Examples: // cubetruss_segment(bracing=false); // cubetruss_segment(bracing=true); @@ -509,6 +509,7 @@ module cubetruss_segment(size, strut, bracing, anchor=CENTER, spin=0, orient=UP) // Module: cubetruss_clip() // Synopsis: Creates a clip for the end of a cubetruss to snap-lock it to another cubetruss. +// SynTags: Geom // Topics: Trusses, CubeTruss, FDM Optimized, Parts // See Also: cubetruss_segment(), cubetruss_support(), cubetruss(), cubetruss_corner() // Usage: @@ -525,7 +526,6 @@ module cubetruss_segment(size, strut, bracing, anchor=CENTER, spin=0, orient=UP) // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` -// Topics: Attachable, Trusses // Examples: // cubetruss_clip(extents=2); // cubetruss_clip(extents=1); @@ -588,7 +588,6 @@ module cubetruss_clip(extents=1, size, strut, clipthick, anchor=CENTER, spin=0, // gaps = The number of extra strut widths to add in, corresponding to each time a truss butts up against another. // size = The length of each side of the cubetruss cubes. Default: `$cubetruss_size` (usually 30) // strut = The width of the struts on the cubetruss cubes. Default: `$cubetruss_strut_size` (usually 3) -// Topics: Trusses function cubetruss_dist(cubes=0, gaps=0, size, strut) = let( size = is_undef(size)? $cubetruss_size : size, diff --git a/distributors.scad b/distributors.scad index 2aad458..d343c78 100644 --- a/distributors.scad +++ b/distributors.scad @@ -93,6 +93,7 @@ // Function&Module: move_copies() // Synopsis: Translates copies of all children. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Translation, Copiers // See Also: xcopies(), ycopies(), zcopies(), line_copies(), grid_copies(), rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies() // @@ -145,6 +146,7 @@ function move_copies(a=[[0,0,0]],p=_NO_ARG) = // Function&Module: xcopies() // Synopsis: Places copies of children along the X axis. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Translation, Copiers // See Also: move_copies(), ycopies(), zcopies(), line_copies(), grid_copies(), rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies() // @@ -230,6 +232,7 @@ function xcopies(spacing, n, l, sp, p=_NO_ARG) = // Function&Module: ycopies() // Synopsis: Places copies of children along the Y axis. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Translation, Copiers // See Also: move_copies(), xcopies(), zcopies(), line_copies(), grid_copies(), rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies() // @@ -315,6 +318,7 @@ function ycopies(spacing, n, l, sp, p=_NO_ARG) = // Function&Module: zcopies() // Synopsis: Places copies of children along the Z axis. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Translation, Copiers // See Also: move_copies(), xcopies(), ycopies(), line_copies(), grid_copies(), rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies() // @@ -415,6 +419,7 @@ function zcopies(spacing, n, l, sp, p=_NO_ARG) = // Function&Module: line_copies() // Synopsis: Places copies of children along an arbitrary line. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Translation, Copiers // See Also: move_copies(), xcopies(), ycopies(), zcopies(), rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies() // @@ -548,6 +553,7 @@ function line_copies(spacing, n, l, p1, p2, p=_NO_ARG) = // Function&Module: grid_copies() // Synopsis: Places copies of children in an [X,Y] grid. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Translation, Copiers // See Also: move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies() // @@ -771,6 +777,7 @@ function grid_copies(spacing, n, size, stagger=false, inside=undef, nonzero, p=_ // Function&Module: rot_copies() // Synopsis: Rotates copies of children. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Rotation, Copiers // See Also: rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() // @@ -895,6 +902,7 @@ function rot_copies(rots=[], v, cp=[0,0,0], n, sa=0, offset=0, delta=[0,0,0], su // Function&Module: xrot_copies() // Synopsis: Rotates copies of children around the X axis. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Rotation, Copiers // See Also: rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() // @@ -974,6 +982,7 @@ function xrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG) // Function&Module: yrot_copies() // Synopsis: Rotates copies of children around the Y axis. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Rotation, Copiers // See Also: rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() // @@ -1053,6 +1062,7 @@ function yrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG) // Function&Module: zrot_copies() // Synopsis: Rotates copies of children around the Z axis. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Rotation, Copiers // See Also: rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), sphere_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() // @@ -1132,6 +1142,7 @@ function zrot_copies(rots=[], cp=[0,0,0], n, sa=0, r, d, subrot=true, p=_NO_ARG) // Function&Module: arc_copies() // Synopsis: Distributes duplicates of children along an arc. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Rotation, Copiers // See Also: rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), sphere_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() // @@ -1256,6 +1267,7 @@ function arc_copies( // Function&Module: sphere_copies() // Synopsis: Distributes copies of children over the surface of a sphere. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Rotation, Copiers // See Also: rot_copies(), xrot_copies(), yrot_copies(), zrot_copies(), arc_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() // @@ -1363,6 +1375,7 @@ function sphere_copies(n=100, r=undef, d=undef, cone_ang=90, scale=[1,1,1], perp // Function&Module: path_copies() // Synopsis: Uniformly distributes copies of children along a path. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Copiers // See Also: line_copies(), move_copies(), xcopies(), ycopies(), zcopies(), grid_copies(), xflip_copy(), yflip_copy(), zflip_copy(), mirror_copy() // @@ -1576,6 +1589,7 @@ function path_copies(path, n, spacing, sp=undef, dist, rotate_children=true, dis // Function&Module: xflip_copy() // Synopsis: Makes a copy of children mirrored across the X axis. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Translation, Copiers // See Also: yflip_copy(), zflip_copy(), mirror_copy(), path_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() // @@ -1626,6 +1640,7 @@ function xflip_copy(offset=0, x=0, p=_NO_ARG) = // Function&Module: yflip_copy() // Synopsis: Makes a copy of children mirrored across the Y axis. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Translation, Copiers // See Also: xflip_copy(), zflip_copy(), mirror_copy(), path_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() // @@ -1676,6 +1691,7 @@ function yflip_copy(offset=0, y=0, p=_NO_ARG) = // Function&Module: zflip_copy() // Synopsis: Makes a copy of children mirrored across the Z axis. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Translation, Copiers // See Also: xflip_copy(), yflip_copy(), mirror_copy(), path_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() // @@ -1726,6 +1742,7 @@ function zflip_copy(offset=0, z=0, p=_NO_ARG) = // Function&Module: mirror_copy() // Synopsis: Makes a copy of children mirrored across a given plane. +// SynTags: MatList, Trans // Topics: Transformations, Distributors, Translation, Copiers // See Also: xflip_copy(), yflip_copy(), zflip_copy(), path_copies(), move_copies(), xcopies(), ycopies(), zcopies(), line_copies(), grid_copies() // @@ -1815,6 +1832,14 @@ function mirror_copy(v=[0,0,1], offset=0, cp, p=_NO_ARG) = // Section: Distributing children individually along a line /////////////////// // Module: xdistribute() +// Synopsis: Distributes each child, individually, out along the X axis. +// SynTags: Trans +// See Also: xdistribute(), ydistribute(), zdistribute(), distribute() +// +// Usage: +// xdistribute(spacing, [sizes]) CHILDREN; +// xdistribute(l=, [sizes=]) CHILDREN; +// // // Description: // Spreads out the children individually along the X axis. @@ -1822,10 +1847,6 @@ function mirror_copy(v=[0,0,1], offset=0, cp, p=_NO_ARG) = // This is useful for laying out groups of disparate objects // where you only really care about the spacing between them. // -// Usage: -// xdistribute(spacing, [sizes]) CHILDREN; -// xdistribute(l=, [sizes=]) CHILDREN; -// // Arguments: // spacing = spacing between each child. (Default: 10.0) // sizes = Array containing how much space each child will need. @@ -1861,6 +1882,13 @@ module xdistribute(spacing=10, sizes=undef, l=undef) // Module: ydistribute() +// Synopsis: Distributes each child, individually, out along the Y axis. +// SynTags: Trans +// See Also: xdistribute(), ydistribute(), zdistribute(), distribute() +// +// Usage: +// ydistribute(spacing, [sizes]) CHILDREN; +// ydistribute(l=, [sizes=]) CHILDREN; // // Description: // Spreads out the children individually along the Y axis. @@ -1868,10 +1896,6 @@ module xdistribute(spacing=10, sizes=undef, l=undef) // This is useful for laying out groups of disparate objects // where you only really care about the spacing between them. // -// Usage: -// ydistribute(spacing, [sizes]) CHILDREN; -// ydistribute(l=, [sizes=]) CHILDREN; -// // Arguments: // spacing = spacing between each child. (Default: 10.0) // sizes = Array containing how much space each child will need. @@ -1907,6 +1931,13 @@ module ydistribute(spacing=10, sizes=undef, l=undef) // Module: zdistribute() +// Synopsis: Distributes each child, individually, out along the Z axis. +// SynTags: Trans +// See Also: xdistribute(), ydistribute(), zdistribute(), distribute() +// +// Usage: +// zdistribute(spacing, [sizes]) CHILDREN; +// zdistribute(l=, [sizes=]) CHILDREN; // // Description: // Spreads out each individual child along the Z axis. @@ -1914,10 +1945,6 @@ module ydistribute(spacing=10, sizes=undef, l=undef) // This is useful for laying out groups of disparate objects // where you only really care about the spacing between them. // -// Usage: -// zdistribute(spacing, [sizes]) CHILDREN; -// zdistribute(l=, [sizes=]) CHILDREN; -// // Arguments: // spacing = spacing between each child. (Default: 10.0) // sizes = Array containing how much space each child will need. @@ -1954,6 +1981,13 @@ module zdistribute(spacing=10, sizes=undef, l=undef) // Module: distribute() +// Synopsis: Distributes each child, individually, out along an arbitrary line. +// SynTags: Trans +// See Also: xdistribute(), ydistribute(), zdistribute(), distribute() +// +// Usage: +// distribute(spacing, sizes, dir) CHILDREN; +// distribute(l=, [sizes=], [dir=]) CHILDREN; // // Description: // Spreads out the children individually along the direction `dir`. @@ -1961,10 +1995,6 @@ module zdistribute(spacing=10, sizes=undef, l=undef) // This is useful for laying out groups of disparate objects // where you only really care about the spacing between them. // -// Usage: -// distribute(spacing, sizes, dir) CHILDREN; -// distribute(l=, [sizes=], [dir=]) CHILDREN; -// // Arguments: // spacing = Spacing to add between each child. (Default: 10.0) // sizes = Array containing how much space each child will need. diff --git a/drawing.scad b/drawing.scad index 8c1386c..14c5a06 100644 --- a/drawing.scad +++ b/drawing.scad @@ -18,6 +18,7 @@ // Module: stroke() // Synopsis: Draws a line along a path or region boundry. +// SynTags: Geom // Topics: Paths (2D), Paths (3D), Drawing Tools // See Also: offset_stroke(), path_sweep() // Usage: @@ -588,6 +589,7 @@ module stroke( // Function&Module: dashed_stroke() // Synopsis: Draws a dashed line along a path or region boundry. +// SynTags: Geom, PathList // Topics: Paths, Drawing Tools // See Also: stroke(), path_cut() // Usage: As a Module @@ -665,8 +667,10 @@ module dashed_stroke(path, dashpat=[3,3], width=1, closed=false, fit=true, round // Section: Computing paths // Function&Module: arc() -// Synopsis: Draws a 2d pie-slice or returns 2D or 3D path forming an arc. -// Topics: Shapes (2D), Path Generators (2D) +// Synopsis: Draws a 2D pie-slice or returns 2D or 3D path forming an arc. +// SynTags: Geom, Path +// Topics: Paths (2D), Paths (3D), Shapes (2D), Path Generators +// See Also: pie_slice(), stroke() // // Usage: 2D arc from 0º to `angle` degrees. // path=arc(n, r|d=, angle); @@ -684,7 +688,6 @@ module dashed_stroke(path, dashpat=[3,3], width=1, closed=false, fit=true, round // path=arc(n, corner=[P0,P1,P2], r=); // Usage: as module // arc(...) [ATTACHMENTS]; -// Topics: Paths (2D), Paths (3D), Shapes (2D), Path Generators // Description: // If called as a function, returns a 2D or 3D path forming an arc. // If called as a module, creates a 2D arc polygon or pie slice shape. @@ -861,7 +864,9 @@ module arc(n, r, angle, d, cp, points, corner, width, thickness, start, wedge=fa // Function: helix() // Synopsis: Creates a 2d spiral or 3d helical path. +// SynTags: Path // Topics: Path Generators, Paths, Drawing Tools +// See Also: pie_slice(), stroke(), thread_helix(), path_sweep() // // Usage: // path = helix(l|h, [turns=], [angle=], r=|r1=|r2=, d=|d1=|d2=); @@ -916,10 +921,11 @@ function _normal_segment(p1,p2) = // Function: turtle() // Synopsis: Uses [turtle graphics](https://en.wikipedia.org/wiki/Turtle_graphics) to generate a 2D path. +// SynTags: Path // Topics: Shapes (2D), Path Generators (2D), Mini-Language -// See Also: turtle3d() +// See Also: turtle3d(), stroke(), path_sweep() // Usage: -// turtle(commands, [state], [full_state=], [repeat=]) +// path = turtle(commands, [state], [full_state=], [repeat=]) // Description: // Use a sequence of [turtle graphics]{https://en.wikipedia.org/wiki/Turtle_graphics} commands to generate a path. The parameter `commands` is a list of // turtle commands and optional parameters for each command. The turtle state has a position, movement direction, @@ -1179,7 +1185,9 @@ function _turtle_command(command, parm, parm2, state, index) = // Module: debug_polygon() // Synopsis: Draws an annotated polygon. +// SynTags: Geom // Topics: Shapes (2D) +// See Also: debug_vnf(), debug_bezier() // // Usage: // debug_polygon(points, paths, [vertices=], [edges=], [convexity=], [size=]); diff --git a/gears.scad b/gears.scad index 6461cd6..ab8f687 100644 --- a/gears.scad +++ b/gears.scad @@ -27,6 +27,7 @@ // Function&Module: spur_gear() // Synopsis: Creates a spur gear shape. +// SynTags: Geom, VNF // Topics: Gears, Parts // See Also: rack(), spur_gear(), spur_gear2d(), bevel_gear() // Usage: As a Module @@ -193,6 +194,7 @@ module spur_gear( // Function&Module: spur_gear2d() // Synopsis: Creates a 2D spur gear shape. +// SynTags: Geom, Path // Topics: Gears, Parts // See Also: rack(), spur_gear(), spur_gear2d(), bevel_gear() // Usage: As Module @@ -294,6 +296,7 @@ module spur_gear2d( // Function&Module: rack() // Synopsis: Creates a gear rack shape. +// SynTags: Geom, VNF // Topics: Gears, Parts // See Also: rack2d(), spur_gear(), spur_gear2d(), bevel_gear() // Usage: As a Module @@ -443,6 +446,7 @@ function rack( // Function&Module: rack2d() // Synopsis: Creates a 2D gear rack shape. +// SynTags: Geom, Path // Topics: Gears, Parts // See Also: rack(), spur_gear(), spur_gear2d(), bevel_gear() // Usage: As a Module @@ -566,6 +570,7 @@ module rack2d( // Function&Module: bevel_gear() // Synopsis: Creates a possibly spiral beveled gear shape. +// SynTags: Geom, VNF // Topics: Gears, Parts // See Also: rack(), rack2d(), spur_gear(), spur_gear2d(), bevel_pitch_angle(), bevel_gear() // Usage: As a Module @@ -826,6 +831,7 @@ module bevel_gear( // Function&Module: worm() // Synopsis: Creates a worm shape that will mate with a worm gear. +// SynTags: Geom, VNF // Topics: Gears, Parts // See Also: worm(), worm_gear(), rack(), rack2d(), spur_gear(), spur_gear2d(), bevel_pitch_angle(), bevel_gear() // Usage: As a Module @@ -946,6 +952,7 @@ module worm( // Function&Module: worm_gear() // Synopsis: Creates a worm gear shape that will mate with a worm. +// SynTags: Geom, VNF // Topics: Gears, Parts // See Also: worm(), worm_gear(), rack(), rack2d(), spur_gear(), spur_gear2d(), bevel_pitch_angle(), bevel_gear() // Usage: As a Module diff --git a/geometry.scad b/geometry.scad index f98b95a..aa5c067 100644 --- a/geometry.scad +++ b/geometry.scad @@ -15,10 +15,11 @@ // Section: Lines, Rays, and Segments // Function: is_point_on_line() +// Synopsis: Determine if a point is on a line, ray or segment. +// Topics: Geometry, Points, Segments +// See Also: is_collinear(), is_point_on_line(), point_line_distance(), line_from_points() // Usage: // pt = is_point_on_line(point, line, [bounded], [eps]); -// Topics: Geometry, Points, Segments -// Synopsis: Determine if a point is on a line, ray or segment. // Description: // Determine if the point is on the line segment, ray or segment defined by the two between two points. // Returns true if yes, and false if not. If bounded is set to true it specifies a segment, with @@ -140,10 +141,11 @@ function _point_left_of_line2d(point, line, eps=EPSILON) = // Function: is_collinear() +// Synopsis: Determine if points are collinear. +// Topics: Geometry, Points, Collinearity +// See Also: is_collinear(), is_point_on_line(), point_line_distance(), line_from_points() // Usage: // bool = is_collinear(a, [b, c], [eps]); -// Synopsis: Determine if a points are collinear. -// Topics: Geometry, Points, Collinearity // Description: // Returns true if the points `a`, `b` and `c` are co-linear or if the list of points `a` is collinear. // Arguments: @@ -162,10 +164,11 @@ function is_collinear(a, b, c, eps=EPSILON) = // Function: point_line_distance() +// Synopsis: Find shortest distance from point to a line, segment or ray. +// Topics: Geometry, Points, Lines, Distance +// See Also: is_collinear(), is_point_on_line(), point_line_distance(), line_from_points() // Usage: // dist = point_line_distance(line, pt, [bounded]); -// Topics: Geometry, Points, Lines, Distance -// Synopsis: Find shortest distance from point to a line, segment or ray. // Description: // Finds the shortest distance from the point `pt` to the specified line, segment or ray. // The bounded parameter specifies the whether the endpoints give a ray or segment. @@ -187,11 +190,11 @@ function point_line_distance(pt, line, bounded=false) = // Function: segment_distance() -// Usage: -// dist = segment_distance(seg1, seg2, [eps]); +// Synopsis: Find smallest distance between two line semgnets. // Topics: Geometry, Segments, Distance // See Also: convex_collision(), convex_distance() -// Synopsis: Find smallest distance between two line semgnets. +// Usage: +// dist = segment_distance(seg1, seg2, [eps]); // Description: // Returns the smallest distance of the points on two given line segments. // Arguments: @@ -207,13 +210,14 @@ function segment_distance(seg1, seg2,eps=EPSILON) = // Function: line_normal() +// Synopsis: Return normal vector to given line. +// Topics: Geometry, Lines +// See Also: line_intersection(), line_from_points() // Usage: // vec = line_normal([P1,P2]) // vec = line_normal(p1,p2) -// Topics: Geometry, Lines // Description: // Returns the 2D normal vector to the given 2D line. This is otherwise known as the perpendicular vector counter-clockwise to the given ray. -// Synopsis: Return normal vector to given line. // Arguments: // p1 = First point on 2D line. // p2 = Second point on 2D line. @@ -255,9 +259,11 @@ function _general_line_intersection(s1,s2,eps=EPSILON) = // Function: line_intersection() +// Synopsis: Compute intersection of two lines, segments or rays. +// Topics: Geometry, Lines +// See Also: line_normal(), line_from_points() // Usage: // pt = line_intersection(line1, line2, [bounded1], [bounded2], [bounded=], [eps=]); -// Synopsis: Compute intersection of two lines, segments or rays. // Description: // Returns the intersection point of any two 2D lines, segments or rays. Returns undef // if they do not intersect. You specify a line by giving two distinct points on the @@ -316,10 +322,11 @@ function line_intersection(line1, line2, bounded1, bounded2, bounded, eps=EPSILO // Function: line_closest_point() +// Synopsis: Find point on given line, segment or ray that is closest to a given point. +// Topics: Geometry, Lines, Distance +// See Also: line_normal(), point_line_distance() // Usage: // pt = line_closest_point(line, pt, [bounded]); -// Topics: Geometry, Lines, Distance -// Synopsis: Find point on given line, segment or ray that is closest to a given point. // Description: // Returns the point on the given line, segment or ray that is closest to the given point `pt`. // The inputs `line` and `pt` args should be of the same dimension. The parameter bounded indicates @@ -401,10 +408,10 @@ function line_closest_point(line, pt, bounded=false) = // Function: line_from_points() -// Usage: -// line = line_from_points(points, [fast], [eps]); // Synopsis: Given a list of collinear points, return the line they define. // Topics: Geometry, Lines, Points +// Usage: +// line = line_from_points(points, [fast], [eps]); // Description: // Given a list of 2 or more collinear points, returns two points defining a line containing them. // If `fast` is false and the points are coincident or non-collinear, then `undef` is returned. @@ -428,10 +435,11 @@ function line_from_points(points, fast=false, eps=EPSILON) = // Function: is_coplanar() +// Synopsis: Check if 3d points are coplanar and not collinear. +// Topics: Geometry, Coplanarity +// See Also: plane3pt(), plane3pt_indexed(), plane_from_normal(), plane_from_points(), plane_from_polygon() // Usage: // bool = is_coplanar(points,[eps]); -// Topics: Geometry, Coplanarity -// Synopsis: Check if 3d points are coplanar and not collinear. // Description: // Returns true if the given 3D points are non-collinear and are on a plane. // Arguments: @@ -449,11 +457,12 @@ function is_coplanar(points, eps=EPSILON) = // Function: plane3pt() +// Synopsis: Return a plane from 3 points. +// Topics: Geometry, Planes +// See Also: plane3pt(), plane3pt_indexed(), plane_from_normal(), plane_from_points(), plane_from_polygon() // Usage: // plane = plane3pt(p1, p2, p3); // plane = plane3pt([p1, p2, p3]); -// Topics: Geometry, Planes -// Synopsis: Return a plane from 3 points. // Description: // Generates the normalized cartesian equation of a plane from three 3d points. // Returns [A,B,C,D] where Ax + By + Cz = D is the equation of a plane. @@ -474,10 +483,11 @@ function plane3pt(p1, p2, p3) = // Function: plane3pt_indexed() +// Synopsis: Given list of 3d points and 3 indices, return the plane they define. +// Topics: Geometry, Planes +// See Also: plane3pt(), plane3pt_indexed(), plane_from_normal(), plane_from_points(), plane_from_polygon() // Usage: // plane = plane3pt_indexed(points, i1, i2, i3); -// Topics: Geometry, Planes -// Synopsis: Given list of 3d points and 3 indices, return the plane they define. // Description: // Given a list of 3d points, and the indices of three of those points, // generates the normalized cartesian equation of a plane that those points all @@ -503,10 +513,11 @@ function plane3pt_indexed(points, i1, i2, i3) = // Function: plane_from_normal() -// Usage: -// plane = plane_from_normal(normal, [pt]) // Synopsis: Return plane defined by normal vector and a point. // Topics: Geometry, Planes +// See Also: plane3pt(), plane3pt_indexed(), plane_from_normal(), plane_from_points(), plane_from_polygon() +// Usage: +// plane = plane_from_normal(normal, [pt]) // Description: // Returns a plane defined by a normal vector and a point. If you omit `pt` you will get a plane // passing through the origin. @@ -564,11 +575,11 @@ function _covariance_evec_eval(points) = // Function: plane_from_points() -// Usage: -// plane = plane_from_points(points, [fast], [eps]); // Synopsis: Return plane defined by a set of coplanar 3d points, with arbitrary normal direction. // Topics: Geometry, Planes, Points -// See Also: plane_from_polygon() +// See Also: plane3pt(), plane3pt_indexed(), plane_from_normal(), plane_from_points(), plane_from_polygon() +// Usage: +// plane = plane_from_points(points, [fast], [eps]); // Description: // Given a list of 3 or more coplanar 3D points, returns the coefficients of the normalized cartesian equation of a plane, // that is [A,B,C,D] where Ax+By+Cz=D is the equation of the plane and norm([A,B,C])=1. @@ -603,11 +614,11 @@ function plane_from_points(points, fast=false, eps=EPSILON) = // Function: plane_from_polygon() -// Usage: -// plane = plane_from_polygon(points, [fast], [eps]); // Synopsis: Given a 3d planar polygon, returns directed plane. // Topics: Geometry, Planes, Polygons -// See Also: plane_from_points() +// See Also: plane3pt(), plane3pt_indexed(), plane_from_normal(), plane_from_points(), plane_from_polygon() +// Usage: +// plane = plane_from_polygon(points, [fast], [eps]); // Description: // Given a 3D planar polygon, returns the normalized cartesian equation of its plane. // Returns [A,B,C,D] where Ax+By+Cz=D is the equation of the plane where norm([A,B,C])=1. @@ -640,10 +651,11 @@ function plane_from_polygon(poly, fast=false, eps=EPSILON) = // Function: plane_normal() +// Synopsis: Returns the normal vector to a plane. +// Topics: Geometry, Planes +// See Also: plane3pt(), plane3pt_indexed(), plane_from_normal(), plane_from_points(), plane_from_polygon(), plane_normal(), plane_offset() // Usage: // vec = plane_normal(plane); -// Topics: Geometry, Planes -// Synopsis: Returns the normal vector to a plane. // Description: // Returns the unit length normal vector for the given plane. // Arguments: @@ -654,10 +666,11 @@ function plane_normal(plane) = // Function: plane_offset() +// Synopsis: Returns the signed offset of the plane from the origin. +// Topics: Geometry, Planes +// See Also: plane3pt(), plane3pt_indexed(), plane_from_normal(), plane_from_points(), plane_from_polygon(), plane_normal(), plane_offset() // Usage: // d = plane_offset(plane); -// Topics: Geometry, Planes -// Synopsis: Returns the signed offset of the plane from the origin. // Description: // Returns coeficient D of the normalized plane equation `Ax+By+Cz=D`, or the scalar offset of the plane from the origin. // This value may be negative. @@ -686,21 +699,22 @@ function _general_plane_line_intersection(plane, line, eps=EPSILON) = /// Internal Function: normalize_plane() -// Usage: -// nplane = normalize_plane(plane); +/// Usage: +/// nplane = normalize_plane(plane); /// Topics: Geometry, Planes -// Description: -// Returns a new representation [A,B,C,D] of `plane` where norm([A,B,C]) is equal to one. +/// Description: +/// Returns a new representation [A,B,C,D] of `plane` where norm([A,B,C]) is equal to one. function _normalize_plane(plane) = assert( _valid_plane(plane), str("Invalid plane. ",plane ) ) plane/norm(point3d(plane)); // Function: plane_line_intersection() +// Synopsis: Returns the intersection of a plane and 3d line, segment or ray. +// Topics: Geometry, Planes, Lines, Intersection +// See Also: plane3pt(), plane_from_normal(), plane_from_points(), plane_from_polygon(), line_intersection() // Usage: // pt = plane_line_intersection(plane, line, [bounded], [eps]); -// Topics: Geometry, Planes, Lines, Intersection -// Synopsis: Returns the intersection of a plane and 3d line, segment or ray. // Description: // Takes a line, and a plane [A,B,C,D] where the equation of that plane is `Ax+By+Cz=D`. // If `line` intersects `plane` at one point, then that intersection point is returned. @@ -727,11 +741,12 @@ function plane_line_intersection(plane, line, bounded=false, eps=EPSILON) = // Function: plane_intersection() +// Synopsis: Returns the intersection of two or three planes. +// Topics: Geometry, Planes, Intersection +// See Also: plane3pt(), plane_from_normal(), plane_from_points(), plane_from_polygon(), line_intersection() // Usage: // line = plane_intersection(plane1, plane2) // pt = plane_intersection(plane1, plane2, plane3) -// Topics: Geometry, Planes, Intersection -// Synopsis: Returns the intersection of two or three planes. // Description: // Compute the point which is the intersection of the three planes, or the line intersection of two planes. // If you give three planes the intersection is returned as a point. If you give two planes the intersection @@ -763,10 +778,11 @@ function plane_intersection(plane1,plane2,plane3) = // Function: plane_line_angle() +// Synopsis: Returns the angle between a plane and a 3d line. +// Topics: Geometry, Planes, Lines, Angle +// See Also: plane3pt(), plane_from_normal(), plane_from_points(), plane_from_polygon(), plane_intersection(), line_intersection(), vector_angle() // Usage: // angle = plane_line_angle(plane,line); -// Topics: Geometry, Planes, Lines, Angle -// Synopsis: Returns the angle between a plane and a 3d line. // Description: // Compute the angle between a plane [A, B, C, D] and a 3d line, specified as a pair of 3d points [p1,p2]. // The resulting angle is signed, with the sign positive if the vector p2-p1 lies above the plane, on @@ -784,10 +800,11 @@ function plane_line_angle(plane, line) = // Function: plane_closest_point() +// Synopsis: Returns the orthogonal projection of points onto a plane. +// Topics: Geometry, Planes, Projection +// See Also: plane3pt(), line_closest_point(), point_plane_distance() // Usage: // pts = plane_closest_point(plane, points); -// Topics: Geometry, Planes, Projection -// Synopsis: Returns the orthogonal projection of points onto a plane. // Description: // Given a plane definition `[A,B,C,D]`, where `Ax+By+Cz=D`, and a list of 2d or // 3d points, return the closest 3D orthogonal projection of the points on the plane. @@ -820,10 +837,11 @@ function plane_closest_point(plane, points) = // Function: point_plane_distance() +// Synopsis: Determine distance between a point and plane. +// Topics: Geometry, Planes, Distance +// See Also: plane3pt(), line_closest_point(), plane_closest_point() // Usage: // dist = point_plane_distance(plane, point) -// Topics: Geometry, Planes, Distance -// Synopsis: Determine distance between a point and plane. // Description: // Given a plane as [A,B,C,D] where the cartesian equation for that plane // is Ax+By+Cz=D, determines how far from that plane the given point is. @@ -852,10 +870,11 @@ function _pointlist_greatest_distance(points,plane) = // Function: are_points_on_plane() +// Synopsis: Determine if all of the listed points are on a plane. +// Topics: Geometry, Planes, Points +// See Also: plane3pt(), line_closest_point(), plane_closest_point(), is_coplanar() // Usage: // bool = are_points_on_plane(points, plane, [eps]); -// Topics: Geometry, Planes, Points -// Synopsis: Determine if all of the listed points are on a plane. // Description: // Returns true if the given 3D points are on the given plane. // Arguments: @@ -889,10 +908,11 @@ function _is_point_above_plane(plane, point) = // Section: Circle Calculations // Function: circle_line_intersection() -// Usage: -// pts = circle_line_intersection(r|d=, cp, line, [bounded], [eps=]); // Synopsis: Find the intersection points between a 2d circle and a line, ray or segment. // Topics: Geometry, Circles, Lines, Intersection +// See Also: circle_line_intersection(), circle_circle_intersection(), circle_2tangents(), circle_3points(), circle_point_tangents(), circle_circle_tangents() +// Usage: +// pts = circle_line_intersection(r|d=, cp, line, [bounded], [eps=]); // Description: // Find intersection points between a 2D circle and a line, ray or segment specified by two points. // By default the line is unbounded. Returns the list of zero or more intersection points. @@ -963,10 +983,11 @@ function _circle_or_sphere_line_intersection(r, cp, line, bounded=false, d, eps= // Function: circle_circle_intersection() -// Usage: -// pts = circle_circle_intersection(r1|d1=, cp1, r2|d2=, cp2, [eps]); // Synopsis: Find the intersection points of two 2d circles. // Topics: Geometry, Circles +// See Also: circle_line_intersection(), circle_circle_intersection(), circle_2tangents(), circle_3points(), circle_point_tangents(), circle_circle_tangents() +// Usage: +// pts = circle_circle_intersection(r1|d1=, cp1, r2|d2=, cp2, [eps]); // Description: // Compute the intersection points of two circles. Returns a list of the intersection points, which // will contain two points in the general case, one point for tangent circles, or will be empty @@ -1030,11 +1051,12 @@ function circle_circle_intersection(r1, cp1, r2, cp2, eps=EPSILON, d1, d2) = // Function: circle_2tangents() +// Synopsis: Given two 2d or 3d rays, find a circle tangent to both. +// Topics: Geometry, Circles, Tangents +// See Also: circle_line_intersection(), circle_circle_intersection(), circle_2tangents(), circle_3points(), circle_point_tangents(), circle_circle_tangents() // Usage: // circ = circle_2tangents(r|d=, pt1, pt2, pt3, [tangents=]); // circ = circle_2tangents(r|d=, [PT1, PT2, PT3], [tangents=]); -// Topics: Geometry, Circles, Tangents -// Synopsis: Given two 2d or 3d rays, find a circle tangent to both. // Description: // Given a pair of 2d or 3d rays with a common origin, and a known circle radius/diameter, finds // the centerpoint for the circle of that size that touches both rays tangentally. @@ -1108,7 +1130,7 @@ function circle_2tangents(r, pt1, pt2, pt3, tangents=false, d) = assert(r!=undef, "Must specify either r or d.") assert( ( is_path(pt1) && len(pt1)==3 && is_undef(pt2) && is_undef(pt3)) || (is_matrix([pt1,pt2,pt3]) && (len(pt1)==2 || len(pt1)==3) ), - "Invalid input points." ) + str("Invalid input points. pt1=",pt1,", pt2=",pt2,", pt3=",pt3)) is_undef(pt2) ? circle_2tangents(r, pt1[0], pt1[1], pt1[2], tangents=tangents) : is_collinear(pt1, pt2, pt3)? undef : @@ -1131,11 +1153,12 @@ function circle_2tangents(r, pt1, pt2, pt3, tangents=false, d) = // Function: circle_3points() +// Synopsis: Find a circle passing through three 2d or 3d points. +// Topics: Geometry, Circles +// See Also: circle_line_intersection(), circle_circle_intersection(), circle_2tangents(), circle_3points(), circle_point_tangents(), circle_circle_tangents() // Usage: // circ = circle_3points(pt1, pt2, pt3); // circ = circle_3points([PT1, PT2, PT3]); -// Topics: Geometry, Circles -// Synopsis: Find a circle passing through three 2d or 3d points. // Description: // Returns the [CENTERPOINT, RADIUS, NORMAL] of the circle that passes through three non-collinear // points where NORMAL is the normal vector of the plane that the circle is on (UP or DOWN if the points are 2D). @@ -1183,10 +1206,11 @@ function circle_3points(pt1, pt2, pt3) = // Function: circle_point_tangents() +// Synopsis: Given a circle and point, find tangents to circle passing through the point. +// Topics: Geometry, Circles, Tangents +// See Also: circle_line_intersection(), circle_circle_intersection(), circle_2tangents(), circle_3points(), circle_point_tangents(), circle_circle_tangents() // Usage: // tangents = circle_point_tangents(r|d=, cp, pt); -// Topics: Geometry, Circles, Tangents -// Synopsis: Given a circle and point, find tangents to circle passing through the point. // Description: // Given a 2d circle and a 2d point outside that circle, finds the 2d tangent point(s) on the circle for a // line passing through the point. Returns a list of zero or more 2D tangent points. @@ -1220,10 +1244,11 @@ function circle_point_tangents(r, cp, pt, d) = // Function: circle_circle_tangents() +// Synopsis: Find tangents to a pair of circles in 2d. +// Topics: Geometry, Circles, Tangents +// See Also: circle_line_intersection(), circle_circle_intersection(), circle_2tangents(), circle_3points(), circle_point_tangents(), circle_circle_tangents() // Usage: // segs = circle_circle_tangents(r1|d1=, cp1, r2|d2=, cp2); -// Topics: Geometry, Circles, Tangents -// Synopsis: Find tangents to a pair of circles in 2d. // Description: // Computes 2d lines tangents to a pair of circles in 2d. Returns a list of line endpoints [p1,p2] where // p1 is the tangent point on circle 1 and p2 is the tangent point on circle 2. @@ -1346,10 +1371,11 @@ function _noncollinear_triple(points,error=true,eps=EPSILON) = // Function: sphere_line_intersection() +// Synopsis: Find intersection between a sphere and line, ray or segment. +// Topics: Geometry, Spheres, Lines, Intersection +// See Also: circle_line_intersection(), circle_circle_intersection(), circle_2tangents(), circle_3points(), circle_point_tangents(), circle_circle_tangents() // Usage: // isect = sphere_line_intersection(r|d=, cp, line, [bounded], [eps=]); -// Topics: Geometry, Spheres, Lines, Intersection -// Synopsis: Find intersection between a sphere and line, ray or segment. // Description: // Find intersection points between a sphere and a line, ray or segment specified by two points. // By default the line is unbounded. @@ -1379,10 +1405,11 @@ function sphere_line_intersection(r, cp, line, bounded=false, d, eps=EPSILON) = // Section: Polygons // Function: polygon_area() +// Synopsis: Calculate area of a 2d or 3d polygon. +// Topics: Geometry, Polygons, Area +// See Also: polygon_area(), centroid(), polygon_normal(), point_in_polygon(), polygon_line_intersection() // Usage: // area = polygon_area(poly, [signed]); -// Topics: Geometry, Polygons, Area -// Synopsis: Calculate area of a 2d or 3d polygon. // Description: // Given a 2D or 3D simple planar polygon, returns the area of that polygon. // If the polygon is non-planar the result is `undef.` If the polygon is self-intersecting @@ -1414,10 +1441,11 @@ function polygon_area(poly, signed=false) = // Function: centroid() +// Synopsis: Compute centroid of a 2d or 3d polygon or a VNF. +// Topics: Geometry, Polygons, Centroid +// See Also: polygon_area(), centroid(), polygon_normal(), point_in_polygon(), polygon_line_intersection() // Usage: // c = centroid(object, [eps]); -// Topics: Geometry, Polygons, Centroid -// Synopsis: Compute centroid of a 2d or 3d polygon or a VNF. // Description: // Given a simple 2D polygon, returns the 2D coordinates of the polygon's centroid. // Given a simple 3D planar polygon, returns the 3D coordinates of the polygon's centroid. @@ -1500,10 +1528,11 @@ function _polygon_centroid(poly, eps=EPSILON) = // Function: polygon_normal() +// Synopsis: Return normal to a polygon. +// Topics: Geometry, Polygons +// See Also: polygon_area(), centroid(), polygon_normal(), point_in_polygon(), polygon_line_intersection() // Usage: // vec = polygon_normal(poly); -// Topics: Geometry, Polygons -// Synopsis: Return normal to a polygon. // Description: // Given a 3D simple planar polygon, returns a unit normal vector for the polygon. The vector // is oriented so that if the normal points towards the viewer, the polygon winds in the clockwise @@ -1529,10 +1558,11 @@ function polygon_normal(poly) = // Function: point_in_polygon() +// Synopsis: Checks if a 2d point is inside or on the boundary of a 2d polygon. +// Topics: Geometry, Polygons +// See Also: polygon_area(), centroid(), polygon_normal(), point_in_polygon(), polygon_line_intersection() // Usage: // bool = point_in_polygon(point, poly, [nonzero], [eps]) -// Topics: Geometry, Polygons -// Synopsis: Checks if a 2d point is inside or on the boundary of a 2d polygon. // Description: // This function tests whether the given 2D point is inside, outside or on the boundary of // the specified 2D polygon. @@ -1673,10 +1703,11 @@ function point_in_polygon(point, poly, nonzero=false, eps=EPSILON) = // Function: polygon_line_intersection() +// Synopsis: Find intersection between 2d or 3d polygon and a line, segment or ray. +// Topics: Geometry, Polygons, Lines, Intersection +// See Also: polygon_area(), centroid(), polygon_normal(), point_in_polygon(), polygon_line_intersection() // Usage: // pt = polygon_line_intersection(poly, line, [bounded], [nonzero], [eps]); -// Topics: Geometry, Polygons, Lines, Intersection -// Synopsis: Find intersection between 2d or 3d polygon and a line, segment or ray. // Description: // Takes a possibly bounded line, and a 2D or 3D planar polygon, and finds their intersection. Note the polygon is // treated as its boundary and interior, so the intersection may include both points and line segments. @@ -1860,9 +1891,11 @@ function _merge_segments(insegs,outsegs, eps, i=1) = // Function: polygon_triangulate() +// Synopsis: Divide a polygon into triangles. +// Topics: Geometry, Triangulation +// See Also: vnf_triangulate() // Usage: // triangles = polygon_triangulate(poly, [ind], [error], [eps]) -// Synopsis: Divide a polygon into triangles. // Description: // Given a simple polygon in 2D or 3D, triangulates it and returns a list // of triples indexing into the polygon vertices. When the optional argument `ind` is @@ -2049,11 +2082,11 @@ function _none_inside(idxs,poly,p0,p1,p2,eps,i=0) = // Function: is_polygon_clockwise() -// Usage: -// bool = is_polygon_clockwise(poly); +// Synopsis: Determine if a 2d polygon winds clockwise. // Topics: Geometry, Polygons, Clockwise // See Also: clockwise_polygon(), ccw_polygon(), reverse_polygon() -// Synopsis: Determine if a 2d polygon winds clockwise. +// Usage: +// bool = is_polygon_clockwise(poly); // Description: // Return true if the given 2D simple polygon is in clockwise order, false otherwise. // Results for complex (self-intersecting) polygon are indeterminate. @@ -2076,11 +2109,11 @@ function is_polygon_clockwise(poly) = // Function: clockwise_polygon() +// Synopsis: Return clockwise version of a polygon. +// Topics: Geometry, Polygons, Clockwise +// See Also: is_polygon_clockwise(), ccw_polygon(), reverse_polygon() // Usage: // newpoly = clockwise_polygon(poly); -// Topics: Geometry, Polygons, Clockwise -// Synopsis: Return clockwise version of a polygon. -// See Also: is_polygon_clockwise(), ccw_polygon(), reverse_polygon() // Description: // Given a 2D polygon path, returns the clockwise winding version of that path. // Arguments: @@ -2091,11 +2124,11 @@ function clockwise_polygon(poly) = // Function: ccw_polygon() +// Synopsis: Return counter-clockwise version of a polygon. +// Topics: Geometry, Polygons, Clockwise +// See Also: is_polygon_clockwise(), clockwise_polygon(), reverse_polygon() // Usage: // newpoly = ccw_polygon(poly); -// Synopsis: Return counter-clockwise version of a polygon. -// See Also: is_polygon_clockwise(), clockwise_polygon(), reverse_polygon() -// Topics: Geometry, Polygons, Clockwise // Description: // Given a 2D polygon poly, returns the counter-clockwise winding version of that poly. // Arguments: @@ -2106,11 +2139,11 @@ function ccw_polygon(poly) = // Function: reverse_polygon() -// Usage: -// newpoly = reverse_polygon(poly) // Synopsis: Reverse winding direction of polygon. // Topics: Geometry, Polygons, Clockwise // See Also: is_polygon_clockwise(), ccw_polygon(), clockwise_polygon() +// Usage: +// newpoly = reverse_polygon(poly) // Description: // Reverses a polygon's winding direction, while still using the same start point. // Arguments: @@ -2122,10 +2155,11 @@ function reverse_polygon(poly) = // Function: reindex_polygon() -// Usage: -// newpoly = reindex_polygon(reference, poly); // Synopsis: Adjust point indexing of polygon to minimize pointwise distance to a reference polygon. // Topics: Geometry, Polygons +// See Also: reindex_polygon(), align_polygon(), are_polygons_equal() +// Usage: +// newpoly = reindex_polygon(reference, poly); // Description: // Rotates and possibly reverses the point order of a 2d or 3d polygon path to optimize its pairwise point // association with a reference polygon. The two polygons must have the same number of vertices and be the same dimension. @@ -2177,10 +2211,11 @@ function reindex_polygon(reference, poly, return_error=false) = // Function: align_polygon() +// Synopsis: Find best alignment of a 2d polygon to a reference 2d polygon over a set of transformations. +// Topics: Geometry, Polygons +// See Also: reindex_polygon(), align_polygon(), are_polygons_equal() // Usage: // newpoly = align_polygon(reference, poly, [angles], [cp], [tran], [return_ind]); -// Topics: Geometry, Polygons -// Synopsis: Find best alignment of a 2d polygon to a reference 2d polygon over a set of transformations. // Description: // Find the best alignment of a specified 2D polygon with a reference 2D polygon over a set of // transformations. You can specify a list or range of angles and a centerpoint or you can @@ -2245,12 +2280,14 @@ function align_polygon(reference, poly, angles, cp, trans, return_ind=false) = // Function: are_polygons_equal() +// Synopsis: Check if two polygons (not necessarily in the same point order) are equal. +// Topics: Geometry, Polygons, Comparators +// See Also: reindex_polygon(), align_polygon(), are_polygons_equal() // Usage: // bool = are_polygons_equal(poly1, poly2, [eps]) // Description: // Returns true if poly1 and poly2 are the same polongs // within given epsilon tolerance. -// Synopsis: Check if two polygons (not necessarily in the same point order) are equal. // Arguments: // poly1 = first polygon // poly2 = second polygon @@ -2301,9 +2338,11 @@ function ___is_polygon_in_list(poly, polys, i) = // Function: hull() +// Synopsis: Convex hull of a list of 2d or 3d points. +// Topics: Geometry, Hulling +// See Also: hull(), hull_points(), hull2d_path(), hull3d_faces() // Usage: // face_list_or_index_list = hull(points); -// Synopsis: Convex hull of a list of 2d or 3d points. // Description: // Takes a list of 2D or 3D points (but not both in the same list) and returns either the list of // indexes into `points` that forms the 2D convex hull perimeter path, or the list of faces that @@ -2322,9 +2361,11 @@ function hull(points) = // Module: hull_points() +// Synopsis: Convex hull of a list of 2d or 3d points. +// Topics: Geometry, Hulling +// See Also: hull(), hull_points(), hull2d_path(), hull3d_faces() // Usage: // hull_points(points, [fast]); -// Synopsis: Convex hull of a list of 2d or 3d points. // Description: // If given a list of 2D points, creates a 2D convex hull polygon that encloses all those points. // If given a list of 3D points, creates a 3D polyhedron that encloses all the points. This should @@ -2380,13 +2421,15 @@ function _is_cw(a,b,c,all) = // Function: hull2d_path() +// Synopsis: Convex hull of a list of 2d points. +// Topics: Geometry, Hulling +// See Also: hull(), hull_points(), hull2d_path(), hull3d_faces() // Usage: // index_list = hull2d_path(points,all) // Description: // Takes a list of arbitrary 2D points, and finds the convex hull polygon to enclose them. // Returns a path as a list of indices into `points`. // When all==true, returns extra points that are on edges of the hull. -// Synopsis: Convex hull of a list of 2d points. // Arguments: // points = list of 2d points to get the hull of. // all = when true, includes all points on the edges of the convex hull. Default: false. @@ -2443,9 +2486,11 @@ function _hull_collinear(points) = // Function: hull3d_faces() +// Synopsis: Convex hull of a list of 3d points. +// Topics: Geometry, Hulling +// See Also: hull(), hull_points(), hull2d_path(), hull3d_faces() // Usage: // faces = hull3d_faces(points) -// Synopsis: Convex hull of a list of 3d points. // Description: // Takes a list of arbitrary 3D points, and finds the convex hull polyhedron to enclose // them. Returns a list of triangular faces, where each face is a list of indexes into the given `points` @@ -2555,10 +2600,11 @@ function _find_first_noncoplanar(plane, points, i=0) = // Function: is_polygon_convex() -// Usage: -// bool = is_polygon_convex(poly, [eps]); // Synopsis: Check if a polygon is convex. // Topics: Geometry, Convexity, Test +// See Also: clockwise_polygon(), ccw_polygon() +// Usage: +// bool = is_polygon_convex(poly, [eps]); // Description: // Returns true if the given 2D or 3D polygon is convex. // The result is meaningless if the polygon is not simple (self-crossing) or non coplanar. @@ -2601,12 +2647,11 @@ function is_polygon_convex(poly,eps=EPSILON) = // Function: convex_distance() -// Usage: -// dist = convex_distance(points1, points2,eps); // Synopsis: Compute distance between convex hull of two point lists. // Topics: Geometry, Convexity, Distance -// See also: -// convex_collision(), hull() +// See also: convex_collision(), hull() +// Usage: +// dist = convex_distance(points1, points2,eps); // Description: // Returns the smallest distance between a point in convex hull of `points1` // and a point in the convex hull of `points2`. All the points in the lists @@ -2661,12 +2706,12 @@ function _GJK_distance(points1, points2, eps=EPSILON, lbd, d, simplex=[]) = // Function: convex_collision() -// Usage: -// bool = convex_collision(points1, points2, [eps]); +// Synopsis: Check whether the convex hulls of two point lists intersect. // Topics: Geometry, Convexity, Collision, Intersection // See also: // convex_distance(), hull() -// Synopsis: Check whether the convex hulls of two point lists intersect. +// Usage: +// bool = convex_collision(points1, points2, [eps]); // Description: // Returns `true` if the convex hull of `points1` intersects the convex hull of `points2` // otherwise, `false`. @@ -2812,10 +2857,10 @@ function _support_diff(p1,p2,d) = // Section: Rotation Decoding // Function: rot_decode() -// Usage: -// info = rot_decode(rotation,[long]); // Returns: [angle,axis,cp,translation] // Synopsis: Extract axis and rotation angle from a rotation matrix. // Topics: Affine, Matrices, Transforms +// Usage: +// info = rot_decode(rotation,[long]); // Returns: [angle,axis,cp,translation] // Description: // Given an input 3D rigid transformation operator (one composed of just rotations and translations) represented // as a 4x4 matrix, compute the rotation and translation parameters of the operator. Returns a list of the diff --git a/hinges.scad b/hinges.scad index 27fc746..dce4503 100644 --- a/hinges.scad +++ b/hinges.scad @@ -15,6 +15,7 @@ include // Module: knuckle_hinge() // Synopsis: Creates a knuckle-hinge shape. +// SynTags: Geom // Topics: Hinges, Parts // See Also: living_hinge_mask(), snap_lock(), snap_socket() // Usage: @@ -346,6 +347,7 @@ module _knuckle_hinge_profile(offset, arm_height, arm_angle=45, knuckle_diam=4, // Module: living_hinge_mask() // Synopsis: Creates a mask to make a folding "living" hinge. +// SynTags: Geom // Topics: Hinges, Parts // See Also: knuckle_hinge(), living_hinge_mask(), snap_lock(), snap_socket(), apply_folding_hinges_and_snaps() // Usage: @@ -391,6 +393,7 @@ module folding_hinge_mask(l, thick, layerheight=0.2, foldangle=90, hingegap=unde // Module: apply_folding_hinges_and_snaps() // Synopsis: Adds snap shapes and removes living hinges from a child shape. +// SynTags: Geom // Topics: Hinges, Parts // See Also: knuckle_hinge(), living_hinge_mask(), snap_lock(), snap_socket() // Usage: @@ -476,6 +479,7 @@ module apply_folding_hinges_and_snaps(thick, foldangle=90, hinges=[], snaps=[], // Module: snap_lock() // Synopsis: Creates a snap-lock shape. +// SynTags: Geom // Topics: Hinges, Parts // See Also: knuckle_hinge(), living_hinge_mask(), snap_lock(), snap_socket() // Usage: @@ -515,6 +519,7 @@ module snap_lock(thick, snaplen=5, snapdiam=5, layerheight=0.2, foldangle=90, hi // Module: snap_socket() // Synopsis: Creates a snap-lock socket shape. +// SynTags: Geom // Topics: Hinges, Parts // See Also: knuckle_hinge(), living_hinge_mask(), snap_lock(), snap_socket() // Usage: diff --git a/joiners.scad b/joiners.scad index bcfa11e..a963b34 100644 --- a/joiners.scad +++ b/joiners.scad @@ -17,6 +17,7 @@ include // Function&Module: half_joiner_clear() // Synopsis: Creates a mask to clear space for a {{half_joiner()}}. +// SynTags: Geom, VNF // Topics: Joiners, Parts // See Also: half_joiner_clear(), half_joiner(), half_joiner2(), joiner_clear(), joiner(), snap_pin(), rabbit_clip(), dovetail() // Usage: As Module @@ -62,6 +63,7 @@ module half_joiner_clear(l=20, w=10, ang=30, clearance=0, overlap=0.01, anchor=C // Function&Module: half_joiner() // Synopsis: Creates a half-joiner shape to mate with a {{half_joiner2()}} shape.. +// SynTags: Geom, VNF // Topics: Joiners, Parts // See Also: half_joiner_clear(), half_joiner(), half_joiner2(), joiner_clear(), joiner(), snap_pin(), rabbit_clip(), dovetail() // Usage: As Module @@ -244,6 +246,7 @@ module half_joiner(l=20, w=10, base=10, ang=30, screwsize, anchor=CENTER, spin=0 // Function&Module: half_joiner2() // Synopsis: Creates a half_joiner2 shape to mate with a {{half_joiner()}} shape.. +// SynTags: Geom, VNF // Topics: Joiners, Parts // See Also: half_joiner_clear(), half_joiner(), half_joiner2(), joiner_clear(), joiner(), snap_pin(), rabbit_clip(), dovetail() // Usage: As Module @@ -459,6 +462,7 @@ module half_joiner2(l=20, w=10, base=10, ang=30, screwsize, anchor=CENTER, spin= // Module: joiner_clear() // Synopsis: Creates a mask to clear space for a {{joiner()}} shape. +// SynTags: Geom // Topics: Joiners, Parts // See Also: half_joiner_clear(), half_joiner(), half_joiner2(), joiner_clear(), joiner(), snap_pin(), rabbit_clip(), dovetail() // Description: @@ -498,6 +502,7 @@ module joiner_clear(l=40, w=10, ang=30, clearance=0, overlap=0.01, anchor=CENTER // Module: joiner() // Synopsis: Creates a joiner shape that can mate with another rotated joiner shape. +// SynTags: Geom // Topics: Joiners, Parts // See Also: half_joiner_clear(), half_joiner(), half_joiner2(), joiner_clear(), joiner(), snap_pin(), rabbit_clip(), dovetail() // Usage: @@ -549,6 +554,7 @@ module joiner(l=40, w=10, base=10, ang=30, screwsize, anchor=CENTER, spin=0, ori // Module: dovetail() // Synopsis: Creates a possibly tapered dovetail shape. +// SynTags: Geom // Topics: Joiners, Parts // See Also: joiner(), snap_pin(), rabbit_clip() // @@ -819,6 +825,7 @@ function _pin_size(size) = // Module: snap_pin() // Synopsis: Creates a snap-pin that can slot into a {{snap_pin_socket()}} to join two parts. +// SynTags: Geom // Topics: Joiners, Parts // See Also: snap_pin_socket(), joiner(), dovetail(), snap_pin(), rabbit_clip() // Usage: @@ -892,6 +899,7 @@ module snap_pin(size,r,radius,d,diameter, l,length, nub_depth, snap, thickness, // Module: snap_pin_socket() // Synopsis: Creates a snap-pin socket for a {{snap_pin()}} to slot into. +// SynTags: Geom // Topics: Joiners, Parts // See Also: snap_pin(), joiner(), dovetail(), snap_pin(), rabbit_clip() // Usage: @@ -965,6 +973,7 @@ module snap_pin_socket(size, r, radius, l,length, d,diameter,nub_depth, snap, fi // Module: rabbit_clip() // Synopsis: Creates a rabbit-eared clip that can snap into a slot. +// SynTags: Geom // Topics: Joiners, Parts // See Also: snap_pin(), joiner(), dovetail(), snap_pin(), rabbit_clip() // Usage: diff --git a/linalg.scad b/linalg.scad index c69ca9a..b5439ee 100644 --- a/linalg.scad +++ b/linalg.scad @@ -31,10 +31,11 @@ // Section: Matrix testing and display // Function: is_matrix() +// Synopsis: Check if input is a numeric matrix, optionally of specified size +// Topics: Matrices +// See Also: is_matrix_symmetric(), is_rotation() // Usage: // test = is_matrix(A, [m], [n], [square]) -// Topics: Matrices -// Synopsis: Check if input is a numeric matrix, optionally of specified size // Description: // Returns true if A is a numeric matrix of height m and width n with finite entries. If m or n // are omitted or set to undef then true is returned for any positive dimension. @@ -52,13 +53,14 @@ function is_matrix(A,m,n,square=false) = // Function: is_matrix_symmetric() +// Synopsis: Checks if matrix is symmetric +// Topics: Matrices +// See Also: is_matrix(), is_rotation() // Usage: // b = is_matrix_symmetric(A, [eps]) -// Topics: Matrices // Description: // Returns true if the input matrix is symmetric, meaning it approximately equals its transpose. // The matrix can have arbitrary entries. -// Synopsis: Checks if matrix is symmetric // Arguments: // A = matrix to test // eps = epsilon for comparing equality. Default: 1e-12 @@ -67,15 +69,16 @@ function is_matrix_symmetric(A,eps=1e-12) = // Function: is_rotation() +// Synopsis: Check if a transformation matrix represents a rotation. +// Topics: Affine, Matrices, Transforms +// See Also: is_matrix(), is_matrix_symmetric(), is_rotation() // Usage: // b = is_rotation(A, [dim], [centered]) -// Topics: Affine, Matrices, Transforms // Description: // Returns true if the input matrix is a square affine matrix that is a rotation around any point, // or around the origin if `centered` is true. // The matrix must be 3x3 (representing a 2d transformation) or 4x4 (representing a 3d transformation). // You can set `dim` to 2 to require a 2d transform (3x3 matrix) or to 3 to require a 3d transform (4x4 matrix). -// Synopsis: Check if a transformation matrix represents a rotation. // Arguments: // A = matrix to test // dim = if set, specify dimension in which the transform operates (2 or 3) @@ -96,11 +99,12 @@ function is_rotation(A,dim,centered=false) = // Function&Module: echo_matrix() +// Synopsis: Print a matrix neatly to the console. +// Topics: Matrices +// See Also: is_matrix(), is_matrix_symmetric(), is_rotation() // Usage: // echo_matrix(M, [description], [sig], [sep], [eps]); // dummy = echo_matrix(M, [description], [sig], [sep], [eps]), -// Topics: Matrices -// Synopsis: Print a matrix neatly to the console. // Description: // Display a numerical matrix in a readable columnar format with `sig` significant // digits. Values smaller than eps display as zero. If you give a description @@ -135,14 +139,14 @@ module echo_matrix(M,description,sig=4,sep=1,eps=1e-9) // Section: Matrix indexing // Function: column() -// Usage: -// list = column(M, i); +// Synopsis: Extract a column from a matrix. // Topics: Matrices, List Handling, Arrays // See Also: select(), slice() +// Usage: +// list = column(M, i); // Description: // Extracts entry `i` from each list in M, or equivalently column i from the matrix M, and returns it as a vector. // This function will return `undef` at all entry positions indexed by i not found in M. -// Synopsis: Extract a column from a matrix. // Arguments: // M = The given list of lists. // i = The index to fetch @@ -162,11 +166,11 @@ function column(M, i) = // Function: submatrix() -// Usage: -// mat = submatrix(M, idx1, idx2); +// Synopsis: Extract a submatrix from a matrix // Topics: Matrices, Arrays // See Also: column(), block_matrix(), submatrix_set() -// Synopsis: Extract a submatrix from a matrix +// Usage: +// mat = submatrix(M, idx1, idx2); // Description: // The input must be a list of lists (a matrix or 2d array). Returns a submatrix by selecting the rows listed in idx1 and columns listed in idx2. // Arguments: @@ -196,12 +200,13 @@ function submatrix(M,idx1,idx2) = // Section: Matrix construction and modification // Function: ident() +// Synopsis: Return identity matrix. +// Topics: Affine, Matrices, Transforms +// See Also: IDENT, submatrix(), column() // Usage: // mat = ident(n); -// Topics: Affine, Matrices, Transforms // Description: // Create an `n` by `n` square identity matrix. -// Synopsis: Return identity matrix. // Arguments: // n = The size of the identity matrix square, `n` by `n`. // Example: @@ -229,15 +234,15 @@ function ident(n) = [ // Function: diagonal_matrix() -// Usage: -// mat = diagonal_matrix(diag, [offdiag]); +// Synopsis: Make a diagonal matrix. // Topics: Affine, Matrices // See Also: column(), submatrix() +// Usage: +// mat = diagonal_matrix(diag, [offdiag]); // Description: // Creates a square matrix with the items in the list `diag` on // its diagonal. The off diagonal entries are set to offdiag, // which is zero by default. -// Synopsis: Make a diagonal matrix. // Arguments: // diag = A list of items to put in the diagnal cells of the matrix. // offdiag = Value to put in non-diagonal matrix cells. @@ -247,11 +252,11 @@ function diagonal_matrix(diag, offdiag=0) = // Function: transpose() +// Synopsis: Transpose a matrix +// Topics: Linear Algebra, Matrices +// See Also: submatrix(), block_matrix(), hstack(), flatten() // Usage: // M = transpose(M, [reverse]); -// Topics: Linear Algebra, Matrices -// Synopsis: Transpose a matrix -// See Also: submatrix(), block_matrix(), hstack(), flatten() // Description: // Returns the transpose of the given input matrix. The input can be a matrix with arbitrary entries or // a numerical vector. If you give a vector then transpose returns it unchanged. @@ -326,10 +331,11 @@ function transpose(M, reverse=false) = // Function: outer_product() +// Synopsis: Compute the outer product of two vectors. +// Topics: Linear Algebra, Matrices +// See Also: submatrix(), determinant() // Usage: // x = outer_product(u,v); -// Topics: Linear Algebra, Matrices -// Synopsis: Compute the outer product of two vectors. // Description: // Compute the outer product of two vectors, which is a matrix. // Usage: @@ -339,11 +345,11 @@ function outer_product(u,v) = [for(ui=u) ui*v]; // Function: submatrix_set() -// Usage: -// mat = submatrix_set(M, A, [m], [n]); +// Synopsis: Takes a matrix as input and change values in a submatrix. // Topics: Matrices, Arrays // See Also: column(), submatrix() -// Synopsis: Takes a matrix as input and change values in a submatrix. +// Usage: +// mat = submatrix_set(M, A, [m], [n]); // Description: // Sets a submatrix of M equal to the matrix A. By default the top left corner of M is set to A, but // you can specify offset coordinates m and n. If A (as adjusted by m and n) extends beyond the bounds @@ -368,13 +374,13 @@ function submatrix_set(M,A,m=0,n=0) = // Function: hstack() +// Synopsis: Make a new matrix by stacking matrices horizontally. +// Topics: Matrices, Arrays +// See Also: column(), submatrix(), block_matrix() // Usage: // A = hstack(M1, M2) // A = hstack(M1, M2, M3) // A = hstack([M1, M2, M3, ...]) -// Synopsis: Make a new matrix by stacking matrices horizontally. -// Topics: Matrices, Arrays -// See Also: column(), submatrix(), block_matrix() // Description: // Constructs a matrix by horizontally "stacking" together compatible matrices or vectors. Vectors are treated as columsn in the stack. // This command is the inverse of `column`. Note: strings given in vectors are broken apart into lists of characters. Strings given @@ -423,11 +429,11 @@ function hstack(M1, M2, M3) = // Function: block_matrix() -// Usage: -// bmat = block_matrix([[M11, M12,...],[M21, M22,...], ... ]); +// Synopsis: Make a new matrix from a block of matrices. // Topics: Matrices, Arrays // See Also: column(), submatrix() -// Synopsis: Make a new matrix from a block of matrices. +// Usage: +// bmat = block_matrix([[M11, M12,...],[M21, M22,...], ... ]); // Description: // Create a block matrix by supplying a matrix of matrices, which will // be combined into one unified matrix. Every matrix in one row @@ -471,10 +477,11 @@ function block_matrix(M) = // Section: Solving Linear Equations and Matrix Factorizations // Function: linear_solve() +// Synopsis: Solve Ax=b or, for overdetermined case, solve the least square problem. +// Topics: Matrices, Linear Algebra +// See Also: linear_solve3(), matrix_inverse(), rot_inverse(), back_substitute(), cholesky() // Usage: // solv = linear_solve(A,b,[pivot]) -// Topics: Matrices, Linear Algebra -// Synopsis: Solve Ax=b or, for overdetermined case, solve the least square problem. // Description: // Solves the linear system Ax=b. If `A` is square and non-singular the unique solution is returned. If `A` is overdetermined // the least squares solution is returned. If `A` is underdetermined, the minimal norm solution is returned. @@ -509,10 +516,11 @@ function linear_solve(A,b,pivot=true) = // Function: linear_solve3() +// Synopsis: Fast solution to Ax=b where A is 3x3. +// Topics: Matrices, Linear Algebra +// See Also: linear_solve(), matrix_inverse(), rot_inverse(), back_substitute(), cholesky() // Usage: // x = linear_solve3(A,b) -// Topics: Matrices, Linear Algebra -// Synopsis: Fast solution to Ax=b where A is 3x3. // Description: // Fast solution to a 3x3 linear system using Cramer's rule (which appears to be the fastest // method in OpenSCAD). The input `A` must be a 3x3 matrix. Returns undef if `A` is singular. @@ -535,10 +543,11 @@ function linear_solve3(A,b) = // Function: matrix_inverse() +// Synopsis: General matrix inverse. +// Topics: Matrices, Linear Algebra +// See Also: linear_solve(), linear_solve3(), matrix_inverse(), rot_inverse(), back_substitute(), cholesky() // Usage: // mat = matrix_inverse(A) -// Topics: Matrices, Linear Algebra -// Synopsis: General matrix inverse. // Description: // Compute the matrix inverse of the square matrix `A`. If `A` is singular, returns `undef`. // Note that if you just want to solve a linear system of equations you should NOT use this function. @@ -550,10 +559,11 @@ function matrix_inverse(A) = // Function: rot_inverse() -// Usage: -// B = rot_inverse(A) // Synopsis: Invert 2d or 3d rotation transformations. // Topics: Matrices, Linear Algebra, Affine +// See Also: linear_solve(), linear_solve3(), matrix_inverse(), rot_inverse(), back_substitute(), cholesky() +// Usage: +// B = rot_inverse(A) // Description: // Inverts a 2d (3x3) or 3d (4x4) rotation matrix. The matrix can be a rotation around any center, // so it may include a translation. This is faster and likely to be more accurate than using `matrix_inverse()`. @@ -572,10 +582,11 @@ function rot_inverse(T) = // Function: null_space() +// Synopsis: Return basis for the null space of A. +// Topics: Matrices, Linear Algebra +// See Also: linear_solve(), linear_solve3(), matrix_inverse(), rot_inverse(), back_substitute(), cholesky() // Usage: // x = null_space(A) -// Topics: Matrices, Linear Algebra -// Synopsis: Return basis for the null space of A. // Description: // Returns an orthonormal basis for the null space of `A`, namely the vectors {x} such that Ax=0. // If the null space is just the origin then returns an empty list. @@ -590,10 +601,11 @@ function null_space(A,eps=1e-12) = select(transpose(Q_R[0]), zrows); // Function: qr_factor() +// Synopsis: Compute QR factorization of a matrix. +// Topics: Matrices, Linear Algebra +// See Also: linear_solve(), linear_solve3(), matrix_inverse(), rot_inverse(), back_substitute(), cholesky() // Usage: // qr = qr_factor(A,[pivot]); -// Topics: Matrices, Linear Algebra -// Synopsis: Compute QR factorization of a matrix. // Description: // Calculates the QR factorization of the input matrix A and returns it as the list [Q,R,P]. This factorization can be // used to solve linear systems of equations. The factorization is `A = Q*R*transpose(P)`. If pivot is false (the default) @@ -642,10 +654,11 @@ function _swap_matrix(n,i,j) = // Function: back_substitute() +// Synopsis: Solve an upper triangular system, Rx=b. +// Topics: Matrices, Linear Algebra +// See Also: linear_solve(), linear_solve3(), matrix_inverse(), rot_inverse(), back_substitute(), cholesky() // Usage: // x = back_substitute(R, b, [transpose]); -// Topics: Matrices, Linear Algebra -// Synopsis: Solve an upper triangular system, Rx=b. // Description: // Solves the problem Rx=b where R is an upper triangular square matrix. The lower triangular entries of R are // ignored. If transpose==true then instead solve transpose(R)*x=b. @@ -675,10 +688,11 @@ function _back_substitute(R, b, x=[]) = // Function: cholesky() +// Synopsis: Compute the Cholesky factorization of a matrix. +// Topics: Matrices, Linear Algebra +// See Also: linear_solve(), linear_solve3(), matrix_inverse(), rot_inverse(), back_substitute(), cholesky() // Usage: // L = cholesky(A); -// Topics: Matrices, Linear Algebra -// Synopsis: Compute the Cholesky factorization of a matrix. // Description: // Compute the cholesky factor, L, of the symmetric positive definite matrix A. // The matrix L is lower triangular and `L * transpose(L) = A`. If the A is @@ -712,10 +726,11 @@ function _cholesky(A,L,n) = // Section: Matrix Properties: Determinants, Norm, Trace // Function: det2() +// Synopsis: Compute determinant of 2x2 matrix. +// Topics: Matrices, Linear Algebra +// See Also: det2(), det3(), det4(), determinant(), norm_fro(), matrix_trace() // Usage: // d = det2(M); -// Topics: Matrices, Linear Algebra -// Synopsis: Compute determinant of 2x2 matrix. // Description: // Rturns the determinant for the given 2x2 matrix. // Arguments: @@ -729,10 +744,11 @@ function det2(M) = // Function: det3() +// Synopsis: Compute determinant of 3x3 matrix. +// Topics: Matrices, Linear Algebra +// See Also: det2(), det3(), det4(), determinant(), norm_fro(), matrix_trace() // Usage: // d = det3(M); -// Topics: Matrices, Linear Algebra -// Synopsis: Compute determinant of 3x3 matrix. // Description: // Returns the determinant for the given 3x3 matrix. // Arguments: @@ -747,10 +763,11 @@ function det3(M) = M[2][0] * (M[0][1]*M[1][2]-M[1][1]*M[0][2]); // Function: det4() +// Synopsis: Compute determinant of 4x4 matrix. +// Topics: Matrices, Linear Algebra +// See Also: det2(), det3(), det4(), determinant(), norm_fro(), matrix_trace() // Usage: // d = det4(M); -// Topics: Matrices, Linear Algebra -// Synopsis: Compute determinant of 4x4 matrix. // Description: // Returns the determinant for the given 4x4 matrix. // Arguments: @@ -770,12 +787,13 @@ function det4(M) = - M[0][3]*M[1][0]*M[2][1]*M[3][2] - M[0][3]*M[1][1]*M[2][2]*M[3][0] - M[0][3]*M[1][2]*M[2][0]*M[3][1]; // Function: determinant() +// Synopsis: compute determinant of an arbitrary square matrix. +// Topics: Matrices, Linear Algebra +// See Also: det2(), det3(), det4(), determinant(), norm_fro(), matrix_trace() // Usage: // d = determinant(M); -// Topics: Matrices, Linear Algebra // Description: // Returns the determinant for the given square matrix. -// Synopsis: compute determinant of an arbitrary square matrix. // Arguments: // M = The NxN square matrix to get the determinant of. // Example: @@ -804,10 +822,11 @@ function determinant(M) = // Function: norm_fro() +// Synopsis: Compute Frobenius norm of a matrix +// Topics: Matrices, Linear Algebra +// See Also: det2(), det3(), det4(), determinant(), norm_fro(), matrix_trace() // Usage: // norm_fro(A) -// Topics: Matrices, Linear Algebra -// Synopsis: Compute Frobenius norm of a matrix // Description: // Computes frobenius norm of input matrix. The frobenius norm is the square root of the sum of the // squares of all of the entries of the matrix. On vectors it is the same as the usual 2-norm. @@ -818,10 +837,11 @@ function norm_fro(A) = // Function: matrix_trace() +// Synopsis: Compute the trace of a square matrix. +// Topics: Matrices, Linear Algebra +// See Also: det2(), det3(), det4(), determinant(), norm_fro(), matrix_trace() // Usage: // matrix_trace(M) -// Topics: Matrices, Linear Algebra -// Synopsis: Compute the trace of a square matrix. // Description: // Computes the trace of a square matrix, the sum of the entries on the diagonal. function matrix_trace(M) = diff --git a/linear_bearings.scad b/linear_bearings.scad index 0ea9a72..8c1dd86 100644 --- a/linear_bearings.scad +++ b/linear_bearings.scad @@ -8,14 +8,14 @@ // FileSummary: Mounts for LMxUU style linear bearings. ////////////////////////////////////////////////////////////////////// - -include +include // Section: Generic Linear Bearings // Module: linear_bearing_housing() // Synopsis: Creates a generic linear bearing mount clamp. +// SynTags: Geom // Topics: Parts, Bearings // See Also: linear_bearing(), lmXuu_info(), ball_bearing() // Usage: @@ -25,7 +25,7 @@ include // Arguments: // d = Diameter of linear bearing. (Default: 15) // l = Length of linear bearing. (Default: 24) -// tab = Clamp tab height. (Default: 7) +// tab = Clamp tab height. (Default: 8) // tabwall = Clamp Tab thickness. (Default: 5) // wall = Wall thickness of clamp housing. (Default: 3) // gap = Gap in clamp. (Default: 5) @@ -35,12 +35,12 @@ include // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // Example: -// linear_bearing_housing(d=19, l=29, wall=2, tab=6, screwsize=2.5); -module linear_bearing_housing(d=15, l=24, tab=7, gap=5, wall=3, tabwall=5, screwsize=3, anchor=BOTTOM, spin=0, orient=UP) +// linear_bearing_housing(d=19, l=29, wall=2, tab=8, screwsize=2.5); +module linear_bearing_housing(d=15, l=24, tab=8, gap=5, wall=3, tabwall=5, screwsize=3, anchor=BOTTOM, spin=0, orient=UP) { od = d+2*wall; ogap = gap+2*tabwall; - tabh = tab/2+od/2*sqrt(2)-ogap/2; + tabh = tab/2+od/2*sqrt(2)-ogap/2-1; h = od+tab/2; anchors = [ named_anchor("axis", [0,0,-tab/2/2]), @@ -68,11 +68,15 @@ module linear_bearing_housing(d=15, l=24, tab=7, gap=5, wall=3, tabwall=5, screw cube([l+0.05,gap,od], anchor=BOTTOM); up(tabh) { + screwsize = is_string(screwsize)? screwsize : str("M",screwsize); + // Screwhole - fwd(ogap/2-2+0.01) generic_screw(screwsize=screwsize*1.06, screwlen=ogap, headsize=screwsize*2, headlen=10, orient=FWD); + fwd(ogap/2-2+0.01) + screw_hole(str(screwsize,",",ogap), head="socket", counterbore=3, anchor="head_bot", orient=FWD, $fn=12); // Nut holder - back(ogap/2-2+0.01) metric_nut(size=screwsize, hole=false, anchor=BOTTOM, orient=BACK); + back(ogap/2-2+0.01) + nut_trap_inline(tabwall, screwsize, orient=BACK); } } children(); @@ -82,6 +86,7 @@ module linear_bearing_housing(d=15, l=24, tab=7, gap=5, wall=3, tabwall=5, screw // Module: linear_bearing() // Synopsis: Creates a generic linear bearing cartridge. +// SynTags: Geom // Topics: Parts, Bearings // See Also: linear_bearing_housing(), lmXuu_info(), ball_bearing() // Usage: @@ -116,6 +121,7 @@ module linear_bearing(l, od=15, id=8, length, anchor=CTR, spin=0, orient=UP) { // Module: lmXuu_housing() // Synopsis: Creates a standardized LM*UU linear bearing mount clamp. +// SynTags: Geom // Topics: Parts, Bearings // See Also: linear_bearing(), linear_bearing_housing(), lmXuu_info(), lmXuu_bearing(), lmXuu_housing(), ball_bearing() // Usage: @@ -146,6 +152,7 @@ module lmXuu_housing(size=8, tab=7, gap=5, wall=3, tabwall=5, screwsize=3, ancho // Module: lmXuu_bearing() // Synopsis: Creates a standardized LM*UU linear bearing cartridge. +// SynTags: Geom // Topics: Parts, Bearings // See Also: linear_bearing(), linear_bearing_housing(), lmXuu_info(), lmXuu_bearing(), lmXuu_housing(), ball_bearing() // Usage: diff --git a/lists.scad b/lists.scad index 8ca1586..a343723 100644 --- a/lists.scad +++ b/lists.scad @@ -268,6 +268,7 @@ function slice(list,start=0,end=-1) = ) [if (start<=end && end>=0 && start<=l) for (i=[max(start,0):1:min(end,l-1)]) list[i]]; + // Function: last() // Synopsis: Returns the last item of a list. // Topics: List Handling diff --git a/masks3d.scad b/masks3d.scad index a44fa9e..4e479e4 100644 --- a/masks3d.scad +++ b/masks3d.scad @@ -17,7 +17,7 @@ // Synopsis: Creates a shape to chamfer a 90° edge. // SynTags: Geom // Topics: Masking, Chamfers, Shapes (3D) -// See Also: chamfer_corner_mask() +// See Also: chamfer_corner_mask(), chamfer_cylinder_mask(), chamfer_edge_mask() // Usage: // chamfer_edge_mask(l|h=|length=|height=, chamfer, [excess]) [ATTACHMENTS]; // Description: @@ -59,7 +59,7 @@ module chamfer_edge_mask(l, chamfer=1, excess=0.1, h, length, height, anchor=CEN // Synopsis: Creates a shape to chamfer a 90° corner. // SynTags: Geom // Topics: Masking, Chamfers, Shapes (3D) -// See Also: chamfer_edge_mask() +// See Also: chamfer_corner_mask(), chamfer_cylinder_mask(), chamfer_edge_mask() // Usage: // chamfer_corner_mask(chamfer) [ATTACHMENTS]; // Description: @@ -98,6 +98,7 @@ module chamfer_corner_mask(chamfer=1, anchor=CENTER, spin=0, orient=UP) { // Synopsis: Creates a shape to chamfer the end of a cylinder. // SynTags: Geom // Topics: Masking, Chamfers, Cylinders +// See Also: chamfer_corner_mask(), chamfer_cylinder_mask(), chamfer_edge_mask() // Usage: // chamfer_cylinder_mask(r|d=, chamfer, [ang], [from_end]) [ATTACHMENTS]; // Description: @@ -419,7 +420,7 @@ module rounding_angled_corner_mask(r, ang=90, d, anchor=CENTER, spin=0, orient=U // Synopsis: Creates a shape to round the end of a cylinder. // SynTags: Geom // Topics: Masking, Rounding, Cylinders -// See Also: rounding_hole_mask() +// See Also: rounding_hole_mask(), rounding_angled_edge_mask(), rounding_corner_mask(), rounding_angled_corner_mask() // Usage: // rounding_cylinder_mask(r|d=, rounding); // Description: @@ -471,7 +472,7 @@ module rounding_cylinder_mask(r, rounding, d, anchor=CENTER, spin=0, orient=UP) // Synopsis: Creates a shape to round the edge of a round hole. // SynTags: Geom // Topics: Masking, Rounding -// See Also: rounding_cylinder_mask() +// See Also: rounding_cylinder_mask(), rounding_hole_mask(), rounding_angled_edge_mask(), rounding_corner_mask(), rounding_angled_corner_mask() // Usage: // rounding_hole_mask(r|d, rounding, [excess]) [ATTACHMENTS]; // Description: @@ -524,7 +525,7 @@ module rounding_hole_mask(r, rounding, excess=0.1, d, anchor=CENTER, spin=0, ori // Synopsis: Creates a shape to round a 90° edge but limit the angle of overhang. // SynTags: Geom // Topics: Masking, Rounding, Shapes (3D), FDM Optimized -// See Also: teardrop_corner_mask() +// See Also: teardrop_corner_mask(), teardrop_edge_mask() // Usage: // teardrop_edge_mask(l|h=|length=|height=, r|d=, [angle], [excess], [anchor], [spin], [orient]) [ATTACHMENTS]; // Description: @@ -567,7 +568,7 @@ module teardrop_edge_mask(l, r, angle=45, excess=0.1, d, anchor=CTR, spin=0, ori // Synopsis: Creates a shape to round a 90° corner but limit the angle of overhang. // SynTags: Geom // Topics: Masking, Rounding, Shapes (3D), FDM Optimized -// See Also: teardrop_edge_mask() +// See Also: teardrop_corner_mask(), teardrop_edge_mask() // Usage: // teardrop_corner_mask(r|d=, [angle], [excess], [anchor], [spin], [orient]) [ATTACHMENTS]; // Description: diff --git a/metric_screws.scad b/metric_screws.scad index 142574f..556e5f0 100644 --- a/metric_screws.scad +++ b/metric_screws.scad @@ -12,6 +12,8 @@ include include +warn = echo("*** WARNING: metric_screws.scad is deprecated and may be removed in the future. Use screws.scad instead. ***"); + // Section: Functions diff --git a/modular_hose.scad b/modular_hose.scad index eb4d8ea..9c76d02 100644 --- a/modular_hose.scad +++ b/modular_hose.scad @@ -10,7 +10,7 @@ // Section: Modular Hose Parts -_small_end = [ +_modhose_small_end = [ turtle([ "left", 90-38.5, // 1/4" hose "arcsteps", 12, @@ -57,7 +57,7 @@ _small_end = [ ]; -_big_end = [ +_modhose_big_end = [ turtle([ // 1/4" hose "left", 90-22, "move", 6.5, @@ -116,7 +116,7 @@ _big_end = [ ]; -_hose_waist = [1.7698, 1.8251, 3.95998]; +_modhose_waist = [1.7698, 1.8251, 3.95998]; // Module: modular_hose() @@ -159,14 +159,14 @@ module modular_hose(size, type, clearance=0, waist_len, anchor=BOTTOM, spin=0,or ind = search([size],[1/4, 1/2, 3/4])[0]; sbound = assert(ind!=[], "Must specify size as 1/4, 1/2 or 3/4") - pointlist_bounds(_small_end[ind]); - bbound = pointlist_bounds(_big_end[ind]); + pointlist_bounds(_modhose_small_end[ind]); + bbound = pointlist_bounds(_modhose_big_end[ind]); smallend = assert(is_vector(clearance,2), "Clearance must be a scalar or length 2 vector") - move([-clearance[0],-sbound[0].y],p=_small_end[ind]); - bigend = move([clearance[1], -bbound[0].y], p=_big_end[ind]); + move([-clearance[0],-sbound[0].y],p=_modhose_small_end[ind]); + bigend = move([clearance[1], -bbound[0].y], p=_modhose_big_end[ind]); - midlength = first_defined([waist_len, _hose_waist[ind]]); + midlength = first_defined([waist_len, _modhose_waist[ind]]); dummy = assert(midlength>=0,"midlength must be nonnegative"); goodtypes = ["small","big","segment","socket","ball"]; @@ -221,8 +221,8 @@ function modular_hose_radius(size, outer=false) = ) assert(ind!=[], "Must specify size as 1/4, 1/2 or 3/4") let( - b = select(_big_end[ind], [0,-1]), - s = select(_small_end[ind], [0,-1]) + b = select(_modhose_big_end[ind], [0,-1]), + s = select(_modhose_small_end[ind], [0,-1]) ) outer ? b[1][0] : b[0][0]; diff --git a/mutators.scad b/mutators.scad index d7b4bf2..f64e574 100644 --- a/mutators.scad +++ b/mutators.scad @@ -14,6 +14,7 @@ // Module: bounding_box() // Synopsis: Creates the smallest bounding box that contains all the children. +// SynTags: Geom // Topics: Mutators, Bounds, Bounding Boxes // See Also: pointlist_bounds() // Usage: @@ -109,6 +110,7 @@ module bounding_box(excess=0, planar=false) { // Module: chain_hull() // Synopsis: Performs the union of hull operations between consecutive pairs of children. +// SynTags: Geom // Topics: Mutators // See Also: hull() // Usage: @@ -162,6 +164,7 @@ module chain_hull() // Module: path_extrude2d() // Synopsis: Extrudes 2D children along a 2D path. +// SynTags: Geom // Topics: Mutators, Extrusion // See Also: path_sweep(), path_extrude() // Usage: @@ -284,6 +287,7 @@ module path_extrude2d(path, caps=false, closed=false, s, convexity=10) { // Module: cylindrical_extrude() // Synopsis: Extrudes 2D children outwards around a cylinder. +// SynTags: Geom // Topics: Mutators, Extrusion, Rotation // See Also: heightfield(), cylindrical_heightfield(), cyl() // Usage: @@ -349,7 +353,8 @@ module cylindrical_extrude(ir, or, od, id, size=1000, convexity=10, spin=0, orie // Module: extrude_from_to() -// Extrudes 2D children between two points in 3D space. +// Synopsis: Extrudes 2D children between two points in 3D space. +// SynTags: Geom // Topics: Extrusion, Mutators // See Also: path_sweep(), path_extrude2d() // Usage: @@ -396,9 +401,11 @@ module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) { // Module: path_extrude() // Synopsis: Extrudes 2D children along a 3D path. +// SynTags: Geom // Topics: Paths, Extrusion, Mutators // See Also: path_sweep(), path_extrude2d() -// Usage: path_extrude(path, [convexity], [clipsize]) 2D-CHILDREN; +// Usage: +// path_extrude(path, [convexity], [clipsize]) 2D-CHILDREN; // Description: // Extrudes 2D children along a 3D path. This may be slow and can have problems with twisting. // Arguments: @@ -461,6 +468,7 @@ module path_extrude(path, convexity=10, clipsize=100) { // Module: minkowski_difference() // Synopsis: Removes diff shapes from base shape surface. +// SynTags: Geom // Topics: Mutators // See Also: offset3d() // Usage: @@ -504,6 +512,7 @@ module minkowski_difference(planar=false) { // Module: offset3d() // Synopsis: Expands or contracts the surface of a 3D object. +// SynTags: Geom // Topics: Mutators // See Also: minkowski_difference(), round3d() // Usage: @@ -550,6 +559,7 @@ module offset3d(r, size=100, convexity=10) { // Module: round3d() // Synopsis: Rounds arbitrary 3d objects. +// SynTags: Geom // Topics: Rounding, Mutators // See Also: offset3d(), minkowski_difference() // Usage: diff --git a/nema_steppers.scad b/nema_steppers.scad index e25e5eb..23e7181 100644 --- a/nema_steppers.scad +++ b/nema_steppers.scad @@ -14,6 +14,7 @@ // Module: nema_stepper_motor() // Synopsis: Creates a NEMA standard stepper motor model. +// SynTags: Geom // Topics: Parts, Motors // See Also: nema_stepper_motor(), nema_mount_mask() // Usage: @@ -119,6 +120,7 @@ module nema_stepper_motor(size=17, h=24, shaft_len=20, details=true, atype="body // Module: nema_mount_mask() // Synopsis: Creates a standard NEMA mount holes mask. +// SynTags: Geom // Topics: Parts, Motors // See Also: nema_stepper_motor(), nema_mount_mask() // Usage: diff --git a/partitions.scad b/partitions.scad index a13cb42..144c768 100644 --- a/partitions.scad +++ b/partitions.scad @@ -15,7 +15,7 @@ // Synopsis: Masks half of an object at a cut plane. // SynTags: Geom, VNF, Path, Region // Topics: Partitions, Masking -// See Also: back_half(), front_half(), left_half(), right_half(), top_half(), bottom_half() +// See Also: back_half(), front_half(), left_half(), right_half(), top_half(), bottom_half(), intersection() // // Usage: as module // half_of(v, [cp], [s], [planar]) CHILDREN; @@ -23,12 +23,13 @@ // result = half_of(p,v,[cp]); // // Description: -// Slices an object at a cut plane, and masks away everything that is on one side. The v parameter is either a plane specification or -// a normal vector. The s parameter is needed for the module -// version to control the size of the masking cube. If s is too large then the preview display will flip around and display the -// wrong half, but if it is too small it won't fully mask your model. -// When called as a function, you must supply a vnf, path or region in p. If planar is set to true for the module version the operation -// is performed in 2D and UP and DOWN are treated as equivalent to BACK and FWD respectively. +// Slices an object at a cut plane, and masks away everything that is on one side. The v parameter +// is either a plane specification or a normal vector. The `s` parameter is needed for the module +// version to control the size of the masking cube. If `s` is too large then the preview display +// will flip around and display the wrong half, but if it is too small it won't fully mask your +// model. When called as a function, you must supply a vnf, path or region in p. If planar is set +// to true for the module version the operation is performed in 2D and UP and DOWN are treated as +// equivalent to BACK and FWD respectively. // // Arguments: // p = path, region or VNF to slice. (Function version) @@ -125,7 +126,7 @@ function half_of(p, v=UP, cp) = // Synopsis: Masks the right half of an object along the Y-Z plane, leaving the left half. // SynTags: Geom, VNF, Path, Region // Topics: Partitions, Masking -// See Also: back_half(), front_half(), right_half(), top_half(), bottom_half(), half_of() +// See Also: back_half(), front_half(), right_half(), top_half(), bottom_half(), half_of(), intersection() // // Usage: as module // left_half([s], [x]) CHILDREN; @@ -135,9 +136,9 @@ function half_of(p, v=UP, cp) = // // Description: // Slices an object at a vertical Y-Z cut plane, and masks away everything that is right of it. -// The s parameter is needed for the module -// version to control the size of the masking cube. If s is too large then the preview display will flip around and display the -// wrong half, but if it is too small it won't fully mask your model. +// The `s` parameter is needed for the module version to control the size of the masking cube. +// If `s` is too large then the preview display will flip around and display the wrong half, +// but if it is too small it won't fully mask your model. // // Arguments: // p = VNF, region or path to slice (function version) @@ -172,7 +173,7 @@ function left_half(p,x=0) = half_of(p, LEFT, [x,0,0]); // SynTags: Geom, VNF, Path, Region // Synopsis: Masks the left half of an object along the Y-Z plane, leaving the right half. // Topics: Partitions, Masking -// See Also: back_half(), front_half(), left_half(), top_half(), bottom_half(), half_of() +// See Also: back_half(), front_half(), left_half(), top_half(), bottom_half(), half_of(), intersection() // // Usage: as module // right_half([s=], [x=]) CHILDREN; @@ -182,9 +183,9 @@ function left_half(p,x=0) = half_of(p, LEFT, [x,0,0]); // // Description: // Slices an object at a vertical Y-Z cut plane, and masks away everything that is left of it. -// The s parameter is needed for the module -// version to control the size of the masking cube. If s is too large then the preview display will flip around and display the -// wrong half, but if it is too small it won't fully mask your model. +// The `s` parameter is needed for the module version to control the size of the masking cube. +// If `s` is too large then the preview display will flip around and display the wrong half, +// but if it is too small it won't fully mask your model. // Arguments: // p = VNF, region or path to slice (function version) // s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, OpenSCAD's preview rendering may display the wrong half. (Module version) Default: 100 @@ -217,7 +218,7 @@ function right_half(p,x=0) = half_of(p, RIGHT, [x,0,0]); // Synopsis: Masks the back half of an object along the X-Z plane, leaving the front half. // SynTags: Geom, VNF, Path, Region // Topics: Partitions, Masking -// See Also: back_half(), left_half(), right_half(), top_half(), bottom_half(), half_of() +// See Also: back_half(), left_half(), right_half(), top_half(), bottom_half(), half_of(), intersection() // // Usage: // front_half([s], [y]) CHILDREN; @@ -227,9 +228,9 @@ function right_half(p,x=0) = half_of(p, RIGHT, [x,0,0]); // // Description: // Slices an object at a vertical X-Z cut plane, and masks away everything that is behind it. -// The s parameter is needed for the module -// version to control the size of the masking cube. If s is too large then the preview display will flip around and display the -// wrong half, but if it is too small it won't fully mask your model. +// The `s` parameter is needed for the module version to control the size of the masking cube. +// If `s` is too large then the preview display will flip around and display the wrong half, +// but if it is too small it won't fully mask your model. // Arguments: // p = VNF, region or path to slice (function version) // s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, OpenSCAD's preview rendering may display the wrong half. (Module version) Default: 100 @@ -263,7 +264,7 @@ function front_half(p,y=0) = half_of(p, FRONT, [0,y,0]); // Synopsis: Masks the front half of an object along the X-Z plane, leaving the back half. // SynTags: Geom, VNF, Path, Region // Topics: Partitions, Masking -// See Also: front_half(), left_half(), right_half(), top_half(), bottom_half(), half_of() +// See Also: front_half(), left_half(), right_half(), top_half(), bottom_half(), half_of(), intersection() // // Usage: // back_half([s], [y]) CHILDREN; @@ -273,9 +274,9 @@ function front_half(p,y=0) = half_of(p, FRONT, [0,y,0]); // // Description: // Slices an object at a vertical X-Z cut plane, and masks away everything that is in front of it. -// The s parameter is needed for the module -// version to control the size of the masking cube. If s is too large then the preview display will flip around and display the -// wrong half, but if it is too small it won't fully mask your model. +// The `s` parameter is needed for the module version to control the size of the masking cube. +// If `s` is too large then the preview display will flip around and display the wrong half, +// but if it is too small it won't fully mask your model. // Arguments: // p = VNF, region or path to slice (function version) // s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, OpenSCAD's preview rendering may display the wrong half. (Module version) Default: 100 @@ -309,7 +310,7 @@ function back_half(p,y=0) = half_of(p, BACK, [0,y,0]); // Synopsis: Masks the top half of an object along the X-Y plane, leaving the bottom half. // SynTags: Geom, VNF, Path, Region // Topics: Partitions, Masking -// See Also: back_half(), front_half(), left_half(), right_half(), top_half(), half_of() +// See Also: back_half(), front_half(), left_half(), right_half(), top_half(), half_of(), intersection() // // Usage: // bottom_half([s], [z]) CHILDREN; @@ -318,9 +319,9 @@ function back_half(p,y=0) = half_of(p, BACK, [0,y,0]); // // Description: // Slices an object at a horizontal X-Y cut plane, and masks away everything that is above it. -// The s parameter is needed for the module -// version to control the size of the masking cube. If s is too large then the preview display will flip around and display the -// wrong half, but if it is too small it won't fully mask your model. +// The `s` parameter is needed for the module version to control the size of the masking cube. +// If `s` is too large then the preview display will flip around and display the wrong half, +// but if it is too small it won't fully mask your model. // Arguments: // p = VNF, region or path to slice (function version) // s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, OpenSCAD's preview rendering may display the wrong half. (Module version) Default: 100 @@ -347,7 +348,7 @@ function bottom_half(p,z=0) = half_of(p,BOTTOM,[0,0,z]); // Synopsis: Masks the bottom half of an object along the X-Y plane, leaving the top half. // SynTags: Geom, VNF, Path, Region // Topics: Partitions, Masking -// See Also: back_half(), front_half(), left_half(), right_half(), bottom_half(), half_of() +// See Also: back_half(), front_half(), left_half(), right_half(), bottom_half(), half_of(), intersection() // // Usage: as module // top_half([s], [z]) CHILDREN; @@ -356,9 +357,9 @@ function bottom_half(p,z=0) = half_of(p,BOTTOM,[0,0,z]); // // Description: // Slices an object at a horizontal X-Y cut plane, and masks away everything that is below it. -// The s parameter is needed for the module -// version to control the size of the masking cube. If s is too large then the preview display will flip around and display the -// wrong half, but if it is too small it won't fully mask your model. +// The `s` parameter is needed for the module version to control the size of the masking cube. +// If `s` is too large then the preview display will flip around and display the wrong half, +// but if it is too small it won't fully mask your model. // Arguments: // p = VNF, region or path to slice (function version) // s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, OpenSCAD's preview rendering may display the wrong half. (Module version) Default: 100 @@ -433,7 +434,8 @@ function _partition_cutpath(l, h, cutsize, cutpath, gap) = // Usage: // partition_mask(l, w, h, [cutsize], [cutpath], [gap], [inverse], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS]; // Description: -// Creates a mask that you can use to difference or intersect with an object to remove half of it, leaving behind a side designed to allow assembly of the sub-parts. +// Creates a mask that you can use to difference or intersect with an object to remove half of it, +// leaving behind a side designed to allow assembly of the sub-parts. // Arguments: // l = The length of the cut axis. // w = The width of the part to be masked, back from the cut plane. diff --git a/paths.scad b/paths.scad index c7c395b..6772ab0 100644 --- a/paths.scad +++ b/paths.scad @@ -78,14 +78,16 @@ function is_1region(path, name="path") = // Function: force_path() // Synopsis: Checks that path is a region with one component. +// SynTags: Path // Topics: Paths, Regions // See Also: is_1region() // Usage: // outpath = force_path(path, [name]) // Description: -// If `path` is a region with one component (a 1-region) then return that component as a path. If path is a region with more components -// then display an error message about the parameter `name` requiring a path or a single component region. If the input -// is not a region then return the input without any checks. This function helps path functions accept 1-regions. +// If `path` is a region with one component (a 1-region) then returns that component as a path. +// If path is a region with more components then displays an error message about the parameter +// `name` requiring a path or a single component region. If the input is not a region then +// returns the input without any checks. This function helps path functions accept 1-regions. // Arguments: // path = input to process // name = name of parameter to use in error message. Default: "path" @@ -129,6 +131,7 @@ function _path_select(path, s1, u1, s2, u2, closed=false) = // Function: path_merge_collinear() // Synopsis: Removes unnecessary points from a path. +// SynTags: Path // Topics: Paths, Regions // Description: // Takes a path and removes unnecessary sequential collinear points. @@ -316,6 +319,7 @@ function _sum_preserving_round(data, index=0) = // Function: subdivide_path() // Synopsis: Subdivides a path to produce a more finely sampled path. +// SynTags: Path // Topics: Paths, Path Subdivision // See Also: subdivide_and_slice(), resample_path(), jittered_poly() // Usage: @@ -461,6 +465,7 @@ function subdivide_path(path, n, refine, maxlen, closed=true, exact, method) = // Function: resample_path() // Synopsis: Returns an equidistant set of points along a path. +// SynTags: Path // Topics: Paths // See Also: subdivide_path() // Usage: @@ -730,6 +735,7 @@ function path_torsion(path, closed=false) = // Function: path_cut() // Synopsis: Cuts a path into subpaths at various points. +// SynTags: PathList // Topics: Paths, Path Subdivision // See Also: split_path_at_self_crossings(), path_cut_points() // Usage: @@ -929,6 +935,7 @@ function _cut_to_seg_u_form(pathcut, path, closed) = // Function: split_path_at_self_crossings() // Synopsis: Split a 2D path wherever it crosses itself. +// SynTags: PathList // Topics: Paths, Path Subdivision // See Also: path_cut(), path_cut_points() // Usage: @@ -999,6 +1006,7 @@ function _tag_self_crossing_subpaths(path, nonzero, closed=true, eps=EPSILON) = // Function: polygon_parts() // Synopsis: Parses a self-intersecting polygon into a list of non-intersecting polygons. +// SynTags: PathList // Topics: Paths, Polygons // See Also: split_path_at_self_crossings(), path_cut(), path_cut_points() // Usage: diff --git a/polyhedra.scad b/polyhedra.scad index 4942552..29f9b5b 100644 --- a/polyhedra.scad +++ b/polyhedra.scad @@ -38,6 +38,7 @@ function _unique_groups(m) = [ // Module: regular_polyhedron() // Synopsis: Creates a regular polyhedron with optional rounding. +// SynTags: Geom // Topics: Polyhedra, Shapes, Parts // See Also: regular_polyhedron_info() // Usage: Selecting a polyhedron diff --git a/regions.scad b/regions.scad index dbb30b6..348bebd 100644 --- a/regions.scad +++ b/regions.scad @@ -248,6 +248,7 @@ function is_region_simple(region, eps=EPSILON) = // Function: make_region() // Synopsis: Converts lists of intersecting polygons into valid regions. +// SynTags: Region // Topics: Regions, Paths, Polygons, List Handling // See Also: force_region(), region() // @@ -281,6 +282,7 @@ function make_region(polys,nonzero=false,eps=EPSILON) = // Function: force_region() // Synopsis: Given a polygon returns a region. +// SynTags: Region // Topics: Regions, Paths, Polygons, List Handling // See Also: make_region(), region() // Usage: @@ -296,6 +298,7 @@ function force_region(poly) = is_path(poly) ? [poly] : poly; // Module: region() // Synopsis: Creates the 2D polygons described by the given region or list of polygons. +// SynTags: Geom // Topics: Regions, Paths, Polygons, List Handling // See Also: make_region(), region() // Usage: @@ -576,6 +579,7 @@ function split_region_at_region_crossings(region1, region2, closed1=true, closed // Function: region_parts() // Synopsis: Splits a region into a list of regions. +// SynTags: RegList // Topics: Regions, List Handling // See Also: split_region_at_region_crossings() // Usage: @@ -739,6 +743,7 @@ function _point_dist(path,pathseg_unit,pathseg_len,pt) = // Function: offset() // Synopsis: Takes a 2D path, polygon or region and returns a path offset by an amount. +// SynTags: Path, Region // Topics: Paths, Polygons, Regions // Usage: // offsetpath = offset(path, [r=|delta=], [chamfer=], [closed=], [check_valid=], [quality=], [same_length=]) @@ -1044,6 +1049,7 @@ function _list_three(a,b,c) = // Function&Module: union() // Synopsis: Performs a Boolean union operation. +// SynTags: Geom, Region // Topics: Boolean Operations, Regions, Polygons, Shapes2D, Shapes3D // See Also: difference(), intersection(), diff(), intersect(), exclusive_or() // Usage: @@ -1077,6 +1083,7 @@ function union(regions=[],b=undef,c=undef,eps=EPSILON) = // Function&Module: difference() // Synopsis: Performs a Boolean difference operation. +// SynTags: Geom, Region // Topics: Boolean Operations, Regions, Polygons, Shapes2D, Shapes3D // See Also: union(), intersection(), diff(), intersect(), exclusive_or() // Usage: @@ -1112,6 +1119,7 @@ function difference(regions=[],b=undef,c=undef,eps=EPSILON) = // Function&Module: intersection() // Synopsis: Performs a Boolean intersection operation. +// SynTags: Geom, Region // Topics: Boolean Operations, Regions, Polygons, Shapes2D, Shapes3D // See Also: difference(), union(), diff(), intersect(), exclusive_or() // Usage: @@ -1146,6 +1154,7 @@ function intersection(regions=[],b=undef,c=undef,eps=EPSILON) = // Function&Module: exclusive_or() // Synopsis: Performs a Boolean exclusive-or operation. +// SynTags: Geom, Region // Topics: Boolean Operations, Regions, Polygons, Shapes2D, Shapes3D // See Also: union(), difference(), intersection(), diff(), intersect() // Usage: diff --git a/rounding.scad b/rounding.scad index ea5a977..a970233 100644 --- a/rounding.scad +++ b/rounding.scad @@ -135,6 +135,7 @@ include // Function: round_corners() // Synopsis: Round or chamfer the corners of a path (clipping them off). +// SynTags: Path // Topics: Rounding, Paths // See Also: round_corners(), smooth_path(), path_join(), offset_stroke() // Usage: @@ -597,6 +598,7 @@ function _rounding_offsets(edgespec,z_dir=1) = // Function: smooth_path() // Synopsis: Create smoothed path that passes through all the points of a given path. +// SynTags: Path // Topics: Rounding, Paths // See Also: round_corners(), smooth_path(), path_join(), offset_stroke() // Usage: @@ -695,6 +697,7 @@ function _scalar_to_vector(value,length,varname) = // Function: path_join() // Synopsis: Join paths end to end with optional rounding. +// SynTags: Path // Topics: Rounding, Paths // See Also: round_corners(), smooth_path(), path_join(), offset_stroke() // Usage: @@ -883,6 +886,7 @@ function _path_join(paths,joint,k=0.5,i=0,result=[],relocate=true,closed=false) // Function&Module: offset_stroke() // Synopsis: Draws a line along a path with options to specify angles and roundings at the ends. +// SynTags: Path, Region // Topics: Rounding, Paths // See Also: round_corners(), smooth_path(), path_join(), offset_stroke() // Usage: as module @@ -1263,10 +1267,11 @@ module offset_stroke(path, width=1, rounded=true, start, end, check_valid=true, // Function&Module: offset_sweep() // Synopsis: Make a solid from a polygon with offset that changes along its length. +// SynTags: Geom, VNF // Topics: Rounding, Offsets // See Also: offset_sweep(), convex_offset_extrude(), rounded_prism(), bent_cutout_mask(), join_prism() // Usage: most common module arguments. See Arguments list below for more. -// offset_sweep(path, [height|length|h|l|], [bottom], [top], [offset=], [convexity=],...) [ATTACHMENTS]; +// offset_sweep(path, [height|length|h|l|], [bottom], [top], [offset=], [convexity=],...) [ATTACHMENTS]; // Usage: most common function arguments. See Arguments list below for more. // vnf = offset_sweep(path, [height|h|l|length], [bottom], [top], [offset=], ...); // Description: @@ -1732,6 +1737,7 @@ function os_mask(mask, out=false, extra,check_valid, quality, offset) = // Module: convex_offset_extrude() // Synopsis: Make a solid from geometry where offset changes along the object's length. +// SynTags: Geom // Topics: Rounding, Offsets // See Also: offset_sweep(), convex_offset_extrude(), rounded_prism(), bent_cutout_mask(), join_prism() // Usage: Basic usage. See below for full options @@ -1969,6 +1975,7 @@ function _rp_compute_patches(top, bot, rtop, rsides, ktop, ksides, concave) = // Function&Module: rounded_prism() // Synopsis: Make a rounded 3d object by connecting two polygons with the same vertex count. +// SynTags: Geom, VNF // Topics: Rounding, Offsets // See Also: offset_sweep(), convex_offset_extrude(), rounded_prism(), bent_cutout_mask(), join_prism() // Usage: as a module @@ -2285,6 +2292,7 @@ function _circle_mask(r) = // Module: bent_cutout_mask() // Synopsis: Create a mask for making a round-edged cutout in a cylindrical shell. +// SynTags: Geom // Topics: Rounding, Offsets // See Also: offset_sweep(), convex_offset_extrude(), rounded_prism(), bent_cutout_mask(), join_prism() // Usage: @@ -2513,6 +2521,7 @@ Access to the derivative smoothing parameter? // Function&Module: join_prism() // Synopsis: Join an arbitrary prism to a plane, sphere, cylinder or another arbitrary prism with a fillet. +// SynTags: Geom, VNF // Topics: Rounding, Offsets // See Also: offset_sweep(), convex_offset_extrude(), rounded_prism(), bent_cutout_mask(), join_prism() // Usage: The two main forms with most common options diff --git a/screw_drive.scad b/screw_drive.scad index 55a50a2..642df16 100644 --- a/screw_drive.scad +++ b/screw_drive.scad @@ -15,6 +15,7 @@ include // Module: phillips_mask() // Synopsis: Creates a mask for a Philips screw drive. +// SynTags: Geom // Topics: Screws, Masks // See Also: hex_drive_mask(), phillips_depth(), phillips_diam(), torx_mask(), robertson_mask() // Usage: @@ -139,6 +140,7 @@ function phillips_diam(size, depth) = // Module: hex_drive_mask() // Synopsis: Creates a mask for a hex drive recess. +// SynTags: Geom // Topics: Screws, Masks // See Also: phillips_mask(), hex_drive_mask(), torx_mask(), phillips_depth(), phillips_diam(), robertson_mask() // Usage: @@ -161,6 +163,7 @@ function hex_drive_mask(size,length,l,h,height,anchor,spin,orient) = no_function // Module: torx_mask() // Synopsis: Creates a mask for a torx drive recess. +// SynTags: Geom // Topics: Screws, Masks // See Also: phillips_mask(), hex_drive_mask(), torx_mask(), phillips_depth(), phillips_diam(), robertson_mask() // Usage: @@ -191,6 +194,7 @@ module torx_mask(size, l=5, center, anchor, spin=0, orient=UP) { // Module: torx_mask2d() // Synopsis: Creates the 2D cross section for a torx drive recess. +// SynTags: Geom // Topics: Screws, Masks // See Also: phillips_mask(), hex_drive_mask(), torx_mask(), phillips_depth(), phillips_diam(), torx_info(), robertson_mask() // Usage: @@ -313,6 +317,7 @@ function torx_depth(size) = torx_info(size)[2]; // Module: robertson_mask() // Synopsis: Creates a mask for a Robertson/Square drive recess. +// SynTags: Geom // Topics: Screws, Masks // See Also: phillips_mask(), hex_drive_mask(), torx_mask(), phillips_depth(), phillips_diam(), torx_info(), robertson_mask() // Usage: diff --git a/screws.scad b/screws.scad index 17d03f2..e7a8d04 100644 --- a/screws.scad +++ b/screws.scad @@ -200,6 +200,7 @@ Torx values: https://www.stanleyengineeredfastening.com/-/media/web/sef/resourc // Module: screw() // Synopsis: Creates a standard screw with optional tolerances. +// SynTags: Geom // Topics: Threading, Screws // See Also: screw_hole(), shoulder_screw() // Usage: @@ -733,6 +734,7 @@ fdsa= echo(pitch=pitch); // Module: screw_hole() // Synopsis: Creates a screw hole. +// SynTags: Geom // Topics: Threading, Screws // See Also: screw() // Usage: @@ -990,6 +992,7 @@ module screw_hole(spec, head, thread, oversize, hole_oversize, head_oversize, // Module: shoulder_screw() // Synopsis: Creates a shoulder screw. +// SynTags: Geom // Topics: Threading, Screws // See Also: screw(), screw_hole() // Usage: @@ -1403,6 +1406,7 @@ function _parse_drive(drive=undef, drive_size=undef) = // Module: screw_head() // Synopsis: Creates a screw head. +// SynTags: Geom // Topics: Threading, Screws // See Also: screw(), screw_hole() // Usage: @@ -1506,6 +1510,7 @@ module screw_head(screw_info,details=false, counterbore=0,flat_height,teardrop=f // Module: nut() // Synopsis: Creates a standard nut. +// SynTags: Geom // Topics: Threading, Screws // See Also: screw(), screw_hole() // Usage: @@ -1628,6 +1633,7 @@ module nut(spec, shape, thickness, nutwidth, thread, tolerance, hole_oversize, // Module: nut_trap_side() // Synopsis: Creates a side nut trap mask. +// SynTags: Geom // Topics: Threading, Screws // See Also: screw(), screw_hole() // Usage: @@ -1718,6 +1724,7 @@ module nut_trap_side(trap_width, spec, shape, thickness, nutwidth, anchor=BOT, o // Module: nut_trap_inline() // Synopsis: Creates an inline nut trap mask. +// SynTags: Geom // Topics: Threading, Screws // See Also: screw(), screw_hole() // Usage: @@ -1897,7 +1904,7 @@ function screw_info(name, head, drive, thread, drive_size, shaft_oversize, head_ // . // Field | What it is // ------------------ | --------------- -// "type" | Always set to "screw_info" +// "type" | Always set to "nut_info" // "system" | Either `"UTS"` or `"ISO"` (used for correct tolerance computation). // "origin" | Module that created the structure // "name" | Name used to specify threading, such as "M6" or "#8" diff --git a/shapes3d.scad b/shapes3d.scad index 0343638..214bdb6 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -90,7 +90,7 @@ function cube(size=1, center, anchor, spin=0, orient=UP) = // Module: cuboid() // Synopsis: Creates a cube with chamfering and roundovers. -// SynTags: Geom, VNF +// SynTags: Geom // Topics: Shapes (3D), Attachable, VNF Generators // See Also: prismoid(), rounded_prism() // Usage: Standard Cubes @@ -604,7 +604,7 @@ function cuboid( // chamfer = The chamfer size for the vertical-ish edges of the prismoid. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no chamfer) // chamfer1 = The chamfer size for the bottom of the vertical-ish edges of the prismoid. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. // chamfer2 = The chamfer size for the top of the vertical-ish edges of the prismoid. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `BOTTOM` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // @@ -816,7 +816,7 @@ function octahedron(size=1, anchor=CENTER, spin=0, orient=UP) = // Module: rect_tube() // Synopsis: Creates a rectangular tube. -// SynTags: Geom, VNF +// SynTags: Geom // Topics: Shapes (3D), Attachable, VNF Generators // See Also: tube() // Usage: Typical Rectangular Tubes @@ -1286,6 +1286,8 @@ function cylinder(h, r1, r2, center, r, d, d1, d2, anchor, spin=0, orient=UP) = // cyl(l|h|length|height, r1=, r2=, texture=, [tex_size=]|[tex_counts=], [tex_scale=], [tex_rot=], [tex_samples=], [tex_style=], [tex_taper=], [tex_inset=], ...); // cyl(l|h|length|height, d1=, d2=, texture=, [tex_size=]|[tex_counts=], [tex_scale=], [tex_rot=], [tex_samples=], [tex_style=], [tex_taper=], [tex_inset=], ...); // +// Usage: Caled as a function to get a VNF +// vnf = cyl(...); // // Description: // Creates cylinders in various anchorings and orientations, with optional rounding, chamfers, or textures. @@ -1348,6 +1350,7 @@ function cylinder(h, r1, r2, center, r, d, d1, d2, anchor, spin=0, orient=UP) = // rounding1 = The radius of the rounding on the bottom end of the cylinder. // rounding2 = The radius of the rounding on the top end of the cylinder. // realign = If true, rotate the cylinder by half the angle of one face. +// teardrop = If given as a number, rounding around the bottom edge of the cylinder won't exceed this many degrees from vertical. If true, the limit angle is 45 degrees. Default: `false` // texture = A texture name string, or a rectangular array of scalar height values (0.0 to 1.0), or a VNF tile that defines the texture to apply to vertical surfaces. See {{texture()}} for what named textures are supported. // tex_size = An optional 2D target size for the textures. Actual texture sizes will be scaled somewhat to evenly fit the available surface. Default: `[5,5]` // tex_counts = If given instead of tex_size, gives the tile repetition counts for textures over the surface length and height. @@ -1385,6 +1388,9 @@ function cylinder(h, r1, r2, center, r, d, d1, d2, anchor, spin=0, orient=UP) = // Example: Rounding // cyl(l=40, d=40, rounding=10); // +// Example(VPD=175;VPR=[90,0,0]): Teardrop Bottom Rounding +// cyl(l=40, d=40, rounding=10, teardrop=true); +// // Example: Heterogenous Chamfers and Rounding // ydistribute(80) { // // Shown Front to Back. @@ -1490,6 +1496,7 @@ function cyl( chamfang, chamfang1, chamfang2, rounding, rounding1, rounding2, circum=false, realign=false, shift=[0,0], + teardrop=false, from_end, from_end1, from_end2, texture, tex_size=[5,5], tex_counts, tex_inset=false, tex_rot=false, @@ -1559,6 +1566,12 @@ function cyl( dy1 = abs(_chamf1 ? chamf1l : round1 ? roundlen1 : 0), dy2 = abs(_chamf2 ? chamf2l : round2 ? roundlen2 : 0), + td_ang = teardrop == true? 45 : + teardrop == false? 90 : + assert(is_finite(teardrop)) + assert(teardrop>=0 && teardrop<=90) + teardrop, + checks2 = assert(is_finite(round1), "rounding1 must be a number if given.") assert(is_finite(round2), "rounding2 must be a number if given.") @@ -1570,14 +1583,18 @@ function cyl( undef, path = [ if (texture==undef) [0,-l/2], + if (!approx(chamf1r,0)) each [ [r1, -l/2] + polar_to_xy(chamf1r,180), [r1, -l/2] + polar_to_xy(chamf1l,90+vang), ] - else if (!approx(round1,0)) + else if (!approx(round1,0) && td_ang < 90) + each _teardrop_corner(r=abs(round1), corner=[[max(0,r1-2*roundlen1),-l/2],[r1,-l/2],[r2,l/2]], ang=td_ang) + else if (!approx(round1,0) && td_ang >= 90) each arc(r=abs(round1), corner=[[max(0,r1-2*roundlen1),-l/2],[r1,-l/2],[r2,l/2]]) else [r1,-l/2], + if (is_finite(chamf2r) && !approx(chamf2r,0)) each [ [r2, l/2] + polar_to_xy(chamf2l,270+vang), @@ -1586,6 +1603,7 @@ function cyl( else if (is_finite(round2) && !approx(round2,0)) each arc(r=abs(round2), corner=[[r1,-l/2],[r2,l/2],[max(0,r2-2*roundlen2),l/2]]) else [r2,l/2], + if (texture==undef) [0,l/2], ] ) rotate_sweep(path, @@ -1603,6 +1621,23 @@ function cyl( reorient(anchor,spin,orient, r1=r1, r2=r2, l=l, shift=shift, p=ovnf); +function _teardrop_corner(r, corner, ang=45) = + let( + check = assert(len(corner)==3) assert(is_finite(r)) assert(is_finite(ang)), + cp = circle_2tangents(r, corner)[0], + path1 = arc(r=r, corner=corner), + path2 = [ + for (p = select(path1,0,-2)) + if (v_theta(p-cp) >= -ang) p, + last(path1) + ], + path = [ + line_intersection([corner[0],corner[1]],[path2[0],path2[0]+polar_to_xy(1,-90-ang)]), + each path2 + ] + ) path; + + module cyl( h, r, center, l, r1, r2, @@ -1611,6 +1646,7 @@ module cyl( chamfang, chamfang1, chamfang2, rounding, rounding1, rounding2, circum=false, realign=false, shift=[0,0], + teardrop=false, from_end, from_end1, from_end2, texture, tex_size=[5,5], tex_counts, tex_inset=false, tex_rot=false, @@ -1640,6 +1676,7 @@ module cyl( chamfang=chamfang, chamfang1=chamfang1, chamfang2=chamfang2, rounding=rounding, rounding1=rounding1, rounding2=rounding2, from_end=from_end, from_end1=from_end1, from_end2=from_end2, + teardrop=teardrop, texture=texture, tex_size=tex_size, tex_counts=tex_counts, tex_scale=tex_scale, tex_inset=tex_inset, tex_rot=tex_rot, @@ -1657,7 +1694,7 @@ module cyl( // Module: xcyl() // Synopsis: creates a cylinder oriented along the X axis. -// SynTags: Geom, VNF +// SynTags: Geom // Topics: Cylinders, Textures, Rounding, Chamfers // See Also: texture(), rotate_sweep(), cyl() // Description: @@ -1740,7 +1777,7 @@ module xcyl( // Module: ycyl() // Synopsis: Creates a cylinder oriented along the y axis. -// SynTags: Geom, VNF +// SynTags: Geom // Topics: Cylinders, Textures, Rounding, Chamfers // See Also: texture(), rotate_sweep(), cyl() // Description: @@ -1825,7 +1862,7 @@ module ycyl( // Module: zcyl() // Synopsis: Creates a cylinder oriented along the Z axis. -// SynTags: Geom, VNF +// SynTags: Geom // Topics: Cylinders, Textures, Rounding, Chamfers // See Also: texture(), rotate_sweep(), cyl() // Description: @@ -1909,7 +1946,7 @@ module zcyl( // Module: tube() // Synopsis: Creates a cylindrical or conical tube. -// SynTags: Geom, VNF +// SynTags: Geom // Topics: Shapes (3D), Attachable, VNF Generators // See Also: rect_tube() // Description: @@ -3211,10 +3248,10 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers -// Topics: Shapes (3D), Attachable // Module: fillet() // Synopsis: Creates a smooth fillet between two faces. // SynTags: Geom, VNF +// Topics: Shapes (3D), Attachable // See Also: mask2d_roundover() // Description: // Creates a shape that can be unioned into a concave joint between two faces, to fillet them. diff --git a/skin.scad b/skin.scad index 31ed7f4..08d2a54 100644 --- a/skin.scad +++ b/skin.scad @@ -2691,14 +2691,14 @@ function associate_vertices(polygons, split, curpoly=0) = // DefineHeader(Table;Headers=Texture Name|Type|Description): Texture Values // Section: Texturing -// Some operations are able to add texture to the objects they create. A texture can be any regularly repeated variation in the height of the surface. -// To define a texture you need to specify how the height should vary over a rectangular block that will be repeated to tile the object. Because textures -// are based on rectangular tiling, this means adding textures to curved shapes may result in distortion of the basic texture unit. For example, if you -// texture a cone, the scale of the texture will be larger at the wide end of the cone and smaller at the narrower end of the cone. -// . -// You can specify a texture using to method: a height field or a VNF. For each method you also must specify the scale of the texture, which -// gives the size of the rectangular unit in your object that will correspond to one texture tile. Note that this scale does not preserve -// aspect ratio: you can stretch the texture as desired. +// Some operations are able to add texture to the objects they create. A texture can be any regularly repeated variation in the height of the surface. +// To define a texture you need to specify how the height should vary over a rectangular block that will be repeated to tile the object. Because textures +// are based on rectangular tiling, this means adding textures to curved shapes may result in distortion of the basic texture unit. For example, if you +// texture a cone, the scale of the texture will be larger at the wide end of the cone and smaller at the narrower end of the cone. +// . +// You can specify a texture using to method: a height field or a VNF. For each method you also must specify the scale of the texture, which +// gives the size of the rectangular unit in your object that will correspond to one texture tile. Note that this scale does not preserve +// aspect ratio: you can stretch the texture as desired. // Subsection: Height Field Texture Maps // The simplest way to specify a texture map is to give a 2d array of // height values which specify the height of the texture on a grid. @@ -2799,7 +2799,7 @@ function associate_vertices(polygons, split, curpoly=0) = // Topics: Textures, Knurling // Synopsis: Produce a standard texture. // Topics: Extrusion, Textures -// See Also: linear_sweep(), rotate_sweep() +// See Also: linear_sweep(), rotate_sweep(), heightfield(), cylindrical_heightfield() // Usage: // tx = texture(tex, [n=], [inset=], [gap=], [roughness=]); // Description: @@ -2817,7 +2817,6 @@ function associate_vertices(polygons, split, curpoly=0) = // inset = The amount to inset part of a VNF tile texture. Generally between 0 and 0.5. // gap = The gap between logically distinct parts of a VNF tile. (ie: gap between bricks, gap between truncated ribs, etc.) // roughness = The amount of roughness used on the surface of some heightfield textures. Generally between 0 and 0.5. -// See Also: heightfield(), cylindrical_heightfield(), texture() // Example(3D): **"bricks"** (Heightfield) = A brick-wall pattern. Giving `n=` sets the number of heightfield samples to `n x n`. Default: 24. Giving `roughness=` adds a level of height randomization to add roughness to the texture. Default: 0.05. Use `style="convex"`. // tex = texture("bricks"); // linear_sweep( diff --git a/sliders.scad b/sliders.scad index a9ec7bd..be15ee6 100644 --- a/sliders.scad +++ b/sliders.scad @@ -14,6 +14,7 @@ // Module: slider() // Synopsis: Creates a V-groove slider. +// SynTags: Geom // Topics: Parts, Sliders // See Also: rail() // Usage: @@ -67,6 +68,7 @@ module slider(l=30, w=10, h=10, base=10, wall=5, ang=30, anchor=BOTTOM, spin=0, // Module: rail() // Synopsis: Creates a V-groove rail. +// SynTags: Geom // Topics: Parts, Sliders // See Also: slider() // Usage: diff --git a/threading.scad b/threading.scad index 6291398..ab5575d 100644 --- a/threading.scad +++ b/threading.scad @@ -451,7 +451,6 @@ module threaded_nut( // right(14)back(19)text("flank",size=4,halign="center"); // right(14)back(14)text("angle",size=4,halign="center"); // } - // Arguments: // d = Outer diameter of threaded rod. // l / length / h / height = Length of threaded rod. @@ -1248,7 +1247,6 @@ module buttress_threaded_nut( // bevel1 = if true bevel the bottom end. // bevel2 = if true bevel the top end. // internal = If true, this is a mask for making internal threads. - // blunt_start = If true apply truncated blunt start threads at both ends. Default: true // blunt_start1 = If true apply truncated blunt start threads bottom end. // blunt_start2 = If true apply truncated blunt start threads top end. @@ -2039,32 +2037,6 @@ module _nutshape(nutwidth, h, shape, bevel1, bevel2) // The taper options specify tapering at of the threads at each end, and is given as the linear distance // over which to taper. If taper is positive the threads are lengthened by the specified distance; if taper // is negative, the taper is included in the thread length specified by `turns`. Tapering works on both internal and external threads. -// Arguments: -// d = Inside base diameter of threads. Default: 10 -// pitch = Distance between threads. Default: 2 -// --- -// thread_depth = Depth of threads from top to bottom. -// flank_angle = Angle of thread faces to plane perpendicular to screw. Default: 15 degrees. -// turns = Number of revolutions to rotate thread around. -// thread_angle = Angle between two thread faces. -// profile = If an asymmetrical thread profile is needed, it can be specified here. -// starts = The number of thread starts. Default: 1 -// left_handed = If true, thread has a left-handed winding. -// internal = if true make internal threads. The only effect this has is to change how the threads taper if tapering is selected. When true, threads taper towards the outside; when false, they taper towards the inside. Default: false -// d1 = Bottom inside base diameter of threads. -// d2 = Top inside base diameter of threads. -// thread_angle = Angle between -// lead_in = Specify linear length of the lead in section of the threading with blunt start threads -// lead_in1 = Specify linear length of the lead in section of the threading at the bottom with blunt start threads -// lead_in2 = Specify linear length of the lead in section of the threading at the top with blunt start threads -// lead_in_ang = Specify angular length in degrees of the lead in section of the threading with blunt start threads -// lead_in_ang1 = Specify angular length in degrees of the lead in section of the threading at the bottom with blunt start threads -// lead_in_ang2 = Specify angular length in degrees of the lead in section of the threading at the top with blunt start threads -// lead_in_shape = Specify the shape of the thread lead in by giving a text string or function. Default: "default" -// lead_in_sample = Factor to increase sample rate in the lead-in section. Default: 10 -// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` -// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` -// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // Figure(2D,Med,NoAxes): // pa_delta = tan(15)/4; // rr1 = -1/2; @@ -2113,6 +2085,32 @@ module _nutshape(nutwidth, h, shape, bevel1, bevel2) // back(10)text("thread",size=4,halign="center"); // back(3)text("angle",size=4,halign="center"); // } +// Arguments: +// d = Inside base diameter of threads. Default: 10 +// pitch = Distance between threads. Default: 2 +// --- +// thread_depth = Depth of threads from top to bottom. +// flank_angle = Angle of thread faces to plane perpendicular to screw. Default: 15 degrees. +// turns = Number of revolutions to rotate thread around. +// thread_angle = Angle between two thread faces. +// profile = If an asymmetrical thread profile is needed, it can be specified here. +// starts = The number of thread starts. Default: 1 +// left_handed = If true, thread has a left-handed winding. +// internal = if true make internal threads. The only effect this has is to change how the threads taper if tapering is selected. When true, threads taper towards the outside; when false, they taper towards the inside. Default: false +// d1 = Bottom inside base diameter of threads. +// d2 = Top inside base diameter of threads. +// thread_angle = Angle between +// lead_in = Specify linear length of the lead in section of the threading with blunt start threads +// lead_in1 = Specify linear length of the lead in section of the threading at the bottom with blunt start threads +// lead_in2 = Specify linear length of the lead in section of the threading at the top with blunt start threads +// lead_in_ang = Specify angular length in degrees of the lead in section of the threading with blunt start threads +// lead_in_ang1 = Specify angular length in degrees of the lead in section of the threading at the bottom with blunt start threads +// lead_in_ang2 = Specify angular length in degrees of the lead in section of the threading at the top with blunt start threads +// lead_in_shape = Specify the shape of the thread lead in by giving a text string or function. Default: "default" +// lead_in_sample = Factor to increase sample rate in the lead-in section. Default: 10 +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // Example(2DMed): Typical Tooth Profile // pitch = 2; // depth = pitch * cos(30) * 5/8; diff --git a/transforms.scad b/transforms.scad index 8f1e81c..4d72810 100644 --- a/transforms.scad +++ b/transforms.scad @@ -74,6 +74,11 @@ _NO_ARG = [true,[123232345],false]; // Function&Module: move() // Aliases: translate() // +// Synopsis: Translates children in an arbitrary direction. +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Translation +// See Also: left(), right(), fwd(), back(), down(), up(), spherical_to_xyz(), altaz_to_xyz(), cylindrical_to_xyz(), polar_to_xy() +// // Usage: As Module // move(v) CHILDREN; // Usage: As a function to translate points, VNF, or Bezier patches @@ -82,11 +87,6 @@ _NO_ARG = [true,[123232345],false]; // Usage: Get Translation Matrix // mat = move(v); // -// Synopsis: Translates children in an arbitrary direction. -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Translation -// See Also: left(), right(), fwd(), back(), down(), up(), spherical_to_xyz(), altaz_to_xyz(), cylindrical_to_xyz(), polar_to_xy() -// // Description: // Translates position by the given amount. // * Called as a module, moves/translates all children. @@ -160,6 +160,11 @@ function translate(v=[0,0,0], p=_NO_ARG) = move(v=v, p=p); // Function&Module: left() // +// Synopsis: Translates children leftwards (X-). +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Translation +// See Also: move(), right(), fwd(), back(), down(), up() +// // Usage: As Module // left(x) CHILDREN; // Usage: Translate Points @@ -167,11 +172,6 @@ function translate(v=[0,0,0], p=_NO_ARG) = move(v=v, p=p); // Usage: Get Translation Matrix // mat = left(x); // -// Synopsis: Translates children leftwards (X-). -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Translation -// See Also: move(), right(), fwd(), back(), down(), up() -// // Description: // If called as a module, moves/translates all children left (in the X- direction) by the given amount. // If called as a function with the `p` argument, returns the translated VNF, point or list of points. @@ -205,6 +205,11 @@ function left(x=0, p=_NO_ARG) = // Function&Module: right() // Aliases: xmove() // +// Synopsis: Translates children rightwards (X+). +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Translation +// See Also: move(), left(), fwd(), back(), down(), up() +// // Usage: As Module // right(x) CHILDREN; // Usage: Translate Points @@ -212,11 +217,6 @@ function left(x=0, p=_NO_ARG) = // Usage: Get Translation Matrix // mat = right(x); // -// Synopsis: Translates children rightwards (X+). -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Translation -// See Also: move(), left(), fwd(), back(), down(), up() -// // Description: // If called as a module, moves/translates all children right (in the X+ direction) by the given amount. // If called as a function with the `p` argument, returns the translated VNF point or list of points. @@ -260,6 +260,11 @@ function xmove(x=0, p=_NO_ARG) = // Function&Module: fwd() // +// Synopsis: Translates children forwards (Y-). +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Translation +// See Also: move(), left(), right(), back(), down(), up() +// // Usage: As Module // fwd(y) CHILDREN; // Usage: Translate Points @@ -267,11 +272,6 @@ function xmove(x=0, p=_NO_ARG) = // Usage: Get Translation Matrix // mat = fwd(y); // -// Synopsis: Translates children forwards (Y-). -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Translation -// See Also: move(), left(), right(), back(), down(), up() -// // Description: // If called as a module, moves/translates all children forward (in the Y- direction) by the given amount. // If called as a function with the `p` argument, returns the translated VNF, point or list of points. @@ -305,6 +305,11 @@ function fwd(y=0, p=_NO_ARG) = // Function&Module: back() // Aliases: ymove() // +// Synopsis: Translates children backwards (Y+). +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Translation +// See Also: move(), left(), right(), fwd(), down(), up() +// // Usage: As Module // back(y) CHILDREN; // Usage: Translate Points @@ -312,11 +317,6 @@ function fwd(y=0, p=_NO_ARG) = // Usage: Get Translation Matrix // mat = back(y); // -// Synopsis: Translates children backwards (Y+). -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Translation -// See Also: move(), left(), right(), fwd(), down(), up() -// // Description: // If called as a module, moves/translates all children back (in the Y+ direction) by the given amount. // If called as a function with the `p` argument, returns the translated VNF, point or list of points. @@ -360,6 +360,11 @@ function ymove(y=0,p=_NO_ARG) = // Function&Module: down() // +// Synopsis: Translates children downwards (Z-). +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Translation +// See Also: move(), left(), right(), fwd(), back(), up() +// // Usage: As Module // down(z) CHILDREN; // Usage: Translate Points @@ -367,11 +372,6 @@ function ymove(y=0,p=_NO_ARG) = // Usage: Get Translation Matrix // mat = down(z); // -// Synopsis: Translates children downwards (Z-). -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Translation -// See Also: move(), left(), right(), fwd(), back(), up() -// // Description: // If called as a module, moves/translates all children down (in the Z- direction) by the given amount. // If called as a function with the `p` argument, returns the translated VNF, point or list of points. @@ -403,6 +403,11 @@ function down(z=0, p=_NO_ARG) = // Function&Module: up() // Aliases: zmove() // +// Synopsis: Translates children upwards (Z+). +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Translation +// See Also: move(), left(), right(), fwd(), back(), down() +// // Usage: As Module // up(z) CHILDREN; // Usage: Translate Points @@ -410,11 +415,6 @@ function down(z=0, p=_NO_ARG) = // Usage: Get Translation Matrix // mat = up(z); // -// Synopsis: Translates children upwards (Z+). -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Translation -// See Also: move(), left(), right(), fwd(), back(), down() -// // Description: // If called as a module, moves/translates all children up (in the Z+ direction) by the given amount. // If called as a function with the `p` argument, returns the translated VNF, point or list of points. @@ -463,6 +463,11 @@ function zmove(z=0, p=_NO_ARG) = // Function&Module: rot() // +// Synopsis: Rotates children in various ways. +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Rotation +// See Also: xrot(), yrot(), zrot() +// // Usage: As a Module // rot(a, [cp=], [reverse=]) CHILDREN; // rot([X,Y,Z], [cp=], [reverse=]) CHILDREN; @@ -479,11 +484,6 @@ function zmove(z=0, p=_NO_ARG) = // M = rot(a, v, [cp=], [reverse=]); // M = rot(from=, to=, [a=], [reverse=]); // -// Synopsis: Rotates children in various ways. -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Rotation -// See Also: xrot(), yrot(), zrot() -// // Description: // This is a shorthand version of the built-in `rotate()`, and operates similarly, with a few additional capabilities. // You can specify the rotation to perform in one of several ways: @@ -569,6 +569,11 @@ function rot(a=0, v, cp, from, to, reverse=false, p=_NO_ARG, _m) = // Function&Module: xrot() // +// Synopsis: Rotates children around the X axis using the right-hand rule. +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Rotation +// See Also: rot(), yrot(), zrot() +// // Usage: As Module // xrot(a, [cp=]) CHILDREN; // Usage: As a function to rotate points @@ -576,11 +581,6 @@ function rot(a=0, v, cp, from, to, reverse=false, p=_NO_ARG, _m) = // Usage: As a function to return rotation matrix // mat = xrot(a, [cp=]); // -// Synopsis: Rotates children around the X axis using the right-hand rule. -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Rotation -// See Also: rot(), yrot(), zrot() -// // Description: // Rotates around the X axis by the given number of degrees. If `cp` is given, rotations are performed around that centerpoint. // * Called as a module, rotates all children. @@ -617,6 +617,11 @@ function xrot(a=0, p=_NO_ARG, cp) = rot([a,0,0], cp=cp, p=p); // Function&Module: yrot() // +// Synopsis: Rotates children around the Y axis using the right-hand rule. +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Rotation +// See Also: rot(), xrot(), zrot() +// // Usage: As Module // yrot(a, [cp=]) CHILDREN; // Usage: Rotate Points @@ -624,11 +629,6 @@ function xrot(a=0, p=_NO_ARG, cp) = rot([a,0,0], cp=cp, p=p); // Usage: Get Rotation Matrix // mat = yrot(a, [cp=]); // -// Synopsis: Rotates children around the Y axis using the right-hand rule. -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Rotation -// See Also: rot(), xrot(), zrot() -// // Description: // Rotates around the Y axis by the given number of degrees. If `cp` is given, rotations are performed around that centerpoint. // * Called as a module, rotates all children. @@ -665,6 +665,11 @@ function yrot(a=0, p=_NO_ARG, cp) = rot([0,a,0], cp=cp, p=p); // Function&Module: zrot() // +// Synopsis: Rotates children around the Z axis using the right-hand rule. +// Topics: Affine, Matrices, Transforms, Rotation +// SynTags: Trans, Path, VNF, Mat +// See Also: rot(), xrot(), yrot() +// // Usage: As Module // zrot(a, [cp=]) CHILDREN; // Usage: As Function to rotate points @@ -672,11 +677,6 @@ function yrot(a=0, p=_NO_ARG, cp) = rot([0,a,0], cp=cp, p=p); // Usage: As Function to return rotation matrix // mat = zrot(a, [cp=]); // -// Synopsis: Rotates children around the Z axis using the right-hand rule. -// Topics: Affine, Matrices, Transforms, Rotation -// SynTags: Trans, Path, VNF, Mat -// See Also: rot(), xrot(), yrot() -// // Description: // Rotates around the Z axis by the given number of degrees. If `cp` is given, rotations are performed around that centerpoint. // * Called as a module, rotates all children. @@ -718,6 +718,12 @@ function zrot(a=0, p=_NO_ARG, cp) = rot(a, cp=cp, p=p); // Function&Module: scale() +// +// Synopsis: Scales children arbitrarily. +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Scaling +// See Also: xscale(), yscale(), zscale() +// // Usage: As Module // scale(SCALAR) CHILDREN; // scale([X,Y,Z]) CHILDREN; @@ -725,10 +731,7 @@ function zrot(a=0, p=_NO_ARG, cp) = rot(a, cp=cp, p=p); // pts = scale(v, p, [cp=]); // Usage: Get Scaling Matrix // mat = scale(v, [cp=]); -// Synopsis: Scales children arbitrarily. -// Topics: Affine, Matrices, Transforms, Scaling -// SynTags: Trans, Path, VNF, Mat -// See Also: xscale(), yscale(), zscale() +// // Description: // Scales by the [X,Y,Z] scaling factors given in `v`. If `v` is given as a scalar number, all axes are scaled uniformly by that amount. // * Called as the built-in module, scales all children. @@ -738,17 +741,20 @@ function zrot(a=0, p=_NO_ARG, cp) = rot(a, cp=cp, p=p); // * Called as a function with a [VNF structure](vnf.scad) in the `p` argument, returns the scaled VNF. // * Called as a function without a `p` argument, and a 2D list of scaling factors in `v`, returns an affine2d scaling matrix. // * Called as a function without a `p` argument, and a 3D list of scaling factors in `v`, returns an affine3d scaling matrix. +// // Arguments: // v = Either a numeric uniform scaling factor, or a list of [X,Y,Z] scaling factors. Default: 1 // p = If called as a function, the point or list of points to scale. // --- // cp = If given, centers the scaling on the point `cp`. +// // Example(NORENDER): // pt1 = scale(3, p=[3,1,4]); // Returns: [9,3,12] // pt2 = scale([2,3,4], p=[3,1,4]); // Returns: [6,3,16] // pt3 = scale([2,3,4], p=[[1,2,3],[4,5,6]]); // Returns: [[2,6,12], [8,15,24]] // mat2d = scale([2,3]); // Returns: [[2,0,0],[0,3,0],[0,0,1]] // mat3d = scale([2,3,4]); // Returns: [[2,0,0,0],[0,3,0,0],[0,0,4,0],[0,0,0,1]] +// // Example(2D): // path = circle(d=50,$fn=12); // #stroke(path,closed=true); @@ -770,6 +776,11 @@ function scale(v=1, p=_NO_ARG, cp=[0,0,0]) = // Function&Module: xscale() // +// Synopsis: Scales children along the X axis. +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Scaling +// See Also: scale(), yscale(), zscale() +// // Usage: As Module // xscale(x, [cp=]) CHILDREN; // Usage: Scale Points @@ -777,11 +788,6 @@ function scale(v=1, p=_NO_ARG, cp=[0,0,0]) = // Usage: Get Affine Matrix // mat = xscale(x, [cp=]); // -// Synopsis: Scales children along the X axis. -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Scaling -// See Also: scale(), yscale(), zscale() -// // Description: // Scales along the X axis by the scaling factor `x`. // * Called as the built-in module, scales all children. @@ -826,6 +832,11 @@ function xscale(x=1, p=_NO_ARG, cp=0) = // Function&Module: yscale() // +// Synopsis: Scales children along the Y axis. +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Scaling +// See Also: scale(), xscale(), zscale() +// // Usage: As Module // yscale(y, [cp=]) CHILDREN; // Usage: Scale Points @@ -833,11 +844,6 @@ function xscale(x=1, p=_NO_ARG, cp=0) = // Usage: Get Affine Matrix // mat = yscale(y, [cp=]); // -// Synopsis: Scales children along the Y axis. -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Scaling -// See Also: scale(), xscale(), zscale() -// // Description: // Scales along the Y axis by the scaling factor `y`. // * Called as the built-in module, scales all children. @@ -882,6 +888,11 @@ function yscale(y=1, p=_NO_ARG, cp=0) = // Function&Module: zscale() // +// Synopsis: Scales children along the Z axis. +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Scaling +// See Also: scale(), xscale(), yscale() +// // Usage: As Module // zscale(z, [cp=]) CHILDREN; // Usage: Scale Points @@ -889,11 +900,6 @@ function yscale(y=1, p=_NO_ARG, cp=0) = // Usage: Get Affine Matrix // mat = zscale(z, [cp=]); // -// Synopsis: Scales children along the Z axis. -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Scaling -// See Also: scale(), xscale(), yscale() -// // Description: // Scales along the Z axis by the scaling factor `z`. // * Called as the built-in module, scales all children. @@ -941,16 +947,19 @@ function zscale(z=1, p=_NO_ARG, cp=0) = ////////////////////////////////////////////////////////////////////// // Function&Module: mirror() +// +// Synopsis: Reflects children across an arbitrary plane. +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Reflection, Mirroring +// See Also: xflip(), yflip(), zflip() +// // Usage: As Module // mirror(v) CHILDREN; // Usage: As Function // pt = mirror(v, p); // Usage: Get Reflection/Mirror Matrix // mat = mirror(v); -// Synopsis: Reflects children across an arbitrary plane. -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Reflection, Mirroring -// See Also: xflip(), yflip(), zflip() +// // Description: // Mirrors/reflects across the plane or line whose normal vector is given in `v`. // * Called as the built-in module, mirrors all children across the line/plane. @@ -960,9 +969,11 @@ function zscale(z=1, p=_NO_ARG, cp=0) = // * Called as a function with a [VNF structure](vnf.scad) in the `p` argument, returns the mirrored VNF. // * Called as a function without a `p` argument, and with a 2D normal vector `v`, returns the affine2d 3x3 mirror matrix. // * Called as a function without a `p` argument, and with a 3D normal vector `v`, returns the affine3d 4x4 mirror matrix. +// // Arguments: // v = The normal vector of the line or plane to mirror across. // p = If called as a function, the point or list of points to scale. +// // Example: // n = [1,0,0]; // module obj() right(20) rotate([0,15,-15]) cube([40,30,20]); @@ -972,6 +983,7 @@ function zscale(z=1, p=_NO_ARG, cp=0) = // color("red") anchor_arrow(s=20, flag=false); // color("#7777") cube([75,75,0.1], center=true); // } +// // Example: // n = [1,1,0]; // module obj() right(20) rotate([0,15,-15]) cube([40,30,20]); @@ -981,6 +993,7 @@ function zscale(z=1, p=_NO_ARG, cp=0) = // color("red") anchor_arrow(s=20, flag=false); // color("#7777") cube([75,75,0.1], center=true); // } +// // Example: // n = [1,1,1]; // module obj() right(20) rotate([0,15,-15]) cube([40,30,20]); @@ -990,6 +1003,7 @@ function zscale(z=1, p=_NO_ARG, cp=0) = // color("red") anchor_arrow(s=20, flag=false); // color("#7777") cube([75,75,0.1], center=true); // } +// // Example(2D): // n = [0,1]; // path = rot(30, p=square([50,30])); @@ -997,6 +1011,7 @@ function zscale(z=1, p=_NO_ARG, cp=0) = // color("red") stroke([[0,0],10*n],endcap2="arrow2"); // #stroke(path,closed=true); // stroke(mirror(n, p=path),closed=true); +// // Example(2D): // n = [1,1]; // path = rot(30, p=square([50,30])); @@ -1004,6 +1019,7 @@ function zscale(z=1, p=_NO_ARG, cp=0) = // color("red") stroke([[0,0],10*n],endcap2="arrow2"); // #stroke(path,closed=true); // stroke(mirror(n, p=path),closed=true); +// function mirror(v, p=_NO_ARG) = assert(is_vector(v)) assert(p==_NO_ARG || is_list(p),"Invalid pointlist") @@ -1013,6 +1029,11 @@ function mirror(v, p=_NO_ARG) = // Function&Module: xflip() // +// Synopsis: Reflects children across the YZ plane. +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Reflection, Mirroring +// See Also: mirror(), yflip(), zflip() +// // Usage: As Module // xflip([x=]) CHILDREN; // Usage: As Function @@ -1020,11 +1041,6 @@ function mirror(v, p=_NO_ARG) = // Usage: Get Affine Matrix // mat = xflip([x=]); // -// Synopsis: Reflects children across the YZ plane. -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Reflection, Mirroring -// See Also: mirror(), yflip(), zflip() -// // Description: // Mirrors/reflects across the origin [0,0,0], along the X axis. If `x` is given, reflects across [x,0,0] instead. // * Called as the built-in module, mirrors all children across the line/plane. @@ -1069,6 +1085,11 @@ function xflip(p=_NO_ARG, x=0) = // Function&Module: yflip() // +// Synopsis: Reflects children across the XZ plane. +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Reflection, Mirroring +// See Also: mirror(), xflip(), zflip() +// // Usage: As Module // yflip([y=]) CHILDREN; // Usage: As Function @@ -1076,11 +1097,6 @@ function xflip(p=_NO_ARG, x=0) = // Usage: Get Affine Matrix // mat = yflip([y=]); // -// Synopsis: Reflects children across the XZ plane. -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Reflection, Mirroring -// See Also: mirror(), xflip(), zflip() -// // Description: // Mirrors/reflects across the origin [0,0,0], along the Y axis. If `y` is given, reflects across [0,y,0] instead. // * Called as the built-in module, mirrors all children across the line/plane. @@ -1125,6 +1141,11 @@ function yflip(p=_NO_ARG, y=0) = // Function&Module: zflip() // +// Synopsis: Reflects children across the XY plane. +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Reflection, Mirroring +// See Also: mirror(), xflip(), yflip() +// // Usage: As Module // zflip([z=]) CHILDREN; // Usage: As Function @@ -1132,11 +1153,6 @@ function yflip(p=_NO_ARG, y=0) = // Usage: Get Affine Matrix // mat = zflip([z=]); // -// Synopsis: Reflects children across the XY plane. -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Reflection, Mirroring -// See Also: mirror(), xflip(), yflip() -// // Description: // Mirrors/reflects across the origin [0,0,0], along the Z axis. If `z` is given, reflects across [0,0,z] instead. // * Called as the built-in module, mirrors all children across the line/plane. @@ -1180,6 +1196,12 @@ function zflip(p=_NO_ARG, z=0) = ////////////////////////////////////////////////////////////////////// // Function&Module: frame_map() +// +// Synopsis: Rotates and possibly skews children from one frame of reference to another. +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Rotation +// See Also: rot(), xrot(), yrot(), zrot() +// // Usage: As module // frame_map(v1, v2, v3, [reverse=]) CHILDREN; // Usage: As function to remap points @@ -1189,10 +1211,7 @@ function zflip(p=_NO_ARG, z=0) = // map = frame_map(x=VECTOR1, y=VECTOR2, [reverse=]); // map = frame_map(x=VECTOR1, z=VECTOR2, [reverse=]); // map = frame_map(y=VECTOR1, z=VECTOR2, [reverse=]); -// Synopsis: Rotates and possibly skews children from one frame of reference to another. -// SynTags: Trans, Path, VNF, Mat -// Topics: Affine, Matrices, Transforms, Rotation -// See Also: rot(), xrot(), yrot(), 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 @@ -1204,19 +1223,24 @@ function zflip(p=_NO_ARG, z=0) = // 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. // p = If given, the point, path, patch, or VNF to operate on. Function use only. // 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]); // multmatrix(mat) frame_ref(); +// // Example: This map is not a rotation because x and y aren't orthogonal // frame_map(x=[1,0,0], y=[1,1,0]) cube(10); +// // Example: This sends [1,1,0] to [0,1,1] and [-1,1,0] to [0,-1,1]. (Original directions shown in light shade, final directions shown dark.) // mat = frame_map(x=[0,1,1], y=[0,-1,1]) * frame_map(x=[1,1,0], y=[-1,1,0],reverse=true); // color("purple",alpha=.2) stroke([[0,0,0],10*[1,1,0]]); @@ -1225,6 +1249,7 @@ function zflip(p=_NO_ARG, z=0) = // color("purple") stroke([[0,0,0],10*[1,1,0]]); // color("green") stroke([[0,0,0],10*[-1,1,0]]); // } +// function frame_map(x,y,z, p=_NO_ARG, reverse=false) = p != _NO_ARG ? apply(frame_map(x,y,z,reverse=reverse), p) @@ -1270,15 +1295,18 @@ module frame_map(x,y,z,p,reverse=false) // Function&Module: skew() +// +// Synopsis: Skews children along various axes. +// SynTags: Trans, Path, VNF, Mat +// Topics: Affine, Matrices, Transforms, Skewing +// See Also: move(), rot(), scale() +// // Usage: As Module // skew([sxy=]|[axy=], [sxz=]|[axz=], [syx=]|[ayx=], [syz=]|[ayz=], [szx=]|[azx=], [szy=]|[azy=]) CHILDREN; // Usage: As Function // pts = skew(p, [sxy=]|[axy=], [sxz=]|[axz=], [syx=]|[ayx=], [syz=]|[ayz=], [szx=]|[azx=], [szy=]|[azy=]); // Usage: Get Affine Matrix // mat = skew([sxy=]|[axy=], [sxz=]|[axz=], [syx=]|[ayx=], [syz=]|[ayz=], [szx=]|[azx=], [szy=]|[azy=]); -// Topics: Affine, Matrices, Transforms, Skewing -// Synopsis: Skews children along various axes. -// SynTags: Trans, Path, VNF, Mat // // Description: // Skews geometry by the given skew factors. @@ -1399,11 +1427,15 @@ function is_2d_transform(t) = // z-parameters are zero, except we allow t[2][ // Function: apply() +// +// Synopsis: Applies a transformation matrix to a point, list of points, array of points, or a VNF. +// SynTags: Path, VNF, Mat +// Topics: Affine, Matrices, Transforms +// See Also: move(), rot(), scale(), skew() +// // Usage: // pts = apply(transform, points); -// Topics: Affine, Matrices, Transforms -// Synopsis: Applies a transformation matrix to a point, list of points, array of points, or VNF. -// SynTags: Path, VNF, Mat +// // Description: // Applies the specified transformation matrix `transform` to a point, point list, bezier patch or VNF. // When `points` contains 2D or 3D points the transform matrix may be a 4x4 affine matrix or a 3x4 @@ -1418,27 +1450,32 @@ function is_2d_transform(t) = // z-parameters are zero, except we allow t[2][ // If the transform matrix is square then apply() checks the determinant and if it is negative, apply() reverses the face order so that // the transformed VNF has faces with the same winding direction as the original VNF. This adjustment applies // only to VNFs, not to beziers or point lists. +// // Arguments: // transform = The 2D (3x3 or 2x3) or 3D (4x4 or 3x4) transformation matrix to apply. // points = The point, point list, bezier patch, or VNF to apply the transformation to. +// // Example(3D): // path1 = path3d(circle(r=40)); // tmat = xrot(45); // path2 = apply(tmat, path1); // #stroke(path1,closed=true); // stroke(path2,closed=true); +// // Example(2D): // path1 = circle(r=40); // tmat = translate([10,5]); // path2 = apply(tmat, path1); // #stroke(path1,closed=true); // stroke(path2,closed=true); +// // Example(2D): // path1 = circle(r=40); // tmat = rot(30) * back(15) * scale([1.5,0.5,1]); // path2 = apply(tmat, path1); // #stroke(path1,closed=true); // stroke(path2,closed=true); +// function apply(transform,points) = points==[] ? [] : is_vector(points) ? _apply(transform, [points])[0] // point diff --git a/tripod_mounts.scad b/tripod_mounts.scad index 303d932..b078608 100644 --- a/tripod_mounts.scad +++ b/tripod_mounts.scad @@ -11,7 +11,9 @@ // Module: manfrotto_rc2_plate() // Synopsis: Creates a Manfrotto RC2 tripod quick release mount plate. +// SynTags: Geom // Topics: Parts +// See Also: threaded_rod() // Usage: // manfrotto_rc2_plate([chamfer],[anchor],[orient],[spin]) [ATTACHMENTS]; // Description: diff --git a/turtle3d.scad b/turtle3d.scad index 571468c..a116ff6 100644 --- a/turtle3d.scad +++ b/turtle3d.scad @@ -21,10 +21,13 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]]; // Function: turtle3d() // Synopsis: Extends [turtle graphics](https://en.wikipedia.org/wiki/Turtle_graphics) to 3d. Generates a 3D path or returns a list of transforms. +// SynTags: MatList, Path // Topics: Shapes (3D), Path Generators (3D), Mini-Language // See Also: turtle() // Usage: -// turtle3d(commands, [state], [transforms], [full_state], [repeat]) +// path = turtle3d(commands, [state=], [repeat=]); +// mats = turtle3d(commands, transforms=true, [state=], [repeat=]); +// state = turtle3d(commands, full_state=true, [state=], [repeat=]); // Description: // Like the classic two dimensional turtle, the 3d turtle flies through space following a sequence // of turtle graphics commands to generate either a sequence of transformations (suitable for input diff --git a/utility.scad b/utility.scad index d1b7082..254c5e3 100644 --- a/utility.scad +++ b/utility.scad @@ -73,10 +73,11 @@ function is_type(x,types) = // Function: is_def() -// Usage: -// bool = is_def(x); +// Synopsis: Returns true if `x` is not `undef`. // Topics: Type Checking // See Also: typeof(), is_type(), is_str() +// Usage: +// bool = is_def(x); // Description: // Returns true if `x` is not `undef`. False if `x==undef`. // Arguments: @@ -108,8 +109,8 @@ function is_str(x) = is_string(x); // Function: is_int() -// Synopsis: Returns true if the argument is an integer. // Alias: is_integer() +// Synopsis: Returns true if the argument is an integer. // Topics: Type Checking // See Also: typeof(), is_type(), is_str(), is_def() // Usage: @@ -197,10 +198,10 @@ function is_finite(x) = is_num(x) && !is_nan(0*x); // Function: is_range() // Synopsis: Returns true if the argument is a range. -// Usage: -// bool = is_range(x); // Topics: Type Checking // See Also: typeof(), is_type(), is_str(), is_def(), is_int() +// Usage: +// bool = is_range(x); // Description: // Returns true if its argument is a range // Arguments: @@ -768,6 +769,7 @@ function scalar_vec3(v, dflt) = // Function: segs() // Synopsis: Returns the number of sides for a circle given `$fn`, `$fa`, and `$fs`. // Topics: Geometry +// See Also: circle(), cyl() // Usage: // sides = segs(r); // Description: @@ -831,10 +833,10 @@ module req_children(count) { // Function: no_function() // Synopsis: Assert that the argument exists only as a module and not as a function. -// Usage: -// dummy = no_function(name) // Topics: Error Checking // See Also: no_children(), no_module() +// Usage: +// dummy = no_function(name) // Description: // Asserts that the function, "name", only exists as a module. // Example: @@ -860,6 +862,8 @@ module no_module() { // Module: deprecate() // Synopsis: Display a console note that a module is deprecated and suggest a replacement. +// Topics: Error Checking +// See Also: no_function(), no_module() // Usage: // deprecate(new_name); // Description: diff --git a/vectors.scad b/vectors.scad index d0f2afa..bb977a9 100644 --- a/vectors.scad +++ b/vectors.scad @@ -339,7 +339,7 @@ function vector_bisect(v1,v2) = let( axis = vector_axis(v1,v2), ang = vector_angle(v1,v2), - v3 = rot(ang/2, v=axis, p=v1) + v3 = unit(rot(ang/2, v=axis, p=v1)) ) v3; diff --git a/vnf.scad b/vnf.scad index 7457e9e..121f5d8 100644 --- a/vnf.scad +++ b/vnf.scad @@ -29,6 +29,7 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces. // Function: vnf_vertex_array() // Synopsis: Returns a VNF structure from a rectangular vertex list. +// SynTags: VNF // Topics: VNF Generators, Lists // See Also: vnf_tri_array(), vnf_join(), vnf_from_polygons(), vnf_from_region() // Usage: @@ -232,6 +233,7 @@ function vnf_vertex_array( // Function: vnf_tri_array() // Synopsis: Returns a VNF from an array of points. +// SynTags: VNF // Topics: VNF Generators, Lists // See Also: vnf_vertex_array(), vnf_join(), vnf_from_polygons(), vnf_from_region() // Usage: @@ -327,6 +329,7 @@ function vnf_tri_array(points, row_wrap=false, reverse=false) = // Function: vnf_join() // Synopsis: Returns a single VNF structure from a list of VNF structures. +// SynTags: VNF // Topics: VNF Generators, Lists // See Also: vnf_tri_array(), vnf_vertex_array(), vnf_from_polygons(), vnf_from_region() // Usage: @@ -413,6 +416,7 @@ function vnf_join(vnfs) = // Function: vnf_from_polygons() // Synopsis: Returns a VNF from a list of 3D polygons. +// SynTags: VNF // Topics: VNF Generators, Lists // See Also: vnf_tri_array(), vnf_join(), vnf_vertex_array(), vnf_from_region() // Usage: @@ -579,8 +583,9 @@ function _bridge(pt, outer,eps) = // Function: vnf_from_region() -// Topics: VNF Generators, Lists // Synopsis: Returns a 3D VNF given a 2D region. +// SynTags: VNF +// Topics: VNF Generators, Lists // See Also: vnf_vertex_array(), vnf_tri_array(), vnf_join(), vnf_from_polygons() // Usage: // vnf = vnf_from_region(region, [transform], [reverse]); @@ -672,6 +677,7 @@ function vnf_faces(vnf) = vnf[1]; // Function: vnf_reverse_faces() // Synopsis: Reverses the faces of a VNF. +// SynTags: VNF // Topics: VNF Manipulation // See Also: vnf_quantize(), vnf_merge_points(), vnf_drop_unused_points(), vnf_triangulate(), vnf_slice() // Usage: @@ -684,6 +690,7 @@ function vnf_reverse_faces(vnf) = // Function: vnf_quantize() // Synopsis: Quantizes the vertex coordinates of a VNF. +// SynTags: VNF // Topics: VNF Manipulation // See Also: vnf_reverse_faces(), vnf_merge_points(), vnf_drop_unused_points(), vnf_triangulate(), vnf_slice() // Usage: @@ -699,7 +706,8 @@ function vnf_quantize(vnf,q=pow(2,-12)) = // Function: vnf_merge_points() -// Synopsis: COnsolidates duplicate vertices of a VNF. +// Synopsis: Consolidates duplicate vertices of a VNF. +// SynTags: VNF // Topics: VNF Manipulation // See Also: vnf_reverse_faces(), vnf_quantize(), vnf_drop_unused_points(), vnf_triangulate(), vnf_slice() // Usage: @@ -735,6 +743,7 @@ function vnf_merge_points(vnf,eps=EPSILON) = // Function: vnf_drop_unused_points() // Synopsis: Removes unreferenced vertices from a VNF. +// SynTags: VNF // Topics: VNF Manipulation // See Also: vnf_reverse_faces(), vnf_quantize(), vnf_merge_points(), vnf_triangulate(), vnf_slice() // Usage: @@ -766,6 +775,7 @@ function _link_indicator(l,imin,imax) = // Function: vnf_triangulate() // Synopsis: Triangulates the faces of a VNF. +// SynTags: VNF // Topics: VNF Manipulation // See Also: vnf_reverse_faces(), vnf_quantize(), vnf_merge_points(), vnf_drop_unused_points(), vnf_slice() // Usage: @@ -809,6 +819,7 @@ function _vnf_sort_vertices(vnf, idx=[2,1,0]) = // Function: vnf_slice() // Synopsis: Slice the faces of a VNF along an axis. +// SynTags: VNF // Topics: VNF Manipulation // See Also: vnf_reverse_faces(), vnf_quantize(), vnf_merge_points(), vnf_drop_unused_points(), vnf_triangulate() // Usage: @@ -937,6 +948,7 @@ function _slice_3dpolygons(polys, dir, cuts) = // Module: vnf_polyhedron() // Synopsis: Returns a polyhedron from a VNF or list of VNFs. +// SynTags: Geom // Topics: VNF Manipulation // See Also: vnf_wireframe() // Usage: @@ -970,6 +982,7 @@ module vnf_polyhedron(vnf, convexity=2, extent=true, cp="centroid", anchor="orig // Module: vnf_wireframe() // Synopsis: Creates a wireframe model from a VNF. +// SynTags: VNF // Topics: VNF Manipulation // See Also: vnf_polyhedron() // Usage: @@ -1082,9 +1095,9 @@ function _vnf_centroid(vnf,eps=EPSILON) = // Function: vnf_halfspace() // Synopsis: Returns the intersection of the vnf with a half space. +// SynTags: VNF // Topics: VNF Manipulation // See Also: vnf_volume(), vnf_area(), vnf_bend() - // Usage: // newvnf = vnf_halfspace(plane, vnf, [closed], [boundary]); // Description: @@ -1286,6 +1299,7 @@ function _triangulate_planar_convex_polygons(polys) = // Function: vnf_bend() // Synopsis: Bends a VNF around an axis. +// SynTags: VNF // Topics: VNF Manipulation // See Also: vnf_volume(), vnf_area(), vnf_halfspace() // Usage: @@ -1495,6 +1509,7 @@ module _show_faces(vertices, faces, size=1, filter) { // Module: debug_vnf() // Synopsis: A replacement for `vnf_polyhedron()` to help with debugging. +// SynTags: VNF // Topics: VNF Manipulation, Debugging // See Also: vnf_validate() // Usage: @@ -1538,6 +1553,7 @@ module debug_vnf(vnf, faces=true, vertices=true, opacity=0.5, size=1, convexity= // Module: vnf_validate() // Synopsis: Echos non-manifold VNF errors to the console. +// SynTags: VNF // Topics: VNF Manipulation, Debugging // See Also: debug_vnf() // diff --git a/walls.scad b/walls.scad index 725c837..a188a9c 100644 --- a/walls.scad +++ b/walls.scad @@ -14,6 +14,7 @@ // Module: sparse_wall() // Synopsis: Makes an open cross-braced rectangular wall. +// SynTags: Geom // Topics: FDM Optimized, Walls // See Also: sparse_wall(), corrugated_wall(), thinning_wall(), thinning_triangle(), narrowing_strut() // @@ -88,6 +89,7 @@ module sparse_wall(h=50, l=100, thick=4, maxang=30, strut=5, max_bridge=20, anch // Module: corrugated_wall() // Synopsis: Makes a corrugated rectangular wall. +// SynTags: Geom // Topics: FDM Optimized, Walls // See Also: sparse_wall(), corrugated_wall(), thinning_wall(), thinning_triangle(), narrowing_strut() // @@ -147,6 +149,7 @@ module corrugated_wall(h=50, l=100, thick=5, strut=5, wall=2, anchor=CENTER, spi // Module: thinning_wall() // Synopsis: Makes a rectangular wall with a thin middle. +// SynTags: Geom // Topics: FDM Optimized, Walls // See Also: sparse_wall(), corrugated_wall(), thinning_wall(), thinning_triangle(), narrowing_strut() // @@ -331,6 +334,7 @@ module thinning_wall(h=50, l=100, thick=5, ang=30, braces=false, strut, wall, an // Module: thinning_triangle() // Synopsis: Makes a triangular wall with a thin middle. +// SynTags: Geom // Topics: FDM Optimized, Walls // See Also: sparse_wall(), corrugated_wall(), thinning_wall(), thinning_triangle(), narrowing_strut() // @@ -399,6 +403,7 @@ module thinning_triangle(h=50, l=100, thick=5, ang=30, strut=5, wall=3, diagonly // Module: narrowing_strut() // Synopsis: Makes a strut like an extruded baseball home plate. +// SynTags: Geom // Topics: FDM Optimized // See Also: sparse_wall(), corrugated_wall(), thinning_wall(), thinning_triangle(), narrowing_strut() // diff --git a/wiring.scad b/wiring.scad index 1c621d3..9298924 100644 --- a/wiring.scad +++ b/wiring.scad @@ -55,6 +55,7 @@ function _hex_offsets(n, d, lev=0, arr=[]) = // Module: wire_bundle() // Synopsis: Creates a wire bundle for a given number of wires. +// SynTags: Geom // Topics: Wiring // See Also: path_sweep(), path_sweep2d() // Usage: