Skip to content

Commit db417e3

Browse files
authored
Merge branch 'master' into issue1692
2 parents d677495 + b03dbd8 commit db417e3

File tree

7 files changed

+215
-127
lines changed

7 files changed

+215
-127
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ Requires OpenSCAD 2021.01 or later.
1515

1616
The BOSL2 library is an enormous library that provides many different kinds of capabilities to simplify the development of models in OpenSCAD, and to make things possible that are difficult in native OpenSCAD. Some of the things BOSL2 provides are:
1717

18-
* **Attachments.** Unless you make models containing just one object the attachments features can revolutionize your modeling. They let you position components of a model relative to other components so you don't have to keep track of the positions and orientations of parts of the model. You can instead place an something on the TOP of something else, perhaps aligned to the RIGHT. For a full introduction to attachments, consult the [Attachments Tutorial.](https://github.com/BelfrySCAD/BOSL2/wiki/Tutorial-Attachments)
19-
* **Rounding and filleting.** Rounding and filleting is hard in OpenSCAD. The library provides modules like [cuboid()](https://github.com/BelfrySCAD/BOSL2/wiki/shapes3d.scad#module-cuboid) to make a cube with any of the edges rounded, [offset_sweep()](https://github.com/BelfrySCAD/BOSL2/wiki/rounding.scad#functionmodule-offset_sweep) to round the ends of a linear extrusion, and [prism_connector()](https://github.com/BelfrySCAD/BOSL2/wiki/rounding.scad#module-prism_connector) which works with the attachments feature to create filleted prisms between a variety of objects, or even rounded holes through a single object. You can also use [edge_profile()](https://github.com/BelfrySCAD/BOSL2/wiki/attachments.scad#module-edge_profile) to apply a variety of different mask profiles to chosen edges of a cubic shape, or you can directly subtract 3d mask shapes from an edge of objects that are not cubes.
18+
* **Attachments.** Unless you make models containing just one object, the attachments features can revolutionize your modeling. They let you position components of a model relative to other components so you don't have to keep track of the positions and orientations of parts of the model. You can instead place something on the TOP of something else, perhaps aligned to the RIGHT. For a full introduction to attachments, consult the [Attachments Tutorial.](https://github.com/BelfrySCAD/BOSL2/wiki/Tutorial-Attachments)
19+
* **Rounding and filleting.** Rounding and filleting is hard in OpenSCAD. The library provides modules like [cuboid()](https://github.com/BelfrySCAD/BOSL2/wiki/shapes3d.scad#module-cuboid) to make a cube with any of the edges rounded, [offset_sweep()](https://github.com/BelfrySCAD/BOSL2/wiki/rounding.scad#functionmodule-offset_sweep) to round the ends of a linear extrusion, and [prism_connector()](https://github.com/BelfrySCAD/BOSL2/wiki/rounding.scad#module-prism_connector) which works with the attachments feature to create filleted prisms between a variety of objects, or holes through a single object with rounded edges at the ends. You can also use [edge_profile()](https://github.com/BelfrySCAD/BOSL2/wiki/attachments.scad#module-edge_profile) to apply a variety of different mask profiles to chosen edges of a cubic shape, or you can directly subtract 3d mask shapes from an edge of objects that are not cubes.
2020
* **Complex object support.** The [path_sweep()](https://github.com/BelfrySCAD/BOSL2/wiki/skin.scad#functionmodule-path_sweep) function/module takes a 2d polygon moves it through space along a path and sweeps out a 3d shape as it moves. You can link together a series of arbitrary polygons with [skin()](https://github.com/BelfrySCAD/BOSL2/wiki/skin.scad#functionmodule-skin) or [vnf_vertex_array().](https://github.com/BelfrySCAD/BOSL2/wiki/vnf.scad#functionmodule-vnf_vertex_array) Support for [beziers](https://github.com/BelfrySCAD/BOSL2/wiki/beziers.scad) and [NURBS](https://github.com/BelfrySCAD/BOSL2/wiki/nurbs.scad) can help you construct the building blocks you need. [Metaballs](https://github.com/BelfrySCAD/BOSL2/wiki/isosurface.scad#functionmodule-metaballs) can create organic surfaces that blend shapes together.
2121
* **Building Blocks.** OpenSCAD provides cubes, cones and spheres. The BOSL2 library extends this to provide different kinds of prisms, tubes, and other abstract geometrical building blocks. In many cases the BOSL2 objects include options to round their edges. Basic objects have extensions like the ability to specify the **inner** radius of a circle to create holes with a guaranteed minimum size.
2222
* **Texturing.** Many kinds of objects can be created with [textures](https://github.com/BelfrySCAD/BOSL2/wiki/skin.scad#section-texturing) applied. This can create knurling, but it can do much more than that. A texture can be any repeating pattern, and applying a texture can actually replace the base object with something different based on repeating copies of the texture element. A texture can also be an image; using texturing you can emboss an arbitrary image onto your model.
2323
* **Parts library.** The parts library includes many useful specific functional parts including [gears](https://github.com/BelfrySCAD/BOSL2/wiki/gears.scad), generic [threading](https://github.com/BelfrySCAD/BOSL2/wiki/threading.scad#section-generic-threading), and specific threading to match plastic [bottles](https://github.com/BelfrySCAD/BOSL2/wiki/bottlecaps.scad), [pipe fittings](https://github.com/BelfrySCAD/BOSL2/wiki/threading.scad#module-npt_threaded_rod), or standard [screws.](https://github.com/BelfrySCAD/BOSL2/wiki/screws.scad) Also included are [clips](https://github.com/BelfrySCAD/BOSL2/wiki/joiners.scad#section-tension-clips), [hinges](https://github.com/BelfrySCAD/BOSL2/wiki/hinges.scad), and [dovetail joints.](https://github.com/BelfrySCAD/BOSL2/wiki/joiners.scad#section-dovetails)
24-
* **Shorthands.** The shorthands make your code a little shorter, and more importantly, they can make it significantly easier to read. Compare `up(x)` to `translate([0,0,x])`. The shorthands include operations for creating [copies of objects](https://github.com/BelfrySCAD/BOSL2/wiki/distributors.scad) and for applying [transformations](https://github.com/BelfrySCAD/BOSL2/wiki/transforms.scad) to objects, including [rot()](https://github.com/BelfrySCAD/BOSL2/wiki/transforms.scad#functionmodule-rot) which extends rotate in some useful ways that are not easy to do directly.
24+
* **Shorthands.** The shorthands make your code a little shorter, and more importantly, they can make it significantly easier to read. Compare `up(z)` to `translate([0,0,z])`. The shorthands include operations for creating [copies of objects](https://github.com/BelfrySCAD/BOSL2/wiki/distributors.scad) and for applying [transformations](https://github.com/BelfrySCAD/BOSL2/wiki/transforms.scad) to objects, including [rot()](https://github.com/BelfrySCAD/BOSL2/wiki/transforms.scad#functionmodule-rot) which extends rotate in some useful ways that are not easy to do directly.
2525
* **Geometrical operations on data.** In OpenSCAD, geometrical operations happen on geometry, and information can never be extracted from geometry. The BOLS2 library provides operations on 2d point lists (called "paths" or "regions") to make [rounded paths](https://github.com/BelfrySCAD/BOSL2/wiki/rounding.scad#function-round_corners) from ones with corners or do operations like [intersection](https://github.com/BelfrySCAD/BOSL2/wiki/regions.scad#functionmodule-intersection) and [offset](https://github.com/BelfrySCAD/BOSL2/wiki/regions.scad#function-offset). It can also do some limited operations on three dimensional data.
2626
* **Programming aids.** The library provides basic [mathematical operations](https://github.com/BelfrySCAD/BOSL2/wiki/math.scad) including solutions to [linear systems of equations](https://github.com/BelfrySCAD/BOSL2/wiki/linalg.scad#function-linear_solve) and [generic](https://github.com/BelfrySCAD/BOSL2/wiki/math.scad#function-root_find) and [polynomial](https://github.com/BelfrySCAD/BOSL2/wiki/math.scad#function-real_roots) numerical root finding. It provides [geometrical operations](https://github.com/BelfrySCAD/BOSL2/wiki/geometry.scad) like [line intersection](https://github.com/BelfrySCAD/BOSL2/wiki/geometry.scad#function-line_intersection) or [circle intersection](https://github.com/BelfrySCAD/BOSL2/wiki/geometry.scad#function-circle_circle_intersection), [coordinate transformations](https://github.com/BelfrySCAD/BOSL2/wiki/coords.scad), [string manipulation,](https://github.com/BelfrySCAD/BOSL2/wiki/strings.scad) and [list processing](https://github.com/BelfrySCAD/BOSL2/wiki/lists.scad).
2727

attachments.scad

Lines changed: 88 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -4079,81 +4079,94 @@ function _find_anchor(anchor, geom)=
40794079
let(
40804080
rpts = apply(rot(from=anchor, to=RIGHT) * move(point3d(-cp)), vnf[0]),
40814081
maxx = max(column(rpts,0)),
4082-
idxs = [for (i = idx(rpts)) if (approx(rpts[i].x, maxx)) i],
4083-
// We want to catch the case where the points lie on an edge. The complication is that the edge
4084-
// may appear twice WITH DIFFERENT VERTEX INDICES if repeated points appear in the vnf.
4085-
edges_faces = len(idxs)==2 ? // Simple case, no repeated points, [idxs] gives the edge
4086-
approx(vnf[0][idxs[0]],vnf[0][idxs[1]]) ? [] // Are edge points identical?
4087-
: let( facelist = _vnf_find_edge_faces(vnf,idxs))
4088-
len(facelist)==2 ? [[idxs], facelist] : []
4089-
: len(idxs)!=4 ? [] // If we don't have four points it's not an edge pair
4090-
: let(
4091-
pts = select(vnf[0],idxs),
4092-
matchind = [for(i=[1:3]) if (approx(pts[i],pts[0])) i] // indices where actual vertex point is the same as point zero
4093-
)
4094-
len(matchind)!=1 ? []
4095-
: let( // After this runs we have two edges as index pairs, and their associated faces as index values
4096-
match1 = select(idxs,[0,matchind[0]]),
4097-
match2 = list_remove(idxs,[0,matchind[0]]),
4098-
facelists = [for(i=[0:1], j=[0:1])
4099-
let(
4100-
ed = [match1[i],match2[j]],
4101-
fl = _vnf_find_edge_faces(vnf,ed)
4102-
)
4103-
if (fl!=[]) [ed,fl]
4104-
],
4105-
final = [column(facelists,0), flatten(column(facelists,1))]
4106-
)
4107-
assert(len(final[1])==2, "invalid!")
4108-
final,
4109-
dir = len(idxs)>2 && edges_faces==[] ? [anchor,oang]
4110-
: edges_faces!=[] ?
4111-
let(
4112-
faces = edges_faces[1],
4113-
edge = select(vnf[0],edges_faces[0][0]),
4114-
facenormals = [for(face=faces) polygon_normal(select(vnf[0],vnf[1][face]))],
4115-
direction= unit(mean(facenormals)),
4116-
projnormals = project_plane(point4d(cross(facenormals[0],facenormals[1])), facenormals),
4117-
ang = 180- posmod(v_theta(projnormals[1])-v_theta(projnormals[0]),360),
4118-
horiz_face = [for(i=[0:1]) if (approx(v_abs(facenormals[i]),UP)) i],
4119-
spin = horiz_face==[] ?
4120-
let(
4121-
edgedir = edge[1]-edge[0],
4122-
nz = [for(i=[0:2]) if (!approx(edgedir[i],0)) i],
4123-
flip = edgedir[last(nz)] < 0 ? -1 : 1
4124-
)
4125-
_compute_spin(direction, flip*edgedir)
4126-
:
4127-
let(
4128-
hedge = len(edges_faces[0])==1 ? edges_faces[0][0]
4129-
: edges_faces[0][horiz_face[0]],
4130-
face = select(vnf[1],faces[horiz_face[0]]),
4131-
edgeind = search([hedge[0]], face)[0],
4132-
flip = select(face,edgeind+1)== hedge[1] ? 1 : -1,
4133-
edgedir = edge[1]-edge[0]
4134-
)
4135-
_compute_spin(direction, flip*edgedir)
4136-
)
4137-
[direction,spin,[["edge_angle",ang],["edge_length",norm(edge[0]-edge[1])]]]
4138-
: let( // This section handles corner anchors, currently spins just point up
4139-
vertices = vnf[0],
4140-
faces = vnf[1],
4141-
cornerfaces = _vnf_find_corner_faces(vnf,idxs[0]), // faces = [3,9,12] indicating which faces
4142-
normals = [for(faceind=cornerfaces) polygon_normal(select(vnf[0], faces[faceind]))],
4143-
angles = [for(faceind=cornerfaces)
4144-
let(
4145-
thisface = faces[faceind],
4146-
vind = search(idxs[0],thisface)[0]
4147-
)
4148-
vector_angle(select(vertices, select(thisface,vind-1,vind+1)))
4149-
],
4150-
direc = unit(angles*normals)
4151-
)
4152-
[direc, atan2(direc.y,direc.x)+90],
4153-
avep = sum(select(rpts,idxs))/len(idxs),
4154-
mpt = approx(point2d(anchor),[0,0])? [maxx,0,0] : avep,
4155-
pos = point3d(cp) + rot(from=RIGHT, to=anchor, p=mpt)
4156-
) [anchor, default(override[0],pos),default(override[1],dir[0]),default(override[2],dir[1]),if (len(dir)==3) dir[2]]
4082+
4083+
idxmax = [for (i = idx(rpts)) approx(rpts[i].x, maxx)],
4084+
idxs = [for (i = idx(rpts)) if(approx(rpts[i].x, maxx)) i],
4085+
veflist=[
4086+
for(face=vnf[1])
4087+
let(
4088+
facemax = [for(vertind=face) if (idxmax[vertind]) vertind],
4089+
flip = facemax[0]==face[0] && facemax[1]==last(face)
4090+
)
4091+
[
4092+
if (len(facemax)==1) facemax else [],
4093+
if (len(facemax)==2) (flip ? reverse(facemax):facemax) else [],
4094+
if (len(facemax)>2) facemax else []
4095+
]],
4096+
vlist = [for(i=idx(veflist)) if (veflist[i][0]!=[]) veflist[i][0]],
4097+
elist = [for(i=idx(veflist)) if (veflist[i][1]!=[] && !approx(rpts[veflist[i][1][0]],rpts[veflist[i][1][1]])) veflist[i][1]],
4098+
flist = [for(i=idx(veflist)) if (veflist[i][2]!=[]) veflist[i][2]],
4099+
faceinfo = [for(face=flist) let(poly=select(vnf[0],face)) [polygon_area(poly), centroid(poly)]], //[ area, centroid]
4100+
facearea = len(faceinfo)==0 ? 0 : sum(column(faceinfo,0)),
4101+
basic_spin = _compute_spin(anchor, v_abs(anchor)==UP ? BACK: UP),
4102+
res = len(flist)>0 && !approx(facearea,0) ?
4103+
let(
4104+
center = column(faceinfo,0)*column(faceinfo,1)/facearea
4105+
)
4106+
[center,anchor,basic_spin]
4107+
: len(elist)==2 ? // One edge (which appears twice, once in each direction)
4108+
let(
4109+
edge = select(vnf[0],elist[0]),
4110+
center = mean(edge),
4111+
edgefaces = _vnf_find_edge_faces(vnf,elist), //unique([for(e=elist) each _vnf_find_edge_faces(vnf,e)]),
4112+
facenormals = [for(face=edgefaces) polygon_normal(select(vnf[0],vnf[1][face]))],
4113+
direction = unit(mean(facenormals)),
4114+
projnormals = project_plane(point4d(cross(facenormals[0],facenormals[1])), facenormals),
4115+
ang = 180- posmod(v_theta(projnormals[1])-v_theta(projnormals[0]),360),
4116+
horiz_face = [for(i=[0:1]) if (approx(v_abs(facenormals[i]),UP)) i], // index of horizontal face, at most one exists
4117+
spin = horiz_face==[] ?
4118+
let(
4119+
edgedir = edge[1]-edge[0],
4120+
nz = [for(i=[0:2]) if (!approx(edgedir[i],0)) i],
4121+
flip = edgedir[last(nz)] < 0 ? -1 : 1
4122+
)
4123+
_compute_spin(direction, flip*edgedir)
4124+
:
4125+
let( // Determine whether the edge is the right or wrong direction compared to the horizongal face
4126+
// which will determine what clockwise means so we can assign spin
4127+
face = select(vnf[1],edgefaces[horiz_face[0]]),
4128+
endptidx=search(column(elist,0),face),
4129+
hedge = elist[endptidx[0]!=[] ? 0:1],
4130+
edgedir = deltas(select(vnf[0],hedge))[0],
4131+
flip = select(face,flatten(endptidx)[0]+1)== hedge[1] ? 1 : -1
4132+
)
4133+
_compute_spin(direction, flip*edgedir)
4134+
)
4135+
[center,direction,spin,[["edge_angle",ang],["edge_length",norm(edge[1]-edge[0])]]]
4136+
: len(elist)>2 ? // multiple edges, which must be coplanar, use average of edge endpoints
4137+
let(
4138+
plist = select(vnf[0],flatten(edge)),
4139+
center = mean(plist)
4140+
)
4141+
[center,anchor,basic_spin]
4142+
: len(vlist)==0 ? assert(false,"Cannot find anchor on the VNF")
4143+
: let(
4144+
vlist = flatten(vlist),
4145+
uind = unique_approx_indexed(select(vnf[0],vlist)),
4146+
ulist = select(vlist,uind)
4147+
)
4148+
len(ulist)>1 ? // Multiple vertices: return average
4149+
let(
4150+
center = mean(select(vnf[0],ulist))
4151+
)
4152+
[center, anchor, basic_spin]
4153+
: let( // one vertex case
4154+
vuniq = unique(vlist),
4155+
vertices = vnf[0],
4156+
faces = vnf[1],
4157+
cornerfaces = _vnf_find_corner_faces(vnf,vuniq), // faces = [3,9,12] indicating which faces
4158+
normals = [for(faceind=cornerfaces) polygon_normal(select(vertices, faces[faceind]))],
4159+
angles = [for(faceind=cornerfaces)
4160+
let(
4161+
thisface = faces[faceind],
4162+
vind = flatten(search(vuniq,thisface))[0]
4163+
)
4164+
vector_angle(select(vertices, select(thisface,vind-1,vind+1)))
4165+
],
4166+
direc = unit(angles*normals)
4167+
)
4168+
[vnf[0][ulist[0]], direc, atan2(direc.y,direc.x)+90]
4169+
) [anchor, default(override[0],res[0]),default(override[1],res[1]),default(override[2],res[2]),if (len(res)==3) res[2]]
41574170
) : type == "trapezoid"? ( //size, size2, shift, override
41584171
let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[])
41594172
assert(all_comps_good, "All components of an anchor for a rectangle/trapezoid must be -1, 0, or 1")

0 commit comments

Comments
 (0)