A path is a sequence of line segments or curves joined end-to-end. The points of the path can be positioned anywhere in 3D space, and the path can be open-ended or closed.
Paths are not typically used directly as part of a scene (except possibly for something like a thin rope or antenna), but they can be used to define the contours or profile of a 3D shape (see builders for details).
As with the polygons in a mesh, you define a path using a series of points. A path must have at least two points to be visible. The following path defines a short, horizontal line along the X axis.
path {
point -1 0
point 1 0
}
In the above example we used the point
command, which accepts a vector value. Paths can be three-dimensional, so point
accepts up to three coordinates, but most paths that you create in practice will be 2D (all points will have a Z value of zero).
Paths can be open or closed (meaning that the points form an unbroken loop). To create a closed path, the first and last point must have the same position. The following path has four points, but it actually describes a triangle rather than a quadrilateral, because the first and last points are the same:
path {
point 0 1
point -1 -1
point 1 -1
point 0 1
}
Points are great for defining polygonal shapes, but what about curves?
Curves in ShapeScript are typically created using quadratic Béziers. A quadratic Bézier is defined by two end-points and a control point.
The curve passes through the end-points, but does not pass through the control point. Instead, the control point defines the point of intersection for the tangents of the curve as it passes through the end-points. This sounds complicated, but it’s quite straightforward to use in practice.
To create a smooth curve, use the point
command to define end points, and the curve
command to define control points. For example, the following creates a smooth arc:
path {
point -1 -1
curve 0 1
point 1 -1
}
You may notice that this “smooth” arc is not actually very smooth. Just as 3D meshes are constructed from flat polygons, curves are approximated using straight lines. You can adjust the smoothness of curves in a path using the detail
command:
path {
detail 99
point -1 -1
curve 0 1
point 1 -1
}
If multiple curve
commands (control points) are used in sequence, an end-point will be interpolated at the mid-point between them. This allows you to easily create complex curves such as an “S” shape.
You can also create closed paths entirely using curve
points. Eight curve
points arranged in an octagon can closely approximate a circle (the ninth point is just a duplicate of the first, to close the path):
path {
curve -0.414 1
curve 0.414 1
curve 1 0.414
curve 1 -0.414
curve 0.414 -1
curve -0.414 -1
curve -1 -0.414
curve -1 0.414
curve -0.414 1
}
Arcs created by Bézier curves can approximate a circle, but not perfectly. If you need a precisely circular curve, you can use the arc
command:
arc {
angle 0.5
}
The angle
option defines the angular span of the arc, starting clockwise from the Y axis. The angle is measured in half-turns (as explained in the Transforms section) so the 0.5
in the example above produces a quarter circle:
To adjust the starting angle, you can use the orientation option or rotate command as you would for any other shape. The following creates an arc from -45 degrees to +90 degrees:
arc {
orientation -0.25
angle 0.75
}
Arcs have a default radius of 0.5 units. You can increase or decrease this using the size
option:
arc {
size 2 // radius of 1 unit (0.5 * 2)
}
Arcs can be used in conjunction with individual path points or other arcs to make more complex shapes. The following creates a slab with curved corners:
extrude path {
// left arc
arc {
angle -0.5
}
// bottom-left corner
point -0.5 0
// bottom-right corner
point 1.5 0
// right arc
arc {
position 1 0
orientation 0.5
angle -0.5
}
// close path with smooth join
curve 0 0.5
}
If you need a complete circle, there’s an even simpler way than using arc
, which is to use the circle
command:
circle
Like the sphere
primitive, the circle
path has a default diameter of 1 unit, and its smoothness is controlled by the current detail
setting, but these can both be overridden by passing explicit size
and detail
options:
circle {
size 2
detail 64
}
To create a square or rectangle you can use the square
command:
square
Like circle
, square
accepts a size
option. The following will create a rectangle exactly twice as wide as it is tall:
square {
size 2 1
}
You can even create a rectangle with rounded corners using the roundrect
command:
roundrect
In addition to size
, roundrect
also accepts a radius
option, which controls the corner radius:
roundrect {
size 1
radius 0.25
}
When used outside of a mesh
, the polygon
command can be used to create a regular polygon. The following creates a pentagon:
polygon {
sides 5
}
The output for this is similar to using the circle
command with a specific detail value:
circle {
detail 5
}
The difference is only apparent if you extrude the result, because in the circle’s case the extruded sides are smoothed by default, whereas for the polygon they are sharply defined:
extrude polygon {
position -1
sides 6
}
extrude circle {
position 1
detail 6
}
The circle
command is fine for creating complete circles, but what if you want a circular arc or semicircle? Calculating the Bézier control points for that manually would be tedious, so this is where the power of ShapeScript’s procedural logic comes in handy.
To create a path procedurally, you can use a for loop along with relative transform commands such as rotate
. For example, the following code generates a semicircle:
path {
for 0 to 8 {
curve 0 1
rotate 1 / 8
}
}
A path can be formed from multiple distinct sub-paths. To add additional sub-paths, you can nest a path
command inside another one (or any other command that returns a path). The following code creates a path made up of two overlapping circles:
path {
circle
translate 0.5
scale 0.5
circle
}
Paths cannot currently be textured, but they inherit the current material color. Color can also be applied to individual points within a path. When neighboring path points have different colors applied, the color will be smoothly interpolated between them, creating a gradient:
path {
color red
point 0 1
color green
point -1 -1
color blue
point 1 -1
}
Scalable Vector Graphics (SVG) is a popular standard for defining vector graphics. SVG is a fairly complex XML-based format, but a subset of the SVG standard defines a simple text-based path syntax that uses a series of single-character instructions, each followed by one or more coordinates to define a path. For example, the following SVG path code defines a triangle:
<path d="M 100 100 L 300 100 L 200 300 z"/>
You can use this syntax in ShapeScript by taking just the d
attribute from the path and passing it to the svgpath
command, as follows:
fill svgpath "M 100 100 L 300 100 L 200 300 z"
You can fill or extrude an svgpath
and adjust its size, position, etc. as follows:
fill svgpath {
size 0.01
position -2 2
"M 100 100 L 300 100 L 200 300 z"
}
Which produces the following output:
Note: SVG uses a flipped vertical coordinate system relative to that used by ShapeScript. To compensate for this, vertical (Y) coordinates passed to the svgpath
command are treated as negative.