Merge pull request #218 from revarbat/revarbat_dev

Improved docs_gen.py error checking.
This commit is contained in:
Revar Desmera 2020-07-27 15:19:33 -07:00 committed by GitHub
commit 513b497560
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 281 additions and 221 deletions

View file

@ -331,7 +331,7 @@ function deduplicate_indexed(list, indices, closed=false, eps=EPSILON) =
// that the final length is exactly as requested. If you set it to `false` then the
// algorithm will favor uniformity and the output list may have a different number of
// entries due to rounding.
//
// .
// When applied to a path the output path is the same geometrical shape but has some vertices
// repeated. This can be useful when you need to align paths with a different number of points.
// (See also subdivide_path for a different way to do that.)

View file

@ -37,16 +37,16 @@ $tags_hidden = [];
// * Spin is a simple rotation around the Z axis.
// * Orientation is rotating an object so that its top is pointed towards a given vector.
// An object will first be translated to its anchor position, then spun, then oriented.
//
// .
// ## Anchor
// Anchoring is specified with the `anchor` argument in most shape modules.
// Specifying `anchor` when creating an object will translate the object so
// that the anchor point is at the origin (0,0,0). Anchoring always occurs
// before spin and orientation are applied.
//
// .
// An anchor can be referred to in one of two ways; as a directional vector,
// or as a named anchor string.
//
// .
// When given as a vector, it points, in a general way, towards the face, edge, or
// corner of the object that you want the anchor for, relative to the center of
// the object. There are directional constants named `TOP`, `BOTTOM`, `FRONT`, `BACK`,
@ -55,7 +55,7 @@ $tags_hidden = [];
// - `[0,0,1]` is the same as `TOP` and refers to the center of the top face.
// - `[-1,0,1]` is the same as `TOP+LEFT`, and refers to the center of the top-left edge.
// - `[1,1,-1]` is the same as `BOTTOM+BACK+RIGHT`, and refers to the bottom-back-right corner.
//
// .
// The components of the directional vector should all be `1`, `0`, or `-1`.
// When the object is cylindrical, conical, or spherical in nature, the anchors will be
// located around the surface of the cylinder, cone, or sphere, relative to the center.
@ -64,20 +64,20 @@ $tags_hidden = [];
// two faces the edge is between. The direction of a corner anchor will be the average
// of the anchor directions of the three faces the corner is on. The spin of all standard
// anchors is 0.
//
// .
// Some more complex objects, like screws and stepper motors, have named anchors
// to refer to places on the object that are not at one of the standard faces, edges
// or corners. For example, stepper motors have anchors for `"screw1"`, `"screw2"`,
// etc. to refer to the various screwholes on the stepper motor shape. The names,
// positions, directions, and spins of these anchors will be specific to the object,
// and will be documented when they exist.
//
// .
// ## Spin
// Spin is specified with the `spin` argument in most shape modules. Specifying `spin`
// when creating an object will rotate the object counter-clockwise around the Z axis
// by the given number of degrees. Spin is always applied after anchoring, and before
// orientation.
//
// .
// ## 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
@ -593,17 +593,17 @@ function attachment_is_shown(tags) =
// the transformation matrix needed to be applied to the contents of that volume. A managed 3D
// volume is assumed to be vertically (Z-axis) oriented, and centered. A managed 2D area is just
// assumed to be centered.
//
// .
// If `p` is not given, then the transformation matrix will be returned.
// If `p` contains a VNF, a new VNF will be returned with the vertices transformed by the matrix.
// If `p` contains a path, a new path will be returned with the vertices transformed by the matrix.
// If `p` contains a point, a new point will be returned, transformed by the matrix.
//
// .
// If `$attach_to` is not defined, then the following transformations are performed in order:
// * Translates so the `anchor` point is at the origin (0,0,0).
// * Rotates around the Z axis by `spin` degrees counter-clockwise.
// * Rotates so the top of the part points towards the vector `orient`.
//
// .
// If `$attach_to` is defined, as a consequence of `attach(from,to)`, then
// the following transformations are performed in order:
// * Translates this part so it's anchor position matches the parent's anchor position.
@ -685,13 +685,13 @@ function reorient(
// children();
// }
// ```
//
// .
// If this is *not* run as a child of `attach()` with the `to` argument
// given, then the following transformations are performed in order:
// * Translates so the `anchor` point is at the origin (0,0,0).
// * Rotates around the Z axis by `spin` degrees counter-clockwise.
// * Rotates so the top of the part points towards the vector `orient`.
//
// .
// If this is called as a child of `attach(from,to)`, then the info
// for the anchor points referred to by `from` and `to` are fetched,
// which will include position, direction, and spin. With that info,

View file

@ -13,26 +13,26 @@ include <skin.scad>
// Section: Terminology
// **Polyline**: A series of points joined by straight line segements.
//
// .
// **Bezier Curve**: A mathematical curve that joins two endpoints, following a curve determined by one or more control points.
//
// .
// **Endpoint**: A point that is on the end of a bezier segment. This point lies on the bezier curve.
//
// .
// **Control Point**: A point that influences the shape of the curve that connects two endpoints. This is often *NOT* on the bezier curve.
//
// .
// **Degree**: The number of control points, plus one endpoint, needed to specify a bezier segment. Most beziers are cubic (degree 3).
//
// .
// **Bezier Segment**: A list consisting of an endpoint, one or more control points, and a final endpoint. The number of control points is one less than the degree of the bezier. A cubic (degree 3) bezier segment looks something like:
// `[endpt1, cp1, cp2, endpt2]`
//
// .
// **Bezier Path**: A list of bezier segments flattened out into a list of points, where each segment shares the endpoint of the previous segment as a start point. A cubic Bezier Path looks something like:
// `[endpt1, cp1, cp2, endpt2, cp3, cp4, endpt3]`
// **NOTE**: A bezier path is *NOT* a polyline. It is only the points and controls used to define the curve.
//
// .
// **Bezier Patch**: A surface defining grid of (N+1) by (N+1) bezier points. If a Bezier Segment defines a curved line, a Bezier Patch defines a curved surface.
//
// .
// **Bezier Surface**: A surface defined by a list of one or more bezier patches.
//
// .
// **Spline Steps**: The number of straight-line segments to split a bezier segment into, to approximate the bezier curve. The more spline steps, the closer the approximation will be to the curve, but the slower it will be to generate. Usually defaults to 16.
@ -1230,7 +1230,7 @@ function bezier_surface(patches=[], splinesteps=16, vnf=EMPTY_VNF, style="defaul
// Module: bezier_polyhedron()
// Useage:
// Usage:
// bezier_polyhedron(patches, [splinesteps], [vnf], [style], [convexity])
// Description:
// Takes a list of two or more bezier patches and attempts to make a complete polyhedron from them.

View file

@ -379,5 +379,41 @@ module assert_equal(got, expected, info) {
}
// Module: shape_compare()
// Usage:
// shape_compare([eps]) {test_shape(); expected_shape();}
// Description:
// Compares two child shapes, returning empty geometry if they are very nearly the same shape and size.
// Returns the differential geometry if they are not nearly the same shape and size.
// Arguments:
// eps = The surface of the two shapes must be within this size of each other. Default: 1/1024
module shape_compare(eps=1/1024) {
union() {
difference() {
children(0);
if (eps==0) {
children(1);
} else {
minkowski() {
children(1);
cube(eps, center=true);
}
}
}
difference() {
children(1);
if (eps==0) {
children(0);
} else {
minkowski() {
children(0);
cube(eps, center=true);
}
}
}
}
}
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -231,7 +231,7 @@ module zcopies(spacing, n, l, sp)
// dir = Vector direction to distribute copies along.
// l = Length to distribute copies along.
//
// Side Effect:
// Side Effects:
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index number of each child being copied.
//
@ -275,7 +275,7 @@ module distribute(spacing=undef, sizes=undef, dir=RIGHT, l=undef)
// sizes = Array containing how much space each child will need.
// l = Length to distribute copies along.
//
// Side Effect:
// Side Effects:
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index number of each child being copied.
//
@ -320,7 +320,7 @@ module xdistribute(spacing=10, sizes=undef, l=undef)
// sizes = Array containing how much space each child will need.
// l = Length to distribute copies along.
//
// Side Effect:
// Side Effects:
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index number of each child being copied.
//
@ -365,7 +365,7 @@ module ydistribute(spacing=10, sizes=undef, l=undef)
// sizes = Array containing how much space each child will need.
// l = Length to distribute copies along.
//
// Side Effect:
// Side Effects:
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index number of each child being copied.
//
@ -538,7 +538,7 @@ module grid2d(spacing, n, size, stagger=false, inside=undef)
// n = Optional number of copies to have per axis.
// spacing = spacing of copies per axis. Use with `n`.
//
// Side Effect:
// Side Effects:
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
// `$idx` is set to the [Xidx,Yidx,Zidx] index values of each child copy, when using `count` and `n`.
//

View file

@ -1,22 +1,22 @@
//////////////////////////////////////////////////////////////////////////////////////////////
// LibFile: involute_gears.scad
// Involute Spur Gears and Racks
//
// .
// by Leemon Baird, 2011, Leemon@Leemon.com
// http://www.thingiverse.com/thing:5505
//
// .
// Additional fixes and improvements by Revar Desmera, 2017-2019, revarbat@gmail.com
//
// .
// This file is public domain. Use it for any purpose, including commercial
// applications. Attribution would be nice, but is not required. There is
// no warranty of any kind, including its correctness, usefulness, or safety.
//
// .
// This is parameterized involute spur (or helical) gear. It is much simpler
// and less powerful than others on Thingiverse. But it is public domain. I
// implemented it from scratch from the descriptions and equations on Wikipedia
// and the web, using Mathematica for calculations and testing, and I now
// release it into the public domain.
//
// .
// To use, add the following line to the beginning of your file:
// ```
// include <BOSL2/std.scad>

View file

@ -657,10 +657,10 @@ function _pin_size(size) =
// be printed. When oriented UP the shaft of the pin runs in the Z direction and the flat sides are the front and back. The default
// orientation (FRONT) and anchor (FRONT) places the pin in a printable configuration, flat side down on the xy plane.
// The tightness of fit is determined by `preload` and `clearance`. To make pins tighter increase `preload` and/or decrease `clearance`.
//
// .
// The "large" or "standard" size pin has a length of 10.8 and diameter of 7. The "medium" pin has a length of 8 and diameter of 4.6. The "small" pin
// has a length of 6 and diameter of 3.2. The "tiny" pin has a length of 4 and a diameter of 2.5.
//
// .
// This pin is based on https://www.thingiverse.com/thing:213310 by Emmett Lalishe
// and a modified version at https://www.thingiverse.com/thing:3218332 by acwest
// and distributed under the Creative Commons - Attribution - Share Alike License
@ -724,7 +724,7 @@ module snap_pin(size,r,radius,d,diameter, l,length, nub_depth, snap, thickness,
// if you add a lubricant. If `pointed` is true the socket is pointed to receive a pointed pin, otherwise it has a rounded and and
// will be shorter. If `fins` is set to true then two fins are included inside the socket to act as supports (which may help when printing tip up,
// especially when `pointed=false`). The default orientation is DOWN with anchor BOTTOM so that you can difference() the socket away from an object.
//
// .
// The "large" or "standard" size pin has a length of 10.8 and diameter of 7. The "medium" pin has a length of 8 and diameter of 4.6. The "small" pin
// has a length of 6 and diameter of 3.2. The "tiny" pin has a length of 4 and a diameter of 2.5.
// Arguments:

View file

@ -916,7 +916,7 @@ function count_true(l, nmax=undef, i=0, cnt=0) =
// data[len(data)-1]. This function uses a symetric derivative approximation
// for internal points, f'(t) = (f(t+h)-f(t-h))/2h. For the endpoints (when closed=false) the algorithm
// uses a two point method if sufficient points are available: f'(t) = (3*(f(t+h)-f(t)) - (f(t+2*h)-f(t+h)))/2h.
//
// .
// If `h` is a vector then it is assumed to be nonuniform, with h[i] giving the sampling distance
// between data[i+1] and data[i], and the data values will be linearly resampled at each corner
// to produce a uniform spacing for the derivative estimate. At the endpoints a single point method
@ -1066,10 +1066,10 @@ function polynomial(p, z, k, zk, total) =
// Function: poly_mult()
// Usage
// Usage:
// polymult(p,q)
// polymult([p1,p2,p3,...])
// Descriptoin:
// Description:
// Given a list of polynomials represented as real coefficient lists, with the highest degree coefficient first,
// computes the coefficient list of the product polynomial.
function poly_mult(p,q) =

View file

@ -968,11 +968,11 @@ module path_extrude(path, convexity=10, clipsize=100) {
// sinwav = [for(theta=[0:360]) 5*[theta/180, sin(theta)]];
// stroke(sinwav,width=.1);
// color("red") path_spread(sinwav, n=5) rect([.2,1.5],anchor=FRONT);
// Example(2D)): Open path, specify `n` and `spacing`
// Example(2D): Open path, specify `n` and `spacing`
// sinwav = [for(theta=[0:360]) 5*[theta/180, sin(theta)]];
// stroke(sinwav,width=.1);
// color("red") path_spread(sinwav, n=5, spacing=1) rect([.2,1.5],anchor=FRONT);
// Example(2D)): Closed path, specify `n` and `spacing`, copies centered around circle[0]
// Example(2D): Closed path, specify `n` and `spacing`, copies centered around circle[0]
// circle = regular_ngon(n=64,or=10);
// stroke(circle,width=.1,closed=true);
// color("red") path_spread(circle, n=10, spacing=1, closed=true) rect([.2,1.5],anchor=FRONT);
@ -1051,7 +1051,7 @@ module path_spread(path, n, spacing, sp=undef, rotate_children=true, closed=fals
// Function: path_cut()
//
// Usage
// Usage:
// path_cut(path, dists, [closed], [direction])
//
// Description:
@ -1177,7 +1177,7 @@ function _sum_preserving_round(data, index=0) =
// the final length is exactly as requested. If you set it to `false` then the
// algorithm will favor uniformity and the output path may have a different number of
// points due to rounding error.
//
// .
// With the `"segment"` method you can also specify a vector of lengths. This vector,
// `N` specfies the desired point count on each segment: with vector input, `subdivide_path`
// attempts to place `N[i]-1` points on segment `i`. The reason for the -1 is to avoid

View file

@ -42,7 +42,7 @@ function _unique_groups(m) = [
//
// Description:
// Creates a regular polyhedron with optional rounding. Children are placed on the polyhedron's faces.
//
// .
// **Selecting the polyhedron:**
// You constrain the polyhedra list by specifying different characteristics, that must all be met
// * `name`: e.g. `"dodecahedron"` or `"pentagonal icositetrahedron"`
@ -54,13 +54,13 @@ function _unique_groups(m) = [
// * hasfaces: The list of vertex counts for faces; at least one listed type must appear:
// * `hasfaces = 3`: polygon has at least one triangular face
// * `hasfaces = [5,6]`: polygon has a hexagonal or a pentagonal face
//
// .
// The result is a list of selected polyhedra. You then specify `index` to choose which one of the
// remaining polyhedra you want. If you don't give `index` the first one on the list is created.
// Two examples:
// * `faces=12, index=2`: Creates the 3rd solid with 12 faces
// * `type="archimedean", faces=14`: Creates the first archimedean solid with 14 faces (there are 3)
//
// .
// **Choosing the size of your polyhedron:**
// The default is to create a polyhedron whose smallest edge has length 1. You can specify the
// smallest edge length with the size option. Alternatively you can specify the size of the
@ -70,18 +70,18 @@ function _unique_groups(m) = [
// For the platonic solids every face meets the inscribed sphere and every corner touches the
// circumscribed sphere. For the Archimedean solids the inscribed sphere will touch only some of
// the faces and for the Catalan solids the circumscribed sphere meets only some of the corners.
//
// .
// **Orientation:**
// Orientation is controled by the facedown parameter. Set this to false to get the canonical orientation.
// Set it to true to get the largest face oriented down. If you set it to a number the module searches for
// a face with the specified number of vertices and orients that face down.
//
// .
// **Rounding:**
// If you specify the rounding parameter the module makes a rounded polyhedron by first creating an
// undersized model and then expanding it with `minkowski()`. This only produces the correct result
// if the in-sphere contacts all of the faces of the polyhedron, which is true for the platonic, the
// catalan solids and the trapezohedra but false for the archimedean solids.
//
// .
// **Children:**
// The module places children on the faces of the polyhedron. The child coordinate system is
// positioned so that the origin is the center of the face. If `rotate_children` is true (default)
@ -90,7 +90,7 @@ function _unique_groups(m) = [
// With `repeat=false` each child is used once. You can specify `draw=false` to suppress drawing of
// the polyhedron, e.g. to use for `difference()` operations. The module sets various parameters
// you can use in your children (see the side effects list below).
//
// .
// **Stellation:**
// Technically stellation is an operation of shifting the polyhedron's faces to produce a new shape
// that may have self-intersecting faces. OpenSCAD cannot handle self-intersecting faces, so we
@ -99,7 +99,7 @@ function _unique_groups(m) = [
// no stellation is performed. Otherwise stellate gives the pyramid height as a multiple of the
// edge length. A negative pyramid height can be used to perform excavation, where a pyramid is
// removed from each face.
//
// .
// **Special Polyhedra:**
// These can be selected only by name and may require different parameters, or ignore some standard
// parameters.
@ -112,7 +112,7 @@ function _unique_groups(m) = [
// * `h`: Distance from the center to the apex.
// * `r`: Radius of the polygon that defines the equatorial vertices.
// * `d`: Diameter of the polygon that defines the equatorial vertices.
//
// .
// * Named stellations: various polyhedra such as the Kepler-Poinsot solids are stellations with
// specific pyramid heights. To make them easier to generate you can specify them by name.
// This is equivalent to giving the name of the appropriate base solid and the magic stellate

View file

@ -550,7 +550,7 @@ function _offset_region(
// Takes an input path and returns a path offset by the specified amount. As with the built-in
// offset() module, you can use `r` to specify rounded offset and `delta` to specify offset with
// corners. Positive offsets shift the path to the left (relative to the direction of the path).
//
// .
// When offsets shrink the path, segments cross and become invalid. By default `offset()` checks
// for this situation. To test validity the code checks that segments have distance larger than (r
// or delta) from the input path. This check takes O(N^2) time and may mistakenly eliminate
@ -560,7 +560,7 @@ function _offset_region(
// increases the number of samples on the segment that are checked.) Run time will increase. In
// some situations you may be able to decrease run time by setting quality to 0, which causes only
// segment ends to be checked.
//
// .
// For construction of polyhedra `offset()` can also return face lists. These list faces between
// the original path and the offset path where the vertices are ordered with the original path
// first, starting at `firstface_index` and the offset path vertices appearing afterwords. The

View file

@ -25,7 +25,7 @@ include <structs.scad>
// and continuous curvature rounding using 4th order bezier curves. Circular rounding can produce a
// tactile "bump" where the curvature changes from flat to circular.
// See https://hackernoon.com/apples-icons-have-that-shape-for-a-very-good-reason-720d4e7c8a14
//
// .
// You select the type of rounding using the `method` option, which should be `"smooth"` to
// get continuous curvature rounding, `"circle"` to get circular rounding, or `"chamfer"` to get chamfers. The default is circle
// rounding. Each method has two options you can use to specify the amount of rounding.
@ -33,29 +33,29 @@ include <structs.scad>
// much of the corner to "cut" off. This can be easier to understand than setting a circular radius, which can be
// unexpectedly extreme when the corner is very sharp. It also allows a systematic specification of
// corner treatments that are the same size for all three methods.
//
// .
// For circular rounding you can also use the `radius` parameter, which sets a circular rounding
// radius. For chamfers and smooth rounding you can specify the `joint` parameter, which specifies the distance
// away from the corner along the path where the roundover or chamfer should start. The figure below shows
// the cut and joint distances for a given roundover.
//
// .
// The `"smooth"` method rounding also has a parameter that specifies how smooth the curvature match
// is. This parameter, `k`, ranges from 0 to 1, with a default of 0.5. Larger values give a more
// abrupt transition and smaller ones a more gradual transition. If you set the value much higher
// than 0.8 the curvature changes abruptly enough that though it is theoretically continuous, it may
// not be continuous in practice. If you set it very small then the transition is so gradual that
// the length of the roundover may be extremely long.
//
// .
// If you select curves that are too large to fit the function will fail with an error. You can set `verbose=true` to
// get a message showing a list of scale factors you can apply to your rounding parameters so that the
// roundovers will fit on the curve. If the scale factors are larger than one
// then they indicate how much you can increase the curve sizes before collisions will occur.
//
// .
// The parameters `radius`, `cut`, `joint` and `k` can be numbers, which round every corner using the same parameters, or you
// can specify a list to round each corner with different parameters. If the curve is not closed then the first and last points
// of the curve are not rounded. In this case you can specify a full list of points anyway, and the endpoint values are ignored,
// or you can specify a list that has length len(path)-2, omitting the two dummy values.
//
// .
// Examples:
// * `method="circle", radius=2`:
// Rounds every point with circular, radius 2 roundover
@ -63,7 +63,7 @@ include <structs.scad>
// Rounds every point with continuous curvature rounding with a cut of 2, and a default 0.5 smoothing parameter
// * `method="smooth", cut=2, k=0.3`:
// Rounds every point with continuous curvature rounding with a cut of 2, and a very gentle 0.3 smoothness setting
//
// .
// The number of segments used for roundovers is determined by `$fa`, `$fs` and `$fn` as usual for
// circular roundovers. For continuous curvature roundovers `$fs` and `$fn` are used and `$fa` is
// ignored. Note that $fn is interpreted as the number of points on the roundover curve, which is
@ -394,7 +394,7 @@ function _rounding_offsets(edgespec,z_dir=1) =
// and will match the tangents at every point. If you do not specify tangents they will be computed using
// path_tangents with uniform=false by default. Note that setting uniform to true with non-uniform
// sampling may be desirable in some cases but tends to produces curves that overshoot the point on the path.
//
// .
// The size or relsize parameter determines how far the curve can bend away from
// the input path. In the case where the curve has a single hump, the size specifies the exact distance
// between the specified path and the curve. If you give relsize then it is relative to the segment
@ -474,21 +474,21 @@ function smooth_path(path, tangents, size, relsize, splinesteps=10, uniform=fals
// disabling the validity check when it is needed can generate invalid polyhedra that will produce CGAL errors upon
// rendering. Such validity errors will also occur if you specify a self-intersecting shape.
// The offset profile is quantized to 1/1024 steps to avoid failures in offset() that can occur with very tiny offsets.
//
// .
// The build-in profiles are: circular rounding, teardrop rounding, chamfer, continuous curvature rounding, and chamfer.
// Also note that when a rounding radius is negative the rounding will flare outwards. The easiest way to specify
// the profile is by using the profile helper functions. These functions take profile parameters, as well as some
// general settings and translate them into a profile specification, with error checking on your input. The description below
// describes the helper functions and the parameters specific to each function. Below that is a description of the generic
// settings that you can optionally use with all of the helper functions.
//
// .
// - profile: os_profile(points)
// Define the offset profile with a list of points. The first point must be [0,0] and the roundover should rise in the positive y direction, with positive x values for inward motion (standard roundover) and negative x values for flaring outward. If the y value ever decreases then you might create a self-intersecting polyhedron, which is invalid. Such invalid polyhedra will create cryptic assertion errors when you render your model and it is your responsibility to avoid creating them. Note that the starting point of the profile is the center of the extrusion. If you use a profile as the top it will rise upwards. If you use it as the bottom it will be inverted, and will go downward.
// - circle: os_circle(r|cut). Define circular rounding either by specifying the radius or cut distance.
// - smooth: os_smooth(cut|joint). Define continuous curvature rounding, with `cut` and `joint` as for round_corners.
// - teardrop: os_teardrop(r|cut). Rounding using a 1/8 circle that then changes to a 45 degree chamfer. The chamfer is at the end, and enables the object to be 3d printed without support. The radius gives the radius of the circular part.
// - chamfer: os_chamfer([height], [width], [cut], [angle]). Chamfer the edge at desired angle or with desired height and width. You can specify height and width together and the angle will be ignored, or specify just one of height and width and the angle is used to determine the shape. Alternatively, specify "cut" along with angle to specify the cut back distance of the chamfer.
//
// .
// The general settings that you can use with all of the helper functions are mostly used to control how offset_sweep() calls the offset() function.
// - extra: Add an extra vertical step of the specified height, to be used for intersections or differences. This extra step will extend the resulting object beyond the height you specify. Default: 0
// - check_valid: passed to offset(). Default: true
@ -496,10 +496,10 @@ function smooth_path(path, tangents, size, relsize, splinesteps=10, uniform=fals
// - steps: Number of vertical steps to use for the profile. (Not used by os_profile). Default: 16
// - offset_maxstep: The maxstep distance for offset() calls; controls the horizontal step density. Set smaller if you don't get the expected rounding. Default: 1
// - offset: Select "round" (r=) or "delta" (delta=) offset types for offset. You can also choose "chamfer" but this leads to exponential growth in the number of vertices with the steps parameter. Default: "round"
//
// .
// Many of the arguments are described as setting "default" values because they establish settings which may be overridden by
// the top and bottom profile specifications.
//
// .
// You will generally want to use the above helper functions to generate the profiles.
// The profile specification is a list of pairs of keywords and values, e.g. ["r",12, type, "circle"]. The keywords are
// - "type" - type of rounding to apply, one of "circle", "teardrop", "chamfer", "smooth", or "profile" (Default: "circle")
@ -517,7 +517,7 @@ function smooth_path(path, tangents, size, relsize, splinesteps=10, uniform=fals
// - "steps" - number of vertical steps to use for the roundover. Default: 16.
// - "offset_maxstep" - maxstep distance for offset() calls; controls the horizontal step density. Set smaller if you don't get expected rounding. Default: 1
// - "offset" - select "round" (r=), "delta" (delta=), or "chamfer" offset type for offset. Default: "round"
//
// .
// Note that if you set the "offset" parameter to "chamfer" then every exterior corner turns from one vertex into two vertices with
// each offset operation. Since the offsets are done one after another, each on the output of the previous one, this leads to
// exponential growth in the number of vertices. This can lead to long run times or yield models that
@ -865,33 +865,33 @@ function os_profile(points, extra,check_valid, quality, offset_maxstep, offset)
// operates on 2d children rather than a point list. Each offset is computed using
// the native `offset()` module from the input geometry. If your geometry has internal holes or is too small for the specified offset then you may get
// unexpected results.
//
// .
// The build-in profiles are: circular rounding, teardrop rounding, chamfer, continuous curvature rounding, and chamfer.
// Also note that when a rounding radius is negative the rounding will flare outwards. The easiest way to specify
// the profile is by using the profile helper functions. These functions take profile parameters, as well as some
// general settings and translate them into a profile specification, with error checking on your input. The description below
// describes the helper functions and the parameters specific to each function. Below that is a description of the generic
// settings that you can optionally use with all of the helper functions.
//
// .
// The final shape is created by combining convex hulls of small extrusions. The thickness of these small extrusions may result
// your model being slightly too long (if the curvature at the end is flaring outward), so if the exact length is very important
// you may need to intersect with a bounding cube. (Note that extra length can also be intentionally added with the `extra` argument.)
//
// .
// - profile: os_profile(points)
// Define the offset profile with a list of points. The first point must be [0,0] and the roundover should rise in the positive y direction, with positive x values for inward motion (standard roundover) and negative x values for flaring outward. If the y value ever decreases then you might create a self-intersecting polyhedron, which is invalid. Such invalid polyhedra will create cryptic assertion errors when you render your model and it is your responsibility to avoid creating them. Note that the starting point of the profile is the center of the extrusion. If you use a profile as the top it will rise upwards. If you use it as the bottom it will be inverted, and will go downward.
// - circle: os_circle(r|cut). Define circular rounding either by specifying the radius or cut distance.
// - smooth: os_smooth(cut|joint). Define continuous curvature rounding, with `cut` and `joint` as for round_corners.
// - teardrop: os_teardrop(r|cut). Rounding using a 1/8 circle that then changes to a 45 degree chamfer. The chamfer is at the end, and enables the object to be 3d printed without support. The radius gives the radius of the circular part.
// - chamfer: os_chamfer([height], [width], [cut], [angle]). Chamfer the edge at desired angle or with desired height and width. You can specify height and width together and the angle will be ignored, or specify just one of height and width and the angle is used to determine the shape. Alternatively, specify "cut" along with angle to specify the cut back distance of the chamfer.
//
// .
// The general settings that you can use with all of the helper functions are mostly used to control how offset_sweep() calls the offset() function.
// - extra: Add an extra vertical step of the specified height, to be used for intersections or differences. This extra step will extend the resulting object beyond the height you specify. Default: 0
// - steps: Number of vertical steps to use for the profile. (Not used by os_profile). Default: 16
// - offset: Select "round" (r=), "delta" (delta=), or "chamfer" offset types for offset. Default: "round"
//
// .
// Many of the arguments are described as setting "default" values because they establish settings which may be overridden by
// the top and bottom profile specifications.
//
// .
// You will generally want to use the above helper functions to generate the profiles.
// The profile specification is a list of pairs of keywords and values, e.g. ["r",12, type, "circle"]. The keywords are
// - "type" - type of rounding to apply, one of "circle", "teardrop", "chamfer", "smooth", or "profile" (Default: "circle")
@ -906,7 +906,7 @@ function os_profile(points, extra,check_valid, quality, offset_maxstep, offset)
// - "extra" - extra height added for unions/differences. This makes the shape taller than the requested height. (Default: 0)
// - "steps" - number of vertical steps to use for the roundover. Default: 16.
// - "offset" - select "round" (r=) or "delta" (delta=) offset type for offset. Default: "round"
//
// .
// Note that unlike `offset_sweep`, because the offset operation is always performed from the base shape, using chamfered offsets does not increase the
// number of vertices or lead to any special complications.
//
@ -1031,22 +1031,22 @@ function _remove_undefined_vals(list) =
// Uses `offset()` to compute a stroke for the input path. Unlike `stroke`, the result does not need to be
// centered on the input path. The corners can be rounded, pointed, or chamfered, and you can make the ends
// rounded, flat or pointed with the `start` and `end` parameters.
//
// .
// The `check_valid`, `quality` and `maxstep` parameters are passed through to `offset()`
//
// .
// If `width` is a scalar then the output will be a centered stroke of the specified width. If width
// is a list of two values then those two values will define the stroke side positions relative to the center line, where
// as with offset(), the shift is to the left for open paths and outward for closed paths. For example,
// setting `width` to `[0,1]` will create a stroke of width 1 that extends entirely to the left of the input, and and [-4,-6]
// will create a stroke of width 2 offset 4 units to the right of the input path.
//
// .
// If closed==false then the function form will return a path. If closed==true then it will return a region. The `start` and
// `end` parameters are forbidden for closed paths.
//
// .
// Three simple end treatments are supported, "flat" (the default), "round" and "pointed". The "flat" treatment
// cuts off the ends perpendicular to the path and the "round" treatment applies a semicircle to the end. The
// "pointed" end treatment caps the stroke with a centered triangle that has 45 degree angles on each side.
//
// .
// More complex end treatments are available through parameter lists with helper functions to ease parameter passing. The parameter list
// keywords are
// - "type": the type of end treatment, one of "shifted_point", "roundover", or "flat"
@ -1054,15 +1054,15 @@ function _remove_undefined_vals(list) =
// - "abs_angle": absolute angle (angle relative to x-axis)
// - "cut": cut distance for roundovers, a single value to round both corners identically or a list of two values for the two corners. Negative values round outward.
// - "k": curvature smoothness parameter for roundovers, default 0.75
//
// .
// Function helpers for defining ends, prefixed by "os" for offset_stroke.
//
// .
// os_flat(angle|absangle): specify a flat end either relative to the path or relative to the x-axis
// os_pointed(loc,dist): specify a pointed tip where the point is distance `loc` from the centerline (positive is the left direction as for offset), and `dist` is the distance from the path end to the point tip. The default value for `loc` is zero (the center). You must specify `dist` when using this option.
// os_round(cut,angle|absangle,k). Rounded ends with the specified cut distance, based on the specified angle or absolute angle. The `k` parameter is the smoothness parameter for continuous curvature rounding.
//
// .
// Note that `offset_stroke()` will attempt to apply roundovers and angles at the ends even when it means deleting segments of the stroke, unlike round_corners which only works on a segment adjacent to a corner. If you specify an overly extreme angle it will fail to find an intersection with the stroke and display an error. When you specify an angle the end segment is rotated around the center of the stroke and the last segment of the stroke one one side is extended to the corner.
//
// .
// The $fn and $fs variables are used to determine the number of segments for rounding, while maxstep is used to determine the segments of `offset`. If you
// get the expected rounding along the path, decrease `maxstep` and if the curves created by `os_round()` are too coarse, adjust $fn or $fs.
//
@ -1419,11 +1419,11 @@ function _rp_compute_patches(top, bot, rtop, rsides, ktop, ksides, concave) =
// not be continuous in practice. A value of 0.92 is a good approximation to a circle. If you set it very small then the transition
// is so gradual that the roundover may be very small. If you want a very smooth roundover, set the joint parameter as large as possible and
// then adjust the k value down as low as gives a sufficiently large roundover.
//
// .
// You can specify the bottom and top polygons by giving two compatible 3d paths. You can also give 2d paths and a height/length and the
// two shapes will be offset in the z direction from each other. The final option is to specify just the bottom along with a height/length;
// in this case the top will be a copy of the bottom, offset in the z direction by the specified height.
//
// .
// You define rounding for all of the top edges, all of the bottom edges, and independently for each of the connecting side edges.
// You specify rounding the rounding by giving the joint distance for where the curved section should start. If the joint distance is 1 then
// it means the curved section begins 1 unit away from the edge (in the perpendicular direction). Typically each joint distance is a scalar
@ -1433,11 +1433,11 @@ function _rp_compute_patches(top, bot, rtop, rsides, ktop, ksides, concave) =
// outward. If you give a vector value then a negative value then if joint_top[0] is negative the shape will flare outward, but if
// joint_top[1] is negative the shape will flare upward. At least one value must be non-negative. The same rules apply for joint_bot.
// The joint_sides parameter must be entirely nonnegative.
//
// .
// If you set `debug` to true the module version will display the polyhedron even when it is invalid and it will show the bezier patches at the corners.
// This can help troubleshoot problems with your parameters. With the function form setting debug to true causes it to return [patches,vnf] where
// patches is a list of the bezier control points for the corner patches.
//
// .
// Arguments:
// bottom = 2d or 3d path describing bottom polygon
// top = 2d or 3d path describing top polygon (must be the same dimension as bottom)

View file

@ -26,68 +26,6 @@ else:
GIT = "git"
def get_header_link(name):
refpat = re.compile("[^a-z0-9_ -]")
return refpat.sub("", name.lower()).replace(" ", "-")
def toc_entry(name, indent, count=None):
lname = "{0}{1}".format(
("%d. " % count) if count else "",
name
)
ref = get_header_link(lname)
if name.endswith( (")", "}", "]") ):
name = "`" + name.replace("\\", "") + "`"
return "{0}{1} [{2}](#{3})".format(
indent,
("%d." % count) if count else "-",
name,
ref
)
def mkdn_esc(txt):
out = ""
quotpat = re.compile(r'([^`]*)(`[^`]*`)(.*$)');
while txt:
m = quotpat.match(txt)
if m:
out += m.group(1).replace(r'_', r'\_')
out += m.group(2)
txt = m.group(3)
else:
out += txt.replace(r'_', r'\_')
txt = ""
return out
def get_comment_block(lines, prefix, blanks=1):
out = []
blankcnt = 0
indent = 0
while lines:
if not lines[0].startswith(prefix+" "):
break
line = lines.pop(0)[len(prefix):]
if not indent:
while line.startswith(" "):
line = line[1:]
indent += 1
else:
line = line[indent:]
if line == "":
blankcnt += 1
if blankcnt >= blanks:
break
else:
blankcnt = 0
if line.rstrip() == '.':
line = "\n"
out.append(line.rstrip())
return (lines, out)
def image_compare(file1, file2):
img1 = Image.open(file1)
img2 = Image.open(file2)
@ -329,6 +267,68 @@ class ImageProcessing(object):
imgprc = ImageProcessing()
def get_header_link(name):
refpat = re.compile("[^a-z0-9_ -]")
return refpat.sub("", name.lower()).replace(" ", "-")
def toc_entry(name, indent, count=None):
lname = "{0}{1}".format(
("%d. " % count) if count else "",
name
)
ref = get_header_link(lname)
if name.endswith( (")", "}", "]") ):
name = "`" + name.replace("\\", "") + "`"
return "{0}{1} [{2}](#{3})".format(
indent,
("%d." % count) if count else "-",
name,
ref
)
def mkdn_esc(txt):
out = ""
quotpat = re.compile(r'([^`]*)(`[^`]*`)(.*$)');
while txt:
m = quotpat.match(txt)
if m:
out += m.group(1).replace(r'_', r'\_')
out += m.group(2)
txt = m.group(3)
else:
out += txt.replace(r'_', r'\_')
txt = ""
return out
def get_comment_block(lines, prefix, blanks=1):
out = []
blankcnt = 0
indent = 0
while lines:
if not lines[0].startswith(prefix+" "):
break
line = lines.pop(0)[len(prefix):]
if not indent:
while line.startswith(" "):
line = line[1:]
indent += 1
else:
line = line[indent:]
if line == "":
blankcnt += 1
if blankcnt >= blanks:
break
else:
blankcnt = 0
if line.rstrip() == '.':
line = "\n"
out.append(line.rstrip())
return (lines, out)
class LeafNode(object):
def __init__(self):
self.name = ""
@ -374,26 +374,39 @@ class LeafNode(object):
break
continue
blankcnt = 0
line = line[len(prefix):]
if line.startswith("Constant:"):
leaftype, title = line.split(":", 1)
self.name = title.strip()
self.leaftype = leaftype.strip()
continue
if line.startswith("Function&Module:"):
leaftype, title = line.split(":", 1)
self.name = title.strip()
self.leaftype = leaftype.strip()
continue
if line.startswith("Function:"):
leaftype, title = line.split(":", 1)
self.name = title.strip()
self.leaftype = leaftype.strip()
continue
if line.startswith("Module:"):
leaftype, title = line.split(":", 1)
self.name = title.strip()
self.leaftype = leaftype.strip()
continue
if line.startswith("Status:"):
dummy, status = line.split(":", 1)
self.status = status.strip()
continue
if line.startswith("Usage:"):
dummy, title = line.split(":", 1)
title = title.strip()
lines, block = get_comment_block(lines, prefix)
self.usages.append([title, block])
continue
if line.startswith("Description:"):
dummy, desc = line.split(":", 1)
desc = desc.strip()
@ -401,50 +414,7 @@ class LeafNode(object):
self.description.append(desc)
lines, block = get_comment_block(lines, prefix)
self.description.extend(block)
if line.startswith("Usage:"):
dummy, title = line.split(":", 1)
title = title.strip()
lines, block = get_comment_block(lines, prefix)
self.usages.append([title, block])
if line.startswith("Arguments:"):
lines, block = get_comment_block(lines, prefix)
for line in block:
if "=" not in line:
print("Error in {}: Could not parse line in Argument block. Missing '='.".format(self.name))
print("Line read was:")
print(line)
sys.exit(-2)
argname, argdesc = line.split("=", 1)
argname = argname.strip()
argdesc = argdesc.strip()
self.arguments.append([argname, argdesc])
if line.startswith("Extra Anchors:") or line.startswith("Anchors:"):
lines, block = get_comment_block(lines, prefix)
for line in block:
if "=" not in line:
print("Error: bad anchor line:")
print(line)
sys.exit(-2)
anchorname, anchordesc = line.split("=", 1)
anchorname = anchorname.strip()
anchordesc = anchordesc.strip()
self.anchors.append([anchorname, anchordesc])
if line.startswith("Side Effects:"):
lines, block = get_comment_block(lines, prefix)
self.side_effects.extend(block)
m = expat.match(line)
if m: # Example(TYPE):
plural = m.group(1) == "Examples"
extype = m.group(3)
title = m.group(4)
lines, block = get_comment_block(lines, prefix)
if not extype:
extype = "3D" if self.leaftype in ["Module", "Function&Module"] else "NORENDER"
if not plural:
self.add_example(title=title, code=block, extype=extype)
else:
for line in block:
self.add_example(title="", code=[line], extype=extype)
continue
m = figpat.match(line)
if m: # Figure(TYPE):
plural = m.group(1) == "Figures"
@ -458,6 +428,60 @@ class LeafNode(object):
else:
for line in block:
self.add_figure("", [line], figtype)
continue
if line.startswith("Arguments:"):
lines, block = get_comment_block(lines, prefix)
for line in block:
if "=" not in line:
print("Error in {}: Could not parse line in Argument block. Missing '='.".format(self.name))
print("Line read was:")
print(line)
sys.exit(-2)
argname, argdesc = line.split("=", 1)
argname = argname.strip()
argdesc = argdesc.strip()
self.arguments.append([argname, argdesc])
continue
if line.startswith("Extra Anchors:") or line.startswith("Anchors:"):
lines, block = get_comment_block(lines, prefix)
for line in block:
if "=" not in line:
print("Error: bad anchor line:")
print(line)
sys.exit(-2)
anchorname, anchordesc = line.split("=", 1)
anchorname = anchorname.strip()
anchordesc = anchordesc.strip()
self.anchors.append([anchorname, anchordesc])
continue
if line.startswith("Side Effects:"):
lines, block = get_comment_block(lines, prefix)
self.side_effects.extend(block)
continue
m = expat.match(line)
if m: # Example(TYPE):
plural = m.group(1) == "Examples"
extype = m.group(3)
title = m.group(4)
lines, block = get_comment_block(lines, prefix)
if not extype:
extype = "3D" if self.leaftype in ["Module", "Function&Module"] else "NORENDER"
if not plural:
self.add_example(title=title, code=block, extype=extype)
else:
for line in block:
self.add_example(title="", code=[line], extype=extype)
continue
if ":" not in line:
print("Error in {}: Unrecognized block header. Missing colon?".format(self.name))
else:
print("Error in {}: Unrecognized block header.".format(self.name))
print("Line read was:")
print(line)
sys.exit(-2)
return lines
def gen_md(self, fileroot, imgroot):

View file

@ -1062,7 +1062,7 @@ module rect_tube(
// Module: torus()
//
// Descriptiom:
// Description:
// Creates a torus shape.
//
// Usage:

View file

@ -480,11 +480,11 @@ function _normal_segment(p1,p2) =
// origin, pointed along the positive x axis with a movement distance of 1. By default, `turtle` returns just
// the computed turtle path. If you set `full_state` to true then it instead returns the full turtle state.
// You can invoke `turtle` again with this full state to continue the turtle path where you left off.
//
// .
// The turtle state is a list with three entries: the path constructed so far, the current step as a 2-vector, and the current default angle.
//
// .
// For the list below, `dist` is the current movement distance.
//
// .
// Commands | Arguments | What it does
// ------------ | ------------------ | -------------------------------
// "move" | [dist] | Move turtle scale*dist units in the turtle direction. Default dist=1.
@ -1754,9 +1754,9 @@ module mask2d_teardrop(r,d,angle=45,excess=0.1,anchor=CENTER,spin=0) {
// Since there are a number of shapes that fall under the name ogee, the shape of this mask is given as a pattern.
// Patterns are given as TYPE, VALUE pairs. ie: `["fillet",10, "xstep",2, "step",[5,5], ...]`. See Patterns below.
// If called as a function, this just returns a 2D path of the outline of the mask shape.
//
// .
// ### Patterns
//
// .
// Type | Argument | Description
// -------- | --------- | ----------------
// "step" | [x,y] | Makes a line to a point `x` right and `y` down.

View file

@ -26,13 +26,13 @@ include <vnf.scad>
// Each profile must rotate in the same clockwise direction. If called as a function, returns a
// [VNF structure](vnf.scad) like `[VERTICES, FACES]`. If called as a module, creates a polyhedron
// of the skinned profiles.
//
// .
// The profiles can be specified either as a list of 3d curves or they can be specified as
// 2d curves with heights given in the `z` parameter. It is your responsibility to ensure
// that the resulting polyhedron is free from self-intersections, which would make it invalid
// and can result in cryptic CGAL errors upon rendering, even though the polyhedron appears
// OK during preview.
//
// .
// For this operation to be well-defined, the profiles must all have the same vertex count and
// we must assume that profiles are aligned so that vertex `i` links to vertex `i` on all polygons.
// Many interesting cases do not comply with this restriction. Two basic methods can handle
@ -44,7 +44,7 @@ include <vnf.scad>
// a square with 5 points (two of which are identical), so that it can match up to a pentagon.
// Such a combination would create a triangular face at the location of the duplicated vertex.
// Alternatively, `skin` provides methods (described below) for matching up incompatible paths.
//
// .
// In order for skinned surfaces to look good it is usually necessary to use a fine sampling of
// points on all of the profiles, and a large number of extra interpolated slices between the
// profiles that you specify. It is generally best if the triangles forming your polyhedron
@ -55,7 +55,7 @@ include <vnf.scad>
// multiplying the number of points by N, so a profile with 8 points will have 8*N points after
// refinement. Note that when dealing with continuous curves it is always better to adjust the
// sampling in your code to generate the desired sampling rather than using the `refine` argument.
//
// .
// Two methods are available for resampling, `"length"` and `"segment"`. Specify them using
// the `sampling` argument. The length resampling method resamples proportional to length.
// The segment method divides each segment of a profile into the same number of points.
@ -66,7 +66,7 @@ include <vnf.scad>
// The available methods are `"distance"`, `"tangent"`, `"direct"` and `"reindex"`.
// It is useful to distinguish between continuous curves like a circle and discrete profiles
// like a hexagon or star, because the algorithms' suitability depend on this distinction.
//
// .
// The "direct" and "reindex" methods work by resampling the profiles if necessary. As noted above,
// for continuous input curves, it is better to generate your curves directly at the desired sample size,
// but for mapping between a discrete profile like a hexagon and a circle, the hexagon must be resampled
@ -81,7 +81,7 @@ include <vnf.scad>
// method which will look for the index choice that will minimize the length of all of the edges
// in the polyhedron---in will produce the least twisted possible result. This algorithm has quadratic
// run time so it can be slow with very large profiles.
//
// .
// The "distance" and "tangent" methods are work by duplicating vertices to create
// triangular faces. The "distance" method finds the global minimum distance method for connecting two
// profiles. This algorithm generally produces a good result when both profiles are discrete ones with
@ -98,7 +98,7 @@ include <vnf.scad>
// have no effect. For best efficiency set `refine=1` and `slices=0`. When you use refinement with either
// of these methods, it is always the "segment" based resampling described above. This is necessary because
// sampling by length will ignore the repeated vertices and break the alignment.
//
// .
// It is possible to specify `method` and `refine` as arrays, but it is important to observe
// matching rules when you do this. If a pair of profiles is connected using "tangent" or "distance"
// then the `refine` values for those two profiles must be equal. If a profile is connected by
@ -107,7 +107,7 @@ include <vnf.scad>
// used for the resampled profiles. The best way to avoid confusion is to ensure that the
// profiles connected by "direct" or "realign" all have the same number of points and at the
// transition, the refined number of points matches.
//
// .
// Arguments:
// profiles = list of 2d or 3d profiles to be skinned. (If 2d must also give `z`.)
// slices = scalar or vector number of slices to insert between each pair of profiles. Set to zero to use only the profiles you provided. Recommend starting with a value around 10.
@ -791,7 +791,7 @@ function associate_vertices(polygons, split, curpoly=0) =
// If `closed=true` then the first and last transformation are linked together.
// The `caps` parameter controls whether the ends of the shape are closed.
// As a function, returns the VNF for the polyhedron. As a module, computes the polyhedron.
//
// .
// Note that this is a very powerful, general framework for producing polyhedra. It is important
// to ensure that your resulting polyhedron does not include any self-intersections, or it will
// be invalid and will generate CGAL errors. If you get such errors, most likely you have an
@ -851,15 +851,15 @@ module sweep(shape, transformations, closed=false, caps, convexity=10) {
// Takes as input a 2d shape (specified as a point list) and a 2d or 3d path and constructs a polyhedron by sweeping the shape along the path.
// When run as a module returns the polyhedron geometry. When run as a function returns a VNF by default or if you set `transforms=true` then
// it returns a list of transformations suitable as input to `sweep`.
//
// .
// The sweep operation has an ambiguity: the shape can rotate around the axis defined by the path. Several options provide
// methods for controlling this rotation. You can choose from three different methods for selecting the rotation of your shape.
// None of these methods will produce good, or even valid, results on all inputs, so it is important to select a suitable method.
// You can also add (or remove) twist to the model. This twist adjustment is done uniformly in arc length by default, or you
// can set `twist_by_length=false` to distribute the twist uniformly over the path point list.
//
// .
// The method is set using the parameter with that name to one of the following:
//
// .
// The "incremental" method (the default) works by adjusting the shape at each step by the minimal rotation that makes the shape normal to the tangent
// at the next point. This method is robust in that it always produces a valid result for well-behaved paths with sufficiently high
// sampling. Unfortunately, it can produce a large amount of undesirable twist. When constructing a closed shape this algorithm in
@ -873,7 +873,7 @@ module sweep(shape, transformations, closed=false, caps, convexity=10) {
// makes an angle of 45 deg or less with the xy plane and it points BACK if the path makes a higher angle with the XY plane. You
// can also supply `last_normal` which provides an ending orientation constraint. Be aware that the curve may still exhibit
// twisting in the middle. This method is the default because it is the most robust, not because it generally produces the best result.
//
// .
// The "natural" method works by computing the Frenet frame at each point on the path. This is defined by the tangent to the curve and
// the normal which lies in the plane defined by the curve at each point. This normal points in the direction of curvature of the curve.
// The result is a very well behaved set of sections without any unexpected twisting---as long as the curvature never falls to zero. At a
@ -881,7 +881,7 @@ module sweep(shape, transformations, closed=false, caps, convexity=10) {
// you skip over this troublesome point so the normal is defined, it can change direction abruptly when the curvature is zero, leading to
// a nasty twist and an invalid model. A simple example is a circular arc joined to another arc that curves the other direction. Note
// that the X axis of the shape is aligned with the normal from the Frenet frame.
//
// .
// The "manual" method allows you to specify your desired normal either globally with a single vector, or locally with
// a list of normal vectors for every path point. The normal you supply is projected to be orthogonal to the tangent to the
// path and the Y direction of your shape will be aligned with the projected normal. (Note this is different from the "natural" method.)
@ -890,7 +890,7 @@ module sweep(shape, transformations, closed=false, caps, convexity=10) {
// uses the actual specified normal. In this case, the tangent is projected to be orthogonal to your supplied normal to define
// the cross section orientation. Specifying a list of normal vectors gives you complete control over the orientation of your
// cross sections and can be useful if you want to position your model to be on the surface of some solid.
//
// .
// For any method you can use the `twist` argument to add the specified number of degrees of twist into the model.
// If the model is closed then the twist must be a multiple of 360/symmetry. The twist is normally spread uniformly along your shape
// based on the path length. If you set `twist_by_length` to false then the twist will be uniform based on the point count of your path.

View file

@ -234,13 +234,13 @@ function str_num(str) =
// Breaks an input string into substrings using a separator or list of separators. If keep_nulls is true
// then two sequential separator characters produce an empty string in the output list. If keep_nulls is false
// then no empty strings are included in the output list.
//
// .
// If sep is a single string then each character in sep is treated as a delimiting character and the input string is
// split at every delimiting character. Empty strings can occur whenever two delimiting characters are sequential.
// If sep is a list of strings then the input string is split sequentially using each string from the list in order.
// If keep_nulls is true then the output will have length equal to `len(sep)+1`, possibly with trailing null strings
// if the string runs out before the separator list.
// Arguments
// Arguments:
// str = String to split.
// sep = a string or list of strings to use for the separator
// keep_nulls = boolean value indicating whether to keep null strings in the output list. Default: true
@ -613,7 +613,7 @@ function is_letter(s) =
// - An optional `.` followed by an integer precision length, for specifying how many digits to display in numeric formats. If not give, 6 digits is assumed.
// - An optional letter to indicate the formatting style to use. If not given, `s` is assumed, which will do it's generic best to format any data type.
// - A trailing `}` character to show the end of the placeholder.
//
// .
// Formatting styles, and their effects are as follows:
// - `s`: Converts the value to a string with `str()` to display. This is very generic.
// - `i` or `d`: Formats numeric values as integers.

View file

@ -8,7 +8,7 @@
//////////////////////////////////////////////////////////////////////
BOSL_VERSION = [2,0,397];
BOSL_VERSION = [2,0,399];
// Section: BOSL Library Version Functions

View file

@ -531,7 +531,7 @@ function vnf_bend(vnf,r,d,axis="Z") =
// Each error has the format `[ERR_OR_WARN,CODE,MESG,POINTS,COLOR]`.
// When called as a module, echoes the non-manifold errors to the console, and color hilites the
// bad edges and vertices, overlaid on a transparent gray polyhedron of the VNF.
//
// .
// Currently checks for these problems:
// Type | Color | Code | Message
// ------- | -------- | ------------ | ---------------------------------
@ -543,7 +543,7 @@ function vnf_bend(vnf,r,d,axis="Z") =
// ERROR | Red | T_JUNCTION | Vertex is mid-edge on another Face
// ERROR | Blue | FACE_ISECT | Faces intersect
// ERROR | Magenta | HOLE_EDGE | Edge bounds Hole
//
// .
// Still to implement:
// - Overlapping coplanar faces.
// Arguments: