mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-30 08:19:36 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
0cff52023f
14 changed files with 1863 additions and 525 deletions
|
@ -1,4 +1,5 @@
|
|||
# BOSL2
|
||||
![BOSL2 Logo](images/BOSL2logo.png)
|
||||
|
||||
**The Belfry OpenScad Library, v2**
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ $tags_hidden = [];
|
|||
|
||||
// Function: anchorpt()
|
||||
// Usage:
|
||||
// anchor(name, pos, [dir], [rot])
|
||||
// anchor(name, pos, <dir>, <rot>)
|
||||
// Description:
|
||||
// Creates a anchor data structure.
|
||||
// Arguments:
|
||||
|
@ -105,21 +105,21 @@ function anchorpt(name, pos=[0,0,0], orient=UP, spin=0) = [name, pos, orient, sp
|
|||
// Function: attach_geom()
|
||||
//
|
||||
// Usage: Square/Trapezoid Geometry
|
||||
// geom = attach_geom(anchor, spin, [orient], two_d, size, [size2], [shift], [cp], [offset], [anchors]);
|
||||
// geom = attach_geom(anchor, spin, two_d, size, <size2>, <shift>, <cp>, <offset>, <anchors>);
|
||||
// Usage: Circle/Oval Geometry
|
||||
// geom = attach_geom(anchor, spin, [orient], two_d, r|d, [cp], [offset], [anchors]);
|
||||
// geom = attach_geom(anchor, spin, two_d, r|d, <cp>, <offset>, <anchors>);
|
||||
// Usage: 2D Path/Polygon Geometry
|
||||
// geom = attach_geom(anchor, spin, [orient], two_d, path, [extent], [cp], [offset], [anchors]);
|
||||
// geom = attach_geom(anchor, spin, two_d, path, <extent>, <cp>, <offset>, <anchors>);
|
||||
// Usage: Cubical/Prismoidal Geometry
|
||||
// geom = attach_geom(anchor, spin, [orient], size, [size2], [shift], [cp], [offset], [anchors]);
|
||||
// geom = attach_geom(anchor, spin, <orient>, size, <size2>, <shift>, <cp>, <offset>, <anchors>);
|
||||
// Usage: Cylindrical Geometry
|
||||
// geom = attach_geom(anchor, spin, [orient], r|d, l, [cp], [axis], [offset], [anchors]);
|
||||
// geom = attach_geom(anchor, spin, <orient>, r|d, l, <cp>, <axis>, <offset>, <anchors>);
|
||||
// Usage: Conical Geometry
|
||||
// geom = attach_geom(anchor, spin, [orient], r1|d1, r2|d2, l, [cp], [axis], [offset], [anchors]);
|
||||
// geom = attach_geom(anchor, spin, <orient>, r1|d1, r2|d2, l, <cp>, <axis>, <offset>, <anchors>);
|
||||
// Usage: Spheroid/Ovoid Geometry
|
||||
// geom = attach_geom(anchor, spin, [orient], r|d, [cp], [offset], [anchors]);
|
||||
// geom = attach_geom(anchor, spin, <orient>, r|d, <cp>, <offset>, <anchors>);
|
||||
// Usage: VNF Geometry
|
||||
// geom = attach_geom(anchor, spin, [orient], vnf, [extent], [cp], [offset], [anchors]);
|
||||
// geom = attach_geom(anchor, spin, <orient>, vnf, <extent>, <cp>, <offset>, <anchors>);
|
||||
//
|
||||
// Description:
|
||||
// Given arguments that describe the geometry of an attachable object, returns the internal geometry description.
|
||||
|
@ -332,8 +332,8 @@ function attach_geom_size(geom) =
|
|||
) delt
|
||||
) : type == "rect"? ( //size, size2
|
||||
let(
|
||||
size=geom[1], size2=geom[2],
|
||||
maxx = max(size.x,size2)
|
||||
size=geom[1], size2=geom[2], shift=geom[3],
|
||||
maxx = max(size.x,size2+abs(shift))
|
||||
) [maxx, size.y]
|
||||
) : type == "circle"? ( //r
|
||||
let( r=geom[1] )
|
||||
|
@ -349,7 +349,7 @@ function attach_geom_size(geom) =
|
|||
|
||||
// Function: attach_transform()
|
||||
// Usage:
|
||||
// mat = attach_transform(anchor=CENTER, spin=0, orient=UP, geom);
|
||||
// mat = attach_transform(anchor, spin, orient, geom);
|
||||
// Description:
|
||||
// Returns the affine3d transformation matrix needed to `anchor`, `spin`, and `orient`
|
||||
// the given geometry `geom` shape into position.
|
||||
|
@ -557,12 +557,12 @@ function find_anchor(anchor, geom) =
|
|||
mpt = approx(point2d(anchor),[0,0])? [maxx,0,0] : [maxx, avgy, avgz],
|
||||
pos = point3d(cp) + rot(from=RIGHT, to=anchor, p=mpt)
|
||||
) [anchor, pos, anchor, oang]
|
||||
) : type == "rect"? ( //size, size2
|
||||
) : type == "rect"? ( //size, size2, shift
|
||||
let(
|
||||
size=geom[1], size2=geom[2],
|
||||
size=geom[1], size2=geom[2], shift=geom[3],
|
||||
u = (anchor.y+1)/2,
|
||||
frpt = [size.x/2*anchor.x, -size.y/2],
|
||||
bkpt = [size2/2*anchor.x, size.y/2],
|
||||
bkpt = [size2/2*anchor.x+shift, size.y/2],
|
||||
pos = point2d(cp) + lerp(frpt, bkpt, u) + offset,
|
||||
vec = unit(rot(from=BACK, to=bkpt-frpt, p=anchor),[0,1])
|
||||
) [anchor, pos, vec, 0]
|
||||
|
@ -629,21 +629,21 @@ function attachment_is_shown(tags) =
|
|||
// Function: reorient()
|
||||
//
|
||||
// Usage: Square/Trapezoid Geometry
|
||||
// reorient(anchor, spin, [orient], two_d, size, [size2], [shift], [cp], [offset], [anchors], [p]);
|
||||
// reorient(anchor, spin, <orient>, two_d, size, <size2>, <shift>, <cp>, <offset>, <anchors>, <p>);
|
||||
// Usage: Circle/Oval Geometry
|
||||
// reorient(anchor, spin, [orient], two_d, r|d, [cp], [offset], [anchors], [p]);
|
||||
// reorient(anchor, spin, <orient>, two_d, r|d, <cp>, <offset>, <anchors>, <p>);
|
||||
// Usage: 2D Path/Polygon Geometry
|
||||
// reorient(anchor, spin, [orient], two_d, path, [extent], [cp], [offset], [anchors], [p]);
|
||||
// reorient(anchor, spin, <orient>, two_d, path, <extent>, <cp>, <offset>, <anchors>, <p>);
|
||||
// Usage: Cubical/Prismoidal Geometry
|
||||
// reorient(anchor, spin, [orient], size, [size2], [shift], [cp], [offset], [anchors], [p]);
|
||||
// reorient(anchor, spin, <orient>, size, <size2>, <shift>, <cp>, <offset>, <anchors>, <p>);
|
||||
// Usage: Cylindrical Geometry
|
||||
// reorient(anchor, spin, [orient], r|d, l, [offset], [axis], [cp], [anchors], [p]);
|
||||
// reorient(anchor, spin, <orient>, r|d, l, <offset>, <axis>, <cp>, <anchors>, <p>);
|
||||
// Usage: Conical Geometry
|
||||
// reorient(anchor, spin, [orient], r1|d1, r2|d2, l, [axis], [cp], [offset], [anchors], [p]);
|
||||
// reorient(anchor, spin, <orient>, r1|d1, r2|d2, l, <axis>, <cp>, <offset>, <anchors>, <p>);
|
||||
// Usage: Spheroid/Ovoid Geometry
|
||||
// reorient(anchor, spin, [orient], r|d, [cp], [offset], [anchors], [p]);
|
||||
// reorient(anchor, spin, <orient>, r|d, <cp>, <offset>, <anchors>, <p>);
|
||||
// Usage: VNF Geometry
|
||||
// reorient(anchor, spin, [orient], vnf, [extent], [cp], [offset], [anchors], [p]);
|
||||
// reorient(anchor, spin, <orient>, vnf, <extent>, <cp>, <offset>, <anchors>, <p>);
|
||||
//
|
||||
// Description:
|
||||
// Given anchor, spin, orient, and general geometry info for a managed volume, this calculates
|
||||
|
@ -723,21 +723,21 @@ function reorient(
|
|||
// Module: attachable()
|
||||
//
|
||||
// Usage: Square/Trapezoid Geometry
|
||||
// attachable(anchor, spin, [orient], two_d, size, [size2], [shift], [cp], [offset], [anchors] ...
|
||||
// attachable(anchor, spin, two_d, size, <size2>, <shift>, <cp>, <offset>, <anchors> ...
|
||||
// Usage: Circle/Oval Geometry
|
||||
// attachable(anchor, spin, [orient], two_d, r|d, [cp], [offset], [anchors]) ...
|
||||
// attachable(anchor, spin, two_d, r|d, <cp>, <offset>, <anchors>) ...
|
||||
// Usage: 2D Path/Polygon Geometry
|
||||
// attachable(anchor, spin, [orient], two_d, path, [extent], [cp], [offset], [anchors] ...
|
||||
// attachable(anchor, spin, two_d, path, <extent>, <cp>, <offset>, <anchors> ...
|
||||
// Usage: Cubical/Prismoidal Geometry
|
||||
// attachable(anchor, spin, [orient], size, [size2], [shift], [cp], [offset], [anchors] ...
|
||||
// attachable(anchor, spin, <orient>, size, <size2>, <shift>, <cp>, <offset>, <anchors> ...
|
||||
// Usage: Cylindrical Geometry
|
||||
// attachable(anchor, spin, [orient], r|d, l, [axis], [cp], [offset], [anchors]) ...
|
||||
// attachable(anchor, spin, <orient>, r|d, l, <axis>, <cp>, <offset>, <anchors>) ...
|
||||
// Usage: Conical Geometry
|
||||
// attachable(anchor, spin, [orient], r1|d1, r2|d2, l, [axis], [cp], [offset], [anchors]) ...
|
||||
// attachable(anchor, spin, <orient>, r1|d1, r2|d2, l, <axis>, <cp>, <offset>, <anchors>) ...
|
||||
// Usage: Spheroid/Ovoid Geometry
|
||||
// attachable(anchor, spin, [orient], r|d, [cp], [offset], [anchors]) ...
|
||||
// attachable(anchor, spin, <orient>, r|d, <cp>, <offset>, <anchors>) ...
|
||||
// Usage: VNF Geometry
|
||||
// attachable(anchor, spin, [orient], vnf, [extent], [cp], [offset], [anchors]) ...
|
||||
// attachable(anchor, spin, <orient>, vnf, <extent>, <cp>, <offset>, <anchors>) ...
|
||||
//
|
||||
// Description:
|
||||
// Manages the anchoring, spin, orientation, and attachments for a 3D volume or 2D area.
|
||||
|
@ -969,8 +969,8 @@ module position(from)
|
|||
|
||||
// Module: attach()
|
||||
// Usage:
|
||||
// attach(from, [overlap]) ...
|
||||
// attach(from, to, [overlap]) ...
|
||||
// attach(from, <overlap>) ...
|
||||
// attach(from, to, <overlap>) ...
|
||||
// Description:
|
||||
// Attaches children to a parent object at an anchor point and orientation.
|
||||
// Attached objects will be overlapped into the parent object by a little bit,
|
||||
|
@ -1012,7 +1012,7 @@ module attach(from, to=undef, overlap=undef, norot=false)
|
|||
|
||||
// Module: face_profile()
|
||||
// Usage:
|
||||
// face_profile(faces=[], convexity=10, r, d) ...
|
||||
// face_profile(faces, r, d, <convexity>) ...
|
||||
// Description:
|
||||
// Given a 2D edge profile, extrudes it into a mask for all edges and corners bounding each given face.
|
||||
// Arguments:
|
||||
|
@ -1032,7 +1032,7 @@ module face_profile(faces=[], r, d, convexity=10) {
|
|||
|
||||
// Module: edge_profile()
|
||||
// Usage:
|
||||
// edge_profile([edges], [except], [convexity]) ...
|
||||
// edge_profile(<edges>, <except>, <convexity>) ...
|
||||
// Description:
|
||||
// Takes a 2D mask shape and attaches it to the selected edges, with the appropriate orientation
|
||||
// and extruded length to be `diff()`ed away, to give the edge a matching profile.
|
||||
|
@ -1082,7 +1082,7 @@ module edge_profile(edges=EDGES_ALL, except=[], convexity=10) {
|
|||
|
||||
// Module: corner_profile()
|
||||
// Usage:
|
||||
// corner_profile([corners], [except], [convexity]) ...
|
||||
// corner_profile(<corners>, <except>, <convexity>) ...
|
||||
// Description:
|
||||
// Takes a 2D mask shape, rotationally extrudes and converts it into a corner mask, and attaches it
|
||||
// to the selected corners with the appropriate orientation. Tags it as a "mask" to allow it to be
|
||||
|
@ -1144,7 +1144,7 @@ module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) {
|
|||
|
||||
// Module: edge_mask()
|
||||
// Usage:
|
||||
// edge_mask([edges], [except]) ...
|
||||
// edge_mask(<edges>, <except>) ...
|
||||
// Description:
|
||||
// Takes a 3D mask shape, and attaches it to the given edges, with the
|
||||
// appropriate orientation to be `diff()`ed away.
|
||||
|
@ -1186,7 +1186,7 @@ module edge_mask(edges=EDGES_ALL, except=[]) {
|
|||
|
||||
// Module: corner_mask()
|
||||
// Usage:
|
||||
// corner_mask([corners], [except]) ...
|
||||
// corner_mask(<corners>, <except>) ...
|
||||
// Description:
|
||||
// Takes a 3D mask shape, and attaches it to the given corners, with the appropriate
|
||||
// orientation to be `diff()`ed away. The 3D corner mask shape should be designed to
|
||||
|
@ -1305,8 +1305,8 @@ module show(tags="")
|
|||
|
||||
// Module: diff()
|
||||
// Usage:
|
||||
// diff(neg, [keep]) ...
|
||||
// diff(neg, pos, [keep]) ...
|
||||
// diff(neg, <keep>) ...
|
||||
// diff(neg, pos, <keep>) ...
|
||||
// Description:
|
||||
// If `neg` is given, takes the union of all children with tags that are in `neg`, and differences
|
||||
// them from the union of all children with tags in `pos`. If `pos` is not given, then all items in
|
||||
|
@ -1363,8 +1363,8 @@ module diff(neg, pos=undef, keep=undef)
|
|||
|
||||
// Module: intersect()
|
||||
// Usage:
|
||||
// intersect(a, [keep]) ...
|
||||
// intersect(a, b, [keep]) ...
|
||||
// intersect(a, <keep>) ...
|
||||
// intersect(a, b, <keep>) ...
|
||||
// Description:
|
||||
// If `a` is given, takes the union of all children with tags that are in `a`, and `intersection()`s
|
||||
// them with the union of all children with tags in `b`. If `b` is not given, then the union of all
|
||||
|
|
26
debug.scad
26
debug.scad
|
@ -274,7 +274,7 @@ function standard_anchors(two_d=false) = [
|
|||
|
||||
// Module: anchor_arrow()
|
||||
// Usage:
|
||||
// anchor_arrow([s], [color], [flag]);
|
||||
// anchor_arrow(<s>, <color>, <flag>);
|
||||
// Description:
|
||||
// Show an anchor orientation arrow.
|
||||
// Arguments:
|
||||
|
@ -303,7 +303,7 @@ module anchor_arrow(s=10, color=[0.333,0.333,1], flag=true, $tags="anchor-arrow"
|
|||
|
||||
// Module: anchor_arrow2d()
|
||||
// Usage:
|
||||
// anchor_arrow2d([s], [color], [flag]);
|
||||
// anchor_arrow2d(<s>, <color>, <flag>);
|
||||
// Description:
|
||||
// Show an anchor orientation arrow.
|
||||
// Arguments:
|
||||
|
@ -312,7 +312,7 @@ module anchor_arrow(s=10, color=[0.333,0.333,1], flag=true, $tags="anchor-arrow"
|
|||
// Example:
|
||||
// anchor_arrow2d(s=20);
|
||||
module anchor_arrow2d(s=15, color=[0.333,0.333,1], $tags="anchor-arrow") {
|
||||
noop() stroke([[0,0],[0,s]], width=s/10, endcap1="butt", endcap2="arrow2");
|
||||
noop() color(color) stroke([[0,0],[0,s]], width=s/10, endcap1="butt", endcap2="arrow2");
|
||||
}
|
||||
|
||||
|
||||
|
@ -341,18 +341,28 @@ module show_internal_anchors(opacity=0.2) {
|
|||
// Example(FlatSpin):
|
||||
// cube(50, center=true) show_anchors();
|
||||
module show_anchors(s=10, std=true, custom=true) {
|
||||
check = assert($parent_geom != undef) 1;
|
||||
two_d = attach_geom_2d($parent_geom);
|
||||
if (std) {
|
||||
for (anchor=standard_anchors()) {
|
||||
attach(anchor) anchor_arrow(s);
|
||||
for (anchor=standard_anchors(two_d=two_d)) {
|
||||
if(two_d) {
|
||||
attach(anchor) anchor_arrow2d(s);
|
||||
} else {
|
||||
attach(anchor) anchor_arrow(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (custom) {
|
||||
for (anchor=select($parent_geom,-1)) {
|
||||
attach(anchor[0]) {
|
||||
anchor_arrow(s, color="cyan");
|
||||
recolor("black")
|
||||
if(two_d) {
|
||||
anchor_arrow2d(s, color="cyan");
|
||||
} else {
|
||||
anchor_arrow(s, color="cyan");
|
||||
}
|
||||
color("black")
|
||||
noop($tags="anchor-arrow") {
|
||||
xrot(90) {
|
||||
xrot(two_d? 0 : 90) {
|
||||
up(s/10) {
|
||||
linear_extrude(height=0.01, convexity=12, center=true) {
|
||||
text(text=anchor[0], size=s/4, halign="center", valign="center");
|
||||
|
|
BIN
images/BOSL2logo.png
Normal file
BIN
images/BOSL2logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 114 KiB |
238
mutators.scad
238
mutators.scad
|
@ -19,6 +19,7 @@
|
|||
// Returns an axis-aligned cube shape that exactly contains all the 3D children given.
|
||||
// Arguments:
|
||||
// excess = The amount that the bounding box should be larger than needed to bound the children, in each axis.
|
||||
// planar = If true, creates a 2D bounding rectangle. Is false, creates a 3D bounding cube. Default: false
|
||||
// Example:
|
||||
// #bounding_box() {
|
||||
// translate([10,8,4]) cube(5);
|
||||
|
@ -26,32 +27,46 @@
|
|||
// }
|
||||
// translate([10,8,4]) cube(5);
|
||||
// translate([3,0,12]) cube(2);
|
||||
module bounding_box(excess=0) {
|
||||
module bounding_box(excess=0, planar=true) {
|
||||
xs = excess>.1? excess : 1;
|
||||
// a 3D approx. of the children projection on X axis
|
||||
module _xProjection()
|
||||
linear_extrude(xs, center=true)
|
||||
if (planar) {
|
||||
projection()
|
||||
rotate([90,0,0])
|
||||
linear_extrude(xs, center=true)
|
||||
projection()
|
||||
hull()
|
||||
children();
|
||||
hull()
|
||||
children();
|
||||
} else {
|
||||
linear_extrude(xs, center=true)
|
||||
projection()
|
||||
rotate([90,0,0])
|
||||
linear_extrude(xs, center=true)
|
||||
projection()
|
||||
hull()
|
||||
children();
|
||||
}
|
||||
|
||||
// a bounding box with an offset of 1 in all axis
|
||||
module _oversize_bbox() {
|
||||
minkowski() {
|
||||
_xProjection() children(); // x axis
|
||||
rotate(-90) _xProjection() rotate(90) children(); // y axis
|
||||
rotate([0,-90,0]) _xProjection() rotate([0,90,0]) children(); // z axis
|
||||
if (planar) {
|
||||
minkowski() {
|
||||
_xProjection() children(); // x axis
|
||||
rotate(-90) _xProjection() rotate(90) children(); // y axis
|
||||
}
|
||||
} else {
|
||||
minkowski() {
|
||||
_xProjection() children(); // x axis
|
||||
rotate(-90) _xProjection() rotate(90) children(); // y axis
|
||||
rotate([0,-90,0]) _xProjection() rotate([0,90,0]) children(); // z axis
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// offset children() (a cube) by -1 in all axis
|
||||
module _shrink_cube() {
|
||||
intersection() {
|
||||
translate((1-excess)*[ 1, 1, 1]) children();
|
||||
translate((1-excess)*[-1,-1,-1]) children();
|
||||
translate((1-excess)*[ 1, 1, planar?0: 1]) children();
|
||||
translate((1-excess)*[-1,-1, planar?0:-1]) children();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,7 +224,6 @@ function left_half(_arg1=_undef, _arg2=_undef, _arg3=_undef,
|
|||
// right_half([s], [x]) ...
|
||||
// right_half(planar=true, [s], [x]) ...
|
||||
//
|
||||
//
|
||||
// Description:
|
||||
// Slices an object at a vertical Y-Z cut plane, and masks away everything that is left of it.
|
||||
//
|
||||
|
@ -516,31 +530,103 @@ module cylindrical_extrude(or, ir, od, id, size=1000, convexity=10, spin=0, orie
|
|||
// Section: Offset Mutators
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Module: round3d()
|
||||
// Module: minkowski_difference()
|
||||
// Usage:
|
||||
// round3d(r) ...
|
||||
// round3d(or) ...
|
||||
// round3d(ir) ...
|
||||
// round3d(or, ir) ...
|
||||
// minkowski_difference() { base_shape(); diff_shape(); ... }
|
||||
// Description:
|
||||
// Rounds arbitrary 3D objects. Giving `r` rounds all concave and convex corners. Giving just `ir`
|
||||
// Takes a 3D base shape and one or more 3D diff shapes, carves out the diff shapes from the
|
||||
// surface of the base shape, in a way complementary to how `minkowski()` unions shapes to the
|
||||
// surface of its base shape.
|
||||
// Arguments:
|
||||
// planar = If true, performs minkowski difference in 2D. Default: false (3D)
|
||||
// Example:
|
||||
// minkowski_difference() {
|
||||
// union() {
|
||||
// cube([120,70,70], center=true);
|
||||
// cube([70,120,70], center=true);
|
||||
// cube([70,70,120], center=true);
|
||||
// }
|
||||
// sphere(r=10);
|
||||
// }
|
||||
module minkowski_difference(planar=false) {
|
||||
difference() {
|
||||
bounding_box(excess=0, planar=planar) children(0);
|
||||
render(convexity=20) {
|
||||
minkowski() {
|
||||
difference() {
|
||||
bounding_box(excess=1, planar=planar) children(0);
|
||||
children(0);
|
||||
}
|
||||
for (i=[1:1:$children-1]) children(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Module: round2d()
|
||||
// Usage:
|
||||
// round2d(r) ...
|
||||
// round2d(or) ...
|
||||
// round2d(ir) ...
|
||||
// round2d(or, ir) ...
|
||||
// Description:
|
||||
// Rounds arbitrary 2D objects. Giving `r` rounds all concave and convex corners. Giving just `ir`
|
||||
// rounds just concave corners. Giving just `or` rounds convex corners. Giving both `ir` and `or`
|
||||
// can let you round to different radii for concave and convex corners. The 3D object must not have
|
||||
// any parts narrower than twice the `or` radius. Such parts will disappear. This is an *extremely*
|
||||
// slow operation. I cannot emphasize enough just how slow it is. It uses `minkowski()` multiple times.
|
||||
// Use this as a last resort. This is so slow that no example images will be rendered.
|
||||
// can let you round to different radii for concave and convex corners. The 2D object must not have
|
||||
// any parts narrower than twice the `or` radius. Such parts will disappear.
|
||||
// Arguments:
|
||||
// r = Radius to round all concave and convex corners to.
|
||||
// or = Radius to round only outside (convex) corners to. Use instead of `r`.
|
||||
// ir = Radius to round only inside (concave) corners to. Use instead of `r`.
|
||||
module round3d(r, or, ir, size=100)
|
||||
// Examples(2D):
|
||||
// round2d(r=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// round2d(or=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// round2d(ir=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// round2d(or=16,ir=8) {square([40,100], center=true); square([100,40], center=true);}
|
||||
module round2d(r, or, ir)
|
||||
{
|
||||
or = get_radius(r1=or, r=r, dflt=0);
|
||||
ir = get_radius(r1=ir, r=r, dflt=0);
|
||||
offset3d(or, size=size)
|
||||
offset3d(-ir-or, size=size)
|
||||
offset3d(ir, size=size)
|
||||
offset(or) offset(-ir-or) offset(delta=ir,chamfer=true) children();
|
||||
}
|
||||
|
||||
|
||||
// Module: shell2d()
|
||||
// Usage:
|
||||
// shell2d(thickness, [or], [ir], [fill], [round])
|
||||
// Description:
|
||||
// Creates a hollow shell from 2D children, with optional rounding.
|
||||
// Arguments:
|
||||
// thickness = Thickness of the shell. Positive to expand outward, negative to shrink inward, or a two-element list to do both.
|
||||
// or = Radius to round corners on the outside of the shell. If given a list of 2 radii, [CONVEX,CONCAVE], specifies the radii for convex and concave corners separately. Default: 0 (no outside rounding)
|
||||
// ir = Radius to round corners on the inside of the shell. If given a list of 2 radii, [CONVEX,CONCAVE], specifies the radii for convex and concave corners separately. Default: 0 (no inside rounding)
|
||||
// Examples(2D):
|
||||
// shell2d(10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(-10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d([-10,10]) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(10,or=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(10,ir=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(10,round=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(10,fill=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(8,or=16,ir=8,round=16,fill=8) {square([40,100], center=true); square([100,40], center=true);}
|
||||
module shell2d(thickness, or=0, ir=0)
|
||||
{
|
||||
thickness = is_num(thickness)? (
|
||||
thickness<0? [thickness,0] : [0,thickness]
|
||||
) : (thickness[0]>thickness[1])? (
|
||||
[thickness[1],thickness[0]]
|
||||
) : thickness;
|
||||
orad = is_finite(or)? [or,or] : or;
|
||||
irad = is_finite(ir)? [ir,ir] : ir;
|
||||
difference() {
|
||||
round2d(or=orad[0],ir=orad[1])
|
||||
offset(delta=thickness[1])
|
||||
children();
|
||||
round2d(or=irad[1],ir=irad[0])
|
||||
offset(delta=thickness[0])
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -583,101 +669,31 @@ module offset3d(r=1, size=100, convexity=10) {
|
|||
}
|
||||
|
||||
|
||||
// Module: round2d()
|
||||
// Module: round3d()
|
||||
// Usage:
|
||||
// round2d(r) ...
|
||||
// round2d(or) ...
|
||||
// round2d(ir) ...
|
||||
// round2d(or, ir) ...
|
||||
// round3d(r) ...
|
||||
// round3d(or) ...
|
||||
// round3d(ir) ...
|
||||
// round3d(or, ir) ...
|
||||
// Description:
|
||||
// Rounds arbitrary 2D objects. Giving `r` rounds all concave and convex corners. Giving just `ir`
|
||||
// Rounds arbitrary 3D objects. Giving `r` rounds all concave and convex corners. Giving just `ir`
|
||||
// rounds just concave corners. Giving just `or` rounds convex corners. Giving both `ir` and `or`
|
||||
// can let you round to different radii for concave and convex corners. The 2D object must not have
|
||||
// any parts narrower than twice the `or` radius. Such parts will disappear.
|
||||
// can let you round to different radii for concave and convex corners. The 3D object must not have
|
||||
// any parts narrower than twice the `or` radius. Such parts will disappear. This is an *extremely*
|
||||
// slow operation. I cannot emphasize enough just how slow it is. It uses `minkowski()` multiple times.
|
||||
// Use this as a last resort. This is so slow that no example images will be rendered.
|
||||
// Arguments:
|
||||
// r = Radius to round all concave and convex corners to.
|
||||
// or = Radius to round only outside (convex) corners to. Use instead of `r`.
|
||||
// ir = Radius to round only inside (concave) corners to. Use instead of `r`.
|
||||
// Examples(2D):
|
||||
// round2d(r=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// round2d(or=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// round2d(ir=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// round2d(or=16,ir=8) {square([40,100], center=true); square([100,40], center=true);}
|
||||
module round2d(r, or, ir)
|
||||
module round3d(r, or, ir, size=100)
|
||||
{
|
||||
or = get_radius(r1=or, r=r, dflt=0);
|
||||
ir = get_radius(r1=ir, r=r, dflt=0);
|
||||
offset(or) offset(-ir-or) offset(delta=ir,chamfer=true) children();
|
||||
}
|
||||
|
||||
|
||||
// Module: shell2d()
|
||||
// Usage:
|
||||
// shell2d(thickness, [or], [ir], [fill], [round])
|
||||
// Description:
|
||||
// Creates a hollow shell from 2D children, with optional rounding.
|
||||
// Arguments:
|
||||
// thickness = Thickness of the shell. Positive to expand outward, negative to shrink inward, or a two-element list to do both.
|
||||
// or = Radius to round convex corners/pointy bits on the outside of the shell.
|
||||
// ir = Radius to round concave corners on the outside of the shell.
|
||||
// round = Radius to round convex corners/pointy bits on the inside of the shell.
|
||||
// fill = Radius to round concave corners on the inside of the shell.
|
||||
// Examples(2D):
|
||||
// shell2d(10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(-10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d([-10,10]) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(10,or=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(10,ir=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(10,round=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(10,fill=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(8,or=16,ir=8,round=16,fill=8) {square([40,100], center=true); square([100,40], center=true);}
|
||||
module shell2d(thickness, or=0, ir=0, fill=0, round=0)
|
||||
{
|
||||
thickness = is_num(thickness)? (
|
||||
thickness<0? [thickness,0] : [0,thickness]
|
||||
) : (thickness[0]>thickness[1])? (
|
||||
[thickness[1],thickness[0]]
|
||||
) : thickness;
|
||||
difference() {
|
||||
round2d(or=or,ir=ir)
|
||||
offset(delta=thickness[1])
|
||||
offset3d(or, size=size)
|
||||
offset3d(-ir-or, size=size)
|
||||
offset3d(ir, size=size)
|
||||
children();
|
||||
round2d(or=fill,ir=round)
|
||||
offset(delta=thickness[0])
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Module: minkowski_difference()
|
||||
// Usage:
|
||||
// minkowski_difference() { base_shape(); diff_shape(); ... }
|
||||
// Description:
|
||||
// Takes a 3D base shape and one or more 3D diff shapes, carves out the diff shapes from the
|
||||
// surface of the base shape, in a way complementary to how `minkowski()` unions shapes to the
|
||||
// surface of its base shape.
|
||||
// Example:
|
||||
// minkowski_difference() {
|
||||
// union() {
|
||||
// cube([120,70,70], center=true);
|
||||
// cube([70,120,70], center=true);
|
||||
// cube([70,70,120], center=true);
|
||||
// }
|
||||
// sphere(r=10);
|
||||
// }
|
||||
module minkowski_difference() {
|
||||
difference() {
|
||||
bounding_box(excess=0) children(0);
|
||||
render(convexity=10) {
|
||||
minkowski() {
|
||||
difference() {
|
||||
bounding_box(excess=1) children(0);
|
||||
children(0);
|
||||
}
|
||||
for (i=[1:1:$children-1]) children(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
152
paths.scad
152
paths.scad
|
@ -407,6 +407,158 @@ function path_torsion(path, closed=false) =
|
|||
];
|
||||
|
||||
|
||||
// Function: path_chamfer_and_rounding()
|
||||
// Usage:
|
||||
// path2 = path_chamfer_and_rounding(path, [closed], [chamfer], [rounding]);
|
||||
// Description:
|
||||
// Rounds or chamfers corners in the given path.
|
||||
// Arguments:
|
||||
// path = The path to chamfer and/or round.
|
||||
// closed = If true, treat path like a closed polygon. Default: true
|
||||
// chamfer = The length of the chamfer faces at the corners. If given as a list of numbers, gives individual chamfers for each corner, from first to last. Default: 0 (no chamfer)
|
||||
// rounding = The rounding radius for the corners. If given as a list of numbers, gives individual radii for each corner, from first to last. Default: 0 (no rounding)
|
||||
// Example(2D): Chamfering a Path
|
||||
// path = star(5, step=2, d=100);
|
||||
// path2 = path_chamfer_and_rounding(path, closed=true, chamfer=5);
|
||||
// stroke(path2, closed=true);
|
||||
// Example(2D): Per-Corner Chamfering
|
||||
// path = star(5, step=2, d=100);
|
||||
// chamfs = [for (i=[0:1:4]) each 3*[i,i]];
|
||||
// path2 = path_chamfer_and_rounding(path, closed=true, chamfer=chamfs);
|
||||
// stroke(path2, closed=true);
|
||||
// Example(2D): Rounding a Path
|
||||
// path = star(5, step=2, d=100);
|
||||
// path2 = path_chamfer_and_rounding(path, closed=true, rounding=5);
|
||||
// stroke(path2, closed=true);
|
||||
// Example(2D): Per-Corner Chamfering
|
||||
// path = star(5, step=2, d=100);
|
||||
// rs = [for (i=[0:1:4]) each 2*[i,i]];
|
||||
// path2 = path_chamfer_and_rounding(path, closed=true, rounding=rs);
|
||||
// stroke(path2, closed=true);
|
||||
// Example(2D): Mixing Chamfers and Roundings
|
||||
// path = star(5, step=2, d=100);
|
||||
// chamfs = [for (i=[0:4]) each [5,0]];
|
||||
// rs = [for (i=[0:4]) each [0,10]];
|
||||
// path2 = path_chamfer_and_rounding(path, closed=true, chamfer=chamfs, rounding=rs);
|
||||
// stroke(path2, closed=true);
|
||||
function path_chamfer_and_rounding(path, closed, chamfer, rounding) =
|
||||
let (
|
||||
path = deduplicate(path,closed=true),
|
||||
lp = len(path),
|
||||
chamfer = is_undef(chamfer)? repeat(0,lp) :
|
||||
is_vector(chamfer)? list_pad(chamfer,lp,0) :
|
||||
is_num(chamfer)? repeat(chamfer,lp) :
|
||||
assert(false, "Bad chamfer value."),
|
||||
rounding = is_undef(rounding)? repeat(0,lp) :
|
||||
is_vector(rounding)? list_pad(rounding,lp,0) :
|
||||
is_num(rounding)? repeat(rounding,lp) :
|
||||
assert(false, "Bad rounding value."),
|
||||
corner_paths = [
|
||||
for (i=(closed? [0:1:lp-1] : [1:1:lp-2])) let(
|
||||
p1 = select(path,i-1),
|
||||
p2 = select(path,i),
|
||||
p3 = select(path,i+1)
|
||||
)
|
||||
chamfer[i] > 0? _corner_chamfer_path(p1, p2, p3, side=chamfer[i]) :
|
||||
rounding[i] > 0? _corner_roundover_path(p1, p2, p3, r=rounding[i]) :
|
||||
[p2]
|
||||
],
|
||||
out = [
|
||||
if (!closed) path[0],
|
||||
for (i=(closed? [0:1:lp-1] : [1:1:lp-2])) let(
|
||||
p1 = select(path,i-1),
|
||||
p2 = select(path,i),
|
||||
crn1 = select(corner_paths,i-1),
|
||||
crn2 = corner_paths[i],
|
||||
l1 = norm(select(crn1,-1)-p1),
|
||||
l2 = norm(crn2[0]-p2),
|
||||
needed = l1 + l2,
|
||||
seglen = norm(p2-p1),
|
||||
check = assert(seglen >= needed, str("Path segment ",i," is too short to fulfill rounding/chamfering for the adjacent corners."))
|
||||
) each crn2,
|
||||
if (!closed) select(path,-1)
|
||||
]
|
||||
) deduplicate(out);
|
||||
|
||||
|
||||
function _corner_chamfer_path(p1, p2, p3, dist1, dist2, side, angle) =
|
||||
let(
|
||||
v1 = unit(p1 - p2),
|
||||
v2 = unit(p3 - p2),
|
||||
n = vector_axis(v1,v2),
|
||||
ang = vector_angle(v1,v2),
|
||||
path = (is_num(dist1) && is_undef(dist2) && is_undef(side))? (
|
||||
// dist1 & optional angle
|
||||
assert(dist1 > 0)
|
||||
let(angle = default(angle,(180-ang)/2))
|
||||
assert(is_num(angle))
|
||||
assert(angle > 0 && angle < 180)
|
||||
let(
|
||||
pta = p2 + dist1*v1,
|
||||
a3 = 180 - angle - ang
|
||||
) assert(a3>0, "Angle too extreme.")
|
||||
let(
|
||||
side = sin(angle) * dist1/sin(a3),
|
||||
ptb = p2 + side*v2
|
||||
) [pta, ptb]
|
||||
) : (is_undef(dist1) && is_num(dist2) && is_undef(side))? (
|
||||
// dist2 & optional angle
|
||||
assert(dist2 > 0)
|
||||
let(angle = default(angle,(180-ang)/2))
|
||||
assert(is_num(angle))
|
||||
assert(angle > 0 && angle < 180)
|
||||
let(
|
||||
ptb = p2 + dist2*v2,
|
||||
a3 = 180 - angle - ang
|
||||
) assert(a3>0, "Angle too extreme.")
|
||||
let(
|
||||
side = sin(angle) * dist2/sin(a3),
|
||||
pta = p2 + side*v1
|
||||
) [pta, ptb]
|
||||
) : (is_undef(dist1) && is_undef(dist2) && is_num(side))? (
|
||||
// side & optional angle
|
||||
assert(side > 0)
|
||||
let(angle = default(angle,(180-ang)/2))
|
||||
assert(is_num(angle))
|
||||
assert(angle > 0 && angle < 180)
|
||||
let(
|
||||
a3 = 180 - angle - ang
|
||||
) assert(a3>0, "Angle too extreme.")
|
||||
let(
|
||||
dist1 = sin(a3) * side/sin(ang),
|
||||
dist2 = sin(angle) * side/sin(ang),
|
||||
pta = p2 + dist1*v1,
|
||||
ptb = p2 + dist2*v2
|
||||
) [pta, ptb]
|
||||
) : (is_num(dist1) && is_num(dist2) && is_undef(side) && is_undef(side))? (
|
||||
// dist1 & dist2
|
||||
assert(dist1 > 0)
|
||||
assert(dist2 > 0)
|
||||
let(
|
||||
pta = p2 + dist1*v1,
|
||||
ptb = p2 + dist2*v2
|
||||
) [pta, ptb]
|
||||
) : (
|
||||
assert(false,"Bad arguments.")
|
||||
)
|
||||
) path;
|
||||
|
||||
|
||||
function _corner_roundover_path(p1, p2, p3, r, d) =
|
||||
let(
|
||||
r = get_radius(r=r,d=d,dflt=undef),
|
||||
res = circle_2tangents(p1, p2, p3, r=r, tangents=true),
|
||||
cp = res[0],
|
||||
n = res[1],
|
||||
tp1 = res[2],
|
||||
ang = res[4]+res[5],
|
||||
steps = floor(segs(r)*ang/360+0.5),
|
||||
step = ang / steps,
|
||||
path = [for (i=[0:1:steps]) move(cp, p=rot(a=-i*step, v=n, p=tp1-cp))]
|
||||
) path;
|
||||
|
||||
|
||||
|
||||
// Function: path3d_spiral()
|
||||
// Description:
|
||||
// Returns a 3D spiral path.
|
||||
|
|
|
@ -15,7 +15,7 @@ done
|
|||
if [[ "$FILES" != "" ]]; then
|
||||
PREVIEW_LIBS="$FILES"
|
||||
else
|
||||
PREVIEW_LIBS="Transforms Distributors Basic_Shapes FractalTree"
|
||||
PREVIEW_LIBS="Shapes2d Shapes3d Transforms Distributors Mutators Paths FractalTree"
|
||||
fi
|
||||
|
||||
dir="$(basename $PWD)"
|
||||
|
|
227
shapes.scad
227
shapes.scad
|
@ -185,7 +185,8 @@ module cuboid(
|
|||
// Add multi-edge corners.
|
||||
if (trimcorners) {
|
||||
for (za=[-1,1], ya=[-1,1], xa=[-1,1]) {
|
||||
if (corner_edge_count(edges, [xa,ya,za]) > 1) {
|
||||
ce = corner_edges(edges, [xa,ya,za]);
|
||||
if (ce.x + ce.y > 1) {
|
||||
translate(vmul([xa,ya,za]/2, size+[ach-0.01,ach-0.01,-ach])) {
|
||||
cube([ach+0.01,ach+0.01,ach], center=true);
|
||||
}
|
||||
|
@ -270,7 +271,8 @@ module cuboid(
|
|||
// Add multi-edge corners.
|
||||
if (trimcorners) {
|
||||
for (za=[-1,1], ya=[-1,1], xa=[-1,1]) {
|
||||
if (corner_edge_count(edges, [xa,ya,za]) > 1) {
|
||||
ce = corner_edges(edges, [xa,ya,za]);
|
||||
if (ce.x + ce.y > 1) {
|
||||
translate(vmul([xa,ya,za]/2, size+[ard-0.01,ard-0.01,-ard])) {
|
||||
cube([ard+0.01,ard+0.01,ard], center=true);
|
||||
}
|
||||
|
@ -1159,6 +1161,14 @@ module torus(
|
|||
// Creates a spheroid object, with support for anchoring and attachments.
|
||||
// This is a drop-in replacement for the built-in `sphere()` module.
|
||||
// When called as a function, returns a [VNF](vnf.scad) for a spheroid.
|
||||
// The exact triangulation of this spheroid can be controlled via the `style=`
|
||||
// argument, where the value can be one of `"orig"`, `"aligned"`, `"stagger"`,
|
||||
// `"octa"`, or `"icosa"`:
|
||||
// - `style="orig"` constructs a sphere the same way that the OpenSCAD `sphere()` built-in does.
|
||||
// - `style="aligned"` constructs a sphere where, if `$fn` is a multiple of 4, it has vertices at all axis maxima and minima. ie: its bounding box is exactly the sphere diameter in length on all three axes. This is the default.
|
||||
// - `style="stagger"` forms a sphere where all faces are triangular, but the top and bottom poles have thinner triangles.
|
||||
// - `style="octa"` forms a sphere by subdividing an octahedron (8-sided platonic solid). This makes more uniform faces over the entirety of the sphere, and guarantees the bounding box is the sphere diameter in size on all axes. The effective `$fn` value is quantized to a multiple of 4, though. This is used in constructing rounded corners for various other shapes.
|
||||
// - `style="icosa"` forms a sphere by subdividing an icosahedron (20-sided platonic solid). This makes even more uniform faces over the entirety of the sphere. The effective `$fn` value is quantized to a multiple of 5, though.
|
||||
// Arguments:
|
||||
// r = Radius of the spheroid.
|
||||
// d = Diameter of the spheroid.
|
||||
|
@ -1200,21 +1210,16 @@ module spheroid(r, d, circum=false, style="aligned", anchor=CENTER, spin=0, orie
|
|||
{
|
||||
r = get_radius(r=r, d=d, dflt=1);
|
||||
sides = segs(r);
|
||||
vsides = ceil(sides/2);
|
||||
attachable(anchor,spin,orient, r=r) {
|
||||
if (style=="orig") {
|
||||
rotate_extrude(convexity=2,$fn=sides) {
|
||||
difference() {
|
||||
oval(r=r, circum=circum, realign=true, $fn=sides);
|
||||
left(r) square(2*r,center=true);
|
||||
}
|
||||
}
|
||||
} else if (style=="aligned") {
|
||||
rotate_extrude(convexity=2,$fn=sides) {
|
||||
difference() {
|
||||
oval(r=r, circum=circum, $fn=sides);
|
||||
left(r) square(2*r,center=true);
|
||||
}
|
||||
}
|
||||
merids = [ for (i=[0:1:vsides-1]) 90-(i+0.5)*180/vsides ];
|
||||
path = [
|
||||
let(a = merids[0]) [0, sin(a)],
|
||||
for (a=merids) [cos(a), sin(a)],
|
||||
let(a = select(merids,-1)) [0, sin(a)]
|
||||
];
|
||||
scale(r) rotate(180) rotate_extrude(convexity=2,$fn=sides) polygon(path);
|
||||
} else {
|
||||
vnf = spheroid(r=r, circum=circum, style=style);
|
||||
vnf_polyhedron(vnf, convexity=2);
|
||||
|
@ -1686,112 +1691,118 @@ module arced_slot(
|
|||
}
|
||||
|
||||
|
||||
// Module: heightfield()
|
||||
// Usage:
|
||||
// heightfield(heightfield, [size], [bottom]);
|
||||
// Function&Module: heightfield()
|
||||
// Usage: As Module
|
||||
// heightfield(data, <size>, <bottom>, <maxz>, <xrange>, <yrange>, <style>, <convexity>);
|
||||
// Usage: As Function
|
||||
// vnf = heightfield(data, <size>, <bottom>, <maxz>, <xrange>, <yrange>, <style>);
|
||||
// Description:
|
||||
// Given a regular rectangular 2D grid of scalar values, generates a 3D surface where the height at
|
||||
// any given point is the scalar value for that position.
|
||||
// Given a regular rectangular 2D grid of scalar values, or a function literal, generates a 3D
|
||||
// surface where the height at any given point is the scalar value for that position.
|
||||
// Arguments:
|
||||
// heightfield = The 2D rectangular array of heights.
|
||||
// size = The [X,Y] size of the surface to create. If given as a scalar, use it for both X and Y sizes.
|
||||
// bottom = The Z coordinate for the bottom of the heightfield object to create. Must be less than the minimum heightfield value. Default: 0
|
||||
// convexity = Max number of times a line could intersect a wall of the surface being formed.
|
||||
// data = This is either the 2D rectangular array of heights, or a function literal that takes X and Y arguments.
|
||||
// size = The [X,Y] size of the surface to create. If given as a scalar, use it for both X and Y sizes. Default: `[100,100]`
|
||||
// bottom = The Z coordinate for the bottom of the heightfield object to create. Any heights lower than this will be truncated to very slightly above this height. Default: -20
|
||||
// maxz = The maximum height to model. Truncates anything taller to this height. Default: 99
|
||||
// xrange = A range of values to iterate X over when calculating a surface from a function literal. Default: [-1 : 0.01 : 1]
|
||||
// yrange = A range of values to iterate Y over when calculating a surface from a function literal. Default: [-1 : 0.01 : 1]
|
||||
// style = The style of subdividing the quads into faces. Valid options are "default", "alt", and "quincunx". Default: "default"
|
||||
// convexity = Max number of times a line could intersect a wall of the surface being formed. Module only. Default: 10
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0`
|
||||
// orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP`
|
||||
// Example:
|
||||
// heightfield(size=[100,100], bottom=-20, heightfield=[
|
||||
// for (x=[-180:4:180]) [for(y=[-180:4:180]) 10*cos(3*norm([x,y]))]
|
||||
// heightfield(size=[100,100], bottom=-20, data=[
|
||||
// for (y=[-180:4:180]) [for(x=[-180:4:180]) 10*cos(3*norm([x,y]))]
|
||||
// ]);
|
||||
// Example:
|
||||
// intersection() {
|
||||
// heightfield(size=[100,100], heightfield=[
|
||||
// for (x=[-180:5:180]) [for(y=[-180:5:180]) 10+5*cos(3*x)*sin(3*y)]
|
||||
// heightfield(size=[100,100], data=[
|
||||
// for (y=[-180:5:180]) [for(x=[-180:5:180]) 10+5*cos(3*x)*sin(3*y)]
|
||||
// ]);
|
||||
// cylinder(h=50,d=100);
|
||||
// }
|
||||
module heightfield(heightfield, size=[100,100], bottom=0, convexity=10)
|
||||
// Example(NORENDER): Heightfield by Function
|
||||
// fn = function (x,y) 10*sin(x*360)*cos(y*360);
|
||||
// heightfield(size=[100,100], data=fn);
|
||||
// Example(NORENDER): Heightfield by Function, with Specific Ranges
|
||||
// fn = function (x,y) 2*cos(5*norm([x,y]));
|
||||
// heightfield(size=[100,100], bottom=-20, data=fn, xrange=[-180:2:180], yrange=[-180:2:180]);
|
||||
module heightfield(data, size=[100,100], xrange=[-1:0.04:1], yrange=[-1:0.04:1], bottom=-20, maxz=100, style="default", convexity=10, anchor=CENTER, spin=0, orient=UP)
|
||||
{
|
||||
size = is_num(size)? [size,size] : point2d(size);
|
||||
dim = array_dim(heightfield);
|
||||
assert(dim.x!=undef);
|
||||
assert(dim.y!=undef);
|
||||
assert(bottom<min(flatten(heightfield)), "bottom must be less than the minimum heightfield value.");
|
||||
spacing = vdiv(size,dim-[1,1]);
|
||||
vertices = concat(
|
||||
[
|
||||
for (i=[0:1:dim.x-1], j=[0:1:dim.y-1]) let(
|
||||
pos = [i*spacing.x-size.x/2, j*spacing.y-size.y/2, heightfield[i][j]]
|
||||
) pos
|
||||
], [
|
||||
for (i=[0:1:dim.x-1]) let(
|
||||
pos = [i*spacing.x-size.x/2, -size.y/2, bottom]
|
||||
) pos
|
||||
], [
|
||||
for (i=[0:1:dim.x-1]) let(
|
||||
pos = [i*spacing.x-size.x/2, size.y/2, bottom]
|
||||
) pos
|
||||
], [
|
||||
for (j=[0:1:dim.y-1]) let(
|
||||
pos = [-size.x/2, j*spacing.y-size.y/2, bottom]
|
||||
) pos
|
||||
], [
|
||||
for (j=[0:1:dim.y-1]) let(
|
||||
pos = [size.x/2, j*spacing.y-size.y/2, bottom]
|
||||
) pos
|
||||
]
|
||||
);
|
||||
faces = concat(
|
||||
[
|
||||
for (i=[0:1:dim.x-2], j=[0:1:dim.y-2]) let(
|
||||
idx1 = (i+0)*dim.y + j+0,
|
||||
idx2 = (i+0)*dim.y + j+1,
|
||||
idx3 = (i+1)*dim.y + j+0,
|
||||
idx4 = (i+1)*dim.y + j+1
|
||||
) each [[idx1, idx2, idx4], [idx1, idx4, idx3]]
|
||||
], [
|
||||
for (i=[0:1:dim.x-2]) let(
|
||||
idx1 = dim.x*dim.y,
|
||||
idx2 = dim.x*dim.y+dim.x+i,
|
||||
idx3 = idx2+1
|
||||
) [idx1,idx3,idx2]
|
||||
], [
|
||||
for (i=[0:1:dim.y-2]) let(
|
||||
idx1 = dim.x*dim.y,
|
||||
idx2 = dim.x*dim.y+dim.x*2+dim.y+i,
|
||||
idx3 = idx2+1
|
||||
) [idx1,idx2,idx3]
|
||||
], [
|
||||
for (i=[0:1:dim.x-2]) let(
|
||||
idx1 = (i+0)*dim.y+0,
|
||||
idx2 = (i+1)*dim.y+0,
|
||||
idx3 = dim.x*dim.y+i,
|
||||
idx4 = idx3+1
|
||||
) each [[idx1, idx2, idx4], [idx1, idx4, idx3]]
|
||||
], [
|
||||
for (i=[0:1:dim.x-2]) let(
|
||||
idx1 = (i+0)*dim.y+dim.y-1,
|
||||
idx2 = (i+1)*dim.y+dim.y-1,
|
||||
idx3 = dim.x*dim.y+dim.x+i,
|
||||
idx4 = idx3+1
|
||||
) each [[idx1, idx4, idx2], [idx1, idx3, idx4]]
|
||||
], [
|
||||
for (j=[0:1:dim.y-2]) let(
|
||||
idx1 = j,
|
||||
idx2 = j+1,
|
||||
idx3 = dim.x*dim.y+dim.x*2+j,
|
||||
idx4 = idx3+1
|
||||
) each [[idx1, idx4, idx2], [idx1, idx3, idx4]]
|
||||
], [
|
||||
for (j=[0:1:dim.y-2]) let(
|
||||
idx1 = (dim.x-1)*dim.y+j,
|
||||
idx2 = idx1+1,
|
||||
idx3 = dim.x*dim.y+dim.x*2+dim.y+j,
|
||||
idx4 = idx3+1
|
||||
) each [[idx1, idx2, idx4], [idx1, idx4, idx3]]
|
||||
]
|
||||
);
|
||||
polyhedron(points=vertices, faces=faces, convexity=convexity);
|
||||
vnf = heightfield(data=data, size=size, xrange=xrange, yrange=yrange, bottom=bottom, maxz=maxz, style=style);
|
||||
attachable(anchor,spin,orient, vnf=vnf) {
|
||||
vnf_polyhedron(vnf, convexity=convexity);
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function heightfield(data, size=[100,100], xrange=[-1:0.04:1], yrange=[-1:0.04:1], bottom=-20, maxz=100, style="default", anchor=CENTER, spin=0, orient=UP) =
|
||||
assert(is_list(data) || is_function(data))
|
||||
let(
|
||||
size = is_num(size)? [size,size] : point2d(size),
|
||||
xvals = is_list(data)
|
||||
? [for (i=idx(data[0])) i]
|
||||
: assert(is_list(xrange)||is_range(xrange)) [for (x=xrange) x],
|
||||
yvals = is_list(data)
|
||||
? [for (i=idx(data)) i]
|
||||
: assert(is_list(yrange)||is_range(yrange)) [for (y=yrange) y],
|
||||
xcnt = len(xvals),
|
||||
minx = min(xvals),
|
||||
maxx = max(xvals),
|
||||
ycnt = len(yvals),
|
||||
miny = min(yvals),
|
||||
maxy = max(yvals),
|
||||
verts = is_list(data) ? [
|
||||
for (y = [0:1:ycnt-1]) [
|
||||
for (x = [0:1:xcnt-1]) [
|
||||
size.x * (x/(xcnt-1)-0.5),
|
||||
size.y * (y/(ycnt-1)-0.5),
|
||||
data[y][x]
|
||||
]
|
||||
]
|
||||
] : [
|
||||
for (y = yrange) [
|
||||
for (x = xrange) let(
|
||||
z = data(x,y)
|
||||
) [
|
||||
size.x * ((x-minx)/(maxx-minx)-0.5),
|
||||
size.y * ((y-miny)/(maxy-miny)-0.5),
|
||||
min(maxz, max(bottom+0.1, default(z,0)))
|
||||
]
|
||||
]
|
||||
],
|
||||
vnf = vnf_merge([
|
||||
vnf_vertex_array(verts, style=style, reverse=true),
|
||||
vnf_vertex_array([
|
||||
verts[0],
|
||||
[for (v=verts[0]) [v.x, v.y, bottom]],
|
||||
]),
|
||||
vnf_vertex_array([
|
||||
[for (v=verts[ycnt-1]) [v.x, v.y, bottom]],
|
||||
verts[ycnt-1],
|
||||
]),
|
||||
vnf_vertex_array([
|
||||
[for (r=verts) let(v=r[0]) [v.x, v.y, bottom]],
|
||||
[for (r=verts) let(v=r[0]) v],
|
||||
]),
|
||||
vnf_vertex_array([
|
||||
[for (r=verts) let(v=r[xcnt-1]) v],
|
||||
[for (r=verts) let(v=r[xcnt-1]) [v.x, v.y, bottom]],
|
||||
]),
|
||||
vnf_vertex_array([
|
||||
[
|
||||
for (v=verts[0]) [v.x, v.y, bottom],
|
||||
for (r=verts) let(v=r[xcnt-1]) [v.x, v.y, bottom],
|
||||
], [
|
||||
for (r=verts) let(v=r[0]) [v.x, v.y, bottom],
|
||||
for (v=verts[ycnt-1]) [v.x, v.y, bottom],
|
||||
]
|
||||
])
|
||||
])
|
||||
) reorient(anchor,spin,orient, vnf=vnf, p=vnf);
|
||||
|
||||
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
|
|
@ -1206,6 +1206,8 @@ module octagon(r, d, or, od, ir, id, side, rounding=0, realign=false, align_tip,
|
|||
// w2 = The X axis width of the back end of the trapezoid.
|
||||
// angle = If given in place of `h`, `w1`, or `w2`, then the missing value is calculated such that the right side has that angle away from the Y axis.
|
||||
// shift = Scalar value to shift the back of the trapezoid along the X axis by. Default: 0
|
||||
// rounding = The rounding radius for the corners. If given as a list of four numbers, gives individual radii for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no rounding)
|
||||
// chamfer = The Length of the chamfer faces at the corners. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no chamfer)
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// Examples(2D):
|
||||
|
@ -1217,42 +1219,68 @@ module octagon(r, d, or, od, ir, id, side, rounding=0, realign=false, align_tip,
|
|||
// trapezoid(h=20, w2=10, angle=30);
|
||||
// trapezoid(h=20, w2=30, angle=-30);
|
||||
// trapezoid(w1=30, w2=10, angle=30);
|
||||
// Example(2D): Chamferred Trapezoid
|
||||
// trapezoid(h=30, w1=60, w2=40, chamfer=5);
|
||||
// Example(2D): Rounded Trapezoid
|
||||
// trapezoid(h=30, w1=60, w2=40, rounding=5);
|
||||
// Example(2D): Mixed Chamfering and Rounding
|
||||
// trapezoid(h=30, w1=60, w2=40, rounding=[5,0,10,0],chamfer=[0,8,0,15],$fa=1,$fs=1);
|
||||
// Example(2D): Called as Function
|
||||
// stroke(closed=true, trapezoid(h=30, w1=40, w2=20));
|
||||
function trapezoid(h, w1, w2, angle, shift=0, anchor=CENTER, spin=0) =
|
||||
function trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENTER, spin=0) =
|
||||
assert(is_undef(h) || is_finite(h))
|
||||
assert(is_undef(w1) || is_finite(w1))
|
||||
assert(is_undef(w2) || is_finite(w2))
|
||||
assert(is_undef(angle) || is_finite(angle))
|
||||
assert(num_defined([h, w1, w2, angle]) == 3, "Must give exactly 3 of the arguments h, w1, w2, and angle.")
|
||||
assert(is_finite(shift))
|
||||
assert(is_finite(chamfer) || is_vector(chamfer,4))
|
||||
assert(is_finite(rounding) || is_vector(rounding,4))
|
||||
let(
|
||||
simple = chamfer==0 && rounding==0,
|
||||
h = !is_undef(h)? h : opp_ang_to_adj(abs(w2-w1)/2, abs(angle)),
|
||||
w1 = !is_undef(w1)? w1 : w2 + 2*(adj_ang_to_opp(h, angle) + shift),
|
||||
w2 = !is_undef(w2)? w2 : w1 - 2*(adj_ang_to_opp(h, angle) + shift),
|
||||
path = [[w1/2,-h/2], [-w1/2,-h/2], [-w2/2+shift,h/2], [w2/2+shift,h/2]]
|
||||
w2 = !is_undef(w2)? w2 : w1 - 2*(adj_ang_to_opp(h, angle) + shift)
|
||||
)
|
||||
assert(w1>=0 && w2>=0 && h>0, "Degenerate trapezoid geometry.")
|
||||
reorient(anchor,spin, two_d=true, size=[w1,h], size2=w2, p=path);
|
||||
assert(w1+w2>0, "Degenerate trapezoid geometry.")
|
||||
let(
|
||||
base_path = [
|
||||
[w2/2+shift,h/2],
|
||||
[-w2/2+shift,h/2],
|
||||
[-w1/2,-h/2],
|
||||
[w1/2,-h/2],
|
||||
],
|
||||
cpath = simple? base_path :
|
||||
path_chamfer_and_rounding(
|
||||
base_path, closed=true,
|
||||
chamfer=chamfer,
|
||||
rounding=rounding
|
||||
),
|
||||
path = reverse(cpath)
|
||||
) simple?
|
||||
reorient(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift, p=path) :
|
||||
reorient(anchor,spin, two_d=true, path=path, p=path);
|
||||
|
||||
|
||||
|
||||
module trapezoid(h, w1, w2, angle, shift=0, anchor=CENTER, spin=0) {
|
||||
assert(is_undef(h) || is_finite(h));
|
||||
assert(is_undef(w1) || is_finite(w1));
|
||||
assert(is_undef(w2) || is_finite(w2));
|
||||
assert(is_undef(angle) || is_finite(angle));
|
||||
assert(num_defined([h, w1, w2, angle]) == 3, "Must give exactly 3 of the arguments h, w1, w2, and angle.");
|
||||
assert(is_finite(shift));
|
||||
module trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENTER, spin=0) {
|
||||
path = trapezoid(h=h, w1=w1, w2=w2, angle=angle, shift=shift, chamfer=chamfer, rounding=rounding);
|
||||
union() {
|
||||
simple = chamfer==0 && rounding==0;
|
||||
h = !is_undef(h)? h : opp_ang_to_adj(abs(w2-w1)/2, abs(angle));
|
||||
w1 = !is_undef(w1)? w1 : w2 + 2*(adj_ang_to_opp(h, angle) + shift);
|
||||
w2 = !is_undef(w2)? w2 : w1 - 2*(adj_ang_to_opp(h, angle) + shift);
|
||||
assert(w1>=0 && w2>=0 && h>0, "Degenerate trapezoid geometry.");
|
||||
path = [[w1/2,-h/2], [-w1/2,-h/2], [-w2/2+shift,h/2], [w2/2+shift,h/2]];
|
||||
attachable(anchor,spin, two_d=true, size=[w1,h], size2=w2) {
|
||||
polygon(path);
|
||||
children();
|
||||
if (simple) {
|
||||
attachable(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift) {
|
||||
polygon(path);
|
||||
children();
|
||||
}
|
||||
} else {
|
||||
attachable(anchor,spin, two_d=true, path=path) {
|
||||
polygon(path);
|
||||
children();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1293,21 +1321,22 @@ module teardrop2d(r, d, ang=45, cap_h, anchor=CENTER, spin=0)
|
|||
function teardrop2d(r, d, ang=45, cap_h, anchor=CENTER, spin=0) =
|
||||
let(
|
||||
r = get_radius(r=r, d=d, dflt=1),
|
||||
cord = 2 * r * cos(ang),
|
||||
cord_h = r * sin(ang),
|
||||
tip_y = (cord/2)/tan(ang),
|
||||
cap_h = min((!is_undef(cap_h)? cap_h : tip_y+cord_h), tip_y+cord_h),
|
||||
cap_w = cord * (1 - (cap_h - cord_h)/tip_y),
|
||||
ang = min(ang,asin(cap_h/r)),
|
||||
sa = 180 - ang,
|
||||
ea = 360 + ang,
|
||||
tanpt = polar_to_xy(r, ang),
|
||||
tip_y = adj_ang_to_hyp(r, 90-ang),
|
||||
cap_h = min(default(cap_h,tip_y), tip_y),
|
||||
cap_w = tanpt.y >= cap_h
|
||||
? hyp_opp_to_adj(r, cap_h)
|
||||
: adj_ang_to_opp(tip_y-cap_h, ang),
|
||||
ang2 = min(ang,atan2(cap_h,cap_w)),
|
||||
sa = 180 - ang2,
|
||||
ea = 360 + ang2,
|
||||
steps = segs(r)*(ea-sa)/360,
|
||||
step = (ea-sa)/steps,
|
||||
path = deduplicate(
|
||||
[
|
||||
[ cap_w/2,cap_h],
|
||||
[ cap_w,cap_h],
|
||||
for (i=[0:1:steps]) let(a=ea-i*step) r*[cos(a),sin(a)],
|
||||
[-cap_w/2,cap_h]
|
||||
[-cap_w,cap_h]
|
||||
], closed=true
|
||||
),
|
||||
maxx_idx = max_index(subindex(path,0)),
|
||||
|
@ -1325,8 +1354,8 @@ function teardrop2d(r, d, ang=45, cap_h, anchor=CENTER, spin=0) =
|
|||
// Arguments:
|
||||
// r = The radius of the end circles.
|
||||
// d = The diameter of the end circles.
|
||||
// spread = The distance between the centers of the end circles.
|
||||
// tangent = The angle in degrees of the tangent point for the joining arcs, measured away from the Y axis.
|
||||
// spread = The distance between the centers of the end circles. Default: 10
|
||||
// tangent = The angle in degrees of the tangent point for the joining arcs, measured away from the Y axis. Default: 30
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// Examples(2D):
|
||||
|
|
273
tutorials/Mutators.md
Normal file
273
tutorials/Mutators.md
Normal file
|
@ -0,0 +1,273 @@
|
|||
# Mutators Tutorial
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
## 3D Space Halving
|
||||
Sometimes you want to take a 3D shape like a sphere, and cut it in half.
|
||||
The BOSL2 library provides a number of ways to do this:
|
||||
|
||||
```openscad
|
||||
left_half() sphere(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
right_half() sphere(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
front_half() sphere(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
back_half() sphere(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
bottom_half() sphere(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
top_half() sphere(d=100);
|
||||
```
|
||||
|
||||
You can use the `half_of()` module if you want to split space in a way not aligned with an axis:
|
||||
|
||||
```openscad
|
||||
half_of([-1,0,-1]) sphere(d=100);
|
||||
```
|
||||
|
||||
The plane of dissection can be shifted along the axis of any of these operators:
|
||||
|
||||
```openscad
|
||||
left_half(x=20) sphere(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
back_half(y=-20) sphere(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
bottom_half(z=20) sphere(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
half_of([-1,0,-1], cp=[20,0,20]) sphere(d=100);
|
||||
```
|
||||
|
||||
By default, these operators can be applied to objects that fit in a cube 1000 on a side. If you need
|
||||
to apply these halving operators to objects larger than this, you can give the size in the `s=`
|
||||
argument:
|
||||
|
||||
```openscad
|
||||
bottom_half(s=2000) sphere(d=1500);
|
||||
```
|
||||
|
||||
## 2D Plane Halving
|
||||
To cut 2D shapes in half, you will need to add the `planar=true` argument:
|
||||
|
||||
```openscad
|
||||
left_half(planar=true) circle(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
right_half(planar=true) circle(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
front_half(planar=true) circle(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
back_half(planar=true) circle(d=100);
|
||||
```
|
||||
|
||||
## Chained Mutators
|
||||
If you have a set of shapes that you want to do pair-wise hulling of, you can use `chain_hull()`:
|
||||
|
||||
```openscad
|
||||
chain_hull() {
|
||||
cube(5, center=true);
|
||||
translate([30, 0, 0]) sphere(d=15);
|
||||
translate([60, 30, 0]) cylinder(d=10, h=20);
|
||||
translate([60, 60, 0]) cube([10,1,20], center=false);
|
||||
}
|
||||
```
|
||||
|
||||
## Extrusion Mutators
|
||||
The OpenSCAD `linear_extrude()` module can take a 2D shape and extrude it vertically in a line:
|
||||
|
||||
```openscad
|
||||
linear_extrude(height=30) zrot(45) square(40,center=true);
|
||||
```
|
||||
|
||||
The `rotate_extrude()` module can take a 2D shape and rotate it around the Z axis.
|
||||
|
||||
```openscad
|
||||
linear_extrude(height=30) left(30) zrot(45) square(40,center=true);
|
||||
```
|
||||
|
||||
In a similar manner, the BOSL2 `cylindrical_extrude()` module can take a 2d shape and extrude it
|
||||
out radially from the center of a cylinder:
|
||||
|
||||
```openscad
|
||||
cylindrical_extrude(or=40, ir=35)
|
||||
text(text="Hello World!", size=10, halign="center", valign="center");
|
||||
```
|
||||
|
||||
|
||||
## Offset Mutators
|
||||
|
||||
### Minkowski Difference
|
||||
Openscad provides the `minkowski()` module to trace a shape over the entire surface of another shape:
|
||||
|
||||
```openscad
|
||||
minkowski() {
|
||||
union() {
|
||||
cube([100,33,33], center=true);
|
||||
cube([33,100,33], center=true);
|
||||
cube([33,33,100], center=true);
|
||||
}
|
||||
sphere(r=8);
|
||||
}
|
||||
```
|
||||
|
||||
However, it doesn't provide the inverse of this operation; to remove a shape from the entire surface
|
||||
of another object. For this, the BOSL2 library provides the `minkowski_difference()` module:
|
||||
|
||||
```openscad
|
||||
minkowski_difference() {
|
||||
union() {
|
||||
cube([100,33,33], center=true);
|
||||
cube([33,100,33], center=true);
|
||||
cube([33,33,100], center=true);
|
||||
}
|
||||
sphere(r=8);
|
||||
}
|
||||
```
|
||||
|
||||
To perform a `minkowski_difference()` on 2D shapes, you need to supply the `planar=true` argument:
|
||||
|
||||
```openscad-2D
|
||||
minkowski_difference(planar=true) {
|
||||
union() {
|
||||
square([100,33], center=true);
|
||||
square([33,100], center=true);
|
||||
}
|
||||
circle(r=8);
|
||||
}
|
||||
```
|
||||
|
||||
### Round2d
|
||||
The `round2d()` module lets you take a 2D shape and round inside and outside corners. The inner concave corners are rounded to the radius `ir=`, while the outer convex corners are rounded to the radius `or=`:
|
||||
|
||||
```openscad-2D
|
||||
round2d(or=8) star(6, step=2, d=100);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
round2d(ir=12) star(6, step=2, d=100);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
round2d(or=8,ir=12) star(6, step=2, d=100);
|
||||
```
|
||||
|
||||
You can use `r=` to effectively set both `ir=` and `or=` to the same value:
|
||||
|
||||
```openscad-2D
|
||||
round2d(r=8) star(6, step=2, d=100);
|
||||
```
|
||||
|
||||
### Shell2d
|
||||
With the `shell2d()` module, you can take an arbitrary shape, and get the shell outline of it.
|
||||
With a positive thickness, the shell is offset outwards from the original shape:
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=5) star(5,step=2,d=100);
|
||||
color("blue") stroke(star(5,step=2,d=100),closed=true);
|
||||
```
|
||||
|
||||
With a negative thickness, the shell if inset from the original shape:
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=-5) star(5,step=2,d=100);
|
||||
color("blue") stroke(star(5,step=2,d=100),closed=true);
|
||||
```
|
||||
|
||||
You can give a pair of thickness values if you want it both inset and outset from the original shape:
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=[-5,5]) star(5,step=2,d=100);
|
||||
color("blue") stroke(star(5,step=2,d=100),closed=true);
|
||||
```
|
||||
|
||||
You can add rounding to the outside by passing a radius to the `or=` argument.
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=-5,or=5) star(5,step=2,d=100);
|
||||
```
|
||||
|
||||
If you need to pass different radii for the convex and concave corners of the outside, you can pass them as `or=[CONVEX,CONCAVE]`:
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=-5,or=[5,10]) star(5,step=2,d=100);
|
||||
```
|
||||
|
||||
A radius of 0 can be used to specify no rounding:
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=-5,or=[5,0]) star(5,step=2,d=100);
|
||||
```
|
||||
|
||||
You can add rounding to the inside by passing a radius to the `ir=` argument.
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=-5,ir=5) star(5,step=2,d=100);
|
||||
```
|
||||
|
||||
If you need to pass different radii for the convex and concave corners of the inside, you can pass them as `ir=[CONVEX,CONCAVE]`:
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=-5,ir=[8,3]) star(5,step=2,d=100);
|
||||
```
|
||||
|
||||
You can use `or=` and `ir=` together to get nice combined rounding effects:
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=-5,or=[7,2],ir=[7,2]) star(5,step=2,d=100);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=-5,or=[5,0],ir=[5,0]) star(5,step=2,d=100);
|
||||
```
|
||||
|
||||
|
||||
### Round3d
|
||||
### Offset3d
|
||||
(To be Written)
|
||||
|
||||
|
||||
## Color Manipulators
|
||||
The built-in OpenSCAD `color()` module can let you set the RGB color of an object, but it's often
|
||||
easier to select colors using other color schemes. You can use the HSL or Hue-Saturation-Lightness
|
||||
color scheme with the `HSL()` module:
|
||||
|
||||
```openscad
|
||||
for (h=[0:0.1:1], s=[0:0.1:1], l=[0:0.1:1]) {
|
||||
translate(100*[h,s,l]) {
|
||||
HSL(h*360,1-s,l) cube(10,center=true);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can use the HSV or Hue-Saturation-Value color scheme with the `HSV()` module:
|
||||
|
||||
```openscad
|
||||
for (h=[0:0.1:1], s=[0:0.1:1], v=[0:0.1:1]) {
|
||||
translate(100*[h,s,v]) {
|
||||
HSV(h*360,1-s,v) cube(10,center=true);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
504
tutorials/Paths.md
Normal file
504
tutorials/Paths.md
Normal file
|
@ -0,0 +1,504 @@
|
|||
# Paths, Polygons and Regions Tutorial
|
||||
|
||||
## Paths
|
||||
A number of advanced features in BOSL2 rely on paths, which are just ordered lists of points.
|
||||
|
||||
First-off, some terminology:
|
||||
- A 2D point is a vectors of X and Y axis position values. ie: `[3,4]` or `[7,-3]`.
|
||||
- A 3D point is a vectors of X, Y and Z axis position values. ie: `[3,4,2]` or `[-7,5,3]`.
|
||||
- A 2D path is simply a list of two or more 2D points. ie: `[[5,7], [1,-5], [-5,6]]`
|
||||
- A 3D path is simply a list of two or more 3D points. ie: `[[5,7,-1], [1,-5,3], [-5,6,1]]`
|
||||
- A polygon is a 2D (or planar 3D) path where the last point is assumed to connect to the first point.
|
||||
- A region is a list of 2D polygons, where each polygon is XORed against all the others. ie: if one polygon is inside another, it makes a hole in the first polygon.
|
||||
|
||||
### Stroke
|
||||
A path can be hard to visualize, since it's just a bunch of numbers in the source code.
|
||||
One way to see the path is to pass it to `polygon()`:
|
||||
|
||||
```openscad-2D
|
||||
path = [[0,0], [-10,10], [0,20], [10,20], [10,10]];
|
||||
polygon(path);
|
||||
```
|
||||
|
||||
Sometimes, however, it's easier to see just the path itself. For this, you can use the `stroke()` module.
|
||||
At its most basic, `stroke()` just shows the path's line segments:
|
||||
|
||||
```openscad-2D
|
||||
path = [[0,0], [-10,10], [0,20], [10,20], [10,10]];
|
||||
stroke(path);
|
||||
```
|
||||
|
||||
You can vary the width of the drawn path with the `width=` argument:
|
||||
|
||||
```openscad-2D
|
||||
path = [[0,0], [-10,10], [0,20], [10,20], [10,10]];
|
||||
stroke(path, width=3);
|
||||
```
|
||||
|
||||
You can vary the line length along the path by giving a list of widths, one per point:
|
||||
|
||||
```openscad-2D
|
||||
path = [[0,0], [-10,10], [0,20], [10,20], [10,10]];
|
||||
stroke(path, width=[3,2,1,2,3]);
|
||||
```
|
||||
|
||||
If a path is meant to represent a closed polygon, you can use `closed=true` to show it that way:
|
||||
|
||||
```openscad-2D
|
||||
path = [[0,0], [-10,10], [0,20], [10,20], [10,10]];
|
||||
stroke(path, closed=true);
|
||||
```
|
||||
|
||||
The ends of the drawn path are normally capped with a "round" endcap, but there are other options:
|
||||
|
||||
```openscad-2D
|
||||
path = [[0,0], [-10,10], [0,20], [10,20], [10,10]];
|
||||
stroke(path, endcaps="round");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = [[0,0], [-10,10], [0,20], [10,20], [10,10]];
|
||||
stroke(path, endcaps="butt");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = [[0,0], [-10,10], [0,20], [10,20], [10,10]];
|
||||
stroke(path, endcaps="line");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = [[0,0], [-10,10], [0,20], [10,20], [10,10]];
|
||||
stroke(path, endcaps="tail");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = [[0,0], [-10,10], [0,20], [10,20], [10,10]];
|
||||
stroke(path, endcaps="arrow2");
|
||||
```
|
||||
|
||||
For more standard supported endcap options, see the docs for [`stroke()`](shapes2d.scad#stroke).
|
||||
|
||||
The start and ending endcaps can be specified individually or separately, using `endcap1=` and `endcap2=`:
|
||||
|
||||
```openscad-2D
|
||||
path = [[0,0], [-10,10], [0,20], [10,20], [10,10]];
|
||||
stroke(path, endcap2="arrow2");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = [[0,0], [-10,10], [0,20], [10,20], [10,10]];
|
||||
stroke(path, endcap1="butt", endcap2="arrow2");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = [[0,0], [-10,10], [0,20], [10,20], [10,10]];
|
||||
stroke(path, endcap1="tail", endcap2="arrow");
|
||||
```
|
||||
|
||||
The size of the endcaps will be relative to the width of the line where the endcap is to be placed:
|
||||
|
||||
```openscad-2D
|
||||
path = [[0,0], [-10,10], [0,20], [10,20], [10,10]];
|
||||
widths = [1, 1.25, 1.5, 1.75, 2];
|
||||
stroke(path, width=widths, endcaps="arrow2");
|
||||
```
|
||||
|
||||
If none of the standard endcaps are useful to you, it is possible to design your own, simply by
|
||||
passing a path to the `endcaps=`, `endcap1=`, or `endcap2=` arguments. You may also need to give
|
||||
`trim=` to tell it how far back to trim the main line, so it renders nicely. The values in the
|
||||
endcap polygon, and in the `trim=` argument are relative to the line width. A value of 1 is one
|
||||
line width size.
|
||||
|
||||
Untrimmed:
|
||||
|
||||
```openscad-2D
|
||||
path = [[0,0], [-10,10], [0,20], [10,20], [10,10]];
|
||||
dblarrow = [[0,0], [2,-3], [0.5,-2.3], [2,-4], [0.5,-3.5], [-0.5,-3.5], [-2,-4], [-0.5,-2.3], [-2,-3]];
|
||||
stroke(path, endcaps=dblarrow);
|
||||
```
|
||||
|
||||
Trimmed:
|
||||
|
||||
```openscad-2D
|
||||
path = [[0,0], [-10,10], [0,20], [10,20], [10,10]];
|
||||
dblarrow = [[0,0], [2,-3], [0.5,-2.3], [2,-4], [0.5,-3.5], [-0.5,-3.5], [-2,-4], [-0.5,-2.3], [-2,-3]];
|
||||
stroke(path, trim=3.5, endcaps=dblarrow);
|
||||
```
|
||||
|
||||
### Standard 2D Shape Polygons
|
||||
BOSL2 will let you get the perimeter polygon for almost all of the standard 2D shapes simply by calling them like a function:
|
||||
|
||||
```openscad-2D
|
||||
path = square(40, center=true);
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = rect([40,30], rounding=5, center=true);
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = trapezoid(w1=40, w2=20, h=30);
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = circle(d=50);
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = oval(d=[50,30]);
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = pentagon(d=50);
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = star(n=5, step=2, d=50);
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
### Arcs
|
||||
Often, when you are constructing a path, you will want to add an arc. The `arc()` command lets you do that:
|
||||
|
||||
```openscad-2D
|
||||
path = arc(r=30, angle=120);
|
||||
stroke(path, endcap2="arrow2");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = arc(d=60, angle=120);
|
||||
stroke(path, endcap2="arrow2");
|
||||
```
|
||||
|
||||
If you give the `N=` argument, you can control exactly how many points the arc is divided into:
|
||||
|
||||
```openscad-2D
|
||||
path = arc(N=5, r=30, angle=120);
|
||||
stroke(path, endcap2="arrow2");
|
||||
```
|
||||
|
||||
With the `start=` argument, you can start the arc somewhere other than the X+ axis:
|
||||
|
||||
```openscad-2D
|
||||
path = arc(start=45, r=30, angle=120);
|
||||
stroke(path, endcap2="arrow2");
|
||||
```
|
||||
|
||||
Alternatively, you can give starting and ending angles in a list in the `angle=` argument:
|
||||
|
||||
```openscad-2D
|
||||
path = arc(angle=[120,45], r=30);
|
||||
stroke(path, endcap2="arrow2");
|
||||
```
|
||||
|
||||
The `cp=` argument lets you center the arc somewhere other than the origin:
|
||||
|
||||
```openscad-2D
|
||||
path = arc(cp=[10,0], r=30, angle=120);
|
||||
stroke(path, endcap2="arrow2");
|
||||
```
|
||||
|
||||
The arc can also be given by three points on the arc:
|
||||
|
||||
```openscad-2D
|
||||
pts = [[-15,10],[0,20],[35,-5]];
|
||||
path = arc(points=pts);
|
||||
stroke(path, endcap2="arrow2");
|
||||
```
|
||||
|
||||
|
||||
### Turtle Graphics
|
||||
Another way you can create a path is using the `turtle()` command. It implements a simple path
|
||||
description language that is similar to LOGO Turtle Graphics. The concept is that you have a virtial
|
||||
turtle or cursor walking a path. It can "move" forward or backward, or turn "left" or "right" in
|
||||
place:
|
||||
|
||||
```openscad-2D
|
||||
path = turtle([
|
||||
"move", 10,
|
||||
"left", 90,
|
||||
"move", 20,
|
||||
"left", 135,
|
||||
"move", 10*sqrt(2),
|
||||
"right", 90,
|
||||
"move", 10*sqrt(2),
|
||||
"left", 135,
|
||||
"move", 20
|
||||
]);
|
||||
stroke(path, endcap2="arrow2");
|
||||
```
|
||||
|
||||
The position and the facing of the turtle/cursor updates after each command. The motion and turning
|
||||
commands can also have default distances or angles given:
|
||||
|
||||
```openscad-2D
|
||||
path = turtle([
|
||||
"angle",360/6,
|
||||
"length",10,
|
||||
"move","turn",
|
||||
"move","turn",
|
||||
"move","turn",
|
||||
"move","turn",
|
||||
"move"
|
||||
]);
|
||||
stroke(path, endcap2="arrow2");
|
||||
```
|
||||
|
||||
You can use "scale" to relatively scale up the default motion length:
|
||||
|
||||
```openscad-2D
|
||||
path = turtle([
|
||||
"angle",360/6,
|
||||
"length",10,
|
||||
"move","turn",
|
||||
"move","turn",
|
||||
"scale",2,
|
||||
"move","turn",
|
||||
"move","turn",
|
||||
"scale",0.5,
|
||||
"move"
|
||||
]);
|
||||
stroke(path, endcap2="arrow2");
|
||||
```
|
||||
|
||||
Sequences of commands can be repeated using the "repeat" command:
|
||||
|
||||
```openscad-2D
|
||||
path=turtle([
|
||||
"angle",360/5,
|
||||
"length",10,
|
||||
"repeat",5,["move","turn"]
|
||||
]);
|
||||
stroke(path, endcap2="arrow2");
|
||||
```
|
||||
|
||||
More complicated commands also exist, including those that form arcs:
|
||||
|
||||
```openscad-2D
|
||||
path = turtle([
|
||||
"move", 10,
|
||||
"left", 90,
|
||||
"move", 20,
|
||||
"arcleft", 10, 180,
|
||||
"move", 20
|
||||
]);
|
||||
stroke(path, endcap2="arrow2");
|
||||
```
|
||||
|
||||
A comprehensive list of supported turtle commands can be found in the docs for [`turtle()`](shapes2d.scad#turtle).
|
||||
|
||||
### Transforming Paths and Polygons
|
||||
To translate a path, you can just pass it to the `move()` (or up/down/left/right/fwd/back) function in the `p=` argument:
|
||||
|
||||
```openscad-2D
|
||||
path = move([-15,-30], p=square(50,center=true));
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = fwd(30, p=square(50,center=true));
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = left(30, p=square(50,center=true));
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
To scale a path, you can just pass it to the `scale()` (or [xyz]scale) function in the `p=` argument:
|
||||
|
||||
```openscad-2D
|
||||
path = scale([1.5,0.75], p=square(50,center=true));
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = xscale(1.5, p=square(50,center=true));
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = yscale(1.5, p=square(50,center=true));
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
To rotate a path, just can pass it to the `rot()` (or [xyz]rot) function in the `p=` argument:
|
||||
|
||||
```openscad-2D
|
||||
path = rot(30, p=square(50,center=true));
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = zrot(30, p=square(50,center=true));
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
To mirror a path, just can pass it to the `mirror()` (or [xyz]flip) function in the `p=` argument:
|
||||
|
||||
```openscad-2D
|
||||
path = mirror([1,1], p=trapezoid(w1=40, w2=10, h=25));
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = xflip(p=trapezoid(w1=40, w2=10, h=25));
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
path = yflip(p=trapezoid(w1=40, w2=10, h=25));
|
||||
stroke(path, closed=true, endcap2="arrow2");
|
||||
```
|
||||
|
||||
You can get raw transformation matrices for various transformations by calling them like a function without a `p=` argument:
|
||||
|
||||
```openscad-2D
|
||||
mat = move([5,10,0]);
|
||||
multmatrix(mat) square(50,center=true);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
mat = scale([1.5,0.75,1]);
|
||||
multmatrix(mat) square(50,center=true);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
mat = rot(30);
|
||||
multmatrix(mat) square(50,center=true);
|
||||
```
|
||||
|
||||
Raw transformation matrices can be multiplied together to precalculate a compound transformation. For example, to scale a shape, then rotate it, then translate the result, you can do something like:
|
||||
|
||||
```openscad-2D
|
||||
mat = move([5,10,0]) * rot(30) * scale([1.5,0.75,1]);
|
||||
multmatrix(mat) square(50,center=true);
|
||||
```
|
||||
|
||||
To apply a compound transformation matrix to a path, you can use the `apply()` function:
|
||||
|
||||
```openscad-2D
|
||||
mat = move([5,10]) * rot(30, planar=true) * scale([1.5,0.75]);
|
||||
path = square(50,center=true);
|
||||
tpath = apply(mat, path);
|
||||
stroke(tpath, endcap2="arrow2");
|
||||
```
|
||||
|
||||
|
||||
### Regions
|
||||
A polygon is good to denote a single closed 2D shape with no holes in it. For more complex 2D
|
||||
shapes, you will need to use regions. A region is a list of 2D polygons, where each polygon is
|
||||
XORed against all the others. You can display a region using the `region()` module.
|
||||
|
||||
If you have a region with one polygon fully inside another, it makes a hole:
|
||||
|
||||
```openscad-2D
|
||||
rgn = [square(50,center=true), circle(d=30)];
|
||||
region(rgn);
|
||||
```
|
||||
|
||||
If you have a region with multiple polygons that are not contained by any others, they make multiple discontiguous shapes:
|
||||
|
||||
```openscad-2D
|
||||
rgn = [
|
||||
move([-30, 20], p=square(20,center=true)),
|
||||
move([ 0,-20], p=trapezoid(w1=20, w2=10, h=20)),
|
||||
move([ 30, 20], p=square(20,center=true)),
|
||||
];
|
||||
region(rgn);
|
||||
```
|
||||
|
||||
Region polygons can be nested abitrarily deep, in multiple discontiguous shapes:
|
||||
|
||||
```openscad-2D
|
||||
rgn = [
|
||||
for (d=[50:-10:10]) left(30, p=circle(d=d)),
|
||||
for (d=[50:-10:10]) right(30, p=circle(d=d))
|
||||
];
|
||||
region(rgn);
|
||||
```
|
||||
|
||||
A region with crossing polygons is somewhat poorly formed, but the intersection(s) of the polygons become holes:
|
||||
|
||||
```openscad-2D
|
||||
rgn = [
|
||||
left(15, p=circle(d=50)),
|
||||
right(15, p=circle(d=50))
|
||||
];
|
||||
region(rgn);
|
||||
```
|
||||
|
||||
### Boolean Region Geometry
|
||||
Similarly to how OpenSCAD can perform operations like union/difference/intersection/offset on shape geometry,
|
||||
the BOSL2 library lets you perform those same operations on regions:
|
||||
|
||||
```openscad-2D
|
||||
rgn1 = [for (d=[40:-10:10]) circle(d=d)];
|
||||
rgn2 = [square([60,12], center=true)];
|
||||
rgn = union(rgn1, rgn2);
|
||||
region(rgn);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
rgn1 = [for (d=[40:-10:10]) circle(d=d)];
|
||||
rgn2 = [square([60,12], center=true)];
|
||||
rgn = difference(rgn1, rgn2);
|
||||
region(rgn);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
rgn1 = [for (d=[40:-10:10]) circle(d=d)];
|
||||
rgn2 = [square([60,12], center=true)];
|
||||
rgn = intersection(rgn1, rgn2);
|
||||
region(rgn);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
rgn1 = [for (d=[40:-10:10]) circle(d=d)];
|
||||
rgn2 = [square([60,12], center=true)];
|
||||
rgn = exclusive_or(rgn1, rgn2);
|
||||
region(rgn);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
orig_rgn = [star(n=5, step=2, d=50)];
|
||||
rgn = offset(orig_rgn, r=-3, closed=true);
|
||||
color("blue") region(orig_rgn);
|
||||
region(rgn);
|
||||
```
|
||||
|
||||
You can use regions for several useful things. If you wanted a grid of holes in your object that
|
||||
form the shape given by a region, you can do that with `grid2d()`:
|
||||
|
||||
```openscad-3D
|
||||
rgn = [
|
||||
circle(d=100),
|
||||
star(n=5,step=2,d=100,spin=90)
|
||||
];
|
||||
difference() {
|
||||
cyl(h=5, d=120);
|
||||
grid2d(size=[120,120], spacing=[4,4], inside=rgn) cyl(h=10,d=2);
|
||||
}
|
||||
```
|
||||
|
||||
You can also sweep a region through 3-space to make a solid:
|
||||
|
||||
```openscad-3D
|
||||
$fa=1; $fs=1;
|
||||
rgn = [ for (d=[50:-10:10]) circle(d=d) ];
|
||||
tforms = [
|
||||
for (a=[90:-5:0]) xrot(a, cp=[0,-70]),
|
||||
for (a=[0:5:90]) xrot(a, cp=[0,70]),
|
||||
move([0,150,-70]) * xrot(90),
|
||||
];
|
||||
sweep(rgn, tforms, closed=false, caps=true);
|
||||
```
|
||||
|
||||
|
||||
|
553
tutorials/Shapes2d.md
Normal file
553
tutorials/Shapes2d.md
Normal file
|
@ -0,0 +1,553 @@
|
|||
# 2D Shapes Tutorial
|
||||
|
||||
## Primitives
|
||||
There are two built-in 2D primitive shapes that OpenSCAD provides: `square()`, and `circle()`.
|
||||
The BOSL2 library provides alternative to these shapes so that they support more features,
|
||||
and more ways to simply reorient them.
|
||||
|
||||
|
||||
### 2D Squares
|
||||
You can still use the built-in `square()` in the familiar ways that OpenSCAD provides:
|
||||
|
||||
```openscad-2D
|
||||
square(100, center=false);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
square(100, center=true);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
square([60,40], center=true);
|
||||
```
|
||||
|
||||
The BOSL2 library provides an enhanced equivalent to `square()` called `rect()`.
|
||||
You can use it in the same way you use `square()`, but it also provides
|
||||
extended functionality. For example, it allows you to round the corners:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], center=true, rounding=10);
|
||||
```
|
||||
|
||||
Or chamfer them:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], center=true, chamfer=10);
|
||||
```
|
||||
|
||||
You can even specify *which* corners get rounded or chamfered. If you pass a
|
||||
list of four size numbers to the `rounding=` or `chamfer=` arguments, it will
|
||||
give each corner its own size. In order, it goes from the back-right (quadrant I)
|
||||
corner, counter-clockwise around to the back-left (quadrant II) corner, to the
|
||||
forward-left (quadrant III) corner, to the forward-right (quadrant IV) corner:
|
||||
|
||||
```openscad-2DImgOnly
|
||||
module text3d(text) color("black") text(
|
||||
text=text, font="Times", size=10,
|
||||
halign="center", valign="center"
|
||||
);
|
||||
translate([ 50, 50]) text3d("I");
|
||||
translate([-50, 50]) text3d("II");
|
||||
translate([-50,-50]) text3d("III");
|
||||
translate([ 50,-50]) text3d("IV");
|
||||
rect([90,80], center=true);
|
||||
```
|
||||
|
||||
If a size is given as `0`, then there is no rounding and/or chamfering for
|
||||
that quadrant's corner:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], center=true, rounding=[0,5,10,15]);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], center=true, chamfer=[0,5,10,15]);
|
||||
```
|
||||
|
||||
You can give both `rounding=` and `chamfer=` arguments to mix rounding and
|
||||
chamfering, but only if you specify per corner. If you want a rounding in
|
||||
a corner, specify a 0 chamfer for that corner, and vice versa:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], center=true, rounding=[5,0,10,0], chamfer=[0,5,0,15]);
|
||||
```
|
||||
|
||||
#### Anchors and Spin
|
||||
Another way that `rect()` is enhanced over `square()`, is that you can anchor,
|
||||
spin and attach it.
|
||||
|
||||
The `anchor=` argument is an alternative to `center=`, which allows more
|
||||
alignment options. It takes a vector as a value, pointing roughly towards
|
||||
the side or corner you want to align to the origin. For example, to align
|
||||
the center of the back edge to the origin, set the anchor to `[0,1]`:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], anchor=[0,1]);
|
||||
```
|
||||
|
||||
To align the front right corner to the origin:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], anchor=[1,-1]);
|
||||
```
|
||||
|
||||
To center:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], anchor=[0,0]);
|
||||
```
|
||||
|
||||
To make it clearer when giving vectors, there are several standard vector
|
||||
constants defined:
|
||||
|
||||
Constant | Direction | Value
|
||||
-------- | --------- | -----------
|
||||
`LEFT` | X- | `[-1, 0, 0]`
|
||||
`RIGHT` | X+ | `[ 1, 0, 0]`
|
||||
`FRONT`/`FORWARD`/`FWD` | Y- | `[ 0,-1, 0]`
|
||||
`BACK` | Y+ | `[ 0, 1, 0]`
|
||||
`BOTTOM`/`BOT`/`BTM`/`DOWN` | Z- | `[ 0, 0,-1]` (3D only.)
|
||||
`TOP`/`UP` | Z+ | `[ 0, 0, 1]` (3D only.)
|
||||
`CENTER`/`CTR` | Centered | `[ 0, 0, 0]`
|
||||
|
||||
Note that even though these are 3D vectors, you can use most of them,
|
||||
(except `UP`/`DOWN`, of course) for anchors in 2D shapes:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], anchor=BACK);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], anchor=CENTER);
|
||||
```
|
||||
|
||||
You can add vectors together to point to corners:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], anchor=FRONT+RIGHT);
|
||||
```
|
||||
|
||||
Finally, the `spin` argument can rotate the shape by a given number of degrees
|
||||
clockwise:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], anchor=CENTER, spin=30);
|
||||
```
|
||||
|
||||
Anchoring or centering is performed before the spin:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], anchor=BACK, spin=30);
|
||||
```
|
||||
|
||||
Anchor points double as attachment points, so that you can attach other shapes:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40],center=true)
|
||||
show_anchors();
|
||||
```
|
||||
|
||||
### 2D Circles and Ovals
|
||||
The built-in `circle()` primitive can be used as expected:
|
||||
|
||||
```openscad-2D
|
||||
circle(r=50);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
circle(d=100);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
circle(d=100, $fn=8);
|
||||
```
|
||||
|
||||
The BOSL2 library also provides an enhanced equivalent of `circle()` called `oval()`.
|
||||
You can use it in the same way you use `circle()`, but it also provides extended
|
||||
functionality. For example, it allows more control over its size and orientation.
|
||||
|
||||
Since a circle in OpenSCAD can only be approximated by a regular polygon with
|
||||
a number of straight sides, this can lead to size and shape inaccuracies.
|
||||
To counter this, the `realign=` and `circum=` arguments are also provided.
|
||||
|
||||
The `realign=` argument, if set `true`, rotates the `oval()` by half the angle
|
||||
between the sides:
|
||||
|
||||
```openscad-2D
|
||||
oval(d=100, $fn=8, realign=true);
|
||||
```
|
||||
|
||||
The `circum=` argument, if true, makes it so that the polygon forming the
|
||||
`oval()` circumscribes the ideal circle instead of inscribing it.
|
||||
|
||||
Inscribing the ideal circle:
|
||||
|
||||
```openscad-2D
|
||||
difference() {
|
||||
circle(d=100, $fn=360);
|
||||
oval(d=100, $fn=8);
|
||||
}
|
||||
```
|
||||
|
||||
Circumscribing the ideal circle:
|
||||
|
||||
```openscad-2D
|
||||
difference() {
|
||||
oval(d=100, $fn=8, circum=true);
|
||||
circle(d=100, $fn=360);
|
||||
}
|
||||
```
|
||||
|
||||
The `oval()` module, as its name suggests, can be given separate X and Y radii
|
||||
or diameters. To do this, just give `r=` or `d=` with a list of two radii or
|
||||
diameters:
|
||||
|
||||
```openscad-2D
|
||||
oval(r=[30,20]);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
oval(d=[60,40]);
|
||||
```
|
||||
|
||||
Another way that `oval()` is enhanced over `circle()`, is that you can anchor,
|
||||
spin and attach it.
|
||||
|
||||
```openscad-2D
|
||||
oval(r=50, anchor=BACK);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
oval(r=50, anchor=FRONT+RIGHT);
|
||||
```
|
||||
|
||||
Using spin on a circle may not make initial sense, until you remember that
|
||||
anchoring is performed before spin:
|
||||
|
||||
```openscad-2D
|
||||
oval(r=50, anchor=FRONT, spin=-30);
|
||||
```
|
||||
|
||||
|
||||
### Trapezoids
|
||||
|
||||
OpenSCAD doesn't provide a simple way to make 2D triangles, trapezoids, or parallelograms.
|
||||
The BOSL2 library can provide all of these shapes with the `trapezoid()` module.
|
||||
|
||||
To make a simple triangle, just make one of the widths zero:
|
||||
|
||||
```openscad-2D
|
||||
trapezoid(w1=50, w2=0, h=50);
|
||||
```
|
||||
|
||||
To make a right triangle, you need to use the `shift=` argument, to shift the back of the trapezoid along the X axis:
|
||||
|
||||
```openscad-2D
|
||||
trapezoid(w1=50, w2=0, h=50, shift=-25);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
trapezoid(w1=50, w2=0, h=50, shift=25);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
trapezoid(w1=0, w2=50, h=50, shift=-25);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
trapezoid(w1=0, w2=50, h=50, shift=25);
|
||||
```
|
||||
|
||||
You can make a trapezoid by specifying non-zero widths for both the front (`w1=`) and back (`w2=`):
|
||||
|
||||
```openscad-2D
|
||||
trapezoid(w1=30, w2=50, h=50);
|
||||
```
|
||||
|
||||
A parallelogram is just a matter of using the same width for front and back, with a shift along the X axis:
|
||||
|
||||
```openscad-2D
|
||||
trapezoid(w1=50, w2=50, shift=20, h=50);
|
||||
```
|
||||
|
||||
A quadrilateral can be made by having unequal, non-zero front (`w1=`) and back (`w2=`) widths, with the back shifted along the X axis:
|
||||
|
||||
```openscad-2D
|
||||
trapezoid(w1=50, w2=30, shift=20, h=50);
|
||||
```
|
||||
|
||||
You can use `anchor=` and `spin=`, just like with other attachable shapes. However, the anchor
|
||||
points are based on the side angles of the faces, and may not be where you expect them:
|
||||
|
||||
```openscad-2D
|
||||
trapezoid(w1=30, w2=50, h=50)
|
||||
show_anchors();
|
||||
```
|
||||
|
||||
### Regular N-Gons
|
||||
|
||||
OpenSCAD lets you make regular N-gons (pentagon, hexagon, etc) by using `circle()` with `$fn`.
|
||||
While this is concise, it may be less than obvious at first glance:
|
||||
|
||||
```openscad-2D
|
||||
circle(d=50, $fn=5);
|
||||
```
|
||||
|
||||
The BOSL2 library has modules that are named more clearly:
|
||||
|
||||
```openscad-2D
|
||||
pentagon(d=50);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
hexagon(d=50);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
octagon(d=50);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
regular_ngon(n=7, d=50);
|
||||
```
|
||||
|
||||
These modules also provide you with extra functionality.
|
||||
|
||||
They can be sized by side length:
|
||||
|
||||
```openscad-2D
|
||||
pentagon(side=20);
|
||||
```
|
||||
|
||||
They can be sized by circumscribed circle radius/diameter:
|
||||
|
||||
```openscad-2D
|
||||
pentagon(ir=25);
|
||||
pentagon(id=50);
|
||||
```
|
||||
|
||||
They can be realigned by half a side's angle:
|
||||
|
||||
```openscad-2D
|
||||
left(30) pentagon(d=50, realign=true);
|
||||
right(30) pentagon(d=50, realign=false);
|
||||
```
|
||||
|
||||
They can be rounded:
|
||||
|
||||
```openscad-2D
|
||||
pentagon(d=50, rounding=10);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
hexagon(d=50, rounding=10);
|
||||
```
|
||||
|
||||
They also have somewhat different attachment behavior:
|
||||
|
||||
```openscad-2D
|
||||
color("green") stroke(circle(d=50), closed=true);
|
||||
oval(d=50,$fn=5)
|
||||
attach(LEFT) color("blue") anchor_arrow2d();
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
pentagon(d=50)
|
||||
attach(LEFT) color("blue") anchor_arrow2d();
|
||||
```
|
||||
|
||||
You can use `anchor=` and `spin=`, just like with other attachable shapes. However, the anchor
|
||||
points are based on where the anchor vector would intersect the side of the N-gon, and may not
|
||||
be where you expect them:
|
||||
|
||||
```openscad-2D
|
||||
pentagon(d=50)
|
||||
show_anchors(custom=false);
|
||||
```
|
||||
|
||||
N-gons also have named anchor points for their sides and tips:
|
||||
|
||||
```openscad-2D
|
||||
pentagon(d=30)
|
||||
show_anchors(std=false);
|
||||
```
|
||||
|
||||
|
||||
### Stars
|
||||
|
||||
The BOSL2 library has stars as a basic supported shape. They can have any number of points.
|
||||
You can specify a star's shape by point count, inner and outer vertex radius/diameters:
|
||||
|
||||
```openscad-2D
|
||||
star(n=3, id=10, d=50);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
star(n=5, id=15, r=25);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
star(n=10, id=30, d=50);
|
||||
```
|
||||
|
||||
Or you can specify the star shape by point count and number of points to step:
|
||||
|
||||
```openscad-2D
|
||||
star(n=7, step=2, d=50);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
star(n=7, step=3, d=50);
|
||||
```
|
||||
|
||||
If the `realign=` argument is given a true value, then the star will be rotated by half a point angle:
|
||||
|
||||
```openscad-2D
|
||||
left(30) star(n=5, step=2, d=50);
|
||||
right(30) star(n=5, step=2, d=50, realign=true);
|
||||
```
|
||||
|
||||
The `align_tip=` argument can be given a vector so that you can align the first point in a specific direction:
|
||||
|
||||
```openscad-2D
|
||||
star(n=5, ir=15, or=30, align_tip=BACK+LEFT)
|
||||
attach("tip0") color("blue") anchor_arrow2d();
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
star(n=5, ir=15, or=30, align_tip=BACK+RIGHT)
|
||||
attach("tip0") color("blue") anchor_arrow2d();
|
||||
```
|
||||
|
||||
Similarly, the first indentation or pit can be oriented towards a specific vector with `align_pit=`:
|
||||
|
||||
|
||||
```openscad-2D
|
||||
star(n=5, ir=15, or=30, align_pit=BACK+LEFT)
|
||||
attach("pit0") color("blue") anchor_arrow2d();
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
star(n=5, ir=15, or=30, align_pit=BACK+RIGHT)
|
||||
attach("pit0") color("blue") anchor_arrow2d();
|
||||
```
|
||||
|
||||
You can use `anchor=` and `spin=`, just like with other attachable shapes. However, the anchor
|
||||
points are based on the furthest extents of the shape, and may not be where you expect them:
|
||||
|
||||
```openscad-2D
|
||||
star(n=5, step=2, d=50)
|
||||
show_anchors(custom=false);
|
||||
```
|
||||
|
||||
Stars also have named anchor points for their pits, tips, and midpoints between tips:
|
||||
|
||||
```openscad-2D
|
||||
star(n=5, step=2, d=40)
|
||||
show_anchors(std=false);
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Teardrop2D
|
||||
|
||||
Often when 3D printing, you may want to make a circular hole in a vertical wall. If the hole is
|
||||
too big, however, the overhang at the top of the hole can cause problems with printing on an
|
||||
FDM/FFF printer. If you don't want to use support material, you can just use the teardrop shape.
|
||||
The `teardrop2d()` module will let you make a 2D version of the teardrop shape, so that you can
|
||||
extrude it later:
|
||||
|
||||
```openscad-2D
|
||||
teardrop2d(r=20);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
teardrop2d(d=50);
|
||||
```
|
||||
|
||||
The default overhang angle is 45 degrees, but you can adjust that with the `ang=` argument:
|
||||
|
||||
```openscad-2D
|
||||
teardrop2d(d=50, ang=30);
|
||||
```
|
||||
|
||||
If you prefer to flatten the top of the teardrop, to encourage bridging, you can use the `cap_h=`
|
||||
argument:
|
||||
|
||||
```openscad-2D
|
||||
teardrop2d(d=50, cap_h=25);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
teardrop2d(d=50, ang=30, cap_h=30);
|
||||
```
|
||||
|
||||
You can use `anchor=` and `spin=`, just like with other attachable shapes. However, the anchor
|
||||
points are based on the furthest extents of the shape, and may not be where you expect them:
|
||||
|
||||
```openscad-2D
|
||||
teardrop2d(d=50, ang=30, cap_h=30)
|
||||
show_anchors();
|
||||
```
|
||||
|
||||
|
||||
### Glued Circles
|
||||
|
||||
A more unusal shape that BOSL2 provides is Glued Circles. It's basically a pair of circles,
|
||||
connected by what looks like a gloopy glued miniscus:
|
||||
|
||||
```openscad-2D
|
||||
glued_circles(d=30, spread=40);
|
||||
```
|
||||
|
||||
The `r=`/`d=` arguments can specify the radius or diameter of the two circles:
|
||||
|
||||
```openscad-2D
|
||||
glued_circles(r=20, spread=45);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
glued_circles(d=40, spread=45);
|
||||
```
|
||||
|
||||
The `spread=` argument specifies the distance between the centers of the two circles:
|
||||
|
||||
```openscad-2D
|
||||
glued_circles(d=30, spread=30);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
glued_circles(d=30, spread=40);
|
||||
```
|
||||
|
||||
The `tangent=` argument gives the angle of the tangent of the meniscus on the two circles:
|
||||
|
||||
```openscad-2D
|
||||
glued_circles(d=30, spread=30, tangent=45);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
glued_circles(d=30, spread=30, tangent=20);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
glued_circles(d=30, spread=30, tangent=-20);
|
||||
```
|
||||
|
||||
One useful thing you can do is to string a few `glued_circle()`s in a line then extrude them to make a ribbed wall:
|
||||
|
||||
```openscad-3D
|
||||
$fn=36; s=10;
|
||||
linear_extrude(height=50,convexity=16,center=true)
|
||||
xcopies(s*sqrt(2),n=3)
|
||||
glued_circles(d=s, spread=s*sqrt(2), tangent=45);
|
||||
```
|
||||
|
||||
You can use `anchor=` and `spin=`, just like with other attachable shapes. However, the anchor
|
||||
points are based on the furthest extents of the shape, and may not be where you expect them:
|
||||
|
||||
```openscad-2D
|
||||
glued_circles(d=40, spread=40, tangent=45)
|
||||
show_anchors();
|
||||
```
|
||||
|
|
@ -1,233 +1,13 @@
|
|||
# Basic Shapes Tutorial
|
||||
|
||||
## Primitives
|
||||
There are 5 built-in primitive shapes that OpenSCAD provides.
|
||||
`square()`, `circle()`, `cube()`, `cylinder()`, and `sphere()`.
|
||||
The BOSL2 library extends or provides alternative to these shapes so
|
||||
There are 3 built-in 3D primitive shapes that OpenSCAD provides: `cube()`, `cylinder()`,
|
||||
and `sphere()`. The BOSL2 library extends and provides alternative to these shapes so
|
||||
that they support more features, and more ways to simply reorient them.
|
||||
|
||||
|
||||
### 2D Squares
|
||||
You can still use the built-in `square()` in the familiar ways that OpenSCAD provides:
|
||||
|
||||
```openscad-2D
|
||||
square(100, center=false);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
square(100, center=true);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
square([60,40], center=true);
|
||||
```
|
||||
|
||||
The BOSL2 library provides an enhanced equivalent to `square()` called `rect()`.
|
||||
You can use it in the same way you use `square()`, but it also provides
|
||||
extended functionality. For example, it allows you to round the corners:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], center=true, rounding=10);
|
||||
```
|
||||
|
||||
Or chamfer them:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], center=true, chamfer=10);
|
||||
```
|
||||
|
||||
You can even specify *which* corners get rounded or chamfered. If you pass a
|
||||
list of four size numbers to the `rounding=` or `chamfer=` arguments, it will
|
||||
give each corner its own size. In order, it goes from the back-right (quadrant I)
|
||||
corner, counter-clockwise around to the back-left (quadrant II) corner, to the
|
||||
forward-left (quadrant III) corner, to the forward-right (quadrant IV) corner:
|
||||
|
||||
```openscad-2DImgOnly
|
||||
module text3d(text) color("black") text(
|
||||
text=text, font="Times", size=10,
|
||||
halign="center", valign="center"
|
||||
);
|
||||
translate([ 50, 50]) text3d("I");
|
||||
translate([-50, 50]) text3d("II");
|
||||
translate([-50,-50]) text3d("III");
|
||||
translate([ 50,-50]) text3d("IV");
|
||||
rect([90,80], center=true);
|
||||
```
|
||||
|
||||
If a size is given as `0`, then there is no rounding and/or chamfering for
|
||||
that quadrant's corner:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], center=true, rounding=[0,5,10,15]);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], center=true, chamfer=[0,5,10,15]);
|
||||
```
|
||||
|
||||
You can give both `rounding=` and `chamfer=` arguments to mix rounding and
|
||||
chamfering, but only if you specify per corner. If you want a rounding in
|
||||
a corner, specify a 0 chamfer for that corner, and vice versa:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], center=true, rounding=[5,0,10,0], chamfer=[0,5,0,15]);
|
||||
```
|
||||
|
||||
#### Anchors and Spin
|
||||
Another way that `rect()` is enhanced over `square()`, is that you can anchor,
|
||||
spin and attach it.
|
||||
|
||||
The `anchor=` argument is an alternative to `center=`, which allows more
|
||||
alignment options. It takes a vector as a value, pointing roughly towards
|
||||
the side or corner you want to align to the origin. For example, to align
|
||||
the center of the back edge to the origin, set the anchor to `[0,1]`:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], anchor=[0,1]);
|
||||
```
|
||||
|
||||
To align the front right corner to the origin:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], anchor=[1,-1]);
|
||||
```
|
||||
|
||||
To center:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], anchor=[0,0]);
|
||||
```
|
||||
|
||||
To make it clearer when giving vectors, there are several standard vector
|
||||
constants defined:
|
||||
|
||||
Constant | Direction | Value
|
||||
-------- | --------- | -----------
|
||||
`LEFT` | X- | `[-1, 0, 0]`
|
||||
`RIGHT` | X+ | `[ 1, 0, 0]`
|
||||
`FRONT`/`FORWARD`/`FWD` | Y- | `[ 0,-1, 0]`
|
||||
`BACK` | Y+ | `[ 0, 1, 0]`
|
||||
`BOTTOM`/`BOT`/`BTM`/`DOWN` | Z- | `[ 0, 0,-1]` (3D only.)
|
||||
`TOP`/`UP` | Z+ | `[ 0, 0, 1]` (3D only.)
|
||||
`CENTER`/`CTR` | Centered | `[ 0, 0, 0]`
|
||||
|
||||
Note that even though these are 3D vectors, you can use most of them,
|
||||
(except `UP`/`DOWN`, of course) for anchors in 2D shapes:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], anchor=BACK);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], anchor=CENTER);
|
||||
```
|
||||
|
||||
You can add vectors together to point to corners:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], anchor=FRONT+RIGHT);
|
||||
```
|
||||
|
||||
Finally, the `spin` argument can rotate the shape by a given number of degrees
|
||||
clockwise:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], anchor=CENTER, spin=30);
|
||||
```
|
||||
|
||||
Anchoring or centering is performed before the spin:
|
||||
|
||||
```openscad-2D
|
||||
rect([60,40], anchor=BACK, spin=30);
|
||||
```
|
||||
|
||||
|
||||
### 2D Circles
|
||||
The built-in `circle()` primitive can be used as expected:
|
||||
|
||||
```openscad-2D
|
||||
circle(r=50);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
circle(d=100);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
circle(d=100, $fn=8);
|
||||
```
|
||||
|
||||
The BOSL2 library provides an enhanced equivalent of `circle()` called `oval()`.
|
||||
You can use it in the same way you use `circle()`, but it also provides
|
||||
extended functionality. For example, it allows more control over its size and
|
||||
orientation.
|
||||
|
||||
Since a circle in OpenSCAD can only be approximated by a regular polygon with
|
||||
a number of straight sides, this can lead to size and shape inaccuracies.
|
||||
To counter this, the `realign=` and `circum=` arguments are also provided.
|
||||
|
||||
The `realign=` argument, if set `true`, rotates the `oval()` by half the angle
|
||||
between the sides:
|
||||
|
||||
```openscad-2D
|
||||
oval(d=100, $fn=8, realign=true);
|
||||
```
|
||||
|
||||
The `circum=` argument, if true, makes it so that the polygon forming the
|
||||
`oval()` circumscribes the ideal circle instead of inscribing it.
|
||||
|
||||
Inscribing the ideal circle:
|
||||
|
||||
```openscad-2D
|
||||
difference() {
|
||||
circle(d=100, $fn=360);
|
||||
oval(d=100, $fn=8);
|
||||
}
|
||||
```
|
||||
|
||||
Circumscribing the ideal circle:
|
||||
|
||||
```openscad-2D
|
||||
difference() {
|
||||
oval(d=100, $fn=8, circum=true);
|
||||
circle(d=100, $fn=360);
|
||||
}
|
||||
```
|
||||
|
||||
The `oval()` module, as its name suggests, can be given separate X and Y radii
|
||||
or diameters. To do this, just give `r=` or `d=` with a list of two radii or
|
||||
diameters:
|
||||
|
||||
```openscad-2D
|
||||
oval(r=[30,20]);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
oval(d=[60,40]);
|
||||
```
|
||||
|
||||
Another way that `oval()` is enhanced over `circle()`, is that you can anchor,
|
||||
spin and attach it.
|
||||
|
||||
```openscad-2D
|
||||
oval(r=50, anchor=BACK);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
oval(r=50, anchor=FRONT+RIGHT);
|
||||
```
|
||||
|
||||
Using spin on a circle may not make initial sense, until you remember that
|
||||
anchoring is performed before spin:
|
||||
|
||||
```openscad-2D
|
||||
oval(r=50, anchor=FRONT, spin=-30);
|
||||
```
|
||||
|
||||
|
||||
### 3D Cubes
|
||||
BOSL2 overrides the built-in `cube()` module. It still can be used as you
|
||||
expect from the built-in:
|
||||
BOSL2 overrides the built-in `cube()` module. It still can be used as you expect from the built-in:
|
||||
|
||||
```openscad-3D
|
||||
cube(100);
|
||||
|
@ -243,7 +23,7 @@ expect from the built-in:
|
|||
|
||||
It is also enhanced to allow you to anchor, spin, orient, and attach it.
|
||||
|
||||
You can use `anchor=` similarly to how you use it with `square()` or `rect()`,
|
||||
You can use `anchor=` similarly to how you use it with `rect()` or `oval()`,
|
||||
except you can also anchor vertically in 3D, allowing anchoring to faces, edges,
|
||||
and corners:
|
||||
|
||||
|
@ -546,11 +326,20 @@ The "stagger" style will stagger the triangulation of the vertical rows:
|
|||
spheroid(d=100, style="stagger", $fn=20);
|
||||
```
|
||||
|
||||
The "icosa"` style will make for roughly equal-sized triangles for the entire
|
||||
sphere surface:
|
||||
The "icosa" style will make for roughly equal-sized triangles for the entire
|
||||
sphere surface, based on subdividing an icosahedron. This style will round the
|
||||
effective `$fn` to a multiple of 5 when constructing the spheroid:
|
||||
|
||||
```openscad-3D
|
||||
spheroid(d=100, style="icosa", $fn=20);
|
||||
```
|
||||
|
||||
The "octa" style will also make for roughly equal-sized triangles for the entire
|
||||
sphere surface, but based on subdividing an octahedron. This is useful in that it
|
||||
guarantees vertices at the axis extrema. This style will round the effective `$fn`
|
||||
to a multiple of 4 when constructing the spheroid:
|
||||
|
||||
```openscad-3D
|
||||
spheroid(d=100, style="octa", $fn=20);
|
||||
```
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
BOSL_VERSION = [2,0,482];
|
||||
BOSL_VERSION = [2,0,505];
|
||||
|
||||
|
||||
// Section: BOSL Library Version Functions
|
||||
|
|
Loading…
Reference in a new issue