All shapes on this page implement Traceable. Pass them to s.draw(...) to stroke the outline or s.fill(...) to fill the interior.
Shape constructors take a configuration object. Common options are at (position), r (radius), w/h (width/height), a (angle in radians) and n (count).
import {
Circle,
Ellipse,
Rect,
Square,
RoundedRect,
RegularPolygon,
Hexagon,
EquilateralTriangle,
Star,
Arc,
HollowArc,
Spiral,
Hatching,
Line,
} from "solandra"
s.background(210, 25, 95)
s.setFillColor(215, 60, 45)
const shapes = [
new Circle({ at: [0, 0], r: 0.11 }),
new Ellipse({ at: [0, 0], w: 0.22, h: 0.14 }),
new Rect({ at: [0, 0], w: 0.2, h: 0.14, align: "center" }),
new Square({ at: [0, 0], s: 0.18, align: "center" }),
new RoundedRect({ at: [0, 0], w: 0.2, h: 0.15, r: 0.04, align: "center" }),
new RegularPolygon({ at: [0, 0], n: 5, r: 0.11 }),
new Hexagon({ at: [0, 0], r: 0.11 }),
new EquilateralTriangle({ at: [0, 0], s: 0.2 }),
new Star({ at: [0, 0], n: 5, r: 0.12 }),
new Star({ at: [0, 0], n: 7, r: 0.12, r2: 0.09 }),
new Arc({ at: [0, 0], r: 0.11, a: 0, a2: (Math.PI * 3) / 2 }),
new HollowArc({ at: [0, 0], r: 0.11, r2: 0.06, a: 0, a2: Math.PI * 1.5 }),
]
s.forTiling(
{ n: 4, type: "square", margin: 0.05 },
(_pt, [dX], [cX, cY], i) => {
if (!shapes[i]) return
s.withTranslation([cX, cY], () => {
s.withScale([dX * 4, dX * 4], () => {
s.fill(shapes[i])
})
})
}
)Rect is positioned by its top-left corner by default; pass align: "center" to position by center. Square is a Rect with a single size s, and RoundedRect adds a corner radius r.
Rect.split divides a rectangle into two parts or a set of weighted pieces:
s.background(40, 30, 94)
const rect = new Rect({ at: [0.1, 0.1], w: 0.8, h: 0.8 })
const columns = rect.split({ orientation: "horizontal", split: [2, 1, 3, 1] })
columns.forEach((column, i) => {
const rows = column.split({
orientation: "vertical",
split: s.samples(3 + i, [1, 2, 3]),
})
rows.forEach((r, j) => {
s.setFillColor(20 + i * 30, 60, 50 + j * 5)
s.fill(
new Rect({
at: [r.at[0] + 0.01, r.at[1] + 0.01],
w: r.w - 0.02,
h: r.h - 0.02,
})
)
})
})Circle is an Ellipse with w = h = 2r. Both use cubic Bézier approximations and are positioned by centre. Pass align: "topLeft" to position by the top-left corner.
Arc is a filled wedge (pie slice) from angle a to a2; HollowArc is a ring segment with outer radius r and inner radius r2. Angles are in radians, with 0 pointing right and increasing clockwise on screen.
s.background(230, 30, 15)
const { center } = s.meta
s.times(12, (n) => {
const a = (n * Math.PI) / 6
s.setFillColor(n * 30, 70, 60, 0.9)
s.fill(
new HollowArc({
at: center,
a,
a2: a + Math.PI / 7,
r: 0.2 + 0.02 * n,
r2: 0.1 + 0.01 * n,
})
)
})RegularPolygon takes a side count n, radius r and optional rotation a. Its first vertex points up by default. Hexagon and EquilateralTriangle work with the grid helpers. Star takes a point count n, outer radius r and inner radius r2 (default r / 2).
s.background(45, 40, 94)
s.forHorizontal({ n: 4, margin: 0.05 }, (_pt, [dX], [cX, cY], i) => {
s.setFillColor(200 + i * 25, 65, 50)
s.fill(new RegularPolygon({ at: [cX, cY - 0.15], n: 3 + i, r: dX * 0.4 }))
s.setFillColor(20 + i * 25, 80, 55)
s.fill(
new Star({
at: [cX, cY + 0.15],
n: 4 + i,
r: dX * 0.4,
r2: dX * (0.15 + 0.05 * i),
})
)
})Line is the simplest shape: two points. Spiral unwinds from a point with segment length l, segment count n and a growth rate. Hatching fills a circular area with parallel lines at angle a, spaced delta apart; layering two at right angles gives cross-hatching.
s.background(0, 0, 96)
s.lineWidth = 0.003
s.setStrokeColor(340, 70, 40)
s.draw(new Hatching({ at: [0.3, 0.3], r: 0.2, a: Math.PI / 4, delta: 0.015 }))
s.draw(new Hatching({ at: [0.3, 0.3], r: 0.2, a: -Math.PI / 4, delta: 0.03 }))
s.setStrokeColor(215, 70, 40)
s.draw(new Spiral({ at: [0.7, 0.65], l: 0.02, n: 500, rate: 0.02 }))CompoundPath combines several traceables into one path. When filled, the non-zero winding rule means an inner shape traced in the opposite direction cuts a hole:
s.background(190, 40, 90)
s.setFillColor(215, 65, 35)
s.fill(
CompoundPath.withPaths(
new Circle({ at: [0.5, 0.5], r: 0.35 }),
new RegularPolygon({ at: [0.5, 0.5], n: 6, r: 0.22 }).path.reversed
)
)Convert a shape to a SimplePath to use its path transformations:
Rect, RegularPolygon, Star and Line have a .path getter (exact)Circle, Ellipse, Arc, HollowArc and RoundedRect have .toPath(detail), where detail controls how many points approximate the curvess.background(160, 25, 12)
s.setStrokeColor(160, 60, 70)
s.lineWidth = 0.004
const circle = new Circle({ at: [0.5, 0.5], r: 0.35 })
s.times(8, (n) => {
const path = circle
.toPath(20)
.transformLooped((pt) => s.perturb({ at: pt, magnitude: 0.03 + n * 0.01 }))
s.draw(path.chaiken({ n: 3, looped: true }))
})Next: build your own shapes with Paths and Curves.
Solandra was made by James Porter.
Check out the GitHub page or install with npm i solandra