mirror of
https://github.com/revarbat/BOSL2.git
synced 2025-10-02 16:57:20 +02:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -15,7 +15,7 @@ Requires OpenSCAD 2021.01 or later.
|
||||
|
||||
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:
|
||||
|
||||
* **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)
|
||||
* **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-Attachment-Overview)
|
||||
* **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.
|
||||
* **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.
|
||||
* **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.
|
||||
|
@@ -1046,6 +1046,7 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
|
||||
// used when attachable() places the child
|
||||
$anchor_override = all_zero(child_adjustment)? inside?child:undef
|
||||
: child+child_adjustment;
|
||||
|
||||
reference = two_d? BACK : UP;
|
||||
// inset_dir is the direction for insetting when alignment is in effect
|
||||
inset_dir = is_undef(align) ? CTR
|
||||
@@ -1058,7 +1059,7 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
|
||||
|
||||
|
||||
spinaxis = two_d? UP : anchor_dir;
|
||||
olap = - overlap * reference - inset*inset_dir + shiftout * (inset_dir + factor*reference);
|
||||
olap = - overlap * reference - inset*inset_dir + shiftout * (inset_dir + factor*reference*($anchor_inside?-1:1));
|
||||
if (norot || (approx(anchor_dir,reference) && anchor_spin==0))
|
||||
translate(pos) rot(v=spinaxis,a=factor*spin) translate(olap) default_tag("remove",removetag) children();
|
||||
else
|
||||
@@ -3985,14 +3986,20 @@ function _find_anchor(anchor, geom)=
|
||||
axy = point2d(anch),
|
||||
bot = point3d(v_mul(point2d(size )/2, axy), -h/2),
|
||||
top = point3d(v_mul(point2d(size2)/2, axy) + shift, h/2),
|
||||
degenerate = sum(v_abs(point2d(anch)))==1 && (point2d(bot)==[0,0] || v_mul(point2d(size2)/2, axy)==[0,0]),
|
||||
edge = top-bot,
|
||||
other_edge = degenerate ? move(shift,mirror(axy, move(-shift,top))) - mirror(point3d(point2d(anch)), p=bot):CTR,
|
||||
pos = point3d(cp) + lerp(bot,top,u) + offset,
|
||||
// Find vectors of the faces involved in the anchor
|
||||
facevecs =
|
||||
facevecs =
|
||||
[
|
||||
if (anch.x!=0) unit(rot(from=UP, to=[edge.x,0,max(0.01,h)], p=[axy.x,0,0]), UP),
|
||||
if (anch.y!=0) unit(rot(from=UP, to=[0,edge.y,max(0.01,h)], p=[0,axy.y,0]), UP),
|
||||
if (anch.z!=0) unit([0,0,anch.z],UP)
|
||||
if (anch.z!=0 && !degenerate) unit([0,0,anch.z],UP),
|
||||
if (anch.z!=0 && degenerate && anch.y!=0)
|
||||
unit(rot(from=UP, to=[0,other_edge.y,max(0.01,h)], p=[0,-axy.y,0]), UP),
|
||||
if (anch.z!=0 && degenerate && anch.x!=0)
|
||||
unit(rot(from=UP, to=[other_edge.x,0,max(0.01,h)], p=[-axy.x,0,0]), UP),
|
||||
],
|
||||
dir = anch==CENTER? UP
|
||||
: len(facevecs)==1? unit(facevecs[0],UP)
|
||||
@@ -4031,8 +4038,8 @@ function _find_anchor(anchor, geom)=
|
||||
// with a correction for top/bottom (anchor.z).
|
||||
// Otherwise use the standard BACK/UP definition
|
||||
// The precomputed oang value seems to be wrong, at least when axis!=UP
|
||||
|
||||
spin = is_def(edgedir) && !approx(edgedir.z,0) ? _compute_spin(final_dir, edgedir * (edgedir*UP>0?1:-1))
|
||||
spin = is_def(edgedir) && degenerate ? _compute_spin(final_dir, unit(((BACK+RIGHT)*edgedir)*edgedir))
|
||||
: is_def(edgedir) && !approx(edgedir.z,0) ? _compute_spin(final_dir, edgedir * (edgedir*UP>0?1:-1))
|
||||
: is_def(edgedir) ? _compute_spin(final_dir,
|
||||
edgedir * (approx(unit(cross(UP,edgedir)),unit([final_dir.x,final_dir.y,0])*anchor.z) ? 1 : -1))
|
||||
: _compute_spin(final_dir, final_dir==DOWN || final_dir==UP ? BACK : UP)
|
||||
@@ -4162,6 +4169,7 @@ function _find_anchor(anchor, geom)=
|
||||
let(
|
||||
vnf=geom[1],
|
||||
override = geom[2](anchor)
|
||||
,fd=echo(cp=cp)
|
||||
) // CENTER anchors anchor on cp, "origin" anchors on [0,0]
|
||||
approx(anchor,CTR)? [anchor, default(override[0],cp),default(override[1],UP),default(override[2], 0)] :
|
||||
vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor,UP), 0] :
|
||||
@@ -4442,15 +4450,15 @@ module show_anchors(s=10, std=true, custom=true) {
|
||||
if (std) {
|
||||
for (anchor=_standard_anchors(two_d=two_d)) {
|
||||
if(two_d) {
|
||||
attach(anchor) anchor_arrow2d(s);
|
||||
attach(anchor,BOT) anchor_arrow2d(s);
|
||||
} else {
|
||||
attach(anchor) anchor_arrow(s);
|
||||
attach(anchor,BOT) anchor_arrow(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (custom) {
|
||||
for (anchor=last($parent_geom)) {
|
||||
attach(anchor[0]) {
|
||||
attach(anchor[0],BOT) {
|
||||
if(two_d) {
|
||||
anchor_arrow2d(s, color="cyan");
|
||||
} else {
|
||||
|
@@ -577,14 +577,14 @@ module partition(size=100, spread=10, cutsize=10, cutpath="jigsaw", gap=0, cutpa
|
||||
move(vec) {
|
||||
$idx = 0;
|
||||
intersection() {
|
||||
children();
|
||||
if ($children>0) children();
|
||||
partition_mask(l=rsize.x, w=rsize.y, h=rsize.z, cutsize=cutsize, cutpath=cutpath, gap=gap, cutpath_centered=cutpath_centered, spin=spin);
|
||||
}
|
||||
}
|
||||
move(-vec) {
|
||||
$idx = 1;
|
||||
intersection() {
|
||||
children();
|
||||
if ($children>0) children();
|
||||
partition_mask(l=rsize.x, w=rsize.y, h=rsize.z, cutsize=cutsize, cutpath=cutpath, gap=gap, cutpath_centered=cutpath_centered, inverse=true, spin=spin);
|
||||
}
|
||||
}
|
||||
|
@@ -4145,7 +4145,9 @@ function _prism_fillet_prism(name, basepoly, bot, top, d, k, N, overlap, uniform
|
||||
// The prism will connect anchor points described by the two descriptions you supply. The supported object
|
||||
// types are prismoids, VNFs, cylinders, spheres, and linear sweeps. For prismoids and VNFs you can use any anchor on a face
|
||||
// or edge anchors that include edge geometry. For spheres you can use any anchor. In the case of cylinders and linear sweeps you can
|
||||
// attach to the flat top or bottom in any case, but for side attachments, the shape must not have scaling (so it cannot
|
||||
// attach to the flat top or bottom, or to named face anchors in any case. When you do this, the attachment is treated as an infinite plane.
|
||||
// You can attach to the side of the extrusion and follow the shape of the extrusion using the standard anchors only, but the
|
||||
// shape must not have scaling (so it cannot
|
||||
// be conical) and it must not have any shift. Only right angle cylinders and extrusions are supported.
|
||||
// Anchors on the top and bottom edges are also not supported. When connecting to an extrusion the selected anchor
|
||||
// point must lie on the surface of the shape. This may requires setting `atype="intersect"` when creating the extrusion.
|
||||
@@ -4479,7 +4481,7 @@ function _get_obj_type(ind,geom,anchor,prof) =
|
||||
)
|
||||
[[x,-y],[0,0], [x,y]]
|
||||
: starts_with(geom[0], "extrusion") ?
|
||||
anchor==UP || anchor==DOWN ? "plane"
|
||||
anchor==UP || anchor==DOWN || starts_with(anchor,"face") ? "plane"
|
||||
:
|
||||
assert(geom[3]==0, str("Extrusion in desc", ind, " has nonzero twist, which is not supported."))
|
||||
assert(geom[5]==[0,0], str("Extrusion in desc", ind, " has nonzero shift, which is not supported."))
|
||||
|
@@ -190,7 +190,7 @@ function cube(size=1, center, anchor, spin=0, orient=UP) =
|
||||
// );
|
||||
// Example: Roundings and Chamfers can be as large as the full size of the cuboid, so long as the edges would not interfere.
|
||||
// cuboid([40,20,10], rounding=20, edges=[FWD+RIGHT,BACK+LEFT]);
|
||||
// Example: Standard Connectors
|
||||
// Example: Standard anchors
|
||||
// cuboid(40) show_anchors();
|
||||
|
||||
module cuboid(
|
||||
@@ -616,6 +616,11 @@ function cuboid(
|
||||
// The anchors on the top and bottom faces have spin pointing back. The anchors on the side faces have spin point UP.
|
||||
// The anchors on the top and bottom edges also have anchors that point clockwise as viewed from outside the shapep.
|
||||
// The anchors on the side edges and the corners have spin with positive Z component, pointing along the edge where the anchor is located.
|
||||
// A degenerate prismoid with a line segment for the top or bottom has its top or bottom edge anchors set to provide an anchor for that top
|
||||
// or bottom edge. So for example, if the top is `[0,10]` then the top edge is parallel to the Y axis and you can anchor to that
|
||||
// edge using the `TOP+RIGHT` or `TOP+LEFT` anchors; these anchors point in the direction that divides the edge in half and provide
|
||||
// the `$edge_angle` and `$edge_length` values generally provided by edge anchors. The UP or DOWN anchor is in the same location but always points
|
||||
// in the Z direction and provides no edge data.
|
||||
// Arguments:
|
||||
// size1 = [width, length] of the bottom end of the prism.
|
||||
// size2 = [width, length] of the top end of the prism.
|
||||
@@ -689,9 +694,14 @@ function cuboid(
|
||||
// mask2d_roundover(h=5,mask_angle=$edge_angle);
|
||||
// }
|
||||
// }
|
||||
// Example(Spin,VPD=160,VPT=[0,0,10]): Standard Connectors
|
||||
// Example(Spin,VPD=160,VPT=[0,0,10]): Standard anchors
|
||||
// prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5])
|
||||
// show_anchors();
|
||||
// Example(3D): When the top or bottom is degenerate, you can anchor to and round the degenerate edge by using either one of the edge anchors that correspond to that edge. But note that {{edge_profile()}} does not work for this degenerate case. We used `TOP+RIGHT` below as the anchor point, but `TOP+LEFT` will produce an identical result.
|
||||
// diff()
|
||||
// prismoid([10,14],[0,8], shift=[4,3], h=7)
|
||||
// attach(TOP+RIGHT, FWD+LEFT, inside=true)
|
||||
// rounding_edge_mask(r=2,l=$edge_length+6);
|
||||
|
||||
module prismoid(
|
||||
size1=undef, size2=undef, h, shift=[undef,undef],
|
||||
@@ -841,8 +851,7 @@ function prismoid(
|
||||
// being located at the bottom of the shape, so confirm anchor positions before use.
|
||||
// Additional named face and edge anchors are located on the side faces and vertical edges of the prism.
|
||||
// You can use `EDGE(i)`, `EDGE(TOP,i)` and `EDGE(BOT,i)` as a shorthand for accessing the named edge anchors, and `FACE(i)` for the face anchors.
|
||||
// When you use `shift`, which moves the top face of the prism, the spin for the side face and edges anchors will align
|
||||
// the child with the edge or face direction. The "edge0" anchor identifies an edge located along the X+ axis, and then edges
|
||||
// The "edge0" anchor identifies an edge located along the X+ axis, and then edges
|
||||
// are labeled counting up in the clockwise direction. Similarly "face0" is the face immediately clockwise from "edge0", and face
|
||||
// labeling proceeds clockwise. The top and bottom edge anchors label edges directly above and below the face with the same label.
|
||||
// If you set `realign=true` then "face0" is oriented in the X+ direction.
|
||||
@@ -1753,6 +1762,7 @@ function rect_tube(
|
||||
// "hypot" = Center of angled wedge face, perpendicular to that face.
|
||||
// "hypot_left" = Left side of angled wedge face, bisecting the angle between the left side and angled faces.
|
||||
// "hypot_right" = Right side of angled wedge face, bisecting the angle between the right side and angled faces.
|
||||
// "top_edge" = Top edge anchor which, unlike the UP anchor, points in direction that bisects the edge, and provides `$edge_length` and `$edge_angle`.
|
||||
//
|
||||
// Example: Centered
|
||||
// wedge([20, 40, 15], center=true);
|
||||
@@ -1766,6 +1776,11 @@ function rect_tube(
|
||||
// Example(3D,Med,VPR=[55.00,0.00,25.00],VPD=151.98,VPT=[2.30,-11.81,-5.66]): Named Anchors
|
||||
// wedge([40, 80, 30], center=true)
|
||||
// show_anchors(std=false);
|
||||
// Example(3D): Rounding the top of the wedge using the "top_edge" anchor
|
||||
// diff()
|
||||
// wedge([10,15,7])
|
||||
// attach("top_edge", FWD+LEFT, inside=true)
|
||||
// rounding_edge_mask(r=2, l=$edge_length+1);
|
||||
|
||||
module wedge(size=[1, 1, 1], center, anchor, spin=0, orient=UP)
|
||||
{
|
||||
@@ -1778,10 +1793,13 @@ module wedge(size=[1, 1, 1], center, anchor, spin=0, orient=UP)
|
||||
left_dir = unit(hypot_dir+LEFT);
|
||||
right_dir = unit(hypot_dir+RIGHT);
|
||||
hedge_spin=vector_angle(spindir,rot(from=UP,to=left_dir, p=BACK));
|
||||
topedge_dir = [0, each unit(unit([size.z,size.y])+[-1,0])];
|
||||
anchors = [
|
||||
named_anchor("hypot", CTR, hypot_dir, 180),
|
||||
named_anchor("hypot_left", [-size.x/2,0,0], left_dir,-hedge_spin),
|
||||
named_anchor("hypot_right", [size.x/2,0,0], right_dir,hedge_spin),
|
||||
named_anchor("top_edge", [0,-size.y/2,size.z/2], topedge_dir, _compute_spin(topedge_dir,RIGHT),
|
||||
info=[["edge_angle",atan2(size.y,size.z)],["edge_length",size.x]])
|
||||
];
|
||||
attachable(anchor,spin,orient, size=size, anchors=anchors) {
|
||||
if (size.z > 0) {
|
||||
@@ -1811,10 +1829,13 @@ function wedge(size=[1,1,1], center, anchor, spin=0, orient=UP) =
|
||||
left_dir = unit(hypot_dir+LEFT),
|
||||
right_dir = unit(hypot_dir+RIGHT),
|
||||
hedge_spin=vector_angle(spindir,rot(from=UP,to=left_dir, p=BACK)),
|
||||
topedge_dir = [0, each unit(unit([size.z,size.y])+[-1,0])],
|
||||
anchors = [
|
||||
named_anchor("hypot", CTR, hypot_dir, 180),
|
||||
named_anchor("hypot_left", [-size.x/2,0,0], left_dir,-hedge_spin),
|
||||
named_anchor("hypot_right", [size.x/2,0,0], right_dir,hedge_spin),
|
||||
named_anchor("top_edge", [0,-size.y/2,size.z/2], topedge_dir, _compute_spin(topedge_dir,RIGHT),
|
||||
info=[["edge_angle",atan2(size.y,size.z)],["edge_length",size.x]])
|
||||
]
|
||||
)
|
||||
reorient(anchor,spin,orient, size=size, anchors=anchors, p=vnf);
|
||||
|
218
skin.scad
218
skin.scad
@@ -541,6 +541,15 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close
|
||||
// correct only for twisted objects, and corner anchors may point in unexpected directions in some cases. These anchors also ignore any applied texture.
|
||||
// If you need anchors directly computed from the surface you can pass the vnf from linear_sweep
|
||||
// to {{vnf_polyhedron()}}, which computes anchors directly from the full VNF.
|
||||
// Additional named face and edge anchors are located on the side faces and vertical edges of the prism.
|
||||
// When you sweep a polygon you can use `EDGE(i)`, `EDGE(TOP,i)` and `EDGE(BOT,i)` as a shorthand for
|
||||
// accessing the named edge anchors, and `FACE(i)` for the face anchors.
|
||||
// The "edge0" anchor identifies an edge located along the X+ axis, and then edges
|
||||
// are labeled counting up in the clockwise direction. Similarly "face0" is the face immediately clockwise from "edge0", and face
|
||||
// labeling proceeds clockwise. The top and bottom edge anchors label edges directly above and below the face with the same label.
|
||||
// When you sweep a region, the region is decomposed using {{region_parts()}} and the anchors are generated for the region components
|
||||
// in the order produced by the decomposition, working entirely through each component and then on to the next component.
|
||||
// The anchors for twisted shapes may be inaccurate.
|
||||
// Arguments:
|
||||
// region = The 2D [Region](regions.scad) or polygon that is to be extruded.
|
||||
// h / height / l / length = The height to extrude the region. Default: 1
|
||||
@@ -574,6 +583,12 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close
|
||||
// "origin" = Centers the extruded shape vertically only, but keeps the original path positions in the X and Y. Oriented UP.
|
||||
// "original_base" = Keeps the original path positions in the X and Y, but at the bottom of the extrusion. Oriented DOWN.
|
||||
// "original_top" = Keeps the original path positions in the X and Y, but at the top of the extrusion. Oriented UP.
|
||||
// "edge0", "edge1", etc. = Center of each side edge, spin pointing up along the edge. Can access with EDGE(i)
|
||||
// "face0", "face1", etc. = Center of each side face, spin pointing up. Can access with FACE(i)
|
||||
// "top_edge0", "top_edge1", etc = Center of each top edge, spin pointing clockwise (from top). Can access with EDGE(TOP,i)
|
||||
// "bot_edge0", "bot_edge1", etc = Center of each bottom edge, spin pointing clockwise (from bottom). Can access with EDGE(BOT,i)
|
||||
// "top_corner0", "top_corner1", etc = Top corner, pointing in direction of associated edge anchor, spin up along associated edge
|
||||
// "bot_corner0", "bot_corner1", etc = Bottom corner, pointing in direction of associated edge anchor, spin up along associated edge
|
||||
// Example: Extruding a Compound Region.
|
||||
// rgn1 = [for (d=[10:10:60]) circle(d=d,$fn=8)];
|
||||
// rgn2 = [square(30,center=false)];
|
||||
@@ -743,7 +758,7 @@ module linear_sweep(
|
||||
anchor = center==true? "origin" :
|
||||
center == false? "original_base" :
|
||||
default(anchor, "original_base");
|
||||
vnf = linear_sweep(
|
||||
vnf_geom = linear_sweep(
|
||||
region, height=h, style=style, caps=caps,
|
||||
twist=twist, scale=scale, shift=shift,
|
||||
texture=texture,
|
||||
@@ -755,31 +770,73 @@ module linear_sweep(
|
||||
tex_depth=tex_depth,
|
||||
tex_samples=tex_samples,
|
||||
slices=slices,
|
||||
maxseg=maxseg,
|
||||
anchor="origin"
|
||||
maxseg=maxseg, atype=atype,
|
||||
anchor="origin", _return_geom=true
|
||||
);
|
||||
anchors = [
|
||||
named_anchor("original_base", [0,0,-h/2], DOWN),
|
||||
named_anchor("original_top", [0,0,h/2], UP),
|
||||
];
|
||||
cp = default(cp, "centroid");
|
||||
geom = atype=="hull"? attach_geom(cp=cp, region=region, h=h, extent=true, shift=shift, scale=scale, twist=twist, anchors=anchors) :
|
||||
atype=="intersect"? attach_geom(cp=cp, region=region, h=h, extent=false, shift=shift, scale=scale, twist=twist, anchors=anchors) :
|
||||
atype=="bbox"?
|
||||
let(
|
||||
bounds = pointlist_bounds(flatten(region)),
|
||||
size = bounds[1] - bounds[0],
|
||||
midpt = (bounds[0] + bounds[1])/2
|
||||
)
|
||||
attach_geom(cp=[0,0,0], size=point3d(size,h), offset=point3d(midpt), shift=shift, scale=scale, twist=twist, anchors=anchors) :
|
||||
assert(in_list(atype, ["hull","intersect","bbox"]), "\nAnchor type must be \"hull\", \"intersect\", or \"bbox\".");
|
||||
attachable(anchor,spin,orient, geom=geom) {
|
||||
vnf_polyhedron(vnf, convexity=convexity);
|
||||
attachable(anchor,spin,orient, geom=vnf_geom[1]) {
|
||||
vnf_polyhedron(vnf_geom[0], convexity=convexity);
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function _make_all_prism_anchors(bot, top, startind=0) =
|
||||
let(
|
||||
facenormal= [
|
||||
for(i=idx(bot))
|
||||
let(
|
||||
edge0 = [top[i],bot[i]], // vertical edge at i
|
||||
edge1 = [select(top,i+1),select(bot,i+1)], // vertical edge at i+1
|
||||
facenormal = unit(unit(cross(edge1[1]-edge0[0], edge0[1]-edge0[0]))+
|
||||
unit(cross(edge0[0]-edge1[1], edge1[0]-edge1[1])))
|
||||
)
|
||||
facenormal
|
||||
],
|
||||
anchors = [for(i=idx(bot))
|
||||
let(
|
||||
|
||||
edge1 = [top[i],bot[i]], // vertical edge at i
|
||||
edge2 = [select(top,i+1),select(bot,i+1)], // vertical edge at i+1
|
||||
|
||||
facecenter = mean(concat(edge1,edge2)),
|
||||
facespin = _compute_spin(facenormal[i], UP),
|
||||
|
||||
side_edge_center = mean(edge1),
|
||||
side_edge_dir = top[i]-bot[i],
|
||||
side_edge_normal = unit(vector_bisect(facenormal[i],select(facenormal,i-1))),
|
||||
side_edge_spin = _compute_spin(side_edge_normal, side_edge_dir),
|
||||
side_edge_angle = 180-vector_angle(facenormal[i], select(facenormal,i-1)),
|
||||
side_edge_len = norm(side_edge_dir),
|
||||
|
||||
top_edge_center = (edge2[0]+edge1[0])/2,
|
||||
top_edge_dir = edge2[0]-edge1[0],
|
||||
bot_edge_center = (edge1[1]+edge2[1])/2,
|
||||
bot_edge_dir = edge1[1]-edge2[1],
|
||||
topnormal = unit(facenormal[i]+UP),
|
||||
botnormal = unit(facenormal[i]+DOWN),
|
||||
topedgespin = _compute_spin(topnormal, top_edge_dir),
|
||||
botedgespin = _compute_spin(botnormal, bot_edge_dir),
|
||||
topedgeangle = 180-vector_angle(UP,facenormal[i])
|
||||
)
|
||||
each [
|
||||
named_anchor(str("face",i+startind), facecenter, facenormal[i], facespin),
|
||||
named_anchor(str("edge",i+startind), side_edge_center, side_edge_normal, side_edge_spin,
|
||||
info=[["edge_angle",side_edge_angle], ["edge_length",side_edge_len]]),
|
||||
named_anchor(str("top_edge",i+startind), top_edge_center, topnormal, topedgespin,
|
||||
info=[["edge_angle",topedgeangle],["edge_length",norm(top_edge_dir)]]),
|
||||
named_anchor(str("bot_edge",i+startind), bot_edge_center, botnormal, botedgespin,
|
||||
info=[["edge_angle",180-topedgeangle],["edge_length",norm(bot_edge_dir)]]),
|
||||
named_anchor(str("top_corner",i+startind), top[i], unit(side_edge_normal+UP),
|
||||
_compute_spin(unit(side_edge_normal+UP),side_edge_dir)),
|
||||
named_anchor(str("bot_corner",i+startind), bot[i], unit(side_edge_normal+DOWN),
|
||||
_compute_spin(unit(side_edge_normal+DOWN),side_edge_dir))
|
||||
]
|
||||
]
|
||||
)
|
||||
anchors;
|
||||
|
||||
|
||||
|
||||
function linear_sweep(
|
||||
region, height, center,
|
||||
twist=0, scale=1, shift=[0,0],
|
||||
@@ -788,7 +845,7 @@ function linear_sweep(
|
||||
texture, tex_size=[5,5], tex_reps, tex_counts,
|
||||
tex_inset=false, tex_rot=0,
|
||||
tex_scale, tex_depth, tex_samples, h, l, length,
|
||||
anchor, spin=0, orient=UP
|
||||
anchor, spin=0, orient=UP, _return_geom=false
|
||||
) =
|
||||
assert(num_defined([tex_reps,tex_counts])<2, "\nIn linear_sweep() the 'tex_counts' parameter has been replaced by 'tex_reps'. You cannot give both.")
|
||||
assert(num_defined([tex_scale,tex_depth])<2, "\nIn linear_sweep() the 'tex_scale' parameter has been replaced by 'tex_depth'. You cannot give both.")
|
||||
@@ -804,63 +861,74 @@ function linear_sweep(
|
||||
assert(is_vector(shift, 2), str(shift))
|
||||
assert(is_bool(caps) || is_bool_list(caps,2), "\ncaps must be boolean or a list of two booleans.")
|
||||
let(
|
||||
h = one_defined([h, height,l,length],"h,height,l,length",dflt=1)
|
||||
)
|
||||
!is_undef(texture)? _textured_linear_sweep(
|
||||
region, h=h, caps=caps,
|
||||
texture=texture, tex_size=tex_size,
|
||||
counts=tex_reps, inset=tex_inset,
|
||||
rot=tex_rot, tex_scale=tex_depth,
|
||||
twist=twist, scale=scale, shift=shift,
|
||||
style=style, samples=tex_samples,
|
||||
anchor=anchor, spin=spin, orient=orient
|
||||
) :
|
||||
let(
|
||||
caps = is_bool(caps) ? [caps,caps] : caps,
|
||||
anchor = center==true? "origin" :
|
||||
center == false? "original_base" :
|
||||
default(anchor, "original_base"),
|
||||
h = one_defined([h, height,l,length],"h,height,l,length",dflt=1),
|
||||
regions = region_parts(region),
|
||||
slices = default(slices, max(1,ceil(abs(twist)/5))),
|
||||
scale = is_num(scale)? [scale,scale] : point2d(scale),
|
||||
topmat = move(shift) * scale(scale) * rot(-twist),
|
||||
trgns = [
|
||||
for (rgn = regions) [
|
||||
for (path = rgn) let(
|
||||
p = list_unwrap(path),
|
||||
path = is_undef(maxseg)? p : [
|
||||
for (seg = pair(p,true)) each
|
||||
let( steps = ceil(norm(seg.y - seg.x) / maxseg) )
|
||||
lerpn(seg.x, seg.y, steps, false)
|
||||
]
|
||||
) apply(topmat, path)
|
||||
]
|
||||
],
|
||||
vnf = vnf_join([
|
||||
for (rgn = regions)
|
||||
for (pathnum = idx(rgn)) let(
|
||||
p = list_unwrap(rgn[pathnum]),
|
||||
path = is_undef(maxseg)? p : [
|
||||
for (seg=pair(p,true)) each
|
||||
let(steps=ceil(norm(seg.y-seg.x)/maxseg))
|
||||
lerpn(seg.x, seg.y, steps, false)
|
||||
],
|
||||
verts = [
|
||||
for (i=[0:1:slices]) let(
|
||||
u = i / slices,
|
||||
scl = lerp([1,1], scale, u),
|
||||
ang = lerp(0, -twist, u),
|
||||
off = lerp([0,0,-h/2], point3d(shift,h/2), u),
|
||||
m = move(off) * scale(scl) * rot(ang)
|
||||
) apply(m, path3d(path))
|
||||
]
|
||||
) vnf_vertex_array(verts, caps=false, col_wrap=true, style=style),
|
||||
if (caps[0]) for (rgn = regions) vnf_from_region(rgn, down(h/2), reverse=true),
|
||||
if (caps[1]) for (rgn = trgns) vnf_from_region(rgn, up(h/2), reverse=false)
|
||||
]),
|
||||
vnf = !is_undef(texture)?
|
||||
_textured_linear_sweep(
|
||||
region, h=h, caps=caps,
|
||||
texture=texture, tex_size=tex_size,
|
||||
counts=tex_reps, inset=tex_inset,
|
||||
rot=tex_rot, tex_scale=tex_depth,
|
||||
twist=twist, scale=scale, shift=shift,
|
||||
style=style, samples=tex_samples)
|
||||
: let(
|
||||
caps = is_bool(caps) ? [caps,caps] : caps,
|
||||
anchor = center==true? "origin" :
|
||||
center == false? "original_base" :
|
||||
default(anchor, "original_base"),
|
||||
slices = default(slices, max(1,ceil(abs(twist)/5))),
|
||||
scale = is_num(scale)? [scale,scale] : point2d(scale),
|
||||
topmat = move(shift) * scale(scale) * rot(-twist),
|
||||
trgns = [
|
||||
for (rgn = regions) [
|
||||
for (path = rgn) let(
|
||||
p = list_unwrap(path),
|
||||
path = is_undef(maxseg)? p : [
|
||||
for (seg = pair(p,true)) each
|
||||
let( steps = ceil(norm(seg.y - seg.x) / maxseg) )
|
||||
lerpn(seg.x, seg.y, steps, false)
|
||||
]
|
||||
) apply(topmat, path)
|
||||
]
|
||||
],
|
||||
vnf = vnf_join([
|
||||
for (rgn = regions)
|
||||
for (pathnum = idx(rgn)) let(
|
||||
p = list_unwrap(rgn[pathnum]),
|
||||
path = is_undef(maxseg)? p : [
|
||||
for (seg=pair(p,true)) each
|
||||
let(steps=ceil(norm(seg.y-seg.x)/maxseg))
|
||||
lerpn(seg.x, seg.y, steps, false)
|
||||
],
|
||||
verts = [
|
||||
for (i=[0:1:slices]) let(
|
||||
u = i / slices,
|
||||
scl = lerp([1,1], scale, u),
|
||||
ang = lerp(0, -twist, u),
|
||||
off = lerp([0,0,-h/2], point3d(shift,h/2), u),
|
||||
m = move(off) * scale(scl) * rot(ang)
|
||||
) apply(m, path3d(path))
|
||||
]
|
||||
) vnf_vertex_array(verts, caps=false, col_wrap=true, style=style),
|
||||
if (caps[0]) for (rgn = regions) vnf_from_region(rgn, down(h/2), reverse=true),
|
||||
if (caps[1]) for (rgn = trgns) vnf_from_region(rgn, up(h/2), reverse=false)
|
||||
])
|
||||
)
|
||||
vnf,
|
||||
regparts = flatten(regions),
|
||||
sizes = [0,each cumsum([for(entry=regparts) len(entry)])],
|
||||
ganchors = [
|
||||
for(i=idx(regparts))
|
||||
let(
|
||||
bot = path3d(regparts[i],-h/2),
|
||||
top = path3d(move(shift,scale(scale, zrot(-twist, regparts[i]))),h/2)
|
||||
)
|
||||
each _make_all_prism_anchors(bot,top, startind=sizes[i])
|
||||
],
|
||||
anchors = [
|
||||
named_anchor("original_base", [0,0,-h/2], DOWN),
|
||||
named_anchor("original_top", [0,0,h/2], UP),
|
||||
each ganchors
|
||||
],
|
||||
cp = default(cp, "centroid"),
|
||||
geom = atype=="hull"? attach_geom(cp=cp, region=region, h=h, extent=true, shift=shift, scale=scale, twist=twist, anchors=anchors) :
|
||||
@@ -873,7 +941,7 @@ function linear_sweep(
|
||||
)
|
||||
attach_geom(cp=[0,0,0], size=point3d(size,h), offset=point3d(midpt), shift=shift, scale=scale, twist=twist, anchors=anchors) :
|
||||
assert(in_list(atype, ["hull","intersect","bbox"]), "\nAnchor type must be \"hull\", \"intersect\", or \"bbox\".")
|
||||
) reorient(anchor,spin,orient, geom=geom, p=vnf);
|
||||
) _return_geom ? [vnf,geom] : reorient(anchor,spin,orient, geom=geom, p=vnf);
|
||||
|
||||
|
||||
// Function&Module: rotate_sweep()
|
||||
|
@@ -1082,7 +1082,7 @@ function zscale(z=1, p=_NO_ARG, cp=0) =
|
||||
function mirror(v, p=_NO_ARG) =
|
||||
assert(is_vector(v))
|
||||
assert(p==_NO_ARG || is_list(p),"Invalid pointlist")
|
||||
let(m = len(v)==2? affine2d_mirror(v) : affine3d_mirror(v))
|
||||
let(m = affine3d_mirror(v))
|
||||
p==_NO_ARG? m : apply(m,p);
|
||||
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
[Previous: Using position()](Tutorial-Attachment-Position)
|
||||
|
||||
# Aligning children with align()
|
||||
|
||||
You may have noticed that with position() and orient(), specifying the
|
||||
@@ -125,3 +127,5 @@ cyl(h=20,d=10,$fn=128)
|
||||
align([1,.3],TOP)
|
||||
color("lightblue")cuboid(5);
|
||||
```
|
||||
|
||||
[Next: Using attach()](Tutorial-Attachment-Attach)
|
||||
|
@@ -1,3 +1,5 @@
|
||||
[Prev: Using align()](Tutorial-Attachment-Align)
|
||||
|
||||
# Attachment using attach()
|
||||
|
||||
The `attach()` module can stick the child object to the parent object
|
||||
@@ -674,3 +676,5 @@ circle(d=50){
|
||||
trapezoid(w1=30,w2=0,h=30);
|
||||
}
|
||||
```
|
||||
|
||||
[Next: Attachable Parts](Tutorial-Attachment-Parts)
|
||||
|
@@ -1,3 +1,5 @@
|
||||
[Previous: Attachments Overview](Tutorial-Attachment-Overview)
|
||||
|
||||
# Basic Object Positioning: Anchor, Spin and Orient
|
||||
|
||||
When you create attachable objects using BOSL2 you have some options
|
||||
@@ -320,3 +322,5 @@ For 2D shapes, you can mix `anchor=` with `spin=`, but not with `orient=`.
|
||||
include <BOSL2/std.scad>
|
||||
square([40,30], anchor=BACK+LEFT, spin=30);
|
||||
```
|
||||
|
||||
[Next: Relative Positioning of Children](Tutorial-Attachment-Relative-Positioning)
|
||||
|
@@ -1,3 +1,5 @@
|
||||
[Prev: Attachable Parts](Tutorial-Attachment-Parts)
|
||||
|
||||
## Coloring Attachables
|
||||
Usually, when coloring a shape with the `color()` module, the parent color overrides the colors of
|
||||
all children. This is often not what you want:
|
||||
@@ -57,3 +59,5 @@ affecting its descendents.
|
||||
As with all of the attachable features, these color, highlight and ghost modules only work
|
||||
on attachable objects, so they will have no effect on objects you
|
||||
create using `linear_extrude()` or `rotate_extrude()`.
|
||||
|
||||
[Next: Tagged Operations with Attachments](Tutorial-Attachment-Tags)
|
||||
|
@@ -1,3 +1,5 @@
|
||||
[Prev: Tagged Operations with Attachments](Tutorial-Attachment-Tags)
|
||||
|
||||
# Using Attachment for Edge Profiling
|
||||
|
||||
You can use attachment in various ways to create edge profiles on
|
||||
@@ -163,4 +165,4 @@ cube([50,60,70],center=true)
|
||||
mask2d_roundover(10);
|
||||
```
|
||||
|
||||
|
||||
[Next: Making Attachable Objects](Tutorial-Attachment-Making)
|
||||
|
@@ -1,3 +1,5 @@
|
||||
[Prev: Edge Profiling with Attachment](Tutorial-Attachment-Edge-Profiling)
|
||||
|
||||
# Making Attachables
|
||||
|
||||
To make a shape attachable, you just need to wrap it with an `attachable()` module with a
|
||||
@@ -531,3 +533,140 @@ module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
|
||||
}
|
||||
cubic_barbell(100) show_anchors(30);
|
||||
```
|
||||
|
||||
## Making Gometry With attach_geom()
|
||||
|
||||
Sometimes it may be advantageous to create the attachable geometry as
|
||||
a data structure. This can be particularly useful if you want to
|
||||
implement anchor types with an object, because it allows for an easy
|
||||
way to create different anchor options without having to repeat code.
|
||||
|
||||
Suppose we create a simple tetrahedron object:
|
||||
|
||||
```openscad-3D
|
||||
include<BOSL2/std.scad>
|
||||
module tetrahedron(base, height, spin=0, anchor=FWD+LEFT+BOT, orient=UP)
|
||||
{
|
||||
base_poly = path3d([[0,0],[0,base],[base,0]]);
|
||||
top = [0,0,height];
|
||||
vnf = vnf_vertex_array([base_poly, repeat(top,3)],col_wrap=true,cap1=true);
|
||||
attachable(anchor=anchor,orient=orient,spin=spin,vnf=vnf,cp="centroid"){
|
||||
vnf_polyhedron(vnf);
|
||||
children();
|
||||
}
|
||||
}
|
||||
tetrahedron(20,18) show_anchors();
|
||||
```
|
||||
|
||||
For this module we have used VNF anchors, but this tetrahedron is the
|
||||
corner of a cuboid, so maybe sometimes you prefer to use anchors
|
||||
based on the corresponding cuboid (its bounding box). You can create a module with bounding
|
||||
box anchors like this, where we have explicitly centered the VNF
|
||||
to make it work with the prismoid type anchoring:
|
||||
|
||||
```openscad-3D
|
||||
include<BOSL2/std.scad>
|
||||
module tetrahedron(base, height, spin=0, anchor=FWD+LEFT+BOT, orient=UP)
|
||||
{
|
||||
base_poly = path3d([[0,0],[0,base],[base,0]]);
|
||||
top = [0,0,height];
|
||||
vnf = move([-base/2,-base/2,-height/2],
|
||||
vnf_vertex_array([base_poly, repeat(top,3)],col_wrap=true,cap1=true));
|
||||
attachable(anchor=anchor,orient=orient,spin=spin,size=[base,base,height]){
|
||||
vnf_polyhedron(vnf);
|
||||
children();
|
||||
}
|
||||
}
|
||||
tetrahedron(20,18) show_anchors();
|
||||
```
|
||||
|
||||
The arguments needed to attachable are different in this case. If you
|
||||
want to conditionally switch between these two modes of operation,
|
||||
this presents a complication. While it is possible to work around
|
||||
this by conditionally setting parameters to `undef`, the resulting
|
||||
code will be more complex and harder to read. A better solution is to
|
||||
compute the geometry conditionally. Then the geometry can be passed
|
||||
to `attachable()`.
|
||||
|
||||
```openscad-3D;Big
|
||||
include<BOSL2/std.scad>
|
||||
module tetrahedron(base, height, atype="vnf", spin=0, anchor=FWD+LEFT+BOT, orient=UP)
|
||||
{
|
||||
assert(atype=="vnf" || atype=="box");
|
||||
base_poly = path3d([[0,0],[0,base],[base,0]]);
|
||||
top = [0,0,height];
|
||||
vnf = move([-base/2,-base/2,-height/2],
|
||||
vnf_vertex_array([base_poly, repeat(top,3)],col_wrap=true,cap1=true));
|
||||
geom = atype=="vnf" ? attach_geom(vnf=vnf,cp="centroid")
|
||||
: attach_geom(size=[base,base,height]);
|
||||
attachable(anchor=anchor,orient=orient,spin=spin,geom=geom){
|
||||
vnf_polyhedron(vnf);
|
||||
children();
|
||||
}
|
||||
}
|
||||
tetrahedron(20,18,atype="vnf")
|
||||
color("green")attach(TOP,BOT) cuboid(4);
|
||||
right(25)
|
||||
tetrahedron(20,18,atype="box")
|
||||
color("lightblue")attach(TOP,BOT) cuboid(4);
|
||||
```
|
||||
|
||||
Here we have created an `atype` argument that accepts two attachment
|
||||
types and we compute the geometry conditionally based on the atype
|
||||
setting. We can then invoke `attachable()` once with the `geom`
|
||||
parameter to specify the geometry.
|
||||
|
||||
|
||||
## Creating Attachable Parts
|
||||
|
||||
If your object has multiple distinct parts you may wish to create
|
||||
attachble parts for your object. In the library, `tube()` create
|
||||
an attachable part called "inside" that lets you attach to the inside
|
||||
of the tube.
|
||||
|
||||
Below we create an example where an object is made from two
|
||||
cylindrical parts, and we want to be able to attach to either
|
||||
one. In order to create attchable parts you must pass a list of the parts
|
||||
to `attachable()`. You create a part using the `define_part()`
|
||||
function which requires the part's name and its geometry. You can
|
||||
optionally provide a transformation using the `T=` parameter and give
|
||||
a flat with the `inside=` parameter.
|
||||
|
||||
```openscad-3D;Big
|
||||
include<BOSL2/std.scad>
|
||||
module twocyl(d1, d2, sep, h, ang=20)
|
||||
{
|
||||
parts = [
|
||||
define_part("left", attach_geom(r=d1/2,h=h),
|
||||
T=left(sep/2)*yrot(-ang)),
|
||||
define_part("right", attach_geom(r=d2/2,h=h),
|
||||
T=right(sep/2)*yrot(ang)),
|
||||
];
|
||||
attachable(size=[sep+d1/2+d2/2,max(d1,d2),h], parts=parts){
|
||||
union(){
|
||||
left(sep/2) yrot(-ang) cyl(d=d1,h=h);
|
||||
right(sep/2) yrot(ang) cyl(d=d2,h=h);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
twocyl(d1=10,d2=13,sep=20,h=10){
|
||||
attach_part("left") attach(FWD,BOT)
|
||||
color("lightblue") cuboid(3);
|
||||
attach_part("right") attach(RIGHT,BOT)
|
||||
color("green") cuboid(3);
|
||||
}
|
||||
```
|
||||
|
||||
In the above example we create a parts list containing two parts named
|
||||
"left" and "right". Each part has its own geometry corresponding to
|
||||
the size of the cylinder, and it has a transformation specifying where
|
||||
the cylinder is located relative to the part's overall geometry.
|
||||
|
||||
If you create an "inside" part for a tube, the inside object will
|
||||
naturally have its anchors on the inner cylinder **pointing
|
||||
outward**. You can anchor on the inside by setting `inside=true` when
|
||||
invoking `attach()` or `align()`, but another option is to set `inside=true`
|
||||
with `define_part()`. This marks the geometry as an inside geometry, which cause `align()` and
|
||||
`attach()` to invert the meaning of the `inside` parameter so that
|
||||
objects will attach on the inside by default.
|
||||
|
@@ -24,4 +24,6 @@ tutorial. The non-attachables are `polyhedron()`, `linear_extrude()`,
|
||||
Some of these have attachable alternatives: `vnf_polyhedron()`,
|
||||
`linear_sweep()`, `rotate_sweep()`, and `region()`.
|
||||
|
||||
[Next: Basic Positioning](Tutorial-Attachment-Basic-Positioning)
|
||||
|
||||
|
||||
|
50
tutorials/Attachment-Parts.md
Normal file
50
tutorials/Attachment-Parts.md
Normal file
@@ -0,0 +1,50 @@
|
||||
[Prev: Using attach()](Tutorial-Attachment-Attach)
|
||||
|
||||
# Attachment Parts
|
||||
|
||||
Some objects provide named attachable parts that you can select
|
||||
instead of using the main geometry for the object. One important kind
|
||||
of attachable part is the inside of a tube.
|
||||
|
||||
Here is a tube with its anchors shown:
|
||||
|
||||
```openscad-3D
|
||||
include<BOSL2/std.scad>
|
||||
tube(id=20,h=15,wall=3)
|
||||
show_anchors();
|
||||
```
|
||||
|
||||
The anchors are all on the outside wall of the tube and give you no
|
||||
method for placing a child **inside** the tube. In order to attach
|
||||
inside the tube, we select the "inside" part using the `attach_part()`
|
||||
module.
|
||||
|
||||
```openscad-3D
|
||||
include<BOSL2/std.scad>
|
||||
tube(id=20,h=15,wall=3)
|
||||
attach_part("inside")
|
||||
align(BACK,TOP)
|
||||
color("lightblue") cuboid(4);
|
||||
```
|
||||
|
||||
Now when we align the cube to the BACK wall of the tube it appears on
|
||||
the inside of the tube. If you need to attach to both the inside and
|
||||
outside you can place some attachments using `attach_part()` and some
|
||||
with the standard attachment geometry on the outside like this:
|
||||
|
||||
```openscad-3D
|
||||
include<BOSL2/std.scad>
|
||||
diff()
|
||||
tube(id=20,h=15,wall=3){
|
||||
attach([1,-1/2],BOT)
|
||||
color("green")cyl(d=4,h=3,$fn=12);
|
||||
attach_part("inside"){
|
||||
attach(LEFT,BOT,align=TOP)
|
||||
color("lightblue")cuboid(4);
|
||||
attach(BACK,CTR,align=TOP,inside=true, inset=-0.1)
|
||||
cuboid(4);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[Next: Using Color with Attachments](Tutorial-Attachment-Color)
|
@@ -1,3 +1,5 @@
|
||||
[Previous: Relative Positioning of Children](Tutorial-Attachment-Relative-Positioning)
|
||||
|
||||
# Placing Children using position()
|
||||
|
||||
If you make an object a child of another object then the child object
|
||||
@@ -203,3 +205,5 @@ prismoid([50,50],[30,30],h=40)
|
||||
orient(RIGHT)
|
||||
anchor_arrow(40);
|
||||
```
|
||||
|
||||
[Next: Using align()](Tutorial-Attachment-Align)
|
||||
|
@@ -1,3 +1,5 @@
|
||||
[Prev: Basic Positioning](Tutorial-Attachment-Basic-Positioning)
|
||||
|
||||
# Relative Positioning: Placing Children using position(), align(), and attach()
|
||||
|
||||
Relative positioning is one of the most useful and powerful features
|
||||
@@ -17,3 +19,5 @@ Relative positioning means that since objects are positioned relative
|
||||
to other objects, you do not need to keep track of absolute positions
|
||||
and orientations of objects in your model. This makes models simpler,
|
||||
more intuitive, and easier to maintain.
|
||||
|
||||
[Next: Using position()](Tutorial-Attachment-Position)
|
||||
|
@@ -1,3 +1,5 @@
|
||||
[Prev: Using Color with Attachments](Tutorial-Attachment-Color)
|
||||
|
||||
# Tagged Operations
|
||||
|
||||
BOSL2 introduces the concept of tags. Tags are names that can be given to attachables, so that
|
||||
@@ -183,3 +185,5 @@ cube(50, center=true) {
|
||||
tag("keep")xcyl(h=100, d=20);
|
||||
}
|
||||
```
|
||||
|
||||
[Next: Edge Profiling with Attachment](Tutorial-Attachment-Edge-Profiling)
|
||||
|
Reference in New Issue
Block a user