-
Notifications
You must be signed in to change notification settings - Fork 205
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 vector of X and Y axis position values. ie:
[3,4]or[7,-3]. - A 3D point is a vector 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.
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():
include <BOSL2/std.scad> path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; polygon(path);
Figure 1
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:
include <BOSL2/std.scad> path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; stroke(path);
Figure 2
You can vary the width of the drawn path with the width= argument:
include <BOSL2/std.scad> path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; stroke(path, width=3);
Figure 3
You can vary the line length along the path by giving a list of widths, one per point:
include <BOSL2/std.scad> path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; stroke(path, width=[3,2,1,2,3]);
Figure 4
If a path is meant to represent a closed polygon, you can use closed=true to show it that way:
include <BOSL2/std.scad> path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; stroke(path, closed=true);
Figure 5
The ends of the drawn path are normally capped with a "round" endcap, but there are other options:
include <BOSL2/std.scad> path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; stroke(path, endcaps="round");
Figure 6
include <BOSL2/std.scad> path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; stroke(path, endcaps="butt");
Figure 7
include <BOSL2/std.scad> path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; stroke(path, endcaps="line");
Figure 8
include <BOSL2/std.scad> path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; stroke(path, endcaps="tail");
Figure 9
include <BOSL2/std.scad> path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; stroke(path, endcaps="arrow2");
Figure 10
For more standard supported endcap options, see the docs for stroke().
The start and ending endcaps can be specified individually or separately, using endcap1= and endcap2=:
include <BOSL2/std.scad> path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; stroke(path, endcap2="arrow2");
Figure 11
include <BOSL2/std.scad> path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; stroke(path, endcap1="butt", endcap2="arrow2");
Figure 12
include <BOSL2/std.scad> path = [[0,0], [-10,10], [0,20], [10,20], [10,10]]; stroke(path, endcap1="tail", endcap2="arrow");
Figure 13
The size of the endcaps will be relative to the width of the line where the endcap is to be placed:
include <BOSL2/std.scad> 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");
Figure 14
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:
include <BOSL2/std.scad> 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);
Figure 15
Trimmed:
include <BOSL2/std.scad> 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);
Figure 16
BOSL2 will let you get the perimeter polygon for almost all of the standard 2D shapes simply by calling them like a function:
include <BOSL2/std.scad> path = square(40, center=true); stroke(list_wrap(path), endcap2="arrow2");
Figure 17
include <BOSL2/std.scad> path = rect([40,30], rounding=5); stroke(list_wrap(path), endcap2="arrow2");
Figure 18
include <BOSL2/std.scad> path = trapezoid(w1=40, w2=20, h=30); stroke(list_wrap(path), endcap2="arrow2");
Figure 19
include <BOSL2/std.scad> path = circle(d=50); stroke(list_wrap(path), endcap2="arrow2");
Figure 20
include <BOSL2/std.scad> path = ellipse(d=[50,30]); stroke(list_wrap(path), endcap2="arrow2");
Figure 21
include <BOSL2/std.scad> path = pentagon(d=50); stroke(list_wrap(path), endcap2="arrow2");
Figure 22
include <BOSL2/std.scad> path = star(n=5, step=2, d=50); stroke(list_wrap(path), endcap2="arrow2");
Figure 23
Often, when you are constructing a path, you will want to add an arc. The arc() command lets you do that:
include <BOSL2/std.scad> path = arc(r=30, angle=120); stroke(path, endcap2="arrow2");
Figure 24
include <BOSL2/std.scad> path = arc(d=60, angle=120); stroke(path, endcap2="arrow2");
Figure 25
If you give the n= argument, you can control exactly how many points the arc is divided into:
include <BOSL2/std.scad> path = arc(n=5, r=30, angle=120); stroke(path, endcap2="arrow2");
Figure 26
With the start= argument, you can start the arc somewhere other than the X+ axis:
include <BOSL2/std.scad> path = arc(start=45, r=30, angle=120); stroke(path, endcap2="arrow2");
Figure 27
Alternatively, you can give starting and ending angles in a list in the angle= argument:
include <BOSL2/std.scad> path = arc(angle=[120,45], r=30); stroke(path, endcap2="arrow2");
Figure 28
The cp= argument lets you center the arc somewhere other than the origin:
include <BOSL2/std.scad> path = arc(cp=[10,0], r=30, angle=120); stroke(path, endcap2="arrow2");
Figure 29
The arc can also be given by three points on the arc:
include <BOSL2/std.scad> pts = [[-15,10],[0,20],[35,-5]]; path = arc(points=pts); stroke(path, endcap2="arrow2");
Figure 30
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:
include <BOSL2/std.scad> 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");
Figure 31
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:
include <BOSL2/std.scad> path = turtle([ "angle",360/6, "length",10, "move","turn", "move","turn", "move","turn", "move","turn", "move" ]); stroke(path, endcap2="arrow2");
Figure 32
You can use "scale" to relatively scale up the default motion length:
include <BOSL2/std.scad> 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");
Figure 33
Sequences of commands can be repeated using the "repeat" command:
include <BOSL2/std.scad> path=turtle([ "angle",360/5, "length",10, "repeat",5,["move","turn"] ]); stroke(path, endcap2="arrow2");
Figure 34
More complicated commands also exist, including those that form arcs:
include <BOSL2/std.scad> path = turtle([ "move", 10, "left", 90, "move", 20, "arcleft", 10, 180, "move", 20 ]); stroke(path, endcap2="arrow2");
Figure 35
A comprehensive list of supported turtle commands can be found in the docs for turtle().
To translate a path, you can just pass it to the move() (or up/down/left/right/fwd/back) function in the p= argument:
include <BOSL2/std.scad> path = move([-15,-30], p=square(50,center=true)); stroke(list_wrap(path), endcap2="arrow2");
Figure 36
include <BOSL2/std.scad> path = fwd(30, p=square(50,center=true)); stroke(list_wrap(path), endcap2="arrow2");
Figure 37
include <BOSL2/std.scad> path = left(30, p=square(50,center=true)); stroke(list_wrap(path), endcap2="arrow2");
Figure 38
To scale a path, you can just pass it to the scale() (or [xyz]scale) function in the p= argument:
include <BOSL2/std.scad> path = scale([1.5,0.75], p=square(50,center=true)); stroke(list_wrap(path), endcap2="arrow2");
Figure 39
include <BOSL2/std.scad> path = xscale(1.5, p=square(50,center=true)); stroke(list_wrap(path), endcap2="arrow2");
Figure 40
include <BOSL2/std.scad> path = yscale(1.5, p=square(50,center=true)); stroke(list_wrap(path), endcap2="arrow2");
Figure 41
To rotate a path, just can pass it to the rot() (or [xyz]rot) function in the p= argument:
include <BOSL2/std.scad> path = rot(30, p=square(50,center=true)); stroke(list_wrap(path), endcap2="arrow2");
Figure 42
include <BOSL2/std.scad> path = zrot(30, p=square(50,center=true)); stroke(list_wrap(path), endcap2="arrow2");
Figure 43
To mirror a path, just can pass it to the mirror() (or [xyz]flip) function in the p= argument:
include <BOSL2/std.scad> path = mirror([1,1], p=trapezoid(w1=40, w2=10, h=25)); stroke(list_wrap(path), endcap2="arrow2");
Figure 44
include <BOSL2/std.scad> path = xflip(p=trapezoid(w1=40, w2=10, h=25)); stroke(list_wrap(path), endcap2="arrow2");
Figure 45
include <BOSL2/std.scad> path = yflip(p=trapezoid(w1=40, w2=10, h=25)); stroke(list_wrap(path), endcap2="arrow2");
Figure 46
You can get raw transformation matrices for various transformations by calling them like a function without a p= argument:
include <BOSL2/std.scad> mat = move([5,10,0]); multmatrix(mat) square(50,center=true);
Figure 47
include <BOSL2/std.scad> mat = scale([1.5,0.75,1]); multmatrix(mat) square(50,center=true);
Figure 48
include <BOSL2/std.scad> mat = rot(30); multmatrix(mat) square(50,center=true);
Figure 49
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:
include <BOSL2/std.scad> mat = move([5,10,0]) * rot(30) * scale([1.5,0.75,1]); multmatrix(mat) square(50,center=true);
Figure 50
To apply a compound transformation matrix to a path, you can use the apply() function:
include <BOSL2/std.scad> mat = move([5,10]) * rot(30) * scale([1.5,0.75]); path = square(50,center=true); tpath = apply(mat, path); stroke(tpath, endcap2="arrow2");
Figure 51
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:
include <BOSL2/std.scad> rgn = [square(50,center=true), circle(d=30)]; region(rgn);
Figure 52
If you have a region with multiple polygons that are not contained by any others, they make multiple discontiguous shapes:
include <BOSL2/std.scad> 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);
Figure 53
Region polygons can be nested abitrarily deep, in multiple discontiguous shapes:
include <BOSL2/std.scad> 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);
Figure 54
A region with crossing polygons is somewhat poorly formed, but the intersection(s) of the polygons become holes:
include <BOSL2/std.scad> rgn = [ left(15, p=circle(d=50)), right(15, p=circle(d=50)) ]; region(rgn);
Figure 55
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:
include <BOSL2/std.scad> rgn1 = [for (d=[40:-10:10]) circle(d=d)]; rgn2 = [square([60,12], center=true)]; rgn = union(rgn1, rgn2); region(rgn);
Figure 56
include <BOSL2/std.scad> rgn1 = [for (d=[40:-10:10]) circle(d=d)]; rgn2 = [square([60,12], center=true)]; rgn = difference(rgn1, rgn2); region(rgn);
Figure 57
include <BOSL2/std.scad> rgn1 = [for (d=[40:-10:10]) circle(d=d)]; rgn2 = [square([60,12], center=true)]; rgn = intersection(rgn1, rgn2); region(rgn);
Figure 58
include <BOSL2/std.scad> rgn1 = [for (d=[40:-10:10]) circle(d=d)]; rgn2 = [square([60,12], center=true)]; rgn = exclusive_or(rgn1, rgn2); region(rgn);
Figure 59
include <BOSL2/std.scad> orig_rgn = [star(n=5, step=2, d=50)]; rgn = offset(orig_rgn, r=-3, closed=true); color("blue") region(orig_rgn); region(rgn);
Figure 60
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 grid_copies():
include <BOSL2/std.scad> rgn = [ circle(d=100), star(n=5,step=2,d=100,spin=90) ]; difference() { cyl(h=5, d=120); grid_copies(size=[120,120], spacing=[4,4], inside=rgn) cyl(h=10,d=2); }
Figure 61
You can also sweep a region through 3-space to make a solid:
include <BOSL2/std.scad> $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);
Figure 62
Table of Contents
Function Index
Topics Index
Cheat Sheet
Tutorials
Basic Modeling:
- constants.scad STD
- transforms.scad STD
- attachments.scad STD
- shapes2d.scad STD
- shapes3d.scad STD
- masks.scad STD
- drawing.scad STD
- distributors.scad STD
- color.scad STD
- partitions.scad STD
- miscellaneous.scad STD
Advanced Modeling:
- paths.scad STD
- regions.scad STD
- skin.scad STD
- vnf.scad STD
- beziers.scad STD
- nurbs.scad
- rounding.scad STD
- turtle3d.scad
- isosurface.scad
Math:
- math.scad STD
- linalg.scad STD
- vectors.scad STD
- coords.scad STD
- geometry.scad STD
- trigonometry.scad STD
Data Management:
- version.scad STD
- comparisons.scad STD
- lists.scad STD
- utility.scad STD
- strings.scad STD
- structs.scad STD
- fnliterals.scad
Threaded Parts:
Parts:
- ball_bearings.scad
- cubetruss.scad
- gears.scad
- hinges.scad
- joiners.scad
- linear_bearings.scad
- modular_hose.scad
- nema_steppers.scad
- polyhedra.scad
- sliders.scad
- tripod_mounts.scad
- walls.scad
- wiring.scad
- hooks.scad
STD = Included in std.scad