mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-19 19:09:36 +00:00
commit
88e2da2be9
4 changed files with 119 additions and 29 deletions
|
@ -58,21 +58,36 @@ _ANCHOR_TYPES = ["intersect","hull"];
|
|||
// 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 with names like `TOP`, `BOTTOM`, `LEFT`, `RIGHT` and `BACK` that you can add together
|
||||
// to specify an anchor point. See ?????? below for the full list of pre-defined directional constants.
|
||||
// object that you want the anchor for, relative to the center of the object. You can simply
|
||||
// specify a vector like `[0,0,1]` to anchor an object at the Z+ end, but you can also use
|
||||
// directional constants with names like `TOP`, `BOTTOM`, `LEFT`, `RIGHT` and `BACK` that you can add together
|
||||
// to specify anchor points. See [specifying directions](subsection-specifying-directions)
|
||||
// below for the full list of pre-defined directional constants.
|
||||
// .
|
||||
// For example:
|
||||
// - `[0,0,1]` is the same as `TOP` and refers to the center of the top face.
|
||||
// - `[-1,0,1]` is the same as `TOP+LEFT`, and refers to the center of the top-left edge.
|
||||
// - `[1,1,-1]` is the same as `BOTTOM+BACK+RIGHT`, and refers to the bottom-back-right corner.
|
||||
// .
|
||||
// When the object is cylindrical, conical, or spherical in nature, the anchors will be located
|
||||
// around the surface of the cylinder, cone, or sphere, relative to the center. The direction of a
|
||||
// face anchor will be perpendicular to the face, pointing outward. The direction of a edge anchor
|
||||
// When the object is cubical or rectangular in shape the anchors must have zero or one values
|
||||
// for their components and they refer to the face centers, edge centers, or corners of the object.
|
||||
// The direction of a face anchor will be perpendicular to the face, pointing outward. The direction of a edge anchor
|
||||
// will be the average of the anchor directions of the two faces the edge is between. The direction
|
||||
// of a corner anchor will be the average of the anchor directions of the three faces the corner is
|
||||
// on. The spin of all standard anchors is 0.
|
||||
// on.
|
||||
// .
|
||||
// When the object is cylindrical, conical, or spherical in nature, the anchors will be located
|
||||
// around the surface of the cylinder, cone, or sphere, relative to the center.
|
||||
// You can generally use an arbitrary vector to get an anchor positioned anywhere on the curved
|
||||
// surface of such an object, and the anchor direction will be the surface normal at the anchor location.
|
||||
// However, for anchor component pointing toward the flat face should be either -1, 1, or 0, and
|
||||
// anchors that point diagonally toward one of the flat faces will select a point on the edge.
|
||||
// .
|
||||
// For objects in two dimensions, the natural expectation is for TOP and BOTTOM to refer to the Y direction
|
||||
// of the shape. To support this, if you give an anchor in 2D that has anchor.y=0 then the Z component
|
||||
// will be mapped to the Y direction. This means you can use TOP and BOTTOM for anchors of 2D objects.
|
||||
// But remember that TOP and BOTTOM are three dimensional vectors and this is a special interpretation
|
||||
// for 2d anchoring.
|
||||
// .
|
||||
// Some more complex objects, like screws and stepper motors, have named anchors to refer to places
|
||||
// on the object that are not at one of the standard faces, edges or corners. For example, stepper
|
||||
|
@ -125,6 +140,26 @@ _ANCHOR_TYPES = ["intersect","hull"];
|
|||
// up(.12)move(TOP) text3d("TOP",size=.1,h=.01,anchor=RIGHT,orient=FRONT);
|
||||
// move(TOP) text3d("UP",size=.1,h=.01,anchor=RIGHT,orient=FRONT);
|
||||
// }
|
||||
// Figure(2D,Big): Named constants for direction vectors in 2D. For anchors the TOP and BOTTOM directions are collapsed into 2D as shown here, but do not try to use them as 2D directions in other situations.
|
||||
// $fn=12;
|
||||
// stroke(path2d([[0,0,0],RIGHT]), endcap2="arrow2", width=.05);
|
||||
// color("black")fwd(.22)left(.05)move(RIGHT) text("RIGHT",size=.1,anchor=RIGHT);
|
||||
// stroke(path2d([[0,0,0],LEFT]), endcap2="arrow2", width=.05);
|
||||
// color("black")right(.05)fwd(.22)move(LEFT) text("LEFT",size=.1,anchor=LEFT);
|
||||
// stroke(path2d([[0,0,0],FRONT]), endcap2="arrow2", width=.05);
|
||||
// color("black")
|
||||
// fwd(.2)
|
||||
// right(.15)
|
||||
// color("black")move(BACK) { text("BACK",size=.1,anchor=LEFT); back(.14) text("(TOP)", size=.1, anchor=LEFT);}
|
||||
// color("black")
|
||||
// left(.15)back(.2+.14)move(FRONT){
|
||||
// back(.14) text("FRONT",size=.1,anchor=RIGHT);
|
||||
// text("FWD",size=.1,anchor=RIGHT);
|
||||
// fwd(.14) text("FORWARD",size=.1,anchor=RIGHT);
|
||||
// fwd(.28) text("(BOTTOM)",size=.1,anchor=RIGHT);
|
||||
// fwd(.14*3) text("(BOT)",size=.1,anchor=RIGHT);
|
||||
// }
|
||||
// stroke(path2d([[0,0,0],BACK]), endcap2="arrow2", width=.05);
|
||||
// Subsection: Specifying Faces
|
||||
// Modules operating on faces accept a list of faces to describe the faces to operate on. Each
|
||||
// face is given by a vector that points to that face. Attachments of cuboid objects onto their faces also
|
||||
|
@ -1797,6 +1832,11 @@ function _get_cp(geom) =
|
|||
: assert(false,"Invalid cp specification");
|
||||
|
||||
|
||||
function _force_anchor_2d(anchor) =
|
||||
assert(anchor.y==0 || anchor.z==0, "Anchor for a 2D shape cannot be fully 3D. It must have either Y or Z component equal to zero.")
|
||||
anchor.y==0 ? [anchor.x,anchor.z] : point2d(anchor);
|
||||
|
||||
|
||||
/// Internal Function: _find_anchor()
|
||||
// Usage:
|
||||
// anchorinfo = _find_anchor(anchor, geom);
|
||||
|
@ -1950,10 +1990,10 @@ function _find_anchor(anchor, geom) =
|
|||
pos = point3d(cp) + rot(from=RIGHT, to=anchor, p=mpt)
|
||||
) [anchor, pos, anchor, oang]
|
||||
) : type == "rect"? ( //size, size2, shift
|
||||
assert(anchor.z==0, "The Z component of an anchor for a 2D shape must be 0.")
|
||||
let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[])
|
||||
assert(all_comps_good, "All components of an anchor for a rectangle/trapezoid must be -1, 0, or 1")
|
||||
let(
|
||||
anchor=_force_anchor_2d(anchor),
|
||||
size=geom[1], size2=geom[2], shift=geom[3],
|
||||
u = (anchor.y+1)/2, // 0<=u<=1
|
||||
frpt = [size.x/2*anchor.x, -size.y/2],
|
||||
|
@ -1973,24 +2013,21 @@ function _find_anchor(anchor, geom) =
|
|||
)
|
||||
) [anchor, pos, vec, 0]
|
||||
) : type == "circle"? ( //r
|
||||
assert(anchor.z==0, "The Z component of an anchor for a 2D shape must be 0.")
|
||||
let(
|
||||
rr = geom[1],
|
||||
r = is_num(rr)? [rr,rr] : point2d(rr),
|
||||
anchor = unit(_force_anchor_2d(anchor),[0,0]),
|
||||
r = force_list(geom[1],2),
|
||||
pos = approx(anchor.x,0) ? [0,sign(anchor.y)*r.y]
|
||||
: let(
|
||||
m = anchor.y/anchor.x,
|
||||
px = sign(anchor.x) * sqrt(1/(1/sqr(r.x) + m*m/sqr(r.y)))
|
||||
)
|
||||
[px,m*px],
|
||||
anchor = unit(point2d(anchor),[0,0]),
|
||||
vec = unit([r.y/r.x*pos.x, r.x/r.y*pos.y])
|
||||
) [anchor, point2d(cp+offset)+pos, vec, 0]
|
||||
) : type == "rgn_isect"? ( //region
|
||||
assert(anchor.z==0, "The Z component of an anchor for a 2D shape must be 0.")
|
||||
let(
|
||||
anchor = _force_anchor_2d(anchor),
|
||||
rgn = force_region(move(-point2d(cp), p=geom[1])),
|
||||
anchor = point2d(anchor),
|
||||
isects = [
|
||||
for (path=rgn, t=triplet(path,true)) let(
|
||||
seg1 = [t[0],t[1]],
|
||||
|
@ -2012,10 +2049,9 @@ function _find_anchor(anchor, geom) =
|
|||
vec = unit(isect[2],[0,1])
|
||||
) [anchor, pos, vec, 0]
|
||||
) : type == "rgn_extent"? ( //region
|
||||
assert(anchor.z==0, "The Z component of an anchor for a 2D shape must be 0.")
|
||||
let(
|
||||
anchor = _force_anchor_2d(anchor),
|
||||
rgn = force_region(geom[1]),
|
||||
anchor = point2d(anchor),
|
||||
rpts = rot(from=anchor, to=RIGHT, p=flatten(rgn)),
|
||||
maxx = max(column(rpts,0)),
|
||||
ys = [for (pt=rpts) if (approx(pt.x, maxx)) pt.y],
|
||||
|
|
|
@ -845,7 +845,7 @@ function offset(
|
|||
goodsegs = bselect(shiftsegs, good),
|
||||
goodpath = bselect(path,good)
|
||||
)
|
||||
assert(len(goodsegs)>0,"Offset of path is degenerate")
|
||||
assert(len(goodsegs)-(!closed && select(good,-1)?1:0)>0,"Offset of path is degenerate")
|
||||
let(
|
||||
// Extend the shifted segments to their intersection points
|
||||
sharpcorners = [for(i=[0:len(goodsegs)-1]) _segment_extension(select(goodsegs,i-1), select(goodsegs,i))],
|
||||
|
|
74
skin.scad
74
skin.scad
|
@ -734,11 +734,62 @@ module spiral_sweep(poly, h, r, turns=1, higbee, center, r1, r2, d, d1, d2, higb
|
|||
// Usage: As function
|
||||
// vnf = path_sweep(shape, path, [method], [normal=], [closed=], [twist=], [twist_by_length=], [symmetry=], [last_normal=], [tangent=], [relaxed=], [caps=], [style=], [transforms=], [anchor=], [cp=], [spin=], [orient=], [atype=]) {attachments};
|
||||
// Description:
|
||||
// Takes as input a 2D polygon path, 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`.
|
||||
// Takes as input `shape`, a 2D polygon path (list of points), and `path`, a 2d or 3d path (also a list of points)
|
||||
// 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
|
||||
// Figure(3D,Big,VPR=[70,0,345],VPD=20,NoScales): This example shows how the shape, in this case the triangle defined by `[[0, 0], [0, 1], [1, 0]]`, appears as the cross section of the swept polyhedron. The blue line shows the path. The normal vector to the shape points upwards, in the Z direction.
|
||||
// tri= [[0, 0], [0, 1], [1, 0]];
|
||||
// % path_sweep(tri,path);
|
||||
// path = arc(r=5,N=81,angle=[-20,65]);
|
||||
// T = path_sweep(tri,path,transforms=true);
|
||||
// color("red")for(i=[0:20:80]) stroke(apply(T[i],path3d(tri)),width=.1,closed=true);
|
||||
// color("blue")stroke(path3d(arc(r=5,N=101,angle=[-20,80])),width=.1,endcap2="arrow2");
|
||||
// color("red")stroke([path3d(tri)],width=.1);
|
||||
// stroke(move(centroid(tri),[CENTER,UP]), width=.07,endcap2="arrow2",color="black");
|
||||
// .
|
||||
// In the figure you can see that the swept polyhedron, shown in transparent gray, has the triangle as its cross
|
||||
// section. The triangle is positioned perpendicular to the path, which is shown in blue, so that the normal
|
||||
// vector for the triangle is parallel to the tangent vector for the path. The origin for the shape is the point
|
||||
// which follows the path. For a 2D path, the Y axis of the shape is mapped to the Z axis and in this case,
|
||||
// pointing the triangle's normal vector (in black) along the tangent line of
|
||||
// the path, which is going in the direction of the blue arrow, requires that the triangle be "turned around". If we
|
||||
// reverse the order of points in the path we get a different result:
|
||||
// Figure(3D,Big,VPR=[70,0,20],VPD=20,NoScales): The same sweep operation with the path traveling in the opposite direction.
|
||||
// tri= [[0, 0], [0, 1], [1, 0]];
|
||||
// % path_sweep(tri,path);
|
||||
// path = reverse(arc(r=5,N=81,angle=[-20,65]));
|
||||
// T = path_sweep(tri,path,transforms=true);
|
||||
// color("red")for(i=[0:20:80]) stroke(apply(T[i],path3d(tri)),width=.1,closed=true);
|
||||
// color("blue")stroke(reverse(path3d(arc(r=5,N=101,angle=[-20-15,65]))),width=.1,endcap2="arrow2");
|
||||
// Text:
|
||||
// If your shape is too large for the curves in the path you can create a situation where the shapes cross each
|
||||
// other. This results in an invalid polyhedron, which may appear OK when previewed, but will give rise
|
||||
// to cryptic CGAL errors when rendered with a second object in your model. You may be able to use {{path_sweep2d()}}
|
||||
// to produce a valid model in cases like this.
|
||||
// Figure(3D,Big,VPR=[47,0,325],VPD=20): We have scaled the path to an ellipse and enlarged the triangle, and it is now sometimes bigger than the local radius of the path, leading to an invalid polyhedron.
|
||||
// .
|
||||
// tri= scale([4.5,2.5],[[0, 0], [0, 1], [1, 0]]);
|
||||
// % path_sweep(tri,path);
|
||||
// path = xscale(1.5,arc(r=5,N=81,angle=[-70,70]));
|
||||
// T = path_sweep(tri,path,transforms=true);
|
||||
// color("red")for(i=[0:20:80]) stroke(apply(T[i],path3d(tri)),width=.1,closed=true);
|
||||
// color("blue")stroke(path3d(xscale(1.5,arc(r=5,N=81,angle=[-70,80]))),width=.1,endcap2="arrow2");
|
||||
// Text:
|
||||
// When performing a path sweep, the normal vector of the shape aligns with the tangent vector of the
|
||||
// path, but this leaves an ambiguity about how the shape is rotated. For 2D paths it is easy to resolve
|
||||
// this ambiguity by aligning the Y axis in the shape to the Z axis in the swept polyhedron. We can force the
|
||||
// shape to twist with the `twist` parameter and get a result like this:
|
||||
// Figure(3D,Big,VPR=[66,0,14],VPD=20): The shape twists as we sweep. Note that it still aligns the origin in the shape with the path, and still aligns the normal vector with the path tangent vector.
|
||||
// tri= [[0, 0], [0, 1], [1, 0]];
|
||||
// % path_sweep(tri,path,twist=-60);
|
||||
// path = arc(r=5,N=81,angle=[-20,65]);
|
||||
// T = path_sweep(tri,path,transforms=true,twist=-60);
|
||||
// color("red")for(i=[0:20:80]) stroke(apply(T[i],path3d(tri)),width=.1,closed=true);
|
||||
// color("blue")stroke(path3d(arc(r=5,N=101,angle=[-20,80])),width=.1,endcap2="arrow2");
|
||||
// Text:
|
||||
// When the path is full three-dimensional, things can become more complex. You may find that the shape rotates unexpectedly
|
||||
// around its axis as it traverses 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
|
||||
|
@ -1157,14 +1208,17 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi
|
|||
// Usage: as function
|
||||
// vnf = path_sweep2d(shape, path, [closed], [caps], [quality], [style], [anchor=], [spin=], [orient=], [atype=], [cp=]);
|
||||
// Description:
|
||||
// Takes an input 2D polygon (the shape) and a 2d path and constructs a polyhedron by sweeping the shape along the path.
|
||||
// Takes an input 2D polygon (the shape) and a 2d 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.
|
||||
// .
|
||||
// Unlike path_sweep(), local self-intersections (creases in the output) are allowed and do not produce CGAL errors.
|
||||
// See {{path_sweep()}} for more details on how the sweep operation works and for introductory examples.
|
||||
// This 2d version is different because local self-intersections (creases in the output) are allowed and do not produce CGAL errors.
|
||||
// This is accomplished by using offset() calculations, which are more expensive than simply copying the shape along
|
||||
// the path, so if you do not have local self-intersections, use path_sweep() instead. Note that global self-intersections
|
||||
// will still give rise to CGAL errors. You should be able to handle these by partitioning your model. The y axis of the
|
||||
// shape is mapped to the z axis in the swept polyhedron.
|
||||
// the path, so if you do not have local self-intersections, use {{path_sweep()}} instead. If xmax is the largest x value (in absolute value)
|
||||
// of the shape, then path_sweep2d() will work as long as the offset of `path` exists at `delta=xmax`. If the offset vanishes, as in the
|
||||
// case of a circle offset by more than its radius, then you will get an error about a degenerate offset.
|
||||
// Note that global self-intersections will still give rise to CGAL errors. You should be able to handle these by partitioning your model. The y axis of the
|
||||
// shape is mapped to the z axis in the swept polyhedron, and no twisting can occur.
|
||||
// The quality parameter is passed to offset to determine the offset quality.
|
||||
// Arguments:
|
||||
// shape = a 2D polygon describing the shape to be swept
|
||||
|
@ -1216,7 +1270,7 @@ function path_sweep2d(shape, path, closed=false, caps, quality=1, style="min_edg
|
|||
path = flip ? reverse(path) : path,
|
||||
proflist= transpose(
|
||||
[for(pt = profile)
|
||||
let(
|
||||
let( e=echo(delta=-flip*pt.x),
|
||||
ofs = offset(path, delta=-flip*pt.x, return_faces=true,closed=closed, quality=quality),
|
||||
map = column(_ofs_vmap(ofs,closed=closed),1)
|
||||
)
|
||||
|
|
2
vnf.scad
2
vnf.scad
|
@ -1132,7 +1132,7 @@ function vnf_bend(vnf,r,d,axis="Z") =
|
|||
span_chk = axis=="Z"?
|
||||
assert(bmin.y > 0 || bmax.y < 0, "Entire shape MUST be completely in front of or behind y=0.") :
|
||||
assert(bmin.z > 0 || bmax.z < 0, "Entire shape MUST be completely above or below z=0."),
|
||||
steps = ceil(segs(r) * (extent[1]-extent[0])/(2*PI*r)),
|
||||
steps = 1+ceil(segs(r) * (extent[1]-extent[0])/(2*PI*r)),
|
||||
step = (extent[1]-extent[0]) / steps,
|
||||
bend_at = [for(i = [1:1:steps-1]) i*step+extent[0]],
|
||||
slicedir = axis=="X"? "Y" : "X", // slice in y dir for X axis case, and x dir otherwise
|
||||
|
|
Loading…
Reference in a new issue