diff --git a/attachments.scad b/attachments.scad index 86db9449..5eff067a 100644 --- a/attachments.scad +++ b/attachments.scad @@ -3,7 +3,7 @@ // 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 +// as differencing them from the parent, or changing their color. Attachment works only 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 @@ -54,13 +54,13 @@ _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 +// - An anchor is a place on an object that 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. +// - Orientation is rotating an object so that its top is pointed toward a given vector. // . -// An object will first be translated to its anchor position, then spun, then oriented. +// An object is 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-Attachment-Relative-Positioning). // . // For describing directions, faces, edges, and corners the library provides a set of shortcuts @@ -69,12 +69,12 @@ _ANCHOR_TYPES = ["intersect","hull"]; // 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 +// when creating an object translates 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 +// When given as a vector, it points, in a general way, toward 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 @@ -88,21 +88,21 @@ _ANCHOR_TYPES = ["intersect","hull"]; // . // 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 +// The direction of a face anchor is perpendicular to the face, pointing outward. The direction of an edge +// anchor is the average of the anchor directions of the two faces the edge is between. The direction +// of a corner anchor is 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 +// When the object is cylindrical, conical, or spherical in nature, the anchors are 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. +// surface of such an object, and the anchor direction is 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. +// anchors that point diagonally toward one of the flat faces 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. +// is 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. // . @@ -116,13 +116,13 @@ _ANCHOR_TYPES = ["intersect","hull"]; // these modules provide their own anchoring for their children. // Subsection: Spin // Spin is specified with the `spin` argument in most shape modules. Specifying a spin -// angle when creating an object will rotate the object counter-clockwise around the Z axis by the given +// angle when creating an object rotates the object counter-clockwise around the Z axis by the given // number of degrees. Spin is always applied after anchoring, and before orientation. // Since spin is applied **after** anchoring it does not, in general, rotate around the object's center, // so it is not always what you might think of intuitively as spinning the shape. // 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 +// when creating an object rotates the object such that the top of the object points // 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 (e.g. `LEFT+BACK`). The orient parameter @@ -206,9 +206,9 @@ _ANCHOR_TYPES = ["intersect","hull"]; // 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. +// - A vector pointing toward an edge, indicating that single edge. +// - A vector pointing toward a face, indicating all edges surrounding that face. +// - A vector pointing toward 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. @@ -295,7 +295,7 @@ _ANCHOR_TYPES = ["intersect","hull"]; // _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. +// 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"]); @@ -318,9 +318,9 @@ _ANCHOR_TYPES = ["intersect","hull"]; // 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. +// - A vector pointing toward a corner, indicating that corner. +// - A vector pointing toward an edge indicating both corners at the ends of that edge. +// - A vector pointing toward 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 @@ -392,7 +392,7 @@ _ANCHOR_TYPES = ["intersect","hull"]; // _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: +// 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 need to give only 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]"]); @@ -464,7 +464,7 @@ _ANCHOR_TYPES = ["intersect","hull"]; // 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. +// a module that does not provide this option, you can use the generic {{region()}} module, which lets 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. @@ -538,7 +538,7 @@ module position(at,from) // 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-Attachment-Relative-Positioning). // Arguments: -// anchor = The anchor on the parent which you want to match the orientation of. +// anchor = The anchor on the parent that you want to match the orientation of. // spin = The spin to add to the children. (Overrides anchor spin.) // Side Effects: // `$attach_to` is set to `undef`. @@ -588,12 +588,12 @@ module orient(anchor, spin) { // PARENT() align(anchor, [align], [inside=], [inset=], [shiftout=], [overlap=]) CHILDREN; // Description: // Place a child on the face identified by `anchor`. If align is not given or is CENTER -// then the child will be centered on top of the specified face, outside the parent object. The align parameter is a -// direction defining an edge or corner to align to. The child will be aligned to that edge or corner by +// then the child is centered on top of the specified face, outside the parent object. The align parameter is a +// direction defining an edge or corner to align to. The child is aligned to that edge or corner by // choosing an appropriate anchor on the child. // Like {{position()}} this module never rotates the child. If you give `anchor=RIGHT` then the child -// will be given the LEFT anchor and placed adjacent to the parent. You can use `orient=` or `spin=` -// with the child and the alignment will adjust to select the correct child anchor. Note that if +// is given the LEFT anchor and placed adjacent to the parent. You can use `orient=` or `spin=` +// with the child and the alignment adjusts to select the correct child anchor. Note that if // you spin the child by an amount not a multiple of 90 degrees then an edge of the child will be // placed against the parent. This module makes it easy to place children aligned flush with the edges // of the parent, even after orienting them or spinning them. In contrast {{position()}} can @@ -604,13 +604,13 @@ module orient(anchor, spin) { // . // Several options can adjust how the child is positioned. You can specify `inset=` to inset the // aligned object from its alignment location. If you set `inside=true` then the -// child will appear inside the parent instead of on its surface so that you can use {{diff()}} to subract it. +// child appears inside the parent instead of on its surface so that you can use {{diff()}} to subract it. // In this case the child recieved a default "remove" tag. The `shiftout=` option works with `inside=true` to // shift the child out by the specified distance so that the child doesn't exactly align with the parent. // . -// Note that in the description above the anchor was said to define a "face". You can also use this module -// with an edge anchor, in which case a corner of the child will be placed in contact with the specified -// edge and the align direction will shift the child to either end of the edge. You can even give a +// In the description above, the anchor was said to define a "face". You can also use this module +// with an edge anchor, in which case a corner of the child is placed in contact with the specified +// edge and the align direction shifts the child to either end of the edge. You can even give a // corner as the anchor point, but in that case the only allowed alignment is CENTER. // . // If you give a list of anchors and/or a list of align directions then all combinations are generated. @@ -623,7 +623,7 @@ module orient(anchor, spin) { // inside = if true, place object inside the parent instead of outside. Default: false // inset = shift the child away from the alignment edge/corner by this amount. Default: 0 // shiftout = Shift an inside object outward so that it overlaps all the aligned faces. Default: 0 -// overlap = Amount to sink the child into the parent. Defaults to `$overlap` which is zero by default. +// overlap = Amount to sink the child into the parent. Defaults to `$overlap`, which is zero by default. // Side Effects: // `$anchor` set to the anchor value used for the child. // `$align` set to the align value used for the child. @@ -773,37 +773,37 @@ function _make_anchor_legal(anchor,geom) = // alignment. You provide an anchor on the parent (`parent`) and an anchor on the child (`child`). // This module connects the `child` anchor on the child to the `parent` anchor on the parent. // Imagine pointing the parent and child anchor arrows at each other and pushing the objects -// together until they meet at the anchor point. The most basic case -// is `attach(TOP,BOT)` which puts the bottom of the child onto the top of the parent. If you +// together until they meet at the anchor point. The most basic case +// is `attach(TOP,BOT)`, which puts the bottom of the child onto the top of the parent. If you // do `attach(RIGHT,BOT)` this puts the bottom of the child onto the right anchor of the parent. -// When an object is attached to the top or bottom its BACK direction will remaing pointing BACK. -// When an object is attached to one of the other anchors its FRONT will be pointed DOWN and its +// When an object is attached to the top or bottom, its BACK direction remains pointing BACK. +// When an object is attached to one of the other anchors its FRONT is pointed DOWN and its // BACK pointed UP. You can change this using the `spin=` argument to attach(). Note that this spin // rotates around the attachment vector and is not the same as the spin argument to the child, which // will usually rotate around some other direction that may be hard to predict. For 2D objects you cannot // give spin because it is not possible to spin around the attachment vector; spinning the object around the Z axis // would change the child orientation so that the anchors are no longer parallel. Furthermore, any spin -// parameter you give to the child will be ignored so that the attachment condition of parallel anchors is preserved. +// parameter you give to the child is ignored so that the attachment condition of parallel anchors is preserved. // . // As with {{align()}} you can use the `align=` parameter to align the child to an edge or corner of the -// face where that child is attached. For example `attach(TOP,BOT,align=RIGHT)` would stand the child -// up on the top while aligning it with the right edge of the top face, and `attach(RIGHT,BOT,align=TOP)` which +// face where that child is attached. For example, `attach(TOP,BOT,align=RIGHT)` would stand the child +// up on the top while aligning it with the right edge of the top face, and `attach(RIGHT,BOT,align=TOP)`, which // stand the object on the right face while aligning with the top edge. If you apply spin using the -// argument to `attach()` then it will be taken into account for the alignment. If you apply spin with -// a parameter to the child it will NOT be taken into account. The special spin value "align" will -// spin the child so that the child's BACK direction is pointed towards the aligned edge on the parent. +// argument to `attach()`, then it is taken into account for the alignment. However, if you apply spin with +// a parameter to the child, it is **not** taken into account. The special spin value "align" +// spins the child so that the child's BACK direction is pointed toward the aligned edge on the parent. // Note that spin is not permitted for // 2D objects because it would change the child orientation so that the anchors are no longer parallel. // When you use `align=` you can also adjust the position using `inset=`, which shifts the child // away from the edge or corner it is aligned to. // . -// Note that the concept of alignment doesn't always make sense for objects without corners, such as spheres or cylinders. -// In same cases the alignments using such children will be odd because the alignment computation is trying to +// The concept of alignment doesn't always make sense for objects without corners, such as spheres or cylinders. +// In same cases, the alignments using such children may look odd because the alignment computation tries to // place a non-existent corner somewhere. Because attach() doesn't have in formation about the child when -// it runs it cannot handle curved shapes differently from cubes, so this behavior cannot be changed. +// it runs, it cannot handle curved shapes differently from cubes, so this behavior cannot be changed. // . -// If you give `inside=true` then the anchor arrows are lined up so they are pointing the same direction and -// the child object will be located inside the parent. In this case a default "remove" tag is applied to +// If you give `inside=true` then the anchor arrows are lined up so they point the same direction and +// the child object is located inside the parent. In this case a default "remove" tag is applied to // the children. // . // Because the attachment process forces an orientation and anchor point for the child, it overrides @@ -811,22 +811,22 @@ function _make_anchor_legal(anchor,geom) = // ignored** with the **double argument** version of `attach()`. As noted above, you can give `spin=` to the // child but using the `spin=` parameter to `attach()` is more likely to be useful. // . -// You can overlap attached children into the parent by giving the `$overlap` value +// You can overlap attached children into the parent by giving the `$overlap` value, // which is 0 by default, or by the `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. When you give `inside=true`, a positive overlap // value shifts the child object outward. // . -// If you specify an `inset=` value then the child is shifted away from any edges it is aligned to, towards the middle +// If you specify an `inset=` value then the child is shifted away from any edges it is aligned to, toward the middle // of the parent. The `shiftout=` parameter is intended to simplify differences with aligned objects -// placed inside the parent. It will shift the child outward along every direction where it is aligned with +// placed inside the parent. It shifts the child outward along every direction where it is aligned with // the parent. For an inside child this is equivalent to giving a positive overlap and negative inset value. // For a child with `inside=false` it is equivalent to a negative overlap and negative inset. // . // The single parameter version of `attach()` is rarely needed; to use it, you give only the `parent` anchor. The `align` direction // is not permitted. In this case the child is placed at the specified parent anchor point -// and rotated to the anchor direction. For example, `attach(TOP) cuboid(2);` will place a small -// cube **with its center** located at the TOP anchor of the parent, so just half the cube will project +// and rotated to the anchor direction. For example, `attach(TOP) cuboid(2);` places a small +// cube **with its center** located at the TOP anchor of the parent, so just half the cube projects // from the parent. If you want the cube sitting on the parent you need to anchor the cube to its bottom: // `attach(TOP) cuboid(2,anchor=BOT);`. // . @@ -844,7 +844,7 @@ function _make_anchor_legal(anchor,geom) = // 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. // inside = If `child` is given you can set `inside=true` to attach the child to the inside of the parent for diff() operations. Default: false // shiftout = Shift an inside object outward so that it overlaps all the aligned faces. Default: 0 -// spin = Amount to rotate the parent around the axis of the parent anchor. Can set to "align" to align the child's BACK with the parent aligned edge. (Only permitted in 3D.) +// spin = Amount to rotate the parent around the axis of the parent anchor. Can set to "align" to align the child's BACK with the parent aligned edge. (Permitted only in 3D.) // Side Effects: // `$anchor` set to the parent anchor value used for the child. // `$align` set to the align value used for the child. @@ -882,7 +882,7 @@ function _make_anchor_legal(anchor,geom) = // Example: Using the `overlap` option can help: // spheroid(d=20) // attach([1,1.5,1], BOTTOM, overlap=1.5) cyl(l=11.5, d1=10, d2=5); -// Example: Alignment works on the sides of cylinders but you can only align with either the top or bototm face: +// Example: Alignment works on the sides of cylinders but you can align only with either the top or bototm face: // cyl(h=30,d=10) // attach([LEFT,[1,1.3]], BOT,align=TOP) cuboid(6); // Example: Attaching to edges. The light blue and orange objects are attached to edges. The purple object is attached to an edge and aligned. @@ -933,7 +933,7 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0, dummy3= assert(num_defined([to,child])<2, "\nCannot combine deprecated 'to' argument with 'child' parameter.") assert(num_defined([from,parent])<2, "\nCannot combine deprecated 'from' argument with 'parent' parameter.") - assert(spin!="align" || is_def(align), "\nCan only set spin to \"align\" when the 'align' parameter is given.") + assert(spin!="align" || is_def(align), "\nCan set spin to \"align\" only when the 'align' parameter is given.") assert(is_finite(spin) || spin=="align", "\nSpin must be a number (unless align is given).") assert((is_undef(overlap) || is_finite(overlap)) && (is_def(overlap) || is_undef($overlap) || is_finite($overlap)), str("Provided ",is_def(overlap)?"":"$","overlap is not valid.")); @@ -1085,7 +1085,7 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0, // but some objects also define attachable parts. This module selects // an attachable part using a name defined by the parent object. Any operations // that use the parent geometry such as {{attach()}}, {{align()}}, {{position()}} or {{parent()}} -// will reference the geometry for the specified part. This allows you to access the inner wall +// references the geometry for the specified part. This allows you to access the inner wall // of tubes, for example. Note that you cannot call `attach_part()` as a child of another `attach_part()`. // Arguments: // name = name of part to use for subsequent attachments. @@ -1196,7 +1196,7 @@ module tag_this(tag) // 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 means that tagging is ignored in children's children. // 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()` @@ -1211,10 +1211,10 @@ module tag_this(tag) // - `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 +// 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 shows the module, and when diff() checks to see if it should subtract the module, +// it subtracts the module. The result appears as a hole, possibly with zero-thickness edges or faces. 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 tagged attachments, see the [Attachments Tutorial](Tutorial-Attachment-Tags). @@ -1260,8 +1260,8 @@ module force_tag(tag) // 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. +// in force from a parent. If you use {{tag()}}, it overrides any previously +// specified tag from a parent, which can be confusing to a user trying to change the tag on a module. // The `do_tag` parameter allows you to apply a default tag conditionally without having to repeat the children. // . // For a step-by-step explanation of tagged attachments, see the [Attachments Tutorial](Tutorial-Attachment-Tags). @@ -1300,10 +1300,10 @@ module default_tag(tag,do_tag=true) // 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. -// Usually you will want to use tag_scope in the first child of {{attachable()}} to isolate the geometry -// of your attachable object. If you put it **outside** the {{attachable()}} call, then it will -// set a scope that also applies to the children passed to your attachable object, which is probably not what you want. +// Note that if you directly set the `$tag` variable, then tag scoping will not work correctly. +// Usually you would to use tag_scope in the first child of {{attachable()}} to isolate the geometry +// of your attachable object. If you put it **outside** the {{attachable()}} call, then it +// sets a scope that also applies to the children passed to your attachable object, which is probably not what you want. // Side Effects: // `$tag_prefix` is set to the value of `scope=` if given, otherwise is set to a random string. // Example(3D,NoAxes): In this example, tag_scope() is required for things to work correctly. @@ -1356,7 +1356,7 @@ module default_tag(tag,do_tag=true) // myring() // color_this("green") cyl(d=20, l=61) // tag("remove") color_this("yellow") cyl(d=10, l=65); -// Example: In this example the myring module uses "remove" tags which will conflict with use of the same tags elsewhere in a diff() operation, even without a parent-child relationship. Without the tag_scope() the result is a solid cylinder. +// Example: In this example the myring module uses "remove" tags, which conflict with use of the same tags elsewhere in a diff() operation, even without a parent-child relationship. Without the `tag_scope()` the result is a solid cylinder. // module myring(r,h,w=1,anchor,spin,orient) // { // attachable(anchor,spin,orient,r=r,h=h){ @@ -1368,7 +1368,7 @@ module default_tag(tag,do_tag=true) // } // } // // Calling the module using "remove" tags -// // will conflict with internal tag use in +// // conflicst with internal tag use in // // the myring module. // $fn=32; // diff(){ @@ -1947,7 +1947,7 @@ module hide_this() // Usage: // show_only(tags) CHILDREN; // Description: -// Show only the children with the listed tags, which you supply 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. +// Show only the children with the listed tags, which you supply as a space separated string. Only unhidden objects are shown, so if an object is hidden either before or after the `show_only()` call then it remains hidden. This overrides any previous `show_only()` calls. Unlike `hide()`, calls to `show_only()` are not cumulative. // For a step-by-step explanation of tagged attachments, see the [Attachments Tutorial](Tutorial-Attachment-Tags). // Side Effects: // Sets `$tags_shown` to the tag you specify. @@ -1974,7 +1974,7 @@ module show_only(tags) // Usage; // show_all() CHILDREN; // Description: -// Shows all children. Clears the list of hidden tags and shown tags so that all child objects will be +// Shows all children. Clears the list of hidden tags and shown tags so that all child objects are // fully displayed. // Side Effects: // Sets `$tags_shown="ALL"` @@ -1989,13 +1989,13 @@ module show_all() // Module: show_int() -// Synopsis: Shows children with the listed tags which were already shown in the parent context. +// Synopsis: Shows children with the listed tags that 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. +// Show only the children with the listed tags that 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 @@ -2025,7 +2025,7 @@ module show_int(tags) // 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. +// `face_mask()` sets the tag for children to "remove" so that it works 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 masking attachments, see the [Attachments Tutorial](Tutorial-Attachment-Edge-Profiling). // Arguments: @@ -2068,7 +2068,7 @@ module face_mask(faces=[LEFT,RIGHT,FRONT,BACK,BOT,TOP]) { // Takes a 3D mask shape, and attaches it to the given edges of a cuboid parent, 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. +// then `edge_mask` sets the tag for children to "remove" so that it works 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 masking attachments, see the [Attachments Tutorial](Tutorial-Attachment-Edge-Profiling). // Figure: A Typical Edge Rounding Mask @@ -2131,7 +2131,7 @@ module edge_mask(edges=EDGES_ALL, except=[]) { // 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. +// then `corner_mask` sets the tag for children to "remove" so that it works 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 masking attachments, see the [Attachments Tutorial](Tutorial-Attachment-Edge-Profiling). // Arguments: @@ -2179,7 +2179,7 @@ module corner_mask(corners=CORNERS_ALL, except=[]) { // 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. +// then `face_profile` sets the tag for children to "remove" so that it works 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 masking attachments, see the [Attachments Tutorial](Tutorial-Attachment-Edge-Profiling). // Arguments: @@ -2220,7 +2220,7 @@ module face_profile(faces=[], r, d, excess=0.01, convexity=10) { // 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. +// then `edge_profile` sets the tag for children to "remove" so that it works 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 masking attachments, see the [Attachments Tutorial](Tutorial-Attachment-Edge-Profiling). // Arguments: @@ -2315,14 +2315,14 @@ module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) { // 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 +// If no tag is set then `edge_profile_asym()` sets the tag for children to "remove" so that it works // 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 masking attachments, see the [Attachments Tutorial](Tutorial-Attachment-Edge-Profiling). // The asymmetric profiles are joined consistently at the corners. This is impossible if all three edges at a corner use the profile, hence // this situation is not permitted. The profile orientation can be inverted using the `flip=true` parameter. // . // The standard profiles are located in the first quadrant and have positive X values. If you provide a profile located in the second quadrant, -// where the X values are negative, then it will produce a fillet. You can flip any of the standard profiles using {{xflip()}}. +// where the X values are negative, then it produces a fillet. You can flip any of the standard profiles using {{xflip()}}. // Fillets are always asymmetric because at a given edge, they can blend in two different directions, so even for symmetric profiles, // the asymmetric logic is required. You can set the `corner_type` parameter to select rounded, chamfered or sharp corners. // However, when the corners are inside (concave) corners, you must provide the size of the profile ([width,height]), because the @@ -2335,7 +2335,7 @@ module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) { // 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"` -// size = If given the width and height of the 2D profile, will enable rounding and chamfering of internal corners when given a negative profile. +// size = If given the width and height of the 2D profile, enable rounding and chamfering of internal corners when given a negative profile. // 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. @@ -2666,7 +2666,7 @@ module edge_profile_asym( // 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. +// sets the tag for children to "remove" so that it works 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 masking attachments, see the [Attachments Tutorial](Tutorial-Attachment-Edge-Profiling). // Arguments: @@ -2778,11 +2778,11 @@ module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) { // 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`. +// * Rotates so the top of the part points toward 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, +// which includes position, direction, and spin. With that info, // the following transformations are performed: // * Translates this part so its anchor position matches the parent's anchor position. // * Rotates this part so its anchor direction vector exactly opposes the parent's anchor direction vector. @@ -2792,10 +2792,10 @@ module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) { // this module is also responsible for handing coloring of objects with {{recolor()}} and {{color_this()}}, and // it is responsible for processing tags and determining whether the object should // display or not in the current context. The determination based on the tags of whether to display the attachable object -// often occurs in this module, which means that an object which does not display (e.g. a "remove" tagged object +// often occurs in this module, meaning that an object that does not display (e.g. a "remove" tagged object // inside {{diff()}}) cannot have internal {{tag()}} calls that change its tags and cause submodel // portions to display: the entire object simply does not run. If you want the use the attachable object's internal tags outside -// of the attachable object you can set `expose_tags=true` which delays the determination to display objects to the children. +// of the attachable object you can set `expose_tags=true` to delay the determination to display objects to the children. // For this to work correctly, all of the children must be attachables. An example situation where you should set // `expose_tags=true` is when you want to have negative space in an attachable object that gets removed from the parent via // a "remove" tagged component of your attachable. @@ -2816,7 +2816,7 @@ module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) { // 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` +// orient = Vector to rotate top toward, 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. @@ -2837,10 +2837,10 @@ module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) { // 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 for 3d returns a triple `[position, direction, spin]` or for 2d 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, spin]]` entries. If the direction/position/spin that is returned is undef then the default will be used. This option applies only to the "trapezoid" and "prismoid" geometry types. +// override = Function that takes an anchor and for 3d returns a triple `[position, direction, spin]` or for 2d 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, spin]]` entries. If the direction/position/spin that is returned is undef then the default is used. This option applies only to the "trapezoid" and "prismoid" geometry types. // geom = If given, uses the pre-defined (via {{attach_geom()}} geometry. -// expose_tags = If true then delay the decision to display or not display this object to the children, which it possible for tags to respond to operations like {{diff()}} used outside the attachble object. Only works correctly if everything in the attachable is also attachable. Default: false -// keep_color = If true then delay application of color to the children, which means that externally applied color is overridden by color specified within the attachable. Only works properly if everything in the attachable is also attacahble. Default: false +// expose_tags = If true then delay the decision to display or not display this object to the children, which it possible for tags to respond to operations like {{diff()}} used outside the attachble object. This works correctly only if everything in the attachable is also attachable. Default: false +// keep_color = If true then delay application of color to the children, which means that externally applied color is overridden by color specified within the attachable. This works properly only if everything in the attachable is also attacahble. Default: false // // Side Effects: // `$parent_anchor` is set to the parent object's `anchor` value. @@ -3071,7 +3071,7 @@ module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) { // recolor("pink") thing() // attach(RIGHT,BOT) // recolor("blue") cyl(d=5,h=5); -// Example(3D,NoScale): This example defines named anchors and then uses them internally in the object definition to make a cutout in the socket() object and to attach the plug on the plug() object. These objects can be connected using the "socket" and "plug" named anchors, which will fit the plug into the socket. +// Example(3D,NoScale): This example defines named anchors and then uses them internally in the object definition to make a cutout in the socket() object and to attach the plug on the plug() object. These objects can be connected using the "socket" and "plug" named anchors, which fit the plug into the socket. // module socket(anchor, spin, orient) { // sz = 50; // prong_size = 10; @@ -3243,15 +3243,15 @@ function _is_geometry(entry) = is_list(entry) && is_string(entry[0]); // 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 `p` is not given, then the transformation matrix is returned. +// If `p` contains a VNF, a new VNF is returned with the vertices transformed by the matrix. +// If `p` contains a path, a new path is returned with the vertices transformed by the matrix. +// If `p` contains a point, a new point is 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`. +// * Rotates so the top of the part points toward the vector `orient`. // . // If `$attach_to` is defined, as a consequence of `attach(from,to)`, then // the following transformations are performed in order: @@ -3264,7 +3264,7 @@ function _is_geometry(entry) = is_list(entry) && is_string(entry[0]); // 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` +// orient = Vector to rotate top toward, 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. @@ -3334,7 +3334,7 @@ function reorient( // a = named_anchor(name, [pos], rot=, [flip=]); // Description: // Creates an anchor data structure. You can specify the position, orient direction and spin directly. -// Alternatively for the 3D case you can give a 4x4 rotation matrix which can specify the orient and spin, and optionally +// Alternatively for the 3D case you can give a 4×4 rotation matrix, which can specify the orient and spin, and optionally // the position, using a translation component of the matrix. If you specify `pos` along with `rot` then the position you // give overrides any translation included in `rot`. For a step-by-step explanation of attachments, // see the [Attachments Tutorial](Tutorial-Attachment-Basic-Positioning). @@ -3345,7 +3345,7 @@ function reorient( // spin = If needed, the angle to rotate the part around the direction vector. Default: 0 // --- // info = structure listing info to be propagated to the attached child, e.g. "edge_anchor" -// rot = A 4x4 rotations matrix, which may include a translation +// rot = A 4×4 rotations matrix, which may include a translation // flip = If true, flip the anchor the opposite direction. Default: false function named_anchor(name, pos, orient, spin, rot, flip, info) = assert(num_defined([orient,spin])==0 || num_defined([rot,flip])==0, "\nCannot mix orient or spin with rot or flip.") @@ -3394,7 +3394,7 @@ function named_anchor(name, pos, orient, spin, rot, flip, info) = // // 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. +// This probably doesn't ever need to be called by the end user. // // Arguments: // --- @@ -3418,7 +3418,7 @@ function named_anchor(name, pos, orient, spin, rot, flip, info) = // 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,spin]` 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,spin]]` entries. If the direction/position/spin that is returned is undef then the default will be used. +// override = Function that takes an anchor and returns a pair `[position,direction,spin]` 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,spin]]` entries. If the direction/position/spin that is returned is undef then the default is used. // // Example(NORENDER): Null/Point Shape // geom = attach_geom(); @@ -3601,7 +3601,7 @@ function attach_geom( // --- // inside = if true, reverse the attachment direction for children. Default: false // T = Transformation to apply to children. Default: IDENT -// Example(3D): This example shows how to create a custom object with two different parts that are both transformed away from the origin. The basic object is two cylinders with a cube shaped attachment geometry that doesn't match the object very well. The "left" and "right" parts attach to each of the two cylinders. +// Example(3D): This example shows how to create a custom object with two different parts that are both transformed away from the origin. The basic object is two cylinders with a cube shaped attachment geometry that doesn't match the object well. The "left" and "right" parts attach to each of the two cylinders. // module twocyl(d, sep, h, ang=20) // { // parts = [ @@ -3825,7 +3825,7 @@ function _attach_geom_edge_path(geom, edge) = /// 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` +/// orient = Vector to rotate top toward, 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. @@ -4508,7 +4508,7 @@ module show_anchors(s=10, std=true, custom=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` +// orient = Vector to rotate top toward, 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) { @@ -4559,7 +4559,7 @@ module anchor_arrow2d(s=15, color=[0.333,0.333,1], $tag="anchor-arrow") { // 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. +// Children appear transparent and any anchor arrows drawn 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): @@ -4588,7 +4588,7 @@ module expose_anchors(opacity=0.2) { // Description: // Given a list of transformation matrices, shows the position and orientation of each one. // A line is drawn from each transform position to the next one, and an orientation indicator is -// shown at each position. If a child is passed, that child will be used as the orientation indicator. +// shown at each position. If a child is passed, that child is used as the orientation indicator. // By default, a {{generic_airplane()}} is used as the orientation indicator. // Arguments: // s = Length of the {{generic_airplane()}}. Default: 5 @@ -5000,7 +5000,7 @@ function _corner_set(v) = /// 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 +/// from the returned corners array. If either argument has only a single corner /// set descriptor, you do not have to pass it in a list. function _corners(v, except=[]) = v==[] ? CORNERS_NONE : @@ -5231,7 +5231,7 @@ function parent_part(name) = // Usage: // restore([desc]) CHILDREN; // Description: -// Restores the transformation and parent geometry contained in the specified description which you obtained with {{parent()}}. +// Restores the transformation and parent geometry contained in the specified description that you obtained with {{parent()}}. // If you don't give a description then restores the global world coordinate system with a zero size cuboid object as the parent. // Arguments: // desc = saved description to restore. Default: restore to world coordinates @@ -5310,7 +5310,7 @@ function desc_point(desc, p, anchor) = // dir = desc_anchor(desc,[dir], [anchor]); // Description: // Computes the direction in the current context of a direction in the context of the description. You can specify -// the direction by giving a direction vector, or you can give an anchor that will be interpreted from the description. +// the direction by giving a direction vector, or you can give an anchor that is interpreted from the description. // If you don't give a description then the direction is computed relative to global world coordinates; in this case you // cannot give an anchor as the direction. // Arguments: @@ -5411,7 +5411,7 @@ function desc_dist(desc1,anchor1=CENTER, desc2, anchor2=CENTER)= // applied. You can also give a list of transformation matrices, in which case the output is // a list of descriptions. // Arguments: -// T = transformation or list of transformations to apply (a 4x4 matrix or list of them) +// T = transformation or list of transformations to apply (a 4×4 matrix or list of them) // desc = description to transform function transform_desc(T,desc) = @@ -5432,8 +5432,8 @@ function transform_desc(T,desc) = // Makes a copy of the children and applies each matrix in the list of transformation matrices. // This is equivalent to running `multmatrix()` over all the transformations for the children. // This function provides a method for working with descriptions of the whole set of copies by -// making all of their descriptions available to the children. This functionality will primarly -// be useful when the transformation consists only of translations and rotations and hence +// making all of their descriptions available to the children. This functionality is useful +// primarily when the transformation consists only of translations and rotations and hence // does not change the size or shape of the children. If you change the shape of the objects, care // is required to ensure that the descriptions match correctly. // . diff --git a/shapes3d.scad b/shapes3d.scad index aa0fe99b..de8e5817 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -38,7 +38,7 @@ use // --- // 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` +// orient = Vector to rotate top toward, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // Example: Simple cube. // cube(40); // Example: Rectangular cube. @@ -69,7 +69,7 @@ function cube(size=1, center, anchor, spin=0, orient=UP) = let( size = force_list(size,3) ) - assert(is_vector(size,3), "\nSize parameter cannot be converted to a 3-vector") + assert(is_vector(size,3), "\nSize parameter cannot be converted to a 3-vector.") assert(all_positive(size), "\nAll size components must be positive.") let( anchor = get_anchor(anchor, center, -[1,1,1], -[1,1,1]), @@ -110,7 +110,7 @@ function cube(size=1, center, anchor, spin=0, orient=UP) = // Creates a cube or cuboid object, with optional chamfering or rounding of edges and corners. // You cannot mix chamfering and rounding: just one edge treatment with the same size applies to all selected edges. // Negative chamfers and roundings can be applied to create external fillets, but they -// only apply to edges around the top or bottom faces. If you specify an edge set other than "ALL" +// apply only to edges around the top or bottom faces. If you specify an edge set other than "ALL" // with negative roundings or chamfers then you will get an error. See [Specifying Edges](attachments.scad#section-specifying-edges) // for information on how to specify edge sets. // Arguments: @@ -127,7 +127,7 @@ function cube(size=1, center, anchor, spin=0, orient=UP) = // p2 = If given with `p1`, defines the cornerpoints of the cuboid. // 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` +// orient = Vector to rotate top toward. See [orient](attachments.scad#subsection-orient). Default: `UP` // Example: Simple regular cube. // cuboid(40); // Example: Cuboid with a corner at the origin @@ -344,23 +344,23 @@ module cuboid( } } sizecheck = assert(num_defined([size,p1,p2])!=3, "\nCannot give size if p2 is given (did you forget brackets on the size argument?)") - assert(is_def(p1) || is_undef(p2), "If p2 is given you must also give p1"); + assert(is_def(p1) || is_undef(p2), "\nIf p2 is given you must also give p1."); size = force_list(default(size,1),3); edges = _edges(edges, except=first_defined([except_edges,except])); teardrop = is_bool(teardrop)&&teardrop? 45 : teardrop; chamfer = approx(chamfer,0) ? undef : chamfer; rounding = approx(rounding,0) ? undef : rounding; checks = - assert(is_vector(size,3),"Size must be a scalar or 3-vector") - assert(all_nonnegative(size), "All components of size= must be >=0") - assert(is_undef(chamfer) || is_finite(chamfer),"chamfer must be a finite value") - assert(is_undef(rounding) || is_finite(rounding),"rounding must be a finite value") - assert(is_undef(rounding) || is_undef(chamfer), "Cannot specify nonzero value for both chamfer and rounding") - assert(teardrop==false || (is_finite(teardrop) && teardrop>0 && teardrop<=90), "teardrop must be either false or an angle number between 0 and 90") - assert(clip_angle==undef || (is_finite(clip_angle) && clip_angle>0 && clip_angle<=90), "clip_angle must be either false or an angle number between 0 and 90") - assert(!teardrop || clip_angle==undef, "teardrop= and clip_angle= are mutually exclusive features.") - assert(is_undef(p1) || is_vector(p1,3), "p1 must be a 3-vector") - assert(is_undef(p2) || is_vector(p2,3), "p2 must be a 3-vector") + assert(is_vector(size,3),"\n'size' must be a scalar or 3-vector.") + assert(all_nonnegative(size), "\nAll components of size= must be >=0.") + assert(is_undef(chamfer) || is_finite(chamfer),"\n'chamfer' must be a finite value.") + assert(is_undef(rounding) || is_finite(rounding),"\n'rounding' must be a finite value.") + assert(is_undef(rounding) || is_undef(chamfer), "\nCannot specify nonzero value for both chamfer and rounding.") + assert(teardrop==false || (is_finite(teardrop) && teardrop>0 && teardrop<=90), "\n'teardrop' must be either false or an angle number between 0 and 90.") + assert(clip_angle==undef || (is_finite(clip_angle) && clip_angle>0 && clip_angle<=90), "\nclip_angle must be either false or an angle number between 0 and 90.") + assert(!teardrop || clip_angle==undef, "\nteardrop= and clip_angle= are mutually exclusive features.") + assert(is_undef(p1) || is_vector(p1,3), "\np1 must be a 3-vector.") + assert(is_undef(p2) || is_vector(p2,3), "\np2 must be a 3-vector.") assert(is_bool(trimcorners)); if (!is_undef(p1)) { if (!is_undef(p2)) { @@ -400,9 +400,9 @@ module cuboid( edges.x[3] + edges.y[0], edges.x[3] + edges.y[1] ) * rr; check = - assert(minx <= size.x, "Rounding or chamfering too large for cuboid size in the X axis.") - assert(miny <= size.y, "Rounding or chamfering too large for cuboid size in the Y axis.") - assert(minz <= size.z, "Rounding or chamfering too large for cuboid size in the Z axis.") + assert(minx <= size.x, "\nRounding or chamfering too large for cuboid size in the X axis.") + assert(miny <= size.y, "\nRounding or chamfering too large for cuboid size in the Y axis.") + assert(minz <= size.z, "\nRounding or chamfering too large for cuboid size in the Z axis.") ; } majrots = [[0,90,0], [90,0,0], [0,0,0]]; @@ -423,7 +423,7 @@ module cuboid( } } } else if (chamfer<0) { - checks = assert(edges == EDGES_ALL || edges[2] == [0,0,0,0], "Cannot use negative chamfer with Z aligned edges."); + checks = assert(edges == EDGES_ALL || edges[2] == [0,0,0,0], "\nCannot use negative chamfer with Z aligned edges."); ach = abs(chamfer); cube(size, center=true); @@ -510,7 +510,7 @@ module cuboid( } } } else if (rounding<0) { - checks = assert(edges == EDGES_ALL || edges[2] == [0,0,0,0], "Cannot use negative rounding with Z aligned edges."); + checks = assert(edges == EDGES_ALL || edges[2] == [0,0,0,0], "\nCannot use negative rounding with Z aligned edges."); ard = abs(rounding); cube(size, center=true); @@ -637,7 +637,7 @@ function cuboid( // 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: `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` +// orient = Vector to rotate top toward, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // // Example: Truncated Pyramid // prismoid(size1=[35,50], size2=[20,30], h=20); @@ -697,7 +697,7 @@ function cuboid( // Example(Spin,VPD=160,VPT=[0,0,10]): Standard anchors // prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5]) // show_anchors(); -// Example(3D): When the top or bottom is degenerate, you can anchor to and round the degenerate edge by using either one of the edge anchors that correspond to that edge. But note that {{edge_profile()}} does not work for this degenerate case. We used `TOP+RIGHT` below as the anchor point, but `TOP+LEFT` will produce an identical result. +// Example(3D): When the top or bottom is degenerate, you can anchor to and round the degenerate edge by using either one of the edge anchors that correspond to that edge. But note that {{edge_profile()}} does not work for this degenerate case. We used `TOP+RIGHT` below as the anchor point, but `TOP+LEFT` produces an identical result. // diff() // prismoid([10,14],[0,8], shift=[4,3], h=7) // attach(TOP+RIGHT, FWD+LEFT, inside=true) @@ -737,9 +737,9 @@ function prismoid( _return_dim=false ) = - assert(is_undef(shift) || is_num(shift) || len(shift)==2, "shift must be a number or list of length 2") - assert(is_undef(size1) || is_num(size1) || len(size1)==2, "size1 must be a number or list of length 2") - assert(is_undef(size2) || is_num(size2) || len(size2)==2, "size2 must be a number or list of length 2") + assert(is_undef(shift) || is_num(shift) || len(shift)==2, "\nshift must be a number or list of length 2.") + assert(is_undef(size1) || is_num(size1) || len(size1)==2, "\nsize1 must be a number or list of length 2.") + assert(is_undef(size2) || is_num(size2) || len(size2)==2, "\nsize2 must be a number or list of length 2.") let( xang = force_list(xang,2), yang = force_list(yang,2), @@ -750,18 +750,18 @@ function prismoid( h=first_defined([l,h,length,height]), shift = force_list(shift,2) ) - assert(xangOK, "prismoid angles must be scalar or 2-vector, strictly between 0 and 180") - assert(yangOK, "prismoid angles must be scalar or 2-vector, strictly between 0 and 180") - assert(xang==[undef,undef] || shift.x==undef, "Cannot specify xang and a shift.x value together") - assert(yang==[undef,undef] || shift.y==undef, "Cannot specify yang and a shift.y value together") - assert(all_positive([h]) || is_undef(h), "h must be a positive value") + assert(xangOK, "\nPrismoid angles must be scalar or 2-vector, strictly between 0 and 180.") + assert(yangOK, "\nPrismoid angles must be scalar or 2-vector, strictly between 0 and 180.") + assert(xang==[undef,undef] || shift.x==undef, "\nCannot specify xang and a shift.x value together.") + assert(yang==[undef,undef] || shift.y==undef, "\nCannot specify yang and a shift.y value together.") + assert(all_positive([h]) || is_undef(h), "\nh must be a positive value.") let( hx = _trapezoid_dims(h,size1.x,size2.x,shift.x,xang)[0], hy = _trapezoid_dims(h,size1.y,size2.y,shift.y,yang)[0] ) - assert(num_defined([hx,hy])>0, "Height not given and specification does not determine prismoid height") + assert(num_defined([hx,hy])>0, "\nHeight not given and specification does not determine prismoid height.") assert(hx==undef || hy==undef || approx(hx,hy), - str("X and Y angle specifications give rise to conflicting height values ",hx," and ",hy)) + str("\nX and Y angle specifications cause conflicting height values ",hx," and ",hy,".")) let( h = first_defined([hx,hy]), x_h_w1_w2_shift = _trapezoid_dims(h,size1.x,size2.x,shift.x,xang), @@ -772,30 +772,30 @@ function prismoid( s2 = [x_h_w1_w2_shift[2], y_h_w1_w2_shift[2]], shift = [x_h_w1_w2_shift[3], y_h_w1_w2_shift[3]] ) - assert(is_vector(s1,2), "Insufficient information to define prismoid") - assert(is_vector(s2,2), "Insufficient information to define prismoid") - assert(all_nonnegative(concat(s1,s2)),"Degenerate prismoid geometry") - assert(s1.x+s2.x>0 && s1.y+s2.y>0, "Degenerate prismoid geometry") - assert(is_num(rounding) || is_vector(rounding,4), "rounding must be a number or 4-vector") - assert(is_undef(rounding1) || is_num(rounding1) || is_vector(rounding1,4), "rounding1 must be a number or 4-vector") - assert(is_undef(rounding2) || is_num(rounding2) || is_vector(rounding2,4), "rounding2 must be a number or 4-vector") - assert(is_num(chamfer) || is_vector(chamfer,4), "chamfer must be a number or 4-vector") - assert(is_undef(chamfer1) || is_num(chamfer1) || is_vector(chamfer1,4), "chamfer1 must be a number or 4-vector") - assert(is_undef(chamfer2) || is_num(chamfer2) || is_vector(chamfer2,4), "chamfer2 must be a number or 4-vector") + assert(is_vector(s1,2), "\nInsufficient information to define prismoid.") + assert(is_vector(s2,2), "\nInsufficient information to define prismoid.") + assert(all_nonnegative(concat(s1,s2)),"\nDegenerate prismoid geometry.") + assert(s1.x+s2.x>0 && s1.y+s2.y>0, "\nDegenerate prismoid geometry.") + assert(is_num(rounding) || is_vector(rounding,4), "\nrounding must be a number or 4-vector.") + assert(is_undef(rounding1) || is_num(rounding1) || is_vector(rounding1,4), "\nrounding1 must be a number or 4-vector.") + assert(is_undef(rounding2) || is_num(rounding2) || is_vector(rounding2,4), "\nrounding2 must be a number or 4-vector.") + assert(is_num(chamfer) || is_vector(chamfer,4), "\nchamfer must be a number or 4-vector.") + assert(is_undef(chamfer1) || is_num(chamfer1) || is_vector(chamfer1,4), "\nchamfer1 must be a number or 4-vector.") + assert(is_undef(chamfer2) || is_num(chamfer2) || is_vector(chamfer2,4), "\nchamfer2 must be a number or 4-vector.") let( chamfer1=force_list(default(chamfer1,chamfer),4), chamfer2=force_list(default(chamfer2,chamfer),4), rounding1=force_list(default(rounding1,rounding),4), rounding2=force_list(default(rounding2,rounding),4) ) - assert(all_nonnegative(chamfer1), "chamfer/chamfer1 must be non-negative") - assert(all_nonnegative(chamfer2), "chamfer/chamfer2 must be non-negative") - assert(all_nonnegative(rounding1), "rounding/rounding1 must be non-negative") - assert(all_nonnegative(rounding2), "rounding/rounding2 must be non-negative") + assert(all_nonnegative(chamfer1), "\nchamfer/chamfer1 must be non-negative.") + assert(all_nonnegative(chamfer2), "\nchamfer/chamfer2 must be non-negative.") + assert(all_nonnegative(rounding1), "\nrounding/rounding1 must be non-negative.") + assert(all_nonnegative(rounding2), "\nrounding/rounding2 must be non-negative.") assert(all_zero(v_mul(rounding1,chamfer1),0), - "rounding1 and chamfer1 (possibly inherited from rounding and chamfer) cannot both be nonzero at the same corner") + "\nrounding1 and chamfer1 (possibly inherited from rounding and chamfer) cannot both be nonzero at the same corner.") assert(all_zero(v_mul(rounding2,chamfer2),0), - "rounding2 and chamfer2 (possibly inherited from rounding and chamfer) cannot both be nonzero at the same corner") + "\nrounding2 and chamfer2 (possibly inherited from rounding and chamfer) cannot both be nonzero at the same corner.") let( rounding1 = default(rounding1, rounding), rounding2 = default(rounding2, rounding), @@ -856,7 +856,7 @@ function prismoid( // labeling proceeds clockwise. The top and bottom edge anchors label edges directly above and below the face with the same label. // If you set `realign=true` then "face0" is oriented in the X+ direction. // . -// This module is very similar to {{cyl()}}. It differs in the following ways: you can specify side length or inner radius/diameter, you can apply roundings with +// This module is similar to {{cyl()}}. It differs in the following ways: you can specify side length or inner radius/diameter, you can apply roundings with // different `$fn` than the number of prism faces, you can apply texture to the flat faces without forcing a high facet count, // anchors are located on the true object instead of the ideal cylinder and you can anchor to the edges and faces. Chamfers and roundings // for this module are **always** evaluated relative to the faces of the prism and never at corners as is done by default in {{cyl()}}. @@ -903,7 +903,7 @@ function prismoid( // teardrop = If given as a number, rounding around the bottom edge of the prism won't exceed this many degrees from vertical. If true, the limit angle is 45 degrees. Default: `false` // clip_angle = If given as a number, rounding around the bottom edge of the prism won't exceed this many degrees from vertical, with the rounding stopping at the bottom of the prism. Default: (no clipping) // 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 (2-vector or scalar) for the textures. Actual texture sizes will be scaled somewhat to evenly fit the available surface. Default: `[5,5]` +// tex_size = An optional 2D target size (2-vector or scalar) for the textures. Actual texture sizes are scaled somewhat to fit evenly on the available surface. Default: `[5,5]` // tex_reps = If given instead of tex_size, a scalar or 2-vector giving the number of texture tile repetitions in the horizontal and vertical directions. // tex_inset = If numeric, lowers the texture into the surface by the specified proportion, e.g. 0.5 would lower it half way into the surface. If `true`, insets by exactly its full depth. Default: `false` // tex_rot = Rotate texture by specified angle, which must be a multiple of 90 degrees. Default: 0 @@ -912,7 +912,7 @@ function prismoid( // style = {{vnf_vertex_array()}} style used to triangulate heightfield textures. Default: "min_edge" // 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` +// orient = Vector to rotate top toward, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // Example: Simple prism // regular_prism(5,r=10,h=25); // Example: With end rounding @@ -996,7 +996,7 @@ function regular_prism(n, tex_taper, style, anchor, spin=0, orient=UP,_return_anchors=false ) = - assert(is_integer(n) && n>2, "n must be an integer 3 or greater") + assert(is_integer(n) && n>2, "\nn must be an integer 3 or greater.") let( style = default(style,"min_edge"), tex_depth = default(tex_depth,1), @@ -1019,14 +1019,14 @@ function regular_prism(n, r2 = get_radius(r1=ir2,r2=or2,r=default(r2,r),d=d2,d1=id2,d2=od2,dflt=side2), anchor = get_anchor(anchor,center,BOT,CENTER) ) - assert(num_defined([side,od,id,or,ir])<=1, "Can only define one of side, id, od, ir, and or") - assert(is_finite(r1), "Must specify finite number for prism bottom radius / diameter / side length") - assert(is_finite(r2), "Must specify finite number for prism top radius / diameter / side length") - assert(is_finite(height), "l/h/length/height must be a finite number.") - assert(is_vector(shift,2), "shift must be a 2D vector.") + assert(num_defined([side,od,id,or,ir])<=1, "\nCan define only one of side, id, od, ir, and or.") + assert(is_finite(r1), "\nMust specify finite number for prism bottom radius / diameter / side length.") + assert(is_finite(r2), "\nMust specify finite number for prism top radius / diameter / side length.") + assert(is_finite(height), "\nl/h/length/height must be a finite number.") + assert(is_vector(shift,2), "\nshift must be a 2D vector.") let( vnf = any_defined([chamfer, chamfer1, chamfer2, rounding, rounding1, rounding2]) - ? assert(is_undef(texture), "Cannot combine roundings or chamfers with texturing") + ? assert(is_undef(texture), "\nCannot combine roundings or chamfers with texturing.") let( path = [ [0,-height/2], @@ -1123,7 +1123,7 @@ function regular_prism(n, // You can specify the size of the object by giving a `size` scalar or vector as is // usual for a cube. If you give a scalar, however, it applies only to the X and Y dimensions: the default is to create a thin tile, not a cube. // The Z size specifies the size of the shape **not** including the applied texture (in the same way that other textured objects work). -// If you omit the Z value then for regular textures, the default thickness will be 0.1 which provides a thin backing layer. A zero thickness +// If you omit the Z value then for regular textures, the default thickness is 0.1, which provides a thin backing layer. A zero thickness // base layer can produce invalid geometry when the texture contacts the bottom layer, so some non-zero base is necessary. If you use a positive `inset` // then the texture actually sinks into its base, so the default is set to the 0.1 more than the inset depth. To ensure a valid geometry, with a positive // `inset` or a texture that has negative values you must select a thickness strictly **larger** than the depth the texture extends below zero. @@ -1134,12 +1134,12 @@ function regular_prism(n, // . // You can also specify a trapzoidal prism using parameters equivalent to those accepted by {{trapezoid()}}, with one change: // `ysize` specifies the width of the prism in the Y direction, and `h`, `height` or `thickness` are used to specify the height -// in the Z direction. When you texture a trapezoid the texture will be scaled to the `w1` length if you specify it by size using `tex_size`. The -// scaling transformation that maps the texture onto the trapezoid is not linear and will result in curvature of your texture. +// in the Z direction. When you texture a trapezoid, the texture is scaled to the `w1` length if you specify it by size using `tex_size`. The +// scaling transformation that maps the texture onto the trapezoid is not linear and results in curvature of your texture. // . -// Two anchor types are available. The default atype is "tex" which assumes you want to place the texture on another object using +// Two anchor types are available. The default `atype` is "tex", which assumes you want to place the texture on another object using // {{attach()}}. It provides anchors that ignore the base object and place the BOTTOM anchor at the bottom of the texture. The TOP anchor -// will be at the top face of the texture. Note that if your texture doesn't span the range from [0,1] these anchors won't be correctly located. +// is at the top face of the texture. Note that if your texture doesn't span the range from [0,1] these anchors won't be correctly located. // For an inset texture, the "tex" anchors are all at the top of the texture. This anchor type works with `attach(face,BOT)` where `face` is some // face on a parent object that needs a texture. If you want to use the textured object directly the "std" anchors are probably more useful. // These anchors are the usual anchors for the base object, ignoring the applied texture. If you want the anchors to be on top of the texture, @@ -1148,9 +1148,9 @@ function regular_prism(n, // To aid in the application of inset anchors into parent objects with the module form, you can set `diff=true`, which causes the module // to create a "remove" tagged cuboid or prism to carve out space for the texture so that inset textures are cut into the parent object. // The texture itself is given a "keep" tag. For this to work you must specify {{diff()}} above the parent; if you don't do that, the -// tags will be ignored and the tile will appear as a solid object with no texture visible. The cutout object extends 0.1 units above the surface +// tags are ignored and the tile appears as a solid object with no texture visible. The cutout object extends 0.1 units above the surface // of the texture to prevent problems with exactly aligned faces. The cutout does not extend out beyond the sides, so if the parent shape -// has the exact same dimensions as the texture tile, you will have exactly aligned faces along the edges. +// has the exact same dimensions as the texture tile, you have exactly aligned faces along the edges. // . // Most of the heightfield textures are designed to repeat in a way that requires one extra line of the texture to complete the pattern. // The `tex_extra` parameter specifies the number of extra lines to repeat at the end of the texture and it defaults to 1 because most textures @@ -1173,7 +1173,7 @@ function regular_prism(n, // ang = Specify the front angle(s) of the trapezoidal prism. Can give a scalar for an isosceles trapezoidal prism or a list of two angles, the left angle and right angle. You must omit one of `h`, `w1`, or `w2` to allow the freedom to control the angles. // shift = Scalar value to shift the back of the trapezoidal prism along the X axis by. Cannot be combined with ang. Default: 0 // h / height / thickness = The thickness in the Z direction of the base that the texture sits on. Default: 0.1 or for inset textures 0.1 more than the inset depth -// tex_size = An optional 2D target size (2-vector or scalar) for the textures. Actual texture sizes will be scaled somewhat to evenly fit the available surface. Default: `[5,5]` +// tex_size = An optional 2D target size (2-vector or scalar) for the textures. Actual texture sizes are scaled somewhat to fit evenly on the available surface. Default: `[5,5]` // tex_reps = If given instead of tex_size, a scalar or 2-vector giving the integer number of texture tile repetitions in the horizontal and vertical directions. // tex_inset = If numeric, lowers the texture into the surface by the specified proportion, e.g. 0.5 would lower it half way into the surface. If `true`, insets by exactly its full depth. Default: `false` // tex_rot = Rotate texture by specified angle, which must be a multiple of 90 degrees. Default: 0 @@ -1184,7 +1184,7 @@ function regular_prism(n, // style = {{vnf_vertex_array()}} style used to triangulate heightfield textures. Default: "min_edge" // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `BOTTOM` if `astyle` is "tex", `CENTER` otherwise // 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` +// orient = Vector to rotate top toward, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // Example(3D,NoScales,VPT=[-0.257402,0.467403,-0.648606],VPR=[46.6,0,16.6],VPD=29.2405): Basic textured tile // textured_tile("trunc_diamonds", 10, tex_reps=[5,5]); // Example(3D,NoAxes,VPT=[-0.0852782,0.259593,0.139667],VPR=[58.5,0,345.1],VPD=36.0994): Attaching a tile to a cube @@ -1202,7 +1202,7 @@ function regular_prism(n, // Example(3D,NoAxes,VPT=[5.86588,-0.107082,-0.311155],VPR=[17.2,0,9.6],VPD=32.4895): Tile shaped like a rhombic prism // textured_tile("ribs", w1=10, w2=10, shift=4, ysize=7, // tex_reps=[5,1]); -// Example(3D,NoAxes,VPT=[-0.487417,-0.398897,-0.143258],VPR=[10.2,0,12.4],VPD=26.3165): A tile shaped like a trapezoidal prism. Note that trapezoidal tiles will always distort the texture, resulting in curves +// Example(3D,NoAxes,VPT=[-0.487417,-0.398897,-0.143258],VPR=[10.2,0,12.4],VPD=26.3165): A tile shaped like a trapezoidal prism. Note that trapezoidal tiles always distort the texture, resulting in curves // textured_tile("diamonds", w1=10, w2=7, ysize=7, tex_reps=5); // Example(3D,NoAxes,VPT=[-0.0889877,-0.31974,0.554444],VPR=[22.1,0,22.2],VPD=32.4895): An inset trapezoidal tile placed into a cube // diff()cuboid([10,10,2]) @@ -1216,7 +1216,7 @@ function regular_prism(n, // textured_tile("trunc_ribs", 10, tex_reps=[5,1]); // Example(3D,NoAxes,VPT=[-0.212176,-0.651766,0.124004],VPR=[58.5,0,21.5],VPD=29.2405): It could be fixed by setting `tex_extra=2`, which would place an extra flat strip on the right. But another option is to use the `tex_skip` parameter to trim the flat part from the left. Note that we are also skipping in the y direction, but it doesn't make a difference for this texture, except that you need to have enough texture tiles to accommodate the skip, so we increased the Y reps value to 2. You can also set `tex_skip` to a vector. // textured_tile("trunc_ribs", 10, tex_reps=[5,2], tex_skip=1); -// Example(3D,NoAxes): Textures can be used to place images onto objects. Here we place a very simple image into a cube, leaving a border around the image. +// Example(3D,NoAxes): Textures can be used to place images onto objects. Here we place a simple image into a cube, leaving a border around the image. // textured_tile("trunc_ribs", 10, tex_reps=[5,2], tex_skip=1); // img = [ // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], @@ -1303,12 +1303,12 @@ function textured_tile( anchor, spin=0, orient=UP, _return_anchor=false ) = - assert(in_list(atype,["tex","std"]), "atype must be \"tex\" or \"std\"") - assert(is_undef(tex_reps) || is_int(tex_reps) || (all_integer(tex_reps) && len(tex_reps)==2), "tex_reps must be an integer or list of two integers") + assert(in_list(atype,["tex","std"]), "\natype must be \"tex\" or \"std\".") + assert(is_undef(tex_reps) || is_int(tex_reps) || (all_integer(tex_reps) && len(tex_reps)==2), "\ntex_reps must be an integer or list of two integers.") assert(is_undef(tex_size) || is_vector(tex_size,2) || is_finite(tex_size)) - assert(num_defined([tex_size, tex_reps])==1, "Must give exactly one of tex_size and tex_reps") - assert(is_undef(size) || is_num(size) || is_vector(size,2) || is_vector(size,3), "size must be a 2-vector or 3-vector") - assert(is_undef(size) || num_defined([ysize,h, height, thickness, w1,w2,ang])==0, "Cannot combine size with any other dimensional specifications") + assert(num_defined([tex_size, tex_reps])==1, "\nMust give exactly one of tex_size and tex_reps.") + assert(is_undef(size) || is_num(size) || is_vector(size,2) || is_vector(size,3), "\n'size' must be a 2-vector or 3-vector.") + assert(is_undef(size) || num_defined([ysize,h, height, thickness, w1,w2,ang])==0, "\nCannot combine size with any other dimensional specifications.") let( anchor = default(anchor, atype=="tex" ? BOTTOM : CENTER), inset = is_num(tex_inset)? tex_inset : tex_inset? 1 : 0, @@ -1319,16 +1319,16 @@ function textured_tile( assert(is_undef(w1) || is_finite(w1)) assert(is_undef(w2) || is_finite(w2)) assert(is_undef(ang) || is_finite(ang) || is_vector(ang,2)) - assert(num_defined([ysize, w1, w2, ang]) == 3, "Must give exactly 3 of the arguments ysize, w1, w2, and angle.") + assert(num_defined([ysize, w1, w2, ang]) == 3, "\nMust give exactly 3 of the arguments ysize, w1, w2, and angle.") assert(is_undef(shift) || is_finite(shift)) - assert(num_defined([shift,ang])<2, "Cannot specify shift and ang together") + assert(num_defined([shift,ang])<2, "\nCannot specify shift and ang together.") _trapezoid_dims(ysize,w1,w2,shift,ang), ysize=h_w1_w2_shift[0], w1=h_w1_w2_shift[1], w2=h_w1_w2_shift[2], shift=h_w1_w2_shift[3], height = is_def(size) ? default(size.z,default_thick) : one_defined([h,height,thickness],"h,height,thickness",dflt=default_thick), - size = is_def(size) ? is_num(size) ? [size,size,1] : point3d(size,1) // We only use the x and y components of size + size = is_def(size) ? is_num(size) ? [size,size,1] : point3d(size,1) // We use only the x and y components of size : [w1,ysize], texture = _get_texture(texture, tex_rot), @@ -1346,14 +1346,14 @@ function textured_tile( texsteps = [len(texture[0]), len(texture)], xn=tex_reps.x*texsteps.x+extra.x-skip.x, yn=tex_reps.y*texsteps.y+extra.y-skip.y, - checks = assert(yn>=2, "Skipped too many points in the y direction: decrease skip.y") - assert(xn>=2, "Skipped too many points in the x direction: decrease skip.x"), + checks = assert(yn>=2, "\nSkipped too many points in the y direction: decrease skip.y") + assert(xn>=2, "\nSkipped too many points in the x direction: decrease skip.x"), xpts=lerpn(-size.x/2,size.x/2,xn), ypts=lerpn(size.y/2,-size.y/2,yn), scaled_tex = tex_depth < 0 ? [for(row=texture) [for(p=row) -(1-p-inset)*tex_depth]] : [for(row=texture) [for(p=row) (p-inset)*tex_depth]], check = [for(row=scaled_tex, p=row) if (p<=-height) p], - dummy=assert(check==[], str("texture extends too far below zero (",min([each check,0]),") to fit entirely within height ",height)), + dummy=assert(check==[], str("\nTexture extends too far below zero (",min([each check,0]),") to fit entirely within height ",height,".")), pts=[for(y=idx(ypts)) [ [xpts[0],ypts[y],-height/2], for(x=idx(xpts)) @@ -1370,7 +1370,7 @@ function textured_tile( texture[1] ], minz = min(column(zadj_vnf[0],2)), - dummy=assert(minz>-height/2, str("texture extends too far below zero (",minz-height/2,") to fit entirely within height ",height)), + dummy=assert(minz>-height/2, str("\nTexture extends too far below zero (",minz-height/2,") to fit entirely within height ",height,".")), scaled_vnf = scale(scale, zadj_vnf), tiled_vnf = [for(i=[0:1:tex_reps.x-1], j=[0:1:tex_reps.y-1]) move([scale.x*i,scale.y*j], scaled_vnf)], @@ -1480,16 +1480,16 @@ function textured_tile( // outside separately. // . // By default if you specify a chamfer or rounding then it applies as specified to the -// outside, and an inside rounding is calculated that will maintain constant width +// outside, and an inside rounding is calculated to maintain constant width // if your wall thickness is uniform. If the wall thickness is not uniform, the default // inside rounding is calculated based on the smaller of the two wall thicknesses. // Note that the values of the more specific chamfers and roundings inherit from the // more general ones, so `rounding2` is determined from `rounding`. The constant -// width default will apply when the inner rounding and chamfer are both undef. +// width default applies when the inner rounding and chamfer are both undef. // You can give an inner chamfer or rounding as a list with undef entries if you want to specify // some corner roundings and allow others to be computed. // . -// Attachment to the rectangular tube will place objects on the **outside** of the tube. +// Attachment to the rectangular tube places objects on the **outside** of the tube. // If you need to anchor to the inside of a tube, use {{attach_part()}} with the part name "inside" // to switch goeomtry to the inside. // Arguments: @@ -1518,7 +1518,7 @@ function textured_tile( // ichamfer2 = The chamfer size for the inside top corner of the rectangular tube. // 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` +// orient = Vector to rotate top toward, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // Examples: // rect_tube(size=50, wall=5, h=30); // rect_tube(size=[100,60], wall=5, h=30); @@ -1608,7 +1608,7 @@ module rect_tube( ) { h = one_defined([h,l,length,height],"h,l,length,height"); checks = - assert(is_num(h), "l or h argument required.") + assert(is_num(h), "\nl or h argument required.") assert(is_vector(shift,2)); s1 = is_num(size1)? [size1, size1] : is_vector(size1,2)? size1 : @@ -1644,37 +1644,37 @@ module rect_tube( undef; checks2 = assert(wall==undef || is_num(wall)) - assert(size1!=undef, "Bad size/size1 argument.") - assert(size2!=undef, "Bad size/size2 argument.") - assert(isize1!=undef, "Bad isize/isize1 argument.") - assert(isize2!=undef, "Bad isize/isize2 argument.") - assert(isize1.x < size1.x, "Inner size is larger than outer size.") - assert(isize1.y < size1.y, "Inner size is larger than outer size.") - assert(isize2.x < size2.x, "Inner size is larger than outer size.") - assert(isize2.y < size2.y, "Inner size is larger than outer size.") - assert(is_num(rounding) || is_vector(rounding,4), "rounding must be a number or 4-vector") - assert(is_undef(rounding1) || is_num(rounding1) || is_vector(rounding1,4), "rounding1 must be a number or 4-vector") - assert(is_undef(rounding2) || is_num(rounding2) || is_vector(rounding2,4), "rounding2 must be a number or 4-vector") - assert(is_num(chamfer) || is_vector(chamfer,4), "chamfer must be a number or 4-vector") - assert(is_undef(chamfer1) || is_num(chamfer1) || is_vector(chamfer1,4), "chamfer1 must be a number or 4-vector") - assert(is_undef(chamfer2) || is_num(chamfer2) || is_vector(chamfer2,4), "chamfer2 must be a number or 4-vector") - assert(is_undef(irounding) || is_num(irounding) || (is_list(irounding) && len(irounding)==4), "irounding must be a number or 4-vector") - assert(is_undef(irounding1) || is_num(irounding1) || (is_list(irounding1) && len(irounding1)==4), "irounding1 must be a number or 4-vector") - assert(is_undef(irounding2) || is_num(irounding2) || (is_list(irounding2) && len(irounding2)==4), "irounding2 must be a number or 4-vector") - assert(is_undef(ichamfer) || is_num(ichamfer) || (is_list(ichamfer) && len(ichamfer)==4), "ichamfer must be a number or 4-vector") - assert(is_undef(ichamfer1) || is_num(ichamfer1) || (is_list(ichamfer1) && len(ichamfer1)==4), "ichamfer1 must be a number or 4-vector") - assert(is_undef(ichamfer2) || is_num(ichamfer2) || (is_list(ichamfer2) && len(ichamfer2)==4), "ichamfer2 must be a number or 4-vector"); + assert(size1!=undef, "\nBad size/size1 argument.") + assert(size2!=undef, "\nBad size/size2 argument.") + assert(isize1!=undef, "\nBad isize/isize1 argument.") + assert(isize2!=undef, "\nBad isize/isize2 argument.") + assert(isize1.x < size1.x, "\nInner size is larger than outer size.") + assert(isize1.y < size1.y, "\nInner size is larger than outer size.") + assert(isize2.x < size2.x, "\nInner size is larger than outer size.") + assert(isize2.y < size2.y, "\nInner size is larger than outer size.") + assert(is_num(rounding) || is_vector(rounding,4), "\nrounding must be a number or 4-vector.") + assert(is_undef(rounding1) || is_num(rounding1) || is_vector(rounding1,4), "\nrounding1 must be a number or 4-vector.") + assert(is_undef(rounding2) || is_num(rounding2) || is_vector(rounding2,4), "\nrounding2 must be a number or 4-vector.") + assert(is_num(chamfer) || is_vector(chamfer,4), "\nchamfer must be a number or 4-vector.") + assert(is_undef(chamfer1) || is_num(chamfer1) || is_vector(chamfer1,4), "\nchamfer1 must be a number or 4-vector.") + assert(is_undef(chamfer2) || is_num(chamfer2) || is_vector(chamfer2,4), "\nchamfer2 must be a number or 4-vector.") + assert(is_undef(irounding) || is_num(irounding) || (is_list(irounding) && len(irounding)==4), "\nirounding must be a number or 4-vector.") + assert(is_undef(irounding1) || is_num(irounding1) || (is_list(irounding1) && len(irounding1)==4), "\nirounding1 must be a number or 4-vector.") + assert(is_undef(irounding2) || is_num(irounding2) || (is_list(irounding2) && len(irounding2)==4), "\nirounding2 must be a number or 4-vector.") + assert(is_undef(ichamfer) || is_num(ichamfer) || (is_list(ichamfer) && len(ichamfer)==4), "\nichamfer must be a number or 4-vector.") + assert(is_undef(ichamfer1) || is_num(ichamfer1) || (is_list(ichamfer1) && len(ichamfer1)==4), "\nichamfer1 must be a number or 4-vector.") + assert(is_undef(ichamfer2) || is_num(ichamfer2) || (is_list(ichamfer2) && len(ichamfer2)==4), "\nichamfer2 must be a number or 4-vector."); chamfer1=force_list(default(chamfer1,chamfer),4); chamfer2=force_list(default(chamfer2,chamfer),4); rounding1=force_list(default(rounding1,rounding),4); rounding2=force_list(default(rounding2,rounding),4); checks3 = - assert(all_nonnegative(chamfer1), "chamfer/chamfer1 must be non-negative") - assert(all_nonnegative(chamfer2), "chamfer/chamfer2 must be non-negative") - assert(all_nonnegative(rounding1), "rounding/rounding1 must be non-negative") - assert(all_nonnegative(rounding2), "rounding/rounding2 must be non-negative") - assert(all_zero(v_mul(rounding1,chamfer1),0), "rounding1 and chamfer1 (possibly inherited from rounding and chamfer) cannot both be nonzero at the same corner") - assert(all_zero(v_mul(rounding2,chamfer2),0), "rounding2 and chamfer2 (possibly inherited from rounding and chamfer) cannot both be nonzero at the same corner"); + assert(all_nonnegative(chamfer1), "\nchamfer/chamfer1 must be non-negative.") + assert(all_nonnegative(chamfer2), "\nchamfer/chamfer2 must be non-negative.") + assert(all_nonnegative(rounding1), "\nrounding/rounding1 must be non-negative.") + assert(all_nonnegative(rounding2), "\nrounding/rounding2 must be non-negative.") + assert(all_zero(v_mul(rounding1,chamfer1),0), "\nrounding1 and chamfer1 (possibly inherited from rounding and chamfer) cannot both be nonzero at the same corner.") + assert(all_zero(v_mul(rounding2,chamfer2),0), "\nrounding2 and chamfer2 (possibly inherited from rounding and chamfer) cannot both be nonzero at the same corner."); irounding1_temp = force_list(default(irounding1,irounding),4); irounding2_temp = force_list(default(irounding2,irounding),4); ichamfer1_temp = force_list(default(ichamfer1,ichamfer),4); @@ -1686,12 +1686,12 @@ module rect_tube( checkconflict1 = [for(i=[0:3]) if (is_def(irounding1_temp[i]) && is_def(ichamfer1_temp[i]) && irounding1_temp[i]!=0 && ichamfer1_temp[i]!=0) 1]==[]; checkconflict2 = [for(i=[0:3]) if (is_def(irounding2_temp[i]) && is_def(ichamfer2_temp[i]) && irounding2_temp[i]!=0 && ichamfer2_temp[i]!=0) 1]==[]; checks4 = - assert(checksignr1, "irounding/irounding1 must be non-negative") - assert(checksignr2, "irounding/irounding2 must be non-negative") - assert(checksignc1, "ichamfer/ichamfer1 must be non-negative") - assert(checksignc2, "ichamfer/ichamfer2 must be non-negative") - assert(checkconflict1, "irounding1 and ichamfer1 (possibly inherited from irounding and ichamfer) cannot both be nonzero at the same corner") - assert(checkconflict2, "irounding2 and ichamfer2 (possibly inherited from irounding and ichamfer) cannot both be nonzero at the same corner"); + assert(checksignr1, "\nirounding/irounding1 must be non-negative.") + assert(checksignr2, "\nirounding/irounding2 must be non-negative.") + assert(checksignc1, "\nichamfer/ichamfer1 must be non-negative.") + assert(checksignc2, "\nichamfer/ichamfer2 must be non-negative.") + assert(checkconflict1, "\nirounding1 and ichamfer1 (possibly inherited from irounding and ichamfer) cannot both be nonzero at the same corner.") + assert(checkconflict2, "\nirounding2 and ichamfer2 (possibly inherited from irounding and ichamfer) cannot both be nonzero at the same corner."); irounding1 = _rect_tube_rounding(1,irounding1_temp, rounding1, ichamfer1_temp, size1, isize1); irounding2 = _rect_tube_rounding(1,irounding2_temp, rounding2, ichamfer2_temp, size2, isize2); ichamfer1 = _rect_tube_rounding(1/sqrt(2),ichamfer1_temp, chamfer1, irounding1_temp, size1, isize1); @@ -1756,7 +1756,7 @@ function rect_tube( // --- // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `FRONT+LEFT+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` +// orient = Vector to rotate top toward, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // Named Anchors: // "hypot" = Center of angled wedge face, perpendicular to that face. // "hypot_left" = Left side of angled wedge face, bisecting the angle between the left side and angled faces. @@ -1784,7 +1784,7 @@ function rect_tube( module wedge(size=[1, 1, 1], center, anchor, spin=0, orient=UP) { size = force_list(size,3); - check=assert(is_vector(size,3) && all_positive(size), "\nsize must be a positive scalar or 3-vector"); + check=assert(is_vector(size,3) && all_positive(size), "\nsize must be a positive scalar or 3-vector."); anchor = get_anchor(anchor, center, -[1,1,1], -[1,1,1]); vnf = wedge(size, anchor="origin"); spindir = unit([0,-size.y,size.z]); @@ -1816,7 +1816,7 @@ module wedge(size=[1, 1, 1], center, anchor, spin=0, orient=UP) function wedge(size=[1,1,1], center, anchor, spin=0, orient=UP) = let( size = force_list(size,3), - check=assert(is_vector(size,3) && all_positive(size), "\nsize must be a positive scalar or 3-vector"), + check=assert(is_vector(size,3) && all_positive(size), "\nsize must be a positive scalar or 3-vector."), anchor = get_anchor(anchor, center, -[1,1,1], -[1,1,1]), pts = [ [ 1,1,-1], [ 1,-1,-1], [ 1,-1,1], @@ -1861,7 +1861,7 @@ function wedge(size=[1,1,1], center, anchor, 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 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` +// orient = Vector to rotate top toward, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // Example: // octahedron(size=40); // Example: Anchors @@ -1881,7 +1881,7 @@ module octahedron(size=1, anchor=CENTER, spin=0, orient=UP) { function octahedron(size=1, anchor=CENTER, spin=0, orient=UP) = let( s = force_list(size,3)/2, - dummy=assert(is_vector(s,3) && all_positive(s), "\nsize must be a positive scalar or 3-vector"), + dummy=assert(is_vector(s,3) && all_positive(s), "\nsize must be a positive scalar or 3-vector."), vnf = [ [ [0,0,s.z], [s.x,0,0], [0,s.y,0], [-s.x,0,0], [0,-s.y,0], [0,0,-s.z] ], [ [0,2,1], [0,3,2], [0,4,3], [0,1,4], [5,1,2], [5,2,3], [5,3,4], [5,4,1] ] @@ -1923,7 +1923,7 @@ function octahedron(size=1, anchor=CENTER, spin=0, orient=UP) = // d = The diameter of the cylinder. // 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` +// orient = Vector to rotate top toward, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // Example: By Radius // xdistribute(30) { // cylinder(h=40, r=10); @@ -2015,7 +2015,7 @@ function cylinder(h, r1, r2, center, r, d, d1, d2, anchor, spin=0, orient=UP) = // You can use `h` and `l` interchangably, and all variants allow specifying size by either `r`|`d`, // or `r1`|`d1` and `r2`|`d2`. Note: the chamfers and rounding cannot be cumulatively longer than // the cylinder or cone's sloped side. The more specific parameters like chamfer1 or rounding2 override the more -// general ones like chamfer or rounding, so if you specify `rounding=3, chamfer2=3` you will get a chamfer at the top and +// general ones like chamfer or rounding, so if you specify `rounding=3, chamfer2=3` you get a chamfer at the top and // rounding at the bottom. You can specify extra height at either end for use with difference(); the extra height is ignored by // anchoring. // . @@ -2043,7 +2043,7 @@ function cylinder(h, r1, r2, center, r, d, d1, d2, anchor, spin=0, orient=UP) = // stroke(arc(cp=[2,10], angle=[0,-30], n=20, r=5), width=.18, endcaps="arrow2"); // stroke(arc(cp=[12,-10], angle=[0,30], n=20, r=5), width=.18, endcaps="arrow2"); // } -// Figure(2D,Big,NoAxes,VPR = [0, 0, 0], VPT = [0,0,0], VPD = 82): The cone in this example is narrow but has the same slope. With negative chamfers, the angle A=30 degrees is on the outside. The chamfers are again quite different looking. As before, the default will feature two congruent angles, and in this case it happens at the bottom of the cone but not the top. The straight arrows again show the size of the chamfer. +// Figure(2D,Big,NoAxes,VPR = [0, 0, 0], VPT = [0,0,0], VPD = 82): The cone in this example is narrow but has the same slope. With negative chamfers, the angle A=30 degrees is on the outside. The chamfers are again quite different looking. As before, the default features two congruent angles, and in this case it happens at the bottom of the cone but not the top. The straight arrows again show the size of the chamfer. // r1=10-7.5;r2=20-7.5; // color("lightgray") // projection() @@ -2089,7 +2089,7 @@ function cylinder(h, r1, r2, center, r, d, d1, d2, anchor, spin=0, orient=UP) = // teardrop = If given as a number, rounding around the bottom edge of the cylinder won't exceed this many degrees from horizontal. If true, the limit angle is 45 degrees. Default: `false` // clip_angle = If given as a number, rounding around the bottom edge of the cylinder won't exceed this many degrees from horizontal, with the rounding stopping at the bottom of the cylinder. Default: (no clipping) // 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 (2-vector or scalar) for the textures. Actual texture sizes will be scaled somewhat to evenly fit the available surface. Default: `[5,5]` +// tex_size = An optional 2D target size (2-vector or scalar) for the textures. Actual texture sizes are scaled somewhat to fit evenly on the available surface. Default: `[5,5]` // tex_reps = If given instead of tex_size, a scalar or 2-vector giving the integer number of texture tile repetitions in the horizontal and vertical directions. // tex_inset = If numeric, lowers the texture into the surface by the specified proportion, e.g. 0.5 would lower it halfway into the surface. If `true`, insets by exactly its full depth. Default: `false` // tex_rot = Rotate texture by specified angle, which must be a multiple of 90 degrees. Default: 0 @@ -2099,7 +2099,7 @@ function cylinder(h, r1, r2, center, r, d, d1, d2, anchor, spin=0, orient=UP) = // style = {{vnf_vertex_array()}} style used to triangulate heightfield textures. Default: "min_edge" // 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` +// orient = Vector to rotate top toward, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // Example: By radius // xdistribute(30) { // cyl(l=40, r=10); @@ -2236,15 +2236,15 @@ function _cyl_path( round1 = first_defined([rounding1, if (is_undef(chamfer1)) rounding, 0]), round2 = first_defined([rounding2, if (is_undef(chamfer2)) rounding, 0]), checks1 = - assert(is_finite(_chamf1), "chamfer1 must be a finite number if given.") - assert(is_finite(_chamf2), "chamfer2 must be a finite number if given.") - assert(is_finite(chang1) && chang1>0, "chamfang1 must be a positive number if given.") - assert(is_finite(chang2) && chang2>0, "chamfang2 must be a positive number if given.") - assert(chang1<90+sign(_chamf1)*vang, "chamfang1 must be smaller than the cone face angle") - assert(chang2<90-sign(_chamf2)*vang, "chamfang2 must be smaller than the cone face angle") - assert(num_defined([chamfer1,rounding1])<2, "cannot define both chamfer1 and rounding1") - assert(num_defined([chamfer2,rounding2])<2, "cannot define both chamfer2 and rounding2") - assert(num_defined([chamfer,rounding])<2, "cannot define both chamfer and rounding") + assert(is_finite(_chamf1), "\nchamfer1 must be a finite number if given.") + assert(is_finite(_chamf2), "\nchamfer2 must be a finite number if given.") + assert(is_finite(chang1) && chang1>0, "\nchamfang1 must be a positive number if given.") + assert(is_finite(chang2) && chang2>0, "\nchamfang2 must be a positive number if given.") + assert(chang1<90+sign(_chamf1)*vang, "\nchamfang1 must be smaller than the cone face angle.") + assert(chang2<90-sign(_chamf2)*vang, "\nchamfang2 must be smaller than the cone face angle.") + assert(num_defined([chamfer1,rounding1])<2, "\nCannot define both chamfer1 and rounding1.") + assert(num_defined([chamfer2,rounding2])<2, "\nCannot define both chamfer2 and rounding2.") + assert(num_defined([chamfer,rounding])<2, "\nCannot define both chamfer and rounding.") undef, unscale = noscale ? scale : 1, chamf1r = !_chamf1? 0 @@ -2278,14 +2278,14 @@ function _cyl_path( assert(clip_angle>=0 && clip_angle<=90) clip_angle ) - assert(is_finite(round1), "rounding1 must be a number if given.") - assert(is_finite(round2), "rounding2 must be a number if given.") - assert(chamf1r/scale <= r1, "chamfer1 is larger than the r1 radius of the cylinder.") - assert(chamf2r/scale <= r2, "chamfer2 is larger than the r2 radius of the cylinder.") - assert(roundlen1*unscale/scale <= r1, "size of rounding1 is larger than the r1 radius of the cylinder.") - assert(roundlen2*unscale/scale <= r2, "size of rounding2 is larger than the r2 radius of the cylinder.") - assert(dy1+dy2 <= facelen, "Chamfers/roundings don't fit on the cylinder/cone. They exceed the length of the cylinder/cone face.") - assert(td_ang==90 || clip_ang==90, "teardrop= and clip_angle= are mutually exclusive options.") + assert(is_finite(round1), "\nrounding1 must be a number if given.") + assert(is_finite(round2), "\nrounding2 must be a number if given.") + assert(chamf1r/scale <= r1, "\nchamfer1 is larger than the r1 radius of the cylinder.") + assert(chamf2r/scale <= r2, "\nchamfer2 is larger than the r2 radius of the cylinder.") + assert(roundlen1*unscale/scale <= r1, "\nSize of rounding1 is larger than the r1 radius of the cylinder.") + assert(roundlen2*unscale/scale <= r2, "\nSize of rounding2 is larger than the r2 radius of the cylinder.") + assert(dy1+dy2 <= facelen, "\nChamfers/roundings don't fit on the cylinder/cone. They exceed the length of the cylinder/cone face.") + assert(td_ang==90 || clip_ang==90, "\nteardrop= and clip_angle= are mutually exclusive options.") [ if (!approx(chamf1r,0)) each [ @@ -2330,9 +2330,9 @@ function cyl( extra, extra1, extra2, anchor, spin=0, orient=UP ) = - assert(num_defined([style,tex_style])<2, "In cyl() the 'tex_style' parameter has been replaced by 'style'. You cannot give both.") - assert(num_defined([tex_reps,tex_counts])<2, "In cyl() the 'tex_counts' parameter has been replaced by 'tex_reps'. You cannot give both.") - assert(num_defined([tex_scale,tex_depth])<2, "In cyl() the 'tex_scale' parameter has been replaced by 'tex_depth'. You cannot give both.") + assert(num_defined([style,tex_style])<2, "\nIn cyl() the 'tex_style' parameter has been replaced by 'style'. You cannot give both.") + assert(num_defined([tex_reps,tex_counts])<2, "\nIn cyl() the 'tex_counts' parameter has been replaced by 'tex_reps'. You cannot give both.") + assert(num_defined([tex_scale,tex_depth])<2, "\nIn cyl() the 'tex_scale' parameter has been replaced by 'tex_depth'. You cannot give both.") let( style = is_def(tex_style)? echo("In cyl() the 'tex_style' parameter is deprecated and has been replaced by 'style'")tex_style : default(style,"min_edge"), @@ -2351,11 +2351,11 @@ function cyl( extra1 = first_defined([extra1,extra,0]), extra2 = first_defined([extra2,extra,0]) ) - assert(all_nonnegative([extra1,extra2]), "extra/extra1/extra2 must be positive") - assert(is_finite(l), "l/h/length/height must be a finite number.") - assert(is_finite(r1) && r1>=0, "r/r1/d/d1 must be a non-negative number.") - assert(is_finite(r2) && r2>=0, "r2 or d2 must be a non-negative number.") - assert(is_vector(shift,2), "shift must be a 2D vector.") + assert(all_nonnegative([extra1,extra2]), "\nextra/extra1/extra2 must be positive.") + assert(is_finite(l), "\nl/h/length/height must be a finite number.") + assert(is_finite(r1) && r1>=0, "\nr/r1/d/d1 must be a non-negative number.") + assert(is_finite(r2) && r2>=0, "\nr2 or d2 must be a non-negative number.") + assert(is_vector(shift,2), "\nshift must be a 2D vector.") let( vnf = !any_defined([chamfer, chamfer1, chamfer2, rounding, rounding1, rounding2, texture, extra1, extra2]) ? cylinder(h=l+extra1+extra2, r1=r1, r2=r2, center=true, $fn=sides) @@ -2448,14 +2448,14 @@ module cyl( anchor, spin=0, orient=UP ) { dummy= - assert(num_defined([style,tex_style])<2, "In cyl() the 'tex_style' parameters has been replaced by 'style'. You cannot give both.") - assert(num_defined([tex_reps,tex_counts])<2, "In cyl() the 'tex_counts' parameters has been replaced by 'tex_reps'. You cannot give both.") - assert(num_defined([tex_scale,tex_depth])<2, "In cyl() the 'tex_scale' parameter has been replaced by 'tex_depth'. You cannot give both."); - style = is_def(tex_style)? echo("In cyl() the 'tex_style' parameter is deprecated and has been replaced by 'style'")tex_style + assert(num_defined([style,tex_style])<2, "\nIn cyl() the 'tex_style' parameters has been replaced by 'style'. You cannot give both.") + assert(num_defined([tex_reps,tex_counts])<2, "\nIn cyl() the 'tex_counts' parameters has been replaced by 'tex_reps'. You cannot give both.") + assert(num_defined([tex_scale,tex_depth])<2, "\nIn cyl() the 'tex_scale' parameter has been replaced by 'tex_depth'. You cannot give both."); + style = is_def(tex_style)? echo("In cyl() the 'tex_style' parameter is deprecated and has been replaced by 'style'.") tex_style : default(style,"min_edge"); - tex_reps = is_def(tex_counts)? echo("In cyl() the 'tex_counts' parameter is deprecated and has been replaced by 'tex_reps'")tex_counts + tex_reps = is_def(tex_counts)? echo("In cyl() the 'tex_counts' parameter is deprecated and has been replaced by 'tex_reps'.")tex_counts : tex_reps; - tex_depth = is_def(tex_scale)? echo("In rotate_sweep() the 'tex_scale' parameter is deprecated and has been replaced by 'tex_depth'")tex_scale + tex_depth = is_def(tex_scale)? echo("In rotate_sweep() the 'tex_scale' parameter is deprecated and has been replaced by 'tex_depth'.")tex_scale : default(tex_depth,1); l = one_defined([l, h, length, height],"l,h,length,height",dflt=1); _r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1); @@ -2839,9 +2839,9 @@ module zcyl( // wall thickness. // . // Chamfering and rounding lengths are measured based on the corners of the object except for the inner diameter when `circum=true`, in -// which case chamfers and roundings are measured from the facets. This only matters when `$fn` is small. +// which case chamfers and roundings are measured from the facets. This matters only when `$fn` is small. // . -// Attachment to the tube will place objects on the **outside** of the tube. +// Attachment to the tube places objects on the **outside** of the tube. // If you need to anchor to the inside of a tube, use {{attach_part()}} with the part name "inside" // to switch goeomtry to the inside. // Usage: Basic cylindrical tube, specifying inner and outer radius or diameter @@ -2873,7 +2873,7 @@ module zcyl( // id1 = Inner diameter of bottom of tube. // id2 = Inner diameter of top of tube. // ifn = Set the number of facets on the inside of the tube. -// circum = If true, the tube hole will circumscribe the circle of the given size. Otherwise inscribes. Default: `false` +// circum = If true, the tube hole circumscribes the circle of the given size. Otherwise inscribes. Default: `false` // shift = [X,Y] amount to shift the center of the top end with respect to the center of the bottom end. // rounding = The radius of the rounding on the ends of the tube. Default: none. // rounding1 = The radius of the rounding on the bottom end of the tube. @@ -2899,7 +2899,7 @@ module zcyl( // realign = If true, rotate the inner and outer parts tube by half the angle of one face so that a face is aligned at the X+ axis. Default: False // 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` +// orient = Vector to rotate top toward, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // // Example: These all Produce the Same Tube // tube(h=30, or=40, wall=5); @@ -2984,23 +2984,23 @@ module tube( ir1 = default(irr1, u_sub(orr1,wall)); ir2 = default(irr2, u_sub(orr2,wall)); checks = - assert(is_vector(shift,2), "shift must be a 2D vector.") - assert(all_defined([r1, r2, ir1, ir2]), "Must specify two of inner radius/diam, outer radius/diam, and wall width.") - assert(num_defined([rounding,chamfer])<2, "Cannot give both rounding and chamfer") - assert(num_defined([irounding,ichamfer])<2, "Cannot give both irounding and ichamfer") - assert(num_defined([orounding,ochamfer])<2, "Cannot give both orounding and ochamfer") - assert(num_defined([rounding1,chamfer1])<2, "Cannot give both rounding1 and chamfer1") - assert(num_defined([irounding1,ichamfer1])<2, "Cannot give both irounding1 and ichamfern") - assert(num_defined([orounding1,ochamfer1])<2, "Cannot give both orounding1 and ochamfer1") - assert(num_defined([rounding2,chamfer2])<2, "Cannot give both rounding2 and chamfer2") - assert(num_defined([irounding2,ichamfer2])<2, "Cannot give both irounding2 and ichamfern") - assert(num_defined([orounding2,ochamfer2])<2, "Cannot give both orounding2 and ochamfer2"); + assert(is_vector(shift,2), "\n'shift' must be a 2D vector.") + assert(all_defined([r1, r2, ir1, ir2]), "\nMust specify two of inner radius/diam, outer radius/diam, and wall width.") + assert(num_defined([rounding,chamfer])<2, "\nCannot give both rounding and chamfer.") + assert(num_defined([irounding,ichamfer])<2, "\nCannot give both irounding and ichamfer.") + assert(num_defined([orounding,ochamfer])<2, "\nCannot give both orounding and ochamfer.") + assert(num_defined([rounding1,chamfer1])<2, "\nCannot give both rounding1 and chamfer1.") + assert(num_defined([irounding1,ichamfer1])<2, "\nCannot give both irounding1 and ichamfern.") + assert(num_defined([orounding1,ochamfer1])<2, "\nCannot give both orounding1 and ochamfer1.") + assert(num_defined([rounding2,chamfer2])<2, "\nCannot give both rounding2 and chamfer2.") + assert(num_defined([irounding2,ichamfer2])<2, "\nCannot give both irounding2 and ichamfern.") + assert(num_defined([orounding2,ochamfer2])<2, "\nCannot give both orounding2 and ochamfer2."); names = ["irounding","orounding","rounding","irounding1","irounding2","orounding1","orounding2", "ichamfer","ochamfer","chamfer","ichamfer1","ichamfer2","ochamfer1","ochamfer2"]; vals = [irounding,orounding,rounding,irounding1,irounding2,orounding1,orounding2, ichamfer,ochamfer,chamfer,ichamfer1,ichamfer2,ochamfer1,ochamfer2]; bad = [for(i=idx(names)) if (is_def(vals[i]) && !is_finite(vals[i])) i]; - checks2 = assert(bad==[],str("Rounding/chamfer parameters must be numbers. The following are invalid: ", + checks2 = assert(bad==[],str("\nRounding/chamfer parameters must be numbers. The following are invalid: ", select(names,bad))); findval = function (factor,vlist,i=0) i>=len(vlist) || is_def(vlist[i][1]) ? undef @@ -3017,10 +3017,10 @@ module tube( /* This is too restrictive, at least on cones dummy = - assert( first_defined([irounding1,ichamfer1,0])+first_defined([orounding1,ochamfer1,0]) <= r1-ir1, "Chamfer/rounding doesn't fit at bottom") - assert( first_defined([irounding2,ichamfer2,0])+first_defined([orounding2,ochamfer2,0]) <= r2-ir2, "Chamfer/rounding doesn't fit at top") - assert( -first_defined([irounding1,ichamfer1,0])0, "length must be positive"); + dummy=assert(is_finite(length) && length>0, "\nlength must be positive."); r1 = get_radius(r=r, r1=r1, d=d, d1=d1); r2 = get_radius(r=r, r1=r2, d=d, d1=d2); tip_y1 = r1/cos(90-ang); @@ -3813,7 +3813,7 @@ function teardrop(h, r, ang=45, cap_h, r1, r2, d, d1, d2, cap_h1, cap_h2, chamf r1 = get_radius(r=r, r1=r1, d=d, d1=d1, dflt=1), r2 = get_radius(r=r, r1=r2, d=d, d1=d2, dflt=1), length = one_defined([l, h, length, height],"l,h,length,height"), - dummy0=assert(is_finite(length) && length>0, "length must be positive"), + dummy0=assert(is_finite(length) && length>0, "\nlength must be positive."), cap_h1 = first_defined([cap_h1, cap_h]), cap_h2 = first_defined([cap_h2, cap_h]), bot_corner1 = first_defined([bot_corner1, bot_corner]), @@ -3829,14 +3829,14 @@ function teardrop(h, r, ang=45, cap_h, r1, r2, d, d1, d2, cap_h1, cap_h2, chamf _cap_h2 = min(default(cap_h2, tip_y2), tip_y2), capvec = unit([0, _cap_h1-_cap_h2, length]), dummy= - assert(abs(chamfer1)+abs(chamfer2) <= length,"chamfers are too big to fit in the length") - assert(chamfer1<=r1 && chamfer2<=r2, "Chamfers cannot be larger than raduis") + assert(abs(chamfer1)+abs(chamfer2) <= length,"\nChamfers are too big to fit in the length.") + assert(chamfer1<=r1 && chamfer2<=r2, "\nChamfers cannot be larger than radius.") assert(bot_corner1==0 || bot_corner1>=chamfer1, "\nchamfer1 doesn't work with bottom corner: must have chamfer1 <= bot_corner1") assert(bot_corner2==0 || bot_corner2>=chamfer2, "\nchamfer2 doesn't work with bottom corner: must have chamfer2 <= bot_corner2") assert(bot_corner1==0 || bot_corner1>chamfer1 || sides%2==(realign?1:0), str("\nWith chamfer1==bot_corner1 and realign=",realign," must have ",realign?"odd":"even"," number of sides, but sides=",sides)) - assert(is_undef(cap_h1) || cap_h1-chamfer1 > r1*sin(ang), "chamfer1 is too big to work with the specified cap_h1") - assert(is_undef(cap_h2) || cap_h2-chamfer2 > r2*sin(ang), "chamfer2 is too big to work with the specified cap_h2"), + assert(is_undef(cap_h1) || cap_h1-chamfer1 > r1*sin(ang), "\nchamfer1 is too big to work with the specified cap_h1.") + assert(is_undef(cap_h2) || cap_h2-chamfer2 > r2*sin(ang), "\nchamfer2 is too big to work with the specified cap_h2."), cprof1 = r1==chamfer1 ? repeat([0,0],len(profile1)) : teardrop2d(r=r1-chamfer1, ang=ang, cap_h=u_add(cap_h1,-chamfer1), bot_corner=bot_corner1==0?0:bot_corner1-chamfer1, $fn=sides, circum=circum, realign=realign,_extrapt=true), @@ -3883,7 +3883,7 @@ function teardrop(h, r, ang=45, cap_h, r1, r2, d, d1, d2, cap_h1, cap_h2, chamf // d = diameter of spherical portion of bottom. // 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` +// orient = Vector to rotate top toward, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // // Named Anchors: // "cap" = The center of the top of the cap, oriented with the cap face normal. @@ -3970,10 +3970,10 @@ function onion(r, ang=45, cap_h, d, anchor=CENTER, spin=0, orient=UP) = // can differ significantly in the actual size of their characters. Typographers // customarily specify the size in the units of "points". A point is 1/72 inch. In // OpenSCAD, you specify the size in OpenSCAD units (often treated as millimeters for 3d -// printing), so if you want points you will need to perform a suitable unit conversion. +// printing), so if you want points you need to perform a suitable unit conversion. // In addition, the OpenSCAD font system has a bug: if you specify size=s you will // instead get a font whose size is s/0.72. For many fonts this means the size of -// capital letters will be approximately equal to s, because it is common for fonts to +// capital letters is approximately equal to s, because it is common for fonts to // use about 70% of their height for the ascenders in the font. To get the customary // font size, you should multiply your desired size by 0.72. // . @@ -3982,7 +3982,7 @@ function onion(r, ang=45, cap_h, d, anchor=CENTER, spin=0, orient=UP) = // Arguments: // text = Text to create. // h / height / thickness = Extrusion height for the text. Default: 1 -// size = The font will be created at this size divided by 0.72. Default: 10 +// size = The font is created at this size divided by 0.72. Default: 10 // font = Font to use. Default: "Liberation Sans" (standard OpenSCAD default) // --- // spacing = The relative spacing multiplier between characters. Default: `1.0` @@ -3993,7 +3993,7 @@ function onion(r, ang=45, cap_h, d, anchor=CENTER, spin=0, orient=UP) = // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `"baseline"` // center = Center the text. Equivalent to `atype="center", anchor=CENTER`. Default: false // 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` +// orient = Vector to rotate top toward. See [orient](attachments.scad#subsection-orient). Default: `UP` // Anchor Types: // baseline = Anchor center is relative to text baseline // ycenter = Anchor center is relative to the actual y direction center of the text @@ -4009,9 +4009,9 @@ module text3d(text, h, size=10, font, spacing=1.0, direction="ltr", language="en anchor, spin=0, orient=UP) { no_children($children); h = one_defined([h,height,thickness],"h,height,thickness",dflt=1); - assert(is_undef(atype) || in_list(atype,["ycenter","baseline"]), "atype must be \"ycenter\" or \"baseline\""); + assert(is_undef(atype) || in_list(atype,["ycenter","baseline"]), "\natype must be \"ycenter\" or \"baseline\"."); assert(is_bool(center)); - assert(is_undef($attach_to),"text3d() does not support parent-child anchor attachment with two parameters"); + assert(is_undef($attach_to),"\ntext3d() does not support parent-child anchor attachment with two parameters."); atype = default(atype, center?"ycenter":"baseline"); anchor = default(anchor, center?CENTER:LEFT); geom = attach_geom(size=[size,size,h]); @@ -4071,17 +4071,17 @@ function _cut_interp(pathcut, path, data) = // or user specified letter spacing. The path can be 2D or 3D. In 2D the text appears along the path with letters upright // as determined by the path direction. In 3D by default letters are positioned on the tangent line to the path with the path normal // pointing toward the reader. The path normal points away from the center of curvature (the opposite of the normal produced -// by path_normals()). Note that this means that if the center of curvature switches sides the text will flip upside down. +// by path_normals()). Note that this means that if the center of curvature switches sides, the text flips upside down. // If you want text on such a path you must supply your own normal or top vector. // . -// Text appears starting at the beginning of the path, so if the 3D path moves right to left -// then a left-to-right reading language will display in the wrong order. (For a 2D path text will appear upside down.) +// Text appears starting at the beginning of the path, so if the 3D path moves right to left, +// then a left-to-right reading language displays in the wrong order (for a 2D path text appears upside down). // The text for a 3D path appears positioned to be read from "outside" of the curve (from a point on the other side of the // curve from the center of curvature). If you need the text to read properly from the inside, you can set reverse to // true to flip the text, or supply your own normal. // . // If you do not have the experimental textmetrics feature enabled then you must specify the space for the letters -// using lettersize, which can be a scalar or array. You will have the easiest time getting good results by using +// using lettersize, which can be a scalar or array. You can easily get good results by using // a monospace font such as "Liberation Mono". Note that even with text metrics, spacing may be different because path_text() // doesn't do kerning to adjust positions of individual glyphs. Also if your font has ligatures they won't be used. // . @@ -4091,7 +4091,7 @@ function _cut_interp(pathcut, path, data) = // You can specify your own normal by setting `normal` to a direction or a list of directions. Your normal vector should // point toward the reader. You can also specify // top, which directs the top of the letters in a desired direction. If you specify your own directions and they -// are not perpendicular to the path then the direction you specify will take priority and the +// are not perpendicular to the path, then the direction you specify takes priority and the // letters will not rest on the tangent line of the path. Note that the normal or top directions that you // specify must not be parallel to the path. // . @@ -4105,10 +4105,10 @@ function _cut_interp(pathcut, path, data) = // can differ significantly in the actual size of their characters. Typographers // customarily specify the size in the units of "points". A point is 1/72 inch. In // OpenSCAD, you specify the size in OpenSCAD units (often treated as millimeters for 3d -// printing), so if you want points you will need to perform a suitable unit conversion. -// In addition, the OpenSCAD font system has a bug: if you specify size=s you will +// printing), so if you want points you need to perform a suitable unit conversion. +// In addition, the OpenSCAD font system has a bug: if you specify size=s you // instead get a font whose size is s/0.72. For many fonts this means the size of -// capital letters will be approximately equal to s, because it is common for fonts to +// capital letters is approximately equal to s, because it is common for fonts to // use about 70% of their height for the ascenders in the font. To get the customary // font size, you should multiply your desired size by 0.72. // . @@ -4117,18 +4117,18 @@ function _cut_interp(pathcut, path, data) = // Arguments: // path = path to place the text on // text = text to create -// size = The font will be created at this size divided by 0.72. +// size = The font is created at this size divided by 0.72. // thickness / h / height = thickness of letters (not allowed for 2D path) // font = Font to use. Default: "Liberation Sans" (standard OpenSCAD default) // --- // lettersize = scalar or array giving size of letters // center = center text on the path instead of starting at the first point. Default: false -// offset = distance to shift letters "up" (towards the reader). Not allowed for 2D path. Default: 0 -// normal = direction or list of directions pointing towards the reader of the text. Not allowed for 2D path. +// offset = distance to shift letters "up" (toward the reader). Not allowed for 2D path. Default: 0 +// normal = direction or list of directions pointing toward the reader of the text. Not allowed for 2D path. // top = direction or list of directions pointing toward the top of the text // reverse = reverse the letters if true. Not allowed for 2D path. Default: false // textmetrics = if set to true and lettersize is not given then use the experimental textmetrics feature. You must be running a dev snapshot that includes this feature and have the feature turned on in your preferences. Default: false -// valign = align text to the path using "top", "bottom", "center" or "baseline". You can also adjust position with a numerical offset as in "top-5" or "bottom+2". This only works with textmetrics enabled. You can give a simple numerical offset, which will be relative to the baseline and works even without textmetrics. Default: "baseline" +// valign = align text to the path using "top", "bottom", "center" or "baseline". You can also adjust position with a numerical offset as in "top-5" or "bottom+2". This works only with textmetrics enabled. You can give a simple numerical offset, which is relative to the baseline and works even without textmetrics. Default: "baseline" // kern = scalar or array giving spacing adjusments between each letter. If it's an array it should have one less entry than the text string. Default: 0 // language = text language, passed to OpenSCAD `text()`. Default: "en" // script = text script, passed to OpenSCAD `text()`. Default: "latin" @@ -4168,7 +4168,7 @@ function _cut_interp(pathcut, path, data) = // path = arc(100, points = [[-20, 0, 20], [0,0,5], [20,0,20]]); // color("red")stroke(path,width=.2); // path_text(path, "Example Text", size=5, lettersize=5/1.2, font="Liberation Mono", top=UP); -// Example(3D,Med,NoScales): This sine wave wrapped around the cylinder has a twisting normal that produces wild letter layout. We fix it with a custom normal which is different at every path point. +// Example(3D,Med,NoScales): This sine wave wrapped around the cylinder has a twisting normal that produces wild letter layout. We fix it with a custom normal that is different at every path point. // path = [for(theta = [0:360]) [25*cos(theta), 25*sin(theta), 4*cos(theta*4)]]; // normal = [for(theta = [0:360]) [cos(theta), sin(theta),0]]; // zrot(-120) @@ -4198,19 +4198,19 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers textmetrics=false, kern=0, height,h, valign="baseline", language, script) { no_children($children); - dummy2=assert(is_path(path,[2,3]),"Must supply a 2d or 3d path") - assert(num_defined([normal,top])<=1, "Cannot define both \"normal\" and \"top\"") - assert(all_positive([size]), "Must give positive text size"); + dummy2=assert(is_path(path,[2,3]),"\nMust supply a 2d or 3d path.") + assert(num_defined([normal,top])<=1, "\nCannot define both \"normal\" and \"top\".") + assert(all_positive([size]), "\nMust give positive text size."); dim = len(path[0]); normalok = is_undef(normal) || is_vector(normal,3) || (is_path(normal,3) && len(normal)==len(path)); topok = is_undef(top) || is_vector(top,dim) || (dim==2 && is_vector(top,3) && top[2]==0) || (is_path(top,dim) && len(top)==len(path)); - dummy4 = assert(dim==3 || !any_defined([thickness,h,height]), "Cannot give a thickness or height with 2d path") - assert(dim==3 || !reverse, "Reverse not allowed with 2d path") - assert(dim==3 || offset==0, "Cannot give offset with 2d path") - assert(dim==3 || is_undef(normal), "Cannot define \"normal\" for a 2d path, only \"top\"") - assert(normalok,"\"normal\" must be a vector or path compatible with the given path") - assert(topok,"\"top\" must be a vector or path compatible with the given path"); + dummy4 = assert(dim==3 || !any_defined([thickness,h,height]), "\nCannot give a thickness or height with 2d path.") + assert(dim==3 || !reverse, "\nReverse not allowed with 2d path.") + assert(dim==3 || offset==0, "\nCannot give offset with 2d path.") + assert(dim==3 || is_undef(normal), "\nCannot define \"normal\" for a 2d path, only \"top\".") + assert(normalok,"\n\"normal\" must be a vector or path compatible with the given path.") + assert(topok,"\n\"top\" must be a vector or path compatible with the given path."); thickness = one_defined([thickness,h,height],"thickness,h,height",dflt=1); normal = is_vector(normal) ? repeat(normal, len(path)) : is_def(normal) ? normal @@ -4221,11 +4221,11 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers : undef; kern = force_list(kern, len(text)-1); - dummy3 = assert(is_list(kern) && len(kern)==len(text)-1, "kern must be a scalar or list whose length is len(text)-1"); + dummy3 = assert(is_list(kern) && len(kern)==len(text)-1, "\nkern must be a scalar or list whose length is len(text)-1."); lsize = is_def(lettersize) ? force_list(lettersize, len(text)) : textmetrics ? [for(letter=text) let(t=textmetrics(letter, font=font, size=size)) t.advance[0]] - : assert(false, "textmetrics disabled: Must specify letter size"); + : assert(false, "\ntextmetrics disabled: Must specify letter size."); lcenter = convolve(lsize,[1,1]/2)+[0,each kern,0] ; textlength = sum(lsize)+sum(kern); @@ -4235,7 +4235,7 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers : textmetrics(text, font=font, size=size).descent; vadjustment = is_num(valign) ? -valign - : !textmetrics ? assert(valign=="baseline","valign requires textmetrics support") 0 + : !textmetrics ? assert(valign=="baseline","\nvalign requires textmetrics support.") 0 : let( table = [ ["baseline", 0], @@ -4245,10 +4245,10 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers ], match = [for(i=idx(table)) if (starts_with(valign,table[i][0])) i] ) - assert(len(match)==1, "Invalid valign value") + assert(len(match)==1, "\nInvalid valign value.") table[match[0]][1] - parse_num(substr(valign,len(table[match[0]][0]))); - dummy1 = assert(textlength<=path_length(path),"Path is too short for the text"); + dummy1 = assert(textlength<=path_length(path),"\nPath is too short for the text."); start = center ? (path_length(path) - textlength)/2 : 0; @@ -4263,9 +4263,9 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers tangent = pts[i][2]; checks = assert(!usetop || !approx(tangent*toppts[i],norm(top[i])*norm(tangent)), - str("Specified top direction parallel to path at character ",i)) + str("\nSpecified top direction parallel to path at character ",i)) assert(usetop || !approx(tangent*normpts[i],norm(normpts[i])*norm(tangent)), - str("Specified normal direction parallel to path at character ",i)); + str("\nSpecified normal direction parallel to path at character ",i)); adjustment = usetop ? (tangent*toppts[i])*toppts[i]/(toppts[i]*toppts[i]) : usernorm ? (tangent*normpts[i])*normpts[i]/(normpts[i]*normpts[i]) : [0,0,0]; @@ -4312,7 +4312,7 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers // Note that this module is the same as {{rounding_edge_mask()}}, except that it does not // apply the default "remove" tag and has a different default angle. // It can be convenient to {{attach()}} the fillet to the edge of a parent object. -// Many objects propagate the $edge_angle and $edge_length which are used as defaults for the fillet. +// Many objects propagate the $edge_angle and $edge_length, which are used as defaults for the fillet. // If you attach the fillet to the edge, it will be hovering in space and you need to apply {{yrot()}} // to place it on the parent object, generally either 90 degrees or -90 degrees dependong on which // face you want the fillet. @@ -4339,7 +4339,7 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers // chamfer2 = Chamfer size of chamfer at top end // 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` +// orient = Vector to rotate top toward, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // // Example: // union() { @@ -4416,7 +4416,7 @@ module fillet(l, r, ang, r1, r2, excess=0.01, d1, d2,d,length, h, height, anchor // the value of the function. You can specify the grid using a range or using a list of points that // need not be uniformly spaced. To create a valid polyhedron, the graph is closed at the sides and // a base is added below the smallest value. By default this base has unit thickness, but you can -// adjust it by setting the `base` parameter. If you set `base=0` then you will get a a zero thickness +// adjust it by setting the `base` parameter. If you set `base=0` then you get a a zero thickness // sheet that is not a manifold without sides or a bottom. // . // Your function may have have excessively large values at some points, or you may not know exactly @@ -4424,8 +4424,8 @@ module fillet(l, r, ang, r1, r2, excess=0.01, d1, d2,d,length, h, height, anchor // parameter (but not both). The `zclip` parameter is a 2-vector giving a minimum and maximum // value, either of which can be infinite. If the function falls below the minimum it is set // equal to the minimum, and if it rises above the maximum it is set equal to the maximum. The -// `zspan` parameter is a 2-vector giving a minum and maximum value which must both be finite. -// The function's values will be scaled and shifted to exactly cover the range you specifiy +// `zspan` parameter is a 2-vector giving a minum and maximum value that must both be finite. +// The function's values are scaled and shifted to exactly cover the range you specifiy // in `zspan`. // Arguments: // f = function literal accepting two arguments (x and y) that defines the function to compute @@ -4439,7 +4439,7 @@ module fillet(l, r, ang, r1, r2, excess=0.01, d1, d2,d,length, h, height, anchor // convexity = Max number of times a line could intersect a wall of the surface being formed. Module only. 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. See [spin](attachments.scad#subsection-spin). Default: `0` -// orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` +// orient = Vector to rotate top toward. See [orient](attachments.scad#subsection-orient). Default: `UP` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top toward, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // atype = Select "hull" or "intersect" anchor type. Default: "hull" @@ -4454,7 +4454,7 @@ module fillet(l, r, ang, r1, r2, excess=0.01, d1, d2,d,length, h, height, anchor // plot3d(func, [-180:4:180], [-180:4:180]); // Example(NoScales): Here we give the function inline and since it blows up we add clipping // plot3d(function (x,y) 1/norm([x,y]), [-2:.1:2], [-2:.1:2], zclip=[0,2],style="default"); -// Example(NoScales): Clipped edges often don't look very good and may be improved somewhat with more points. Here we give lists with varying point spacing to improve the point density around the clipped top of the shape. +// Example(NoScales): Clipped edges often don't look good and may be improved somewhat with more points. Here we give lists with varying point spacing to improve the point density around the clipped top of the shape. // range = concat( // lerpn(-2,-1,10,endpoint=false), // lerpn(-1,1,75,endpoint=false), @@ -4472,18 +4472,18 @@ module plot3d(f,x,y,zclip, zspan, base=1, anchor="origin", orient=UP, spin=0, at vnf_polyhedron(plot3d(f,x,y,zclip, zspan,base, style=style), atype=atype, orient=orient, anchor=anchor, cp=cp, convexity=convexity) children(); function plot3d(f,x,y,zclip, zspan, base=1, anchor="origin", orient=UP, spin=0, atype="hull", cp="box", style="default") = - assert(is_finite(base) && base>=0, "base must be a nonnegative number") - assert(is_vector(x) || valid_range(x), "x must be a vector or nonempty range") - assert(is_vector(y) || valid_range(y), "y must be a vector or nonempty range") - assert(is_range(x) || is_increasing(x, strict=true), "x must be strictly increasing") - assert(is_range(y) || is_increasing(y, strict=true), "y must be strictly increasing") - assert(num_defined([zclip,zspan])<2, "Cannot give both zclip and zspan") - assert(is_undef(zclip) || (is_list(zclip) && len(zclip)==2 && is_num(zclip[0]) && is_num(zclip[1])), "zclip must be a list of two values (which may be infinite)") - assert(is_undef(zspan) || (is_vector(zspan,2) && zspan[0]=0, "\nbase must be a nonnegative number.") + assert(is_vector(x) || valid_range(x), "\nx must be a vector or nonempty range.") + assert(is_vector(y) || valid_range(y), "\ny must be a vector or nonempty range.") + assert(is_range(x) || is_increasing(x, strict=true), "\nx must be strictly increasing.") + assert(is_range(y) || is_increasing(y, strict=true), "\ny must be strictly increasing.") + assert(num_defined([zclip,zspan])<2, "\nCannot give both zclip and zspan.") + assert(is_undef(zclip) || (is_list(zclip) && len(zclip)==2 && is_num(zclip[0]) && is_num(zclip[1])), "zclip must be a list of two values (which may be infinite).") + assert(is_undef(zspan) || (is_vector(zspan,2) && zspan[0]1 && len(data)>1, "x and y must both provide at least 2 points"), + dummy=assert(len(data[0])>1 && len(data)>1, "\nx and y must both provide at least 2 points."), minval = min(column(flatten(data),2)), maxval = max(column(flatten(data),2)), sdata = is_undef(zspan) ? data @@ -4525,15 +4525,15 @@ function plot3d(f,x,y,zclip, zspan, base=1, anchor="origin", orient=UP, spin=0, // Given a function literal, `f`, sets `r=f(theta,z)` over a range of theta and z values, and uses the // computed r values to define the offset from a cylinder or surface of revolution. You can specify // the theta range as a `angle` to give an angle range in degrees or with `arclength` to give an arc length -// range in distance units. Your function will receive its parameters in +// range in distance units. Your function receives its parameters in // the form you specify, as angle or as arclength. If you use `angle` then as the radius decreases, the // function shrinks in the horizontal direction to fit. If you use `arclength` distance is preserved for -// the function and as you move toward the top of a cone, the function will occupy a larger amount +// the function and as you move toward the top of a cone, the function occupies a larger amount // of total angle so that the arc length stays the same. // . -// If the computed value produces a radius smaller than zero it will be rounded up to 0.01. You can +// If the computed value produces a radius smaller than zero, it is rounded up to 0.01. You can // specify a cylinder using the usual length and -// radius or diameter parameters, or you can give `path`, a path which whose x values are strictly positive +// radius or diameter parameters, or you can give `path`, a path having strictly positive x values // to define the textured surface of revolution. // . // Your function may have have excessively large values at some points, or you may not know exactly @@ -4541,8 +4541,8 @@ function plot3d(f,x,y,zclip, zspan, base=1, anchor="origin", orient=UP, spin=0, // parameter (but not both). The `rclip` parameter is a 2-vector giving a minimum and maximum // value, either of which can be infinite. If the function falls below the minimum it is set // equal to the minimum, and if it rises above the maximum it is set equal to the maximum. The -// `rspan` parameter is a 2-vector giving a minum and maximum value which must both be finite. -// The function's values will be scaled and shifted to exactly cover the range you specifiy +// `rspan` parameter is a 2-vector giving a minum and maximum value, which must both be finite. +// The function's values are scaled and shifted to exactly cover the range you specifiy // in `rspan`. // . // The default is to erect the function normal to the surface. You can also set `horiz=true` to @@ -4566,7 +4566,7 @@ function plot3d(f,x,y,zclip, zspan, base=1, anchor="origin", orient=UP, spin=0, // convexity = Max number of times a line could intersect a wall of the surface being formed. Module only. 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. See [spin](attachments.scad#subsection-spin). Default: `0` -// orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` +// orient = Vector to rotate top toward. See [orient](attachments.scad#subsection-orient). Default: `UP` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top toward, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // atype = Select "hull" or "intersect" anchor type. Default: "hull" @@ -4612,12 +4612,12 @@ module plot_revolution(f,angle,z,arclength, path, rclip, rspan, horiz=false,r1,r function plot_revolution(f,angle,z,arclength, path, rclip, rspan, horiz=false,r1,r2,r,d1,d2,d, anchor="origin", orient=UP, spin=0, atype="hull", cp="centroid", style="min_edge", reverse=false) = - assert(num_defined([angle,arclength])==1, "must define exactly one of angle and arclength") - assert(is_undef(z) || is_vector(z) || valid_range(z), "z must be a vector or nonempty range") - assert(is_undef(path) || num_defined([r1,r2,d1,d2,r,d,z])==0, "Cannot define the z parameter or any radius or diameter parameters in combination with path") - assert(num_defined([rclip,rspan])<2, "Cannot give both rclip and rspan") + assert(num_defined([angle,arclength])==1, "\nMust define exactly one of angle and arclength.") + assert(is_undef(z) || is_vector(z) || valid_range(z), "\nz must be a vector or nonempty range.") + assert(is_undef(path) || num_defined([r1,r2,d1,d2,r,d,z])==0, "\nCannot define the z parameter or any radius or diameter parameters in combination with path.") + assert(num_defined([rclip,rspan])<2, "\nCannot give both rclip and rspan.") assert(is_undef(rclip) || (is_list(rclip) && len(rclip)==2 && is_finite(rclip[0]) && rclip[0]>0 && is_num(rclip[1])), - "rclip must be a list of two values (r[1] may be infinite)") + "\nrclip must be a list of two values (r[1] may be infinite).") assert(is_undef(rspan) || (is_vector(rspan,2) && rspan[0]>0 && rspan[0]1 && is_increasing(thetarange,strict=true), - "angle/arclength must be a strictly increasing array or range with at least 2 elements") - assert(is_def(path)|| (len(z)>1 && is_increasing(z, strict=true)),"z must be a strictly increasing array or range with at least 2 elements") - assert(is_def(arclength) || (last(thetarange)-thetarange[0])<=360, "angle span exceeds 360 degrees"), + "\nangle/arclength must be a strictly increasing array or range with at least 2 elements.") + assert(is_def(path)|| (len(z)>1 && is_increasing(z, strict=true)),"\nz must be a strictly increasing array or range with at least 2 elements.") + assert(is_def(arclength) || (last(thetarange)-thetarange[0])<=360, "\nAngle span exceeds 360°."), path = is_def(path) ? path : let( rvals = add_scalar(add_scalar(z,-z[0]) / (last(z)-z[0]) * (r2-r1) ,r1) @@ -4640,7 +4640,7 @@ function plot_revolution(f,angle,z,arclength, path, rclip, rspan, horiz=false,r1 rclip = default(rclip, [-INF,INF]), rdata = [for(pt=path) [for(theta=thetarange) min(max(f(theta,pt.y),rclip[0]),rclip[1])]], - dummy2=assert(len(rdata[0])>1 && len(rdata)>1, "xrange and yrange must both provide at least 2 points"), + dummy2=assert(len(rdata[0])>1 && len(rdata)>1, "\nxrange and yrange must both provide at least 2 points."), minval = min(flatten(rdata)), maxval = max(flatten(rdata)), sdata = is_undef(rspan) ? rdata @@ -4654,7 +4654,7 @@ function plot_revolution(f,angle,z,arclength, path, rclip, rspan, horiz=false,r1 angscale = is_def(angle) ? 1 : 360/2/PI/path[i].x ) - assert(angscale*(last(thetarange)-thetarange[0])<=360, str("arclength span is more than 360 degrees at profile index ",i," with radius ",path[i].x)) + assert(angscale*(last(thetarange)-thetarange[0])<=360, str("\nArclength span is more than 360° at profile index ",i," with radius ",path[i].x,".")) [ if (!closed) [0,0,path[i].y], for(j=idx(sdata[0])) @@ -4682,11 +4682,11 @@ function plot_revolution(f,angle,z,arclength, path, rclip, rspan, horiz=false,r1 /// One script to convert a grayscale image to a heightfield array in a .scad file can be found at: /// https://raw.githubusercontent.com/BelfrySCAD/BOSL2/master/scripts/img2scad.py /// The bottom value defines a planar base for the resulting shape and it must be strictly less than -/// the model data to produce valid geometry, so data which is too small is set to 0.1 units above the bottom value. +/// the model data to produce valid geometry, so data that is too small is set to 0.1 units above the bottom value. /// Arguments: /// data = This is either the 2D rectangular array of heights, or a function literal that takes X and Y arguments. /// size = The [X,Y] size of the surface to create. If given as a scalar, use it for both X and Y sizes. Default: `[100,100]` -/// bottom = The Z coordinate for the bottom of the heightfield object to create. Any heights lower than this will be truncated to very slightly (0.1) above this height. Default: -20 +/// bottom = The Z coordinate for the bottom of the heightfield object to create. Any heights lower than this are truncated to slightly (0.1) above this height. Default: -20 /// maxz = The maximum height to model. Truncates anything taller to this height. Set to INF for no truncation. Default: 100 /// xrange = A range of values to iterate X over when calculating a surface from a function literal. Default: [-1 : 0.01 : 1] /// yrange = A range of values to iterate Y over when calculating a surface from a function literal. Default: [-1 : 0.01 : 1] @@ -4695,7 +4695,7 @@ function plot_revolution(f,angle,z,arclength, path, rclip, rspan, horiz=false,r1 /// convexity = Max number of times a line could intersect a wall of the surface being formed. Module only. 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. See [spin](attachments.scad#subsection-spin). Default: `0` -/// orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` +/// orient = Vector to rotate top toward. See [orient](attachments.scad#subsection-orient). Default: `UP` /// Example: /// heightfield(size=[100,100], bottom=-20, data=[ /// for (y=[-180:4:180]) [ @@ -4828,7 +4828,7 @@ function heightfield(data, size=[100,100], bottom=-20, maxz=100, xrange=[-1:0.04 /// d = The diameter of the cylinder to wrap around. /// d1 = The diameter of the bottom of the cylinder to wrap around. /// d2 = The diameter of the top of the cylinder to wrap around. -/// base = The radius for the bottom of the heightfield object to create. Any heights smaller than this will be truncated to very slightly above this height. Default: -20 +/// base = The radius for the bottom of the heightfield object to create. Any heights smaller than this are truncated to slightly above this height. Default: -20 /// transpose = If true, swaps the radial and length axes of the data. Default: false /// aspect = The aspect ratio of the generated heightfield at the surface of the cylinder. Default: 1 /// xrange = A range of values to iterate X over when calculating a surface from a function literal. Default: [-1 : 0.01 : 1] @@ -4838,7 +4838,7 @@ function heightfield(data, size=[100,100], bottom=-20, maxz=100, xrange=[-1:0.04 /// convexity = Max number of times a line could intersect a wall of the surface being formed. Module only. 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. See [spin](attachments.scad#subsection-spin). Default: `0` -/// orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` +/// orient = Vector to rotate top toward. See [orient](attachments.scad#subsection-orient). Default: `UP` /// Example(VPD=400;VPR=[55,0,150]): /// cylindrical_heightfield(l=100, r=30, base=5, data=[ /// for (y=[-180:4:180]) [ @@ -4881,18 +4881,18 @@ function cylindrical_heightfield( r1 = get_radius(r1=r1, r=r, d1=d1, d=d), r2 = get_radius(r1=r2, r=r, d1=d2, d=d) ) - assert(is_finite(l) && l>0, "Must supply one of l=, h=, or height= as a finite positive number.") - assert(is_finite(r1) && r1>0, "Must supply one of r=, r1=, d=, or d1= as a finite positive number.") - assert(is_finite(r2) && r2>0, "Must supply one of r=, r2=, d=, or d2= as a finite positive number.") - assert(is_finite(base) && base>0, "Must supply base= as a finite positive number.") - assert(is_matrix(data)||is_function(data), "data= must be a function literal, or contain a 2D array of numbers.") + assert(is_finite(l) && l>0, "\nMust supply one of l=, h=, or height= as a finite positive number.") + assert(is_finite(r1) && r1>0, "\nMust supply one of r=, r1=, d=, or d1= as a finite positive number.") + assert(is_finite(r2) && r2>0, "\nMust supply one of r=, r2=, d=, or d2= as a finite positive number.") + assert(is_finite(base) && base>0, "\nMust supply base= as a finite positive number.") + assert(is_matrix(data)||is_function(data), "\ndata= must be a function literal, or contain a 2D array of numbers.") let( xvals = is_list(data)? [for (x = idx(data[0])) x] : is_range(xrange)? [for (x = xrange) x] : - assert(false, "xrange= must be given as a range if data= is a function literal."), + assert(false, "\nxrange= must be given as a range if data= is a function literal."), yvals = is_list(data)? [for (y = idx(data)) y] : is_range(yrange)? [for (y = yrange) y] : - assert(false, "yrange= must be given as a range if data= is a function literal."), + assert(false, "\nyrange= must be given as a range if data= is a function literal."), xlen = len(xvals), ylen = len(yvals), stepy = l / (ylen-1), @@ -4904,7 +4904,7 @@ function cylindrical_heightfield( bsteps = round(segs(maxr-base) * arc / 360), bstep = arc / bsteps ) - assert(stepx*xlen <= circ, str("heightfield (",xlen," x ",ylen,") needs a radius of at least ",maxr*stepx*xlen/circ)) + assert(stepx*xlen <= circ, str("\nheightfield (",xlen," x ",ylen,") needs a radius of at least ",maxr*stepx*xlen/circ,".")) let( verts = [ for (yi = idx(yvals)) let( @@ -4979,7 +4979,7 @@ module cylindrical_heightfield( // inch = set to true for a ruler scaled to inches (assuming base dimension is mm). Default: false // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `LEFT+BACK+TOP` // 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` +// orient = Vector to rotate top toward. See [orient](attachments.scad#subsection-orient). Default: `UP` // Examples(2D,Big): // ruler(100,depth=3); // ruler(100,depth=3,labels=true); @@ -4996,8 +4996,8 @@ module ruler(length=100, width, thickness=1, depth=3, labels=false, pipscale=1/3 { if ($preview){ checks = - assert(depth<=5, "Cannot render scales smaller than depth=5") - assert(len(colors)==2, "colors must contain a list of exactly two colors."); + assert(depth<=5, "\nCannot render scales smaller than depth=5") + assert(len(colors)==2, "\n'colors' must contain a list of exactly two colors."); length = inch ? INCH * length : length; unit = inch ? INCH*unit : unit; maxscale = is_def(maxscale)? maxscale : floor(log(length/unit-EPSILON));