Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Richard Milewski
2025-03-26 11:30:16 -07:00
10 changed files with 995 additions and 124 deletions

View File

@@ -1881,7 +1881,7 @@ module hide(tags)
// attach(RIGHT,BOT) cyl(r=1,h=5);
// attach(LEFT,BOT) cyl(r=1,h=5);
// }
// Example: Nexting applications of hide_this()
// Example: Nesting applications of hide_this()
// $fn=32;
// hide_this() cuboid(10)
// attach(TOP,BOT) cyl(r=2,h=5)
@@ -3982,8 +3982,8 @@ function _find_anchor(anchor, geom)=
pos2 = rot(from=UP, to=axis, p=pos),
vec2 = anch==CENTER? UP : rot(from=UP, to=axis, p=vec),
// Set spin for top/bottom to be clockwise
spin = anch.z!=0 && (anch.x!=0 || anch.y!=0) ? _compute_spin(vec2,rot(from=UP,to=axis,p=point3d(tangent)*anch.z))
: anch.z==0 && norm(anch)>0 ? _compute_spin(vec2, (vec2==DOWN || vec2==UP)?BACK:UP)
spin = anch.z!=0 && (!approx(anch.x,0) || !approx(anch.y,0)) ? _compute_spin(vec2,rot(from=UP,to=axis,p=point3d(tangent)*anch.z))
: anch.z==0 && norm(anch)>EPSILON ? _compute_spin(vec2, (approx(vec2,DOWN) || approx(vec2,UP))?BACK:UP)
: oang
) [anchor, pos2, vec2, spin]
) : type == "point"? (
@@ -4448,7 +4448,7 @@ module expose_anchors(opacity=0.2) {
// See Also: generic_airplane(), anchor_arrow(), show_anchors(), expose_anchors(), frame_ref()
// Usage:
// show_transform_list(tlist, [s]);
// show_transform_list(tlist) {CHILDREN};
// show_transform_list(tlist) CHILDREN;
// 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
@@ -5012,6 +5012,7 @@ function _canonical_edge(edge) =
flip * edge;
// Section: Attachable Descriptions for Operating on Attachables or Restoring a Previous State
// Function: parent()
@@ -5064,7 +5065,7 @@ module restore(desc)
multmatrix(T) children();
}
else{
assert(!is_undef(desc) && is_list(desc) && len(desc)==2, "Invalid desc");
check=assert(is_description(desc), "Invalid description");
T = linear_solve($transform, desc[0]);
$parent_geom = desc[1];
multmatrix(T) children();
@@ -5076,46 +5077,61 @@ module restore(desc)
// Topics: Descriptions, Attachments
// See Also: parent(), desc_dist()
// Usage:
// point = desc_point(desc,[anchor]);
// point = desc_point(desc,[p],[anchor]);
// Description:
// Computes the coordinates of the specified anchor point in the given description relative to the current transformation state.
// Computes the coordinates of the specified point or anchor point in the given description relative to the current transformation state.
// Arguments:
// desc = Description to use to get the point
// anchor = Anchor point that you want to extract. Default: CENTER
// p = Point or point list to transform. Default: CENTER (if anchor not given)
// --
// anchor = Anchor point (only one) that you want to extract. Default: CENTER
// Example(3D): In this example we translate away from the parent object and then compute points on that object. Note that with OpenSCAD 2021.01 you must use union() or alternatively place the pt1 and pt2 assignments in a let() statement. This is not necessary in development versions.
// cuboid(10) let(desc=parent())
// right(12) up(27)
// union(){
// pt1 = desc_point(desc,TOP+BACK+LEFT);
// pt2 = desc_point(desc,TOP+FWD+RIGHT);
// pt1 = desc_point(desc,anchor=TOP+BACK+LEFT);
// pt2 = desc_point(desc,anchor=TOP+FWD+RIGHT);
// stroke([pt1,pt2,CENTER], closed=true, width=.5,color="red");
// }
// Example(3D): Here we compute the point on the parent so we can draw a line anchored on the child object that connects to a computed point on the parent
// cuboid(10) let(desc=parent())
// attach(FWD,BOT) cuboid([3,3,7])
// attach(TOP+BACK+RIGHT, BOT)
// stroke([[0,0,0], desc_point(desc,TOP+FWD+RIGHT)],width=.5,color="red");
function desc_point(desc, anchor=CENTER) =
is_undef(desc) ? linear_solve($transform, [0,0,0,1])
: let(
anch = _find_anchor(anchor, desc[1]),
T = linear_solve($transform, desc[0])
// stroke([[0,0,0], desc_point(desc,anchor=TOP+FWD+RIGHT)],width=.5,color="red");
function desc_point(desc, p, anchor) =
is_undef(desc) ?
assert(is_undef(anchor), "Cannot give anchor withot desc")
let(
T = matrix_inverse($transform)
)
apply(T, default(p,UP))
: assert(is_description(desc), "Invalid description")
assert(num_defined([anchor,p])<2, "Cannot give both anchor and p")
let (
T = linear_solve($transform, desc[0]),
p = is_def(p) ? p
: let(anch = _find_anchor(anchor, desc[1]))
anch[1]
)
apply(T, anch[1]);
apply(T, p);
// Function: desc_dir()
// Synopsis: Computes the direction in the current context of a direction from an attachable description
// Synopsis: Computes the direction in the current context of a direction or anchor in a description's context
// Topics: Descriptions, Attachment
// See Also: parent(), desc_point()
// Usage:
// dir = desc_dir([desc],[anchor]);
// dir = desc_anchor(desc,[dir], [anchor]);
// Description:
// Computes the direction in the current context of an anchor direction from an attachable description. If you don't give a description
// then the direction is computed relative to global world coordinates.
// 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.
// 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:
// desc = Description to use. Default: use the global world coordinate system
// anchor = Anchor to get the direction from. Default: UP
// dir = Direction or list of directions to use. Default: UP (if anchor is not given)
// --
// anchor = Anchor (only one) to get the direction from.
// Example(3D): Here we don't give a description so the reference is to the global world coordinate system, and we don't give a direction, so the default of UP applies. This lets the cylinder be placed so it is horizontal in world coordinates.
// prismoid(20,10,h=15)
// attach(RIGHT,BOT) cuboid([4,4,15])
@@ -5123,17 +5139,39 @@ function desc_point(desc, anchor=CENTER) =
// Example(3D,VPR=[78.1,0,76.1]): Here we use the description of the prismoid, which lets us place the rod so that it is oriented in the direction of the prismoid's face.
// prismoid(20,10,h=15) let(pris=parent())
// attach(RIGHT,BOT) cuboid([4,4,15])
// position(TOP) cyl(d=2,h=15,orient=desc_dir(pris,FWD),anchor=LEFT);
function desc_dir(desc, anchor=UP) =
// position(TOP) cyl(d=2,h=15,orient=desc_dir(pris,anchor=FWD),anchor=LEFT);
function desc_dir(desc, dir, anchor) =
is_undef(desc) ?
assert(is_undef(anchor), "Cannot give anchor without desc")
let(
T = matrix_inverse($transform)
)
move(-apply(T,CENTER), apply(T, default(dir,UP)))
:
assert(is_description(desc), "Invalid description")
assert(num_defined([dir,anchor])<2, "Cannot give both dir and anchor")
let(
T = is_undef(desc) ? matrix_inverse($transform)
: linear_solve($transform, desc[0]),
dir = is_undef(desc) ? anchor
: let(anch = _find_anchor(anchor, desc[1]))
T = linear_solve($transform, desc[0]),
dir = is_def(dir) ? dir
: let(
anch = _find_anchor(anchor, desc[1])
)
anch[2]
)
apply(T, dir)-apply(T,CENTER);
move(-apply(T,CENTER),apply(T, dir));
function desc_attach(desc, anchor=UP, p, reverse=false) =
assert(is_description(desc), "Invalid description")
let(
T = linear_solve($transform, desc[0]),
anch = _find_anchor(anchor,desc[1]),
centerpoint = apply(T,CENTER),
pos = apply(T, anch[1]),
y = apply(T*rot(from=UP,to=anch[2])*zrot(anch[3]),BACK)-centerpoint,
z = apply(T,anch[2])-centerpoint
)
reverse ? frame_map(z=z,y=y,reverse=true, p=move(-pos,p))
: move(pos,frame_map(z=z,y=y, p=p));
// Function: desc_dist()
@@ -5145,13 +5183,20 @@ function desc_dir(desc, anchor=UP) =
// dest = desc_dist(desc1=, desc2=, [anchor1=], [anchor2=]);
// Description:
// Computes the distance between two points specified using attachable descriptions and optional anchor
// points. If you omit the anchor point(s) then the computation uses the CENTER anchor.
// points. If you omit the anchor point(s) then the computation uses the CENTER anchor.
// Arguments:
// desc1 = First description
// anchor1 = Anchor for first description
// desc2 = Second description
// anchor2 = Anchor for second description
// Example(3D): Computes the distance between a point on each cube.
// cuboid(10) let(desc=parent())
// right(15) cuboid(10)
// echo(desc_dist(parent(),TOP+RIGHT+BACK, desc, TOP+LEFT+FWD));
function desc_dist(desc1,anchor1=CENTER, desc2, anchor2=CENTER)=
assert(is_description(desc1),"Invalid description: desc1")
assert(is_description(desc2),"Invalid description: desc2")
let(
anch1 = _find_anchor(anchor1, desc1[1]),
anch2 = _find_anchor(anchor2, desc2[1]),
@@ -5163,13 +5208,12 @@ function desc_dist(desc1,anchor1=CENTER, desc2, anchor2=CENTER)=
)
norm(pt1-pt2);
// Function: trans_desc()
// Function: transform_desc()
// Synopsis: Applies a transformation matrix to a description
// Topics: Descriptions, Attachments
// See Also: parent()
// Usage:
// new_desc = trans_desc(T, desc);
// new_desc = transform_desc(T, desc);
// Description:
// Applies a transformation matrix to a description, producing a new transformed description as
// output. The transformation matrix can be produced using any of the usual transform commands.
@@ -5180,9 +5224,88 @@ function desc_dist(desc1,anchor1=CENTER, desc2, anchor2=CENTER)=
// T = transformation or list of transformations to apply (a 4x4 matrix or list of them)
// desc = description to transform
function trans_desc(T,desc) =
function transform_desc(T,desc) =
assert(is_description(desc), "Invalid description")
is_consistent(T, ident(4)) ? [for(t=T) [t*desc[0], desc[1]]]
: is_matrix(T,4,4) ? [T*desc[0], desc[1]]
: assert(false,"T must be a 4x4 matrix or list of 4x4 matrices");
// Module: desc_copies()
// Synopsis: Places copies according to a list of transformation matrices and supplies descriptions for the copies.
// SynTags: MatList, Trans
// Topics: Transformations, Distributors, Copiers, Descriptions
// See Also: line_copies(), move_copies(), xcopies(), ycopies(), zcopies(), grid_copies(), xflip_copy(), yflip_copy(), zflip_copy(), mirror_copy()
// Usage:
// desc_copies(transforms) CHILDREN;
// Description:
// 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
// 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.
// .
// In a child object you obtain its description using {{parent()}} as usual. Once you have
// that description you can also access descriptions of the other objects, assuming they have
// identical geometry. (The geometry can vary if you make your object conditional on `$idx` for example.)
// To get the next object use `$next()` and to get the previous one use `$prev()`. You can also
// get an arbitrary object description by index using `$desc(i)`. You can use these descriptions
// with {{prism_connector()}} to create prisms between the corresponding objects.
// .
// Note that in OpenSCAD version 2021.01 you cannot directly call `$next` or the other `$` functions.
// You have to write `let(next=$next)` and then you can use the `next()` function. Similar steps
// are necessary for the other functions. In development versions you can directly invoke `$next()`
// and the other functions.
// .
// The descriptions are made available through function literals provided in the `$` variables. The
// available functions are
// * $next([di], [desc]): Returns the description of the next object, or if i is given, the object i steps forward. The indexing wraps around.
// * $prev([di], [desc]): Returns the description of the previoud object, or if i is given, the object i steps before. The indexing wraps around.
// * $desc(i, [desc]): Returns a description of the object with index `i`. Indexing does not wrap around.
// All of these functions have an optional `desc` parameter, which is the description that will be transformed to produce the next, previous, or indexed
// description. By default `desc` is set to {{parent()}}, but you may wish to use a different description if you have objects that vary.
// .
// See the last examples in {{prism_connector()}} for examples using this module.
// Arguments:
// transforms = list of transformation matrices to apply to the children
// Side Effects:
// `$count` is set to the number of transformations
// `$idx` is set to the index number of the current transformation
// `$is_last` is set to true if this is the last copy and false otherwise
// `$next()` is set to a function literal that produces the next description (see above)
// `$prev()` is set to a function literal that produces the previous description (see above)
// `$desc()` is set to a function literal that produces the description at a specified index (see above)
module desc_copies(transforms)
{
$count=len(transforms);
for(i=idx(transforms))
let(
$idx=i,
$is_last = i==len(transforms)-1,
$desc = function(i,desc) transform_desc(transforms[i]*matrix_inverse(transforms[i]),default(desc,parent())),
$next = function(di=1,desc) transform_desc(select(transforms,i+di)*matrix_inverse(transforms[i]), default(desc,parent())),
$prev = function(di=1,desc) transform_desc(select(transforms,i-di)*matrix_inverse(transforms[i]), default(desc,parent()))
)
multmatrix(transforms[i])children();
}
// Function: is_description()
// Synopsis: Check if its argument is a descriptioni
// Topics: Descriptions
// Usage:
// bool = is_description(desc);
// Description:
// Returns true if the argument appears to be a description.
// Arguments:
// desc = argument to check
function is_description(desc) =
is_list(desc) && len(desc)==2 && is_matrix(desc[0],4,4) && is_list(desc[1]) && is_string(desc[1][0]);
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View File

@@ -1238,13 +1238,13 @@ module arc_copies(
sa=0, ea=360,
rot=true
) {
req_children($children);
req_children($children);
rx = get_radius(r1=rx, r=r, d1=dx, d=d, dflt=1);
ry = get_radius(r1=ry, r=r, d1=dy, d=d, dflt=1);
sa = posmod(sa, 360);
ea = posmod(ea, 360);
n = (abs(ea-sa)<0.01)?(n+1):n;
delt = (((ea<=sa)?360.0:0)+ea-sa)/(n-1);
extra_n = (abs(ea-sa)<0.01)?1:0;
delt = (((ea<=sa)?360.0:0)+ea-sa)/(n-1+extra_n);
for ($idx = [0:1:n-1]) {
$ang = sa + ($idx * delt);
$pos =[rx*cos($ang), ry*sin($ang), 0];
@@ -1271,8 +1271,8 @@ function arc_copies(
ry = get_radius(r1=ry, r=r, d1=dy, d=d, dflt=1),
sa = posmod(sa, 360),
ea = posmod(ea, 360),
n = (abs(ea-sa)<0.01)?(n+1):n,
delt = (((ea<=sa)?360.0:0)+ea-sa)/(n-1),
extra_n = (abs(ea-sa)<0.01)?1:0,
delt = (((ea<=sa)?360.0:0)+ea-sa)/(n-1+extra_n),
mats = [
for (i = [0:1:n-1])
let(

View File

@@ -302,9 +302,9 @@ module stroke(
attachable(){
for (path = paths) {
pathvalid = is_path(path,[2,3]) || same_shape(path,[[0,0]]) || same_shape(path,[[0,0,0]]);
assert(pathvalid,"The path argument must be a list of 2D or 3D points, or a region.");
check4 = assert(is_num(width) || len(width)==len(path),
check4 = assert(pathvalid,"The path argument must be a list of 2D or 3D points, or a region.")
assert(is_num(width) || len(width)==len(path),
"width must be a number or a vector the same length as the path (or all components of a region)");
path = deduplicate( closed? list_wrap(path) : path );
width = is_num(width)? [for (x=path) width]
@@ -1232,15 +1232,16 @@ function _turtle_command(command, parm, parm2, state, index) =
lrsign = command=="arcleft" ? 1 : -1,
radius = parm*sign(myangle),
center = lastpt + lrsign*radius*line_normal([0,0],state[step]),
steps = state[arcsteps]==0 ? segs(abs(radius)) : state[arcsteps],
arcpath = myangle == 0 || radius == 0 ? [] : arc(
steps,
points = [
lastpt,
rot(cp=center, p=lastpt, a=sign(parm)*lrsign*myangle/2),
rot(cp=center, p=lastpt, a=sign(parm)*lrsign*myangle)
]
)
steps = state[arcsteps]==0 ? segs(abs(radius)) : state[arcsteps],
arcpath = myangle == 0 || radius == 0 ? []
: arc(
steps,
points = [
lastpt,
rot(cp=center, p=lastpt, a=sign(parm)*lrsign*myangle/2),
rot(cp=center, p=lastpt, a=sign(parm)*lrsign*myangle)
]
)
)
list_set(
state, [path,step], [

View File

@@ -41,9 +41,10 @@ function _is_point_on_line(point, line, bounded=false, eps=EPSILON) =
v1 = (line[1]-line[0]),
v0 = (point-line[0]),
t = v0*v1/(v1*v1),
bounded = force_list(bounded,2)
bounded = force_list(bounded,2),
norm_crossprod = len(v1)==2 ? abs(cross(v0,v1)) : norm(cross(v0,v1))
)
abs(cross(v0,v1))<=eps*norm(v1)
norm_crossprod <= eps*norm(v1)
&& (!bounded[0] || t>=-eps)
&& (!bounded[1] || t<1+eps) ;
@@ -904,6 +905,48 @@ function _is_point_above_plane(plane, point) =
point_plane_distance(plane, point) > EPSILON;
// Module: show_plane()
// Synopsis: Display (part of) a plane
// SynTags: Geom
// Topics: Planes
// Usage:
// show_plane(plane, size, [offset]) [ATTACHMENTS];
// Description:
// Display a rectangular portion of the specified plane for debugging or visualization purposes.
// The size parameter specifies the size of the plane when projected along the coordinate axis that is closest to
// the plane's normal vector. The offset parameter will shift the plane location perpendicular to the normal vector.
// This object is a non-manifold VNF (it has edges) so it will not render.
// Arguments:
// plane = Plane to display
// size = scalar or 2-vector size parameter
// offset = scalar of 2-vector offset
// Example(3D):
// sphere(r=15,$fn=48);
// plane = plane_from_normal([2,-3,9],[4,-5,12]);
// %show_plane(plane, [35,25], [4,-7]);
module show_plane(plane, size, offset=0)
{
size = force_list(size,2);
offset = force_list(offset, 2, 0);
checks =
assert(is_vector(size,2), "The size parameter must be a scalar or 2-vector")
assert(is_vector(offset,2), "The offset parameter must be a scalar or 2-vector");
pts = move(offset,rect(size));
axes = [UP, BACK, RIGHT];
n = plane_normal(plane);
ang = [for(v=axes) abs(plane_line_angle(plane, [CTR,v]))];
axis = axes[max_index(ang)];
face = [for(pt=pts)
axis==UP? [pt.x,pt.y,(plane[3]-plane.x*pt.x-plane.y*pt.y)/plane.z]
: axis==BACK? [pt[0],(plane[3]-plane.x*pt[0]-plane.z*pt[1])/plane.y,pt[1]]
: [(plane[3]-plane.y*pt[0]-plane.z*pt[1])/plane.x,pt[0],pt[1]]
];
vnf = [face, [count(face)]];
vnf_polyhedron(vnf) children();
}
// Section: Circle Calculations

BIN
images/metaball_demo2d.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@@ -1648,7 +1648,6 @@ function debug_tetra(r) = let(size=r/norm([1,1,1])) [
// Section: Metaballs
// ![Metaball animation](https://raw.githubusercontent.com/BelfrySCAD/BOSL2/master/images/metaball_demo.gif)
// ![Metaball animation](https://raw.githubusercontent.com/BelfrySCAD/BOSL2/master/images/metaball_demo2d.gif)
// .
// [Metaballs](https://en.wikipedia.org/wiki/Metaballs), also known as "blobby objects",
// can produce smoothly varying blobs and organic forms. You create metaballs by placing metaball
@@ -2653,13 +2652,13 @@ function mb_stadium(size, cutoff=INF, influence=1, negative=false, hide_debug=fa
length = shape>=0 ? siz[1] : siz[0],
r = shape>=0 ? siz[0]/2 : siz[1]/2,
sl = length-2*r, // straight side length
dum3 = assert(sl>0, "\nTotal length must accommodate rounded ends of rectangle."),
//dum3 = assert(sl>=0, "\nTotal length must accommodate rounded ends of rectangle."),
neg = negative ? -1 : 1,
poly = shape<=EPSILON ? [neg, hide_debug ? circle(r=0.02, $fn=3) : circle(r=r, $fn=20)]
poly = abs(shape)<=EPSILON ? [neg, hide_debug ? circle(r=0.02, $fn=3) : circle(r=r, $fn=20)]
: shape>0 ? [neg, hide_debug ? square(0.02,center=true) : rect([2*r,length], rounding=0.999*r, $fn=20)]
: [neg, hide_debug ? square(0.02,center=true) : rect([length,2*r], rounding=0.999*r, $fn=20)]
) abs(shape)<EPSILON ?
[function (dv) _mb_circle_full(point, r, cutoff, 1/influence, neg), poly]
[function (dv) _mb_circle_full(dv, r, cutoff, 1/influence, neg), poly]
: shape>0 ? [function (dv) _mb_stadium_full(dv, sl/2, r, cutoff, 1/influence, neg), poly]
: [function (dv) _mb_stadium_sideways_full(dv, sl/2, r, cutoff, 1/influence, neg), poly];
@@ -2719,6 +2718,8 @@ function mb_ring(r1,r2, cutoff=INF, influence=1, negative=false, hide_debug=fals
// Usage: As a function
// region = metaballs2d(spec, bounding_box, pixel_size, [isovalue=], [closed=], [use_centers=], [smoothing=], [exact_bounds=], [show_stats=]);
// Description:
// ![Metaball animation](https://raw.githubusercontent.com/BelfrySCAD/BOSL2/master/images/metaball_demo2d.gif)
// .
// 2D metaball shapes can be useful to create interesting polygons for extrusion. When invoked as a
// module, a 2D metaball scene is displayed. When called as a function, a list containing one or more
// [paths](paths.scad) is returned.
@@ -2954,7 +2955,7 @@ function mb_ring(r1,r2, cutoff=INF, influence=1, negative=false, hide_debug=fals
module metaballs2d(spec, bounding_box, pixel_size, pixel_count, isovalue=1, use_centers=false, smoothing=undef, exact_bounds=false, convexity=6, cp="centroid", anchor="origin", spin=0, atype="hull", show_stats=false, show_box=false, debug=false) {
regionlist = metaballs2d(spec, bounding_box, pixel_size, pixel_count, isovalue, true, use_centers, smoothing, exact_bounds, show_stats, _debug=debug);
$metaball_pathlist = debug ? regionlist[0] : regionlist; // for possible use with children
wid = min(0.5, 0.25 * (is_num(pixel_size) ? pixel_size : 0.5*(pixel_size[0]+pixel_size[1])));
wid = min(0.5, 0.5 * (is_num(pixel_size) ? pixel_size : 0.5*(pixel_size[0]+pixel_size[1])));
if(debug) {
// display debug polygons
for(a=regionlist[1])
@@ -3259,7 +3260,7 @@ function _metaballs2dfield(funclist, transmatrix, bbox, pixsize, nballs) = let(
// (x*y*z^3 - 3*x^2*z^2) / np^2 + np^2,
// isovalue=[-INF,35], bounding_box=[[-32,-32,-14],[32,32,14]],
// voxel_size = 0.8, show_box=true);
// Example(3D,Med,NoAxes,VPD=47,VPT=[0,0,2]): You can specify non-cubical voxels for efficiency. This example shows the result of two identical surface functions. The figure on the left uses a `voxel_size=1`, which washes out the detail in the z direction. The figure on the right shows the same shape with `voxel_size=[0.5,1,0.2]` to give a bit more resolution in the x direction and much more resolution in the z direction. This example runs about six times faster than if we used a cubical voxel of size 0.2 to capture the detail in only one axis at the expense of unnecessary detail in other axes.
// Example(3D,Med,NoAxes,VPD=47,VPT=[0,0,2]): You can specify non-cubical voxels for efficiency. This example shows the result of two identical surface functions. The figure on the left uses `voxel_size=1`, which washes out the detail in the z direction. The figure on the right shows the same shape with `voxel_size=[0.5,1,0.2]` to give a bit more resolution in the x direction and much more resolution in the z direction. This example runs about six times faster than if we used a cubical voxel of size 0.2 to capture the detail in only one axis at the expense of unnecessary detail in other axes.
// function shape(x,y,z, r=5) =
// r / sqrt(x^2 + 0.5*(y^2 + z^2) + 0.5*r*cos(200*z));
// bbox = [[-6,-8,0], [6,8,7]];
@@ -3478,18 +3479,11 @@ function _showstats_isosurface(voxsize, bbox, isoval, cubes, triangles, faces) =
// .
// When `closed=false`, paths that intersect the edge of the bounding box end at the bounding box. This
// means that the list of paths may include a mixture of closed and open paths. Regardless of whether
// any of the output paths are open, all closed paths have identical first and last points so that closed and open paths can be distinguished. You can use {{are_ends_equal()}} to determine if a path is closed. A path list that includes open paths is not a region, since regions are lists of closed polygons. Duplicating the ends of closed paths can cause problems for some functions such as {{offset()}} which will complain about repeated points; to deal with this problem you can pass the closed components to {{list_unwrap()}} to remove the extra endpoint.
// The parameter `closed=true` is set by default, which causes polygon segments to be generated wherever a
// contour is clipped by the bounding box, so that all contours are closed polygons. When `closed=true`,
// the list of paths returned by `contour()` is a valid [region](regions.scad) with no duplicated
// vertices in any path, and all paths are treated as as closed polygons by the `contour()` module.
// When calling `contour()` as a module, the `closed` parameter is unavailable and always true.
// .
// When `closed=false`, however, the list of paths returned by the `contour()` function may include a
// mixture of closed and unclosed paths, in which the closed paths can be identified as having equivalent
// start and end points (this duplication makes the path list an invalid [region](regions.scad)).
// any of the output paths are open, all closed paths have identical first and last points so that closed and
// open paths can be distinguished. You can use {{are_ends_equal()}} to determine if a path is closed. A path
// list that includes open paths is not a region, because regions are lists of closed polygons. Duplicating the
// ends of closed paths can cause problems for functions such as {{offset()}}, which would complain about
// repeated points. You can pass a closed path to {{list_unwrap()}} to remove the extra endpoint.
// Arguments:
// f = The contour function or array.
// isovalue = a scalar giving the isovalue parameter.

File diff suppressed because it is too large Load Diff

View File

@@ -1817,7 +1817,7 @@ module spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fals
// path_sweep(shape,path,method="natural"){
// attach(["start","end"]) anchor_arrow(s=5);
// }
// Example(Med,NoScales,VPR=[78.1,0,43.2],VPT=[2.18042,-0.485127,1.90371],VPD=74.4017): The "start" and "end" anchors are located at the origin point of the swept shape.
// Example(Med,NoScales,VPR=[78.1,0,43.2],VPT=[2.18042,-0.485127,1.90371],VPD=74.4017): The "start-centroid" and "end-centroid" anchors are located at the centroid the swept shape.
// shape = back_half(right_half(star(n=5,id=5,od=10)),y=-1);
// path = arc(angle=[0,180],d=30);
// path_sweep(shape,path,method="natural"){

View File

@@ -29,14 +29,15 @@
// base = Height of slider base.
// wall = Width of wall behind each side of the slider.
// ang = Overhang angle for slider, to facilitate supportless printig.
// chamfer = Size of chamfer. Default: 2.
// 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`
// $slop = The printer-specific slop value to make parts fit just right.
// Example:
// slider(l=30, base=10, wall=4, $slop=0.2, spin=90);
function slider(l=30, w=10, h=10, base=10, wall=5, ang=30, anchor=BOTTOM, spin=0, orient=UP) = no_function("slider");
module slider(l=30, w=10, h=10, base=10, wall=5, ang=30, anchor=BOTTOM, spin=0, orient=UP)
function slider(l=30, w=10, h=10, base=10, wall=5, ang=30, chamfer=2, anchor=BOTTOM, spin=0, orient=UP) = no_function("slider");
module slider(l=30, w=10, h=10, base=10, wall=5, ang=30, chamfer=2, anchor=BOTTOM, spin=0, orient=UP)
{
full_width = w + 2*wall;
full_height = h + base;
@@ -45,11 +46,11 @@ module slider(l=30, w=10, h=10, base=10, wall=5, ang=30, anchor=BOTTOM, spin=0,
zrot(90)
down(base+h/2) {
// Base
cuboid([full_width, l, base-get_slop()], chamfer=2, edges=[FRONT,BACK], except_edges=BOT, anchor=BOTTOM);
cuboid([full_width, l, base-get_slop()], chamfer=chamfer, edges=[FRONT,BACK], except_edges=BOT, anchor=BOTTOM);
// Wall
xflip_copy(offset=w/2+get_slop()) {
cuboid([wall, l, full_height], chamfer=2, edges=RIGHT, except_edges=BOT, anchor=BOTTOM+LEFT);
cuboid([wall, l, full_height], chamfer=chamfer, edges=RIGHT, except_edges=BOT, anchor=BOTTOM+LEFT);
}
// Sliders

View File

@@ -1046,7 +1046,7 @@ module shape_compare(eps=1/1024) {
// Description:
// Returns true if the `state` value indicates the current loop should continue. This is useful
// when using C-style for loops to iteratively calculate a value. Used with `loop_while()` and
// `loop_done()`. See [Looping Helpers](section-looping-helpers) for an example.
// `loop_done()`. See [Looping Helpers](utility.scad#section-c-style-for-loop-helpers) for an example.
// Arguments:
// state = The loop state value.
function looping(state) = state < 2;
@@ -1062,7 +1062,7 @@ function looping(state) = state < 2;
// Given the current `state`, and a boolean `continue` that indicates if the loop should still be
// continuing, returns the updated state value for the the next loop. This is useful when using
// C-style for loops to iteratively calculate a value. Used with `looping()` and `loop_done()`.
// See [Looping Helpers](section-looping-helpers) for an example.
// See [Looping Helpers](utility.scad#section-c-style-for-loop-helpers) for an example.
// Arguments:
// state = The loop state value.
// continue = A boolean value indicating whether the current loop should progress.
@@ -1080,7 +1080,7 @@ function loop_while(state, continue) =
// Description:
// Returns true if the `state` value indicates the loop is finishing. This is useful when using
// C-style for loops to iteratively calculate a value. Used with `looping()` and `loop_while()`.
// See [Looping Helpers](#5-looping-helpers) for an example.
// See [Looping Helpers](utility.scad#section-c-style-for-loop-helpers) for an example.
// Arguments:
// state = The loop state value.
function loop_done(state) = state > 0;