Quick Reference
SWPie is a SketchWave composite class that represents a circular disk divided into n equal radial "pizza slice" sectors. Each slice is an independent SWSector instance with its own fill color, drawn on an SWGrid. SWPie is not a subclass of SWSector or SWDisk — it owns an internal array of SWSector objects and delegates all drawing, animation, and styling to them through a unified API.
- Design Pattern: Composition (not inheritance)
- Internal Structure: Array of
nSWSector objects, each withtheta = 360 / n - Dependencies: SWPoint, SWColor, SWSinusoid, SWGrid, SWSector, p5.js
- Key Features: Per-slice fill colors (cycled from array), shared border color, spin rotation, radius breathing, fill opacity control, dynamic slice-count changes
- Common Uses: ROYGBIV spectrum displays, pie charts, color wheels, pizza graphics, animated spinning disks with distinct sections
Overview
The SWPie class represents a full circular disk cut into n equal radial slices arranged around a shared center. Each slice is an SWSector with an angular size of 360 / n degrees and its own fill color taken from a supplied colors array (cycled if the array is shorter than n). All slices share the same center, radius, stroke weight, and border color.
draw(), and one breatheRadius() — none of which are appropriate for a multi-colored pie. The IS-A test fails: an SWPie is not an SWDisk. Composition lets each slice own its own fill independently while the SWPie presents a clean unified API at the top level.
Angle Convention
SWPie uses the same user-space (math-space) angle convention as all other SketchWaveJS classes: angles are measured counterclockwise (CCW) from the positive x-axis, and y increases upward. The p5.js y-flip is handled internally. You never deal with radians or y-negation.
// Default: 6 slices, startAngle=0 → first edge at +x axis
let pie = new SWPie(new SWPoint(0, 0), 6, 6, colors);
// startAngle=90 → first edge points straight up (+y axis)
let pie2 = new SWPie(new SWPoint(0, 0), 6, 7, roygbivColors, 90);
Internal Slice Geometry
Each SWSector (slice) is assigned a fixed angular offset of i × (360 / n) degrees for i = 0, 1, ..., n-1. This offset is the sector's own startAngle and never changes. The pie's current startAngle + rotation is written into every sector's rotation property each frame by the internal _syncSlices() method, so the sector draws at: its fixed offset + the pie's accumulated orientation.
Key Capabilities
- Per-Slice Colors: Each slice gets its own fill color cycled from the supplied array
- Shared Border: A single strokeColor and strokeWeight apply to all radial edges simultaneously
- Spin Animation: Continuously rotate all slices together via
rotate() - Radius Breathing: Oscillate the radius of all slices with an SWSinusoid
- Fill Opacity: Set transparency of all slice fills simultaneously with
setFillAlpha() - Dynamic Slice Count: Change
nat runtime viasetN(); colors re-cycle automatically - Individual Slice Recoloring: Change any single slice's color via
setSliceColor(index, color) - Dual Coordinate Systems: Draw in screen pixels or grid user coordinates
- Center Marker: Optional display of the shared center SWPoint
Typical Workflow
- Create an array of SWColor fill colors (one per slice, or fewer to cycle)
- Construct an SWPie with center, radius, n, colors array, startAngle, and optional border
- Draw each frame using
drawOnGrid(grid) - Call
rotate()before drawing to spin; callbreatheRadius()after drawing to oscillate the radius - Use
setN()to dynamically change the slice count; colors re-apply automatically - Call
reset()to restore all original values
Constructor
new SWPie(center, radius, n, colors, startAngle, thickness, strokeColor)Creates a new SWPie instance with the given geometry and styling, and immediately builds the internal SWSector array.
| Parameter | Type | Default | Description |
|---|---|---|---|
center |
SWPoint | required | Center point of the pie — shared by all slices |
radius |
number | required | Radius in user units (> 0) |
n |
number | 6 | Number of equal slices (integer, clamped to ≥ 2) |
colors |
SWColor[] | [] | Array of fill colors for slices; cycled (modulo) if shorter than n. Pass [] for no fill. |
startAngle |
number | 0 | First slice edge angle in degrees CCW from +x axis |
thickness |
number | 2 | Border (stroke) thickness in pixels applied to all radial edges |
strokeColor |
SWColor | undefined | Shared border color for all slice edges; undefined = no borders |
// Simple 6-slice pie with cycling colors
const colors = [
new SWColor(0, 100, 100, 100, "red"),
new SWColor(120, 100, 100, 100, "green"),
new SWColor(240, 100, 100, 100, "blue"),
];
let pie = new SWPie(new SWPoint(0, 0), 6, 6, colors);
// ROYGBIV 7-slice pie, first edge at top (+y axis), with dark border
const roygbiv = [
SWColor.copy(swRed), SWColor.copy(swOrange), SWColor.copy(swYellow),
SWColor.copy(swGreen), SWColor.copy(swBlue), SWColor.copy(swIndigo),
SWColor.copy(swViolet),
];
const border = new SWColor(0, 0, 20, 100, "darkBorder");
let spectrumPie = new SWPie(new SWPoint(0, 0), 6, 7, roygbiv, 90, 2, border);
// Minimal: 4-slice no-border pie
let simple = new SWPie(new SWPoint(2, 1), 4, 4, colors, 45);
Properties
center SWPointThe center point shared by all slices. Changing center.x / center.y moves the entire pie. The center is drawn as a small dot when shouldShowCenter is true.
pie.center.x = 3; pie.center.y = -1;
radius numberThe radius in user units shared by all slices. Use setRadius(r) to update — calling it propagates the new radius to every SWSector immediately.
pie.setRadius(8);
n numberThe number of slices (integer, clamped to ≥ 2). Each slice subtends 360 / n degrees. Use setN() to change this at runtime; the internal sector array is rebuilt and colors re-cycle automatically. The accumulated rotation is reset to 0 on rebuild to avoid a visual jump.
pie.setN(12); // divide into 12 slices of 30° each
colors SWColor[]The fill color array used at slice-build time. Colors are cycled (modulo) if the array is shorter than n. This array is the source for rebuilds; individual slice colors live in the sector objects. Use setSliceColor() to change a single slice after construction.
// Re-cycle happens automatically when setN() is called
startAngle numberThe static orientation of the first slice edge, in degrees CCW from +x. This is the baseline before any rotation accumulates. Use setStartAngle() to change it. startAngle = 90 places the first edge pointing straight up.
pie.setStartAngle(90); // first edge at top
rotation numberAccumulated rotation in degrees (CCW positive). Set to 0 by the constructor and by setN(); incremented by rotate(). The effective orientation of slice i's first edge is: i × (360/n) + startAngle + rotation.
console.log(pie.rotation.toFixed(1) + "°");
thickness numberThe stroke (border) weight in pixels, shared by all slices. Use setStrokeWeight(w) to update all slices simultaneously.
pie.setStrokeWeight(3);
strokeColor SWColor | undefinedThe shared border color applied to all radial edges. Pass undefined to setStrokeColor() to remove all borders. Each slice receives an independent copy of this color.
pie.setStrokeColor(new SWColor(0, 0, 0, 100, "black"));
pie.setStrokeColor(undefined); // remove borders
fillAlpha numberThe fill opacity for all slices, in the range [0, 100]. 100 = fully opaque, 0 = fully transparent. Use setFillAlpha() to update all slice fill alphas simultaneously. Survives setN() rebuilds.
pie.setFillAlpha(70); // 70% opaque slices
shouldShowCenter booleanWhether to draw the center SWPoint marker. Default is true. Useful for interactive demos where the user drags the center to reposition the pie.
pie.setShowCenter(false); // hide center dot
sectors SWSector[]The internal array of SWSector objects — one per slice. Rebuilt whenever setN() is called. Generally accessed via the SWPie API, but can be read directly for advanced use (e.g., reading a specific sector's current radius).
console.log(`Slice count: ${pie.sectors.length}`);
originalRadius / originalN / originalStartAngle / originalThickness / originalStrokeColor / originalColors / originalFillAlpha various restore targetsSnapshot values captured at construction. reset() uses all of these to restore the pie to its initial state. originalColors is a deep copy of the colors array.
// Read-only; used internally by reset()
Methods
Core Drawing Methods
draw()Draws all slices in screen (pixel) coordinates using p5.js. Rarely used directly — prefer drawOnGrid() for user-coordinate rendering.
void
function draw() {
background(220);
pie.draw(); // center.x/y treated as screen pixels
}
drawOnGrid(grid)Draws all slices mapped through the given SWGrid's coordinate system. Converts center position and radius from user units to screen pixels. This is the standard method to call in a p5.js draw() loop.
grid(SWGrid) — the coordinate grid
void
function draw() {
background(220);
grid.draw();
pie.drawOnGrid(grid);
}
Rotation Animation
rotate(deltaAngle)Increments the pie's accumulated rotation by deltaAngle degrees (CCW positive, CW negative). All slices rotate together about the shared center. Call each frame before drawOnGrid().
deltaAngle(number) — degrees to add torotation
// Spin at 45°/second using elapsed time (deltaT)
pie.rotate(spinSpeed * deltaT); // call BEFORE drawOnGrid
// Fixed increment per frame
pie.rotate(1); // 1 degree per frame, CCW
Breathing Animation
breatheRadius(sinusoid, t)Modulates the radius of all slices using an SWSinusoid. The radius is set to sinusoid.getValue(t), clamped to a minimum of 0.01. Call after drawOnGrid() so the new value takes effect on the next frame.
sinusoid(SWSinusoid) — controls radius oscillationt(number) — elapsed time in seconds
// Radius oscillates between 2 and 8 over 4 seconds
// Using adjustWaveUsingExtrema for convenient setup:
let radSin = SWSinusoid.copy(UNIT_SINUSOID);
radSin.setPeriod(4);
radSin.adjustWaveUsingExtrema(2, 8);
pie.drawOnGrid(grid);
pie.breatheRadius(radSin, elapsedSeconds); // call AFTER draw
Setter Methods
setRadius(r)Sets the radius for all slices immediately.
r(number) — new radius in user units
pie.setRadius(7);
setStrokeColor(swColor)Sets the border color for all slices. Pass undefined to remove borders from all slices entirely.
swColor(SWColor | undefined) — new border color, or undefined to remove
pie.setStrokeColor(new SWColor(0, 0, 20, 100, "darkBorder"));
pie.setStrokeColor(undefined); // remove all borders
setStrokeWeight(w)Sets the border thickness in pixels for all slices.
w(number) — thickness in pixels
pie.setStrokeWeight(4);
setStartAngle(degrees)Sets the static starting orientation of the first slice edge (degrees CCW from +x), without affecting accumulated rotation.
degrees(number) — new start angle
pie.setStartAngle(90); // first edge points straight up
setSliceColor(index, swColor)Sets the fill color for a single slice by index (0-based). Does nothing if the index is out of range. Useful for highlighting or animating individual slices after the pie is built.
index(number) — slice index (0 to n-1)swColor(SWColor) — new fill color for that slice
// Highlight the first slice in bright white
pie.setSliceColor(0, new SWColor(0, 0, 100, 100, "white"));
setFillAlpha(alpha)Sets the fill opacity for all slices simultaneously. Clamped to [0, 100]. The alpha value is stored in pie.fillAlpha and re-applied whenever slices are rebuilt via setN().
alpha(number) — 0 = fully transparent, 100 = fully opaque
pie.setFillAlpha(60); // 60% opaque fills — all slices
setN(newN)Changes the number of slices and rebuilds the internal sector array. Colors are re-cycled from the current colors array. Current radius, startAngle, thickness, strokeColor, and fillAlpha are all preserved. Accumulated rotation is reset to 0 to avoid a visual jump.
newN(number) — new slice count (clamped to ≥ 2)
pie.setN(12); // rebuild with 12 slices of 30° each
setShowCenter(show)Controls whether the center SWPoint dot is drawn.
show(boolean) — true to show, false to hide (default: true)
pie.setShowCenter(false);
Reset Method
reset()Restores all animated / slider-driven properties — radius, n, startAngle, thickness, strokeColor, colors, and fillAlpha — to their originals captured at construction. Rotation is reset to 0. Does not move the center position.
pie.reset(); // full restore to factory state
Utility Methods
static copy(other)Returns a deep copy of an SWPie instance. All geometry and color values are independently duplicated. The current rotation is preserved in the copy.
other(SWPie) — the pie to copy
SWPie — a new independent instance
let pieCopy = SWPie.copy(pie1);
toString()Returns a string summarizing the pie's key properties: center, radius, n, startAngle, rotation, and computed sliceTheta.
string
console.log(pie.toString());
// "SWPie(center=SWPoint(x:0, y:0), r=6.00, n=7, startAngle=90.0, rotation=0.0, sliceTheta=51.43)"
Usage Examples
Example 1: Basic ROYGBIV Pie
let grid;
let pie;
function setup() {
createCanvas(400, 400);
colorMode(HSB, 360, 100, 100, 100);
initializeSWColors(); // loads swRed, swOrange, ... swViolet globals
grid = new SWGrid({ UL: new SWPoint(-10, 10), LR: new SWPoint(10, -10) });
const colors = [
SWColor.copy(swRed), SWColor.copy(swOrange), SWColor.copy(swYellow),
SWColor.copy(swGreen), SWColor.copy(swBlue), SWColor.copy(swIndigo),
SWColor.copy(swViolet),
];
const border = new SWColor(0, 0, 20, 100, "border");
pie = new SWPie(new SWPoint(0, 0), 6, 7, colors, 90, 2, border);
}
function draw() {
background(0, 0, 93);
grid.draw();
pie.drawOnGrid(grid);
}
Example 2: Spinning Pie (Elapsed-Time Approach)
let grid, pie;
let prevT = 0;
const SPIN_SPEED = 45; // degrees per second
function setup() {
createCanvas(400, 400);
colorMode(HSB, 360, 100, 100, 100);
initializeSWColors();
grid = new SWGrid({ UL: new SWPoint(-10, 10), LR: new SWPoint(10, -10) });
const colors = [
SWColor.copy(swRed), SWColor.copy(swOrange), SWColor.copy(swYellow),
SWColor.copy(swGreen), SWColor.copy(swBlue), SWColor.copy(swIndigo),
SWColor.copy(swViolet),
];
pie = new SWPie(new SWPoint(0, 0), 6, 7, colors, 90, 2,
new SWColor(0, 0, 20, 100, "border"));
}
function draw() {
background(0, 0, 93);
grid.draw();
// deltaT in seconds; spin BEFORE drawing
const t = millis() / 1000;
const deltaT = (prevT > 0) ? (t - prevT) : 0;
prevT = t;
pie.rotate(SPIN_SPEED * deltaT); // CCW positive; use negative for CW
pie.drawOnGrid(grid);
}
Example 3: Breathing Radius
let grid, pie, radSin;
let breathStart = 0, breathElapsed = 0;
let shouldBreathe = false;
function setup() {
createCanvas(400, 400);
colorMode(HSB, 360, 100, 100, 100);
initializeSWColors();
grid = new SWGrid({ UL: new SWPoint(-10, 10), LR: new SWPoint(10, -10) });
const colors = [
SWColor.copy(swRed), SWColor.copy(swGreen), SWColor.copy(swBlue),
];
pie = new SWPie(new SWPoint(0, 0), 5, 3, colors, 90, 2,
new SWColor(0, 0, 20, 100, "border"));
// Radius oscillates between 2 and 8 over 4 seconds
radSin = SWSinusoid.copy(UNIT_SINUSOID);
radSin.setPeriod(4);
radSin.adjustWaveUsingExtrema(2, 8);
}
function draw() {
background(0, 0, 93);
grid.draw();
pie.drawOnGrid(grid); // draw first
if (shouldBreathe) {
const t = millis() / 1000;
breathElapsed += (t - breathStart);
breathStart = t;
pie.breatheRadius(radSin, breathElapsed); // breathe AFTER draw
}
}
function keyPressed() {
if (key === 'b') {
shouldBreathe = !shouldBreathe;
breathStart = millis() / 1000;
}
if (key === 'r') { pie.reset(); breathElapsed = 0; }
}
Example 4: Dynamically Changing Slice Count
let grid, pie;
let n = 7;
function setup() {
createCanvas(400, 400);
colorMode(HSB, 360, 100, 100, 100);
initializeSWColors();
grid = new SWGrid({ UL: new SWPoint(-10, 10), LR: new SWPoint(10, -10) });
buildPie(n);
}
function buildPie(count) {
const colors = [];
for (let i = 0; i < count; i++) {
colors.push(new SWColor((i / count) * 360, 90, 90, 100, `c${i}`));
}
pie = new SWPie(new SWPoint(0, 0), 6, count, colors, 90, 2,
new SWColor(0, 0, 20, 100, "border"));
}
function draw() {
background(0, 0, 93);
grid.draw();
pie.drawOnGrid(grid);
text(`n = ${n} slices (↑↓ to change)`, 10, height - 10);
}
function keyPressed() {
if (keyCode === UP_ARROW) { n = min(n + 1, 16); pie.setN(n); }
if (keyCode === DOWN_ARROW) { n = max(n - 1, 2); pie.setN(n); }
}
Example 5: Spin + Breathe Radius Together
let grid, pie, radSin;
let prevT = 0;
let rStart = 0, rElapsed = 0;
const SPIN_SPEED = 30;
function setup() {
createCanvas(400, 400);
colorMode(HSB, 360, 100, 100, 100);
initializeSWColors();
grid = new SWGrid({ UL: new SWPoint(-10, 10), LR: new SWPoint(10, -10) });
const colors = [
SWColor.copy(swRed), SWColor.copy(swOrange), SWColor.copy(swYellow),
SWColor.copy(swGreen), SWColor.copy(swBlue), SWColor.copy(swIndigo),
SWColor.copy(swViolet),
];
pie = new SWPie(new SWPoint(0, 0), 5, 7, colors, 90, 2,
new SWColor(0, 0, 20, 100, "border"));
radSin = SWSinusoid.copy(UNIT_SINUSOID);
radSin.setPeriod(3);
radSin.adjustWaveUsingExtrema(2, 8);
rStart = millis() / 1000;
}
function draw() {
background(0, 0, 93);
grid.draw();
const t = millis() / 1000;
const deltaT = (prevT > 0) ? (t - prevT) : 0;
prevT = t;
// 1. Spin BEFORE draw
pie.rotate(SPIN_SPEED * deltaT);
pie.drawOnGrid(grid);
// 2. Breathe AFTER draw
rElapsed += deltaT;
pie.breatheRadius(radSin, rElapsed);
}
Example 6: Two Concentric Pies
let grid, pieOuter, pieInner;
let prevT = 0;
function setup() {
createCanvas(400, 400);
colorMode(HSB, 360, 100, 100, 100);
initializeSWColors();
grid = new SWGrid({ UL: new SWPoint(-10, 10), LR: new SWPoint(10, -10) });
// Outer pie: 6 warm-cool slices, slow CW spin
const outerColors = [
new SWColor(0, 90, 90, 100, "red"),
new SWColor(60, 90, 90, 100, "yellow"),
new SWColor(120, 90, 90, 100, "green"),
new SWColor(180, 90, 90, 100, "cyan"),
new SWColor(240, 90, 90, 100, "blue"),
new SWColor(300, 90, 90, 100, "magenta"),
];
pieOuter = new SWPie(new SWPoint(0, 0), 8, 6, outerColors, 0, 2,
new SWColor(0, 0, 100, 100, "white"));
// Inner pie: 3 slices, fast CCW spin, semi-transparent
const innerColors = [
new SWColor(0, 0, 100, 80, "white"),
new SWColor(0, 0, 50, 80, "gray"),
new SWColor(0, 0, 0, 80, "black"),
];
pieInner = new SWPie(new SWPoint(0, 0), 4, 3, innerColors, 0, 2,
new SWColor(0, 0, 100, 100, "white"));
}
function draw() {
background(0, 0, 93);
grid.draw();
const t = millis() / 1000;
const deltaT = (prevT > 0) ? (t - prevT) : 0;
prevT = t;
pieOuter.rotate(-20 * deltaT); // CW (negative)
pieInner.rotate(60 * deltaT); // CCW (positive)
pieOuter.drawOnGrid(grid);
pieInner.drawOnGrid(grid);
}
Best Practices
1. Animation Ordering
- Spin BEFORE draw:
rotate()updates the orientation used during this frame's render - Breathing AFTER draw:
breatheRadius()sets the new value that takes effect next frame — matching the SWSector/SWDisk convention
pie.rotate(speed * deltaT); // spin → before draw
pie.drawOnGrid(grid); // draw
pie.breatheRadius(radSin, t); // breathe → after draw
2. Elapsed Time vs. frameCount
- Use elapsed seconds (not frame count) for all sinusoid time parameters; this keeps animation speed frame-rate-independent
- Track
startTimeandelapsedseparately per animation so each can be paused and resumed independently
// Pattern for pauseable elapsed-time animation
let breathStart = 0, breathElapsed = 0, isBreathing = false;
function toggleBreathe() {
isBreathing = !isBreathing;
if (isBreathing) breathStart = millis() / 1000;
else breathElapsed += (millis() / 1000) - breathStart;
}
// In draw():
if (isBreathing) {
const t = millis() / 1000;
breathElapsed += (t - breathStart);
breathStart = t;
pie.breatheRadius(radSin, breathElapsed);
}
3. Angle Convention
- Always pass user-space degrees (CCW from +x) to SWPie; the class handles the p5.js y-flip internally
startAngle=0places the first slice edge along the +x axis;startAngle=90places it at the top (+y axis)- Positive
rotate()deltas spin CCW; negative values spin CW
4. Colors Array Design
- Always pass SWColor instances; the constructor copies each one independently to prevent shared-mutation bugs
- If the array is shorter than n, colors are cycled modulo — useful for repeating palettes
- If the array is longer than n, only the first n colors are used
- Use
setSliceColor(i, color)to change individual slices after construction without a full rebuild - After a
setN()rebuild, individual slice color changes are lost — reapply them if needed
5. SWSinusoid Setup for Breathing
- The most convenient setup uses the
UNIT_SINUSOIDglobal,setPeriod(), andadjustWaveUsingExtrema(min, max) - Or use the constructor directly: center = (min+max)/2, amplitude = (max-min)/2, frequency = 1/period
// Radius breathes between 2 and 8 with a 4-second period
let radSin = SWSinusoid.copy(UNIT_SINUSOID);
radSin.setPeriod(4);
radSin.adjustWaveUsingExtrema(2, 8);
// Equivalent long form:
let radSin2 = new SWSinusoid(5, 3, 0.25, 0);
6. Preserving State During setN() Rebuilds
setN()rebuilds the entire sector array — individual slice color changes, setShowVertex states, and any manual sector tweaks are lost- Preserve the center position before calling
setN()if it was moved by dragging, and reapply it afterward - The demo uses this pattern: save
cx/cy, callsetN(), restore center, then reapply slider values
// Safe n-slider rebuild pattern
const cx = pie.center.x;
const cy = pie.center.y;
pie.setN(newN);
pie.center.x = cx;
pie.center.y = cy;
pie.setRadius(currentRadius);
pie.setFillAlpha(currentAlpha);
Integration with Other SketchWave Classes
Script Loading Order
SWPie depends on SWSector, which must itself be loaded after SWGrid, SWPoint, SWColor, and SWSinusoid:
<!-- p5.js library -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.6.0/lib/p5.js"></script>
<!-- SketchWaveJS classes in dependency order -->
<script src="shapeClasses/swSinusoid.js"></script>
<script src="shapeClasses/swColor.js"></script>
<script src="shapeClasses/swPoint.js"></script>
<script src="shapeClasses/swGrid.js"></script>
<script src="shapeClasses/swSector.js"></script> <!-- must precede swPie -->
<script src="shapeClasses/swPie.js"></script>
<!-- Your sketch -->
<script src="sketches/yourSketch.js"></script>
Working with SWSector
SWPie's slices are SWSector instances. Understanding SWSector helps explain SWPie's behavior:
- Each sector has its own
startAngle(the fixed slice offset:i × 360/n) that never changes - The pie writes its accumulated
startAngle + rotationinto every sector'srotationproperty each frame via_syncSlices() - SWSector draws in
PIEmode (two radii + arc, connected to vertex/center)
Working with SWPoint
SWPie uses SWPoint for its center:
- The center is a full SWPoint instance — it is drawn as a small dot when
shouldShowCenteris true - Drag or reposition the pie by changing
pie.center.xandpie.center.ydirectly - Each internal sector gets its own copy of the center coordinates each frame via
_syncSlices()
Working with SWColor
SWPie uses SWColor for all color management (HSB mode):
- Colors in the array are individually copied at slice-build time — mutations to the source array after construction have no effect
- The shared
strokeColoris also copied; each sector receives its own independent copy - Predefined globals (
swRed,swOrange, etc.) are available after callinginitializeSWColors()insetup()
Working with SWSinusoid
SWPie's breatheRadius() method accepts a SWSinusoid instance:
- The sinusoid's
getValue(t)is called with elapsed time in seconds - Use
SWSinusoid.copy(UNIT_SINUSOID)withsetPeriod()andadjustWaveUsingExtrema()for the most convenient setup
Comparing SWPie and SWTwoTonedDisk
Both are composite classes that own internal sub-shape objects, but they serve different purposes:
| Feature | SWPie | SWTwoTonedDisk |
|---|---|---|
| Internal objects | n equal SWSectors | 2 SWSectors |
| Number of regions | Configurable (2–n) | Always 2 |
| Slice angular size | Equal: 360 / n degrees | Configurable split angle |
| Colors | Array (one per slice, cycled) | Two fixed colors |
| Dynamic resizing | setN() rebuilds all slices |
Not applicable |
| Per-slice recoloring | setSliceColor(i, color) |
Not available |
| Fill opacity | setFillAlpha() — all slices |
Not available |
| Use case | Pie charts, ROYGBIV wheels, color wheels | Yin-yang style split disks |
Source Code
The complete SWPie class implementation:
Show/Hide Source Code
/*
File: swPie.js
Date: 2026-04-21
Author: klp
App: SketchWaveTNT2026-04-21-Stg8
Purpose: SWPie class for SketchWaveJS
SWPie represents a circular disk divided into n equal "pizza slice" sectors
arranged radially about a shared center. Each slice is an SWSector with:
- An equal angular size theta = 360 / n degrees
- A fixed offset of i × theta from the pie's startAngle
- Its own fill color (drawn from a colors array, cycled if shorter than n)
Geometry:
- All slices share the same center, radius, stroke weight, and stroke color.
- The shared stroke color controls the radial edge borders between slices.
- Changing n via setN() rebuilds the slices array; colors are re-applied.
Animations (all composable):
- rotate(delta) : spins the entire pie CCW (positive) or CW (negative).
- breatheRadius(sin, t) : oscillates the radius of all slices with an SWSinusoid.
Color model:
- Each slice gets fillColor = colors[i % colors.length] at build time.
- A single strokeColor is shared by all slices (undefined = no border).
- Use setSliceColor(i, color) to change one slice's fill after construction.
- setFillAlpha(alpha) sets the transparency of all fills simultaneously.
Angle convention (same as SWSector):
User space: CCW from +x axis, y increases upward.
p5 / screen: CW from +x axis, y increases downward.
Dependencies: p5.js, SWColor, SWPoint, SWGrid, SWSinusoid, SWSector.
Notes:
- Consistent API with SWTwoTonedDisk, SWDisk, etc.
- SWSector must be loaded before SWPie.
*/
console.log("[swPie.js] SWPie class loaded.");
class SWPie {
constructor(center, radius, n = 6, colors = [], startAngle = 0,
thickness = 2, strokeColor = undefined) {
this.center = center;
this.radius = radius;
this.n = Math.max(2, Math.round(n));
this.colors = colors;
this.startAngle = startAngle;
this.rotation = 0;
this.thickness = thickness;
this.strokeColor = strokeColor ? SWColor.copy(strokeColor) : undefined;
this.shouldShowCenter = true;
this.fillAlpha = 100;
this.originalRadius = radius;
this.originalN = this.n;
this.originalStartAngle = startAngle;
this.originalThickness = thickness;
this.originalStrokeColor = strokeColor ? SWColor.copy(strokeColor) : undefined;
this.originalColors = colors.map(c => c ? SWColor.copy(c) : undefined);
this.originalFillAlpha = 100;
this._buildSlices();
}
_buildSlices() {
const sliceTheta = 360 / this.n;
this.sectors = [];
for (let i = 0; i < this.n; i++) {
const fillColor = this.colors.length > 0
? SWColor.copy(this.colors[i % this.colors.length])
: undefined;
const sec = new SWSector(
new SWPoint(this.center.x, this.center.y),
this.radius,
sliceTheta,
i * sliceTheta,
this.thickness,
fillColor,
this.strokeColor ? SWColor.copy(this.strokeColor) : undefined
);
sec.setShowVertex(false);
if (this.fillAlpha !== undefined && this.fillAlpha < 100) {
sec.setFillAlpha(this.fillAlpha);
}
this.sectors.push(sec);
}
}
_syncSlices() {
const cx = this.center.x;
const cy = this.center.y;
const r = this.startAngle + this.rotation;
for (const sec of this.sectors) {
sec.vertex.x = cx;
sec.vertex.y = cy;
sec.rotation = r;
}
}
draw() {
this._syncSlices();
for (const sec of this.sectors) sec.draw();
if (this.shouldShowCenter && this.center && this.center.draw) {
this.center.draw();
}
}
drawOnGrid(grid) {
this._syncSlices();
for (const sec of this.sectors) sec.drawOnGrid(grid);
if (this.shouldShowCenter && this.center && this.center.drawOnGrid) {
this.center.drawOnGrid(grid);
}
}
rotate(deltaAngle) {
this.rotation += deltaAngle;
}
breatheRadius(sinusoid, t) {
this.radius = Math.max(0.01, sinusoid.getValue(t));
for (const sec of this.sectors) sec.setRadius(this.radius);
}
setRadius(r) {
this.radius = r;
for (const sec of this.sectors) sec.setRadius(r);
}
setStrokeColor(swColor) {
this.strokeColor = swColor ? SWColor.copy(swColor) : undefined;
for (const sec of this.sectors) {
sec.setStrokeColor(this.strokeColor ? SWColor.copy(this.strokeColor) : undefined);
}
}
setStrokeWeight(w) {
this.thickness = w;
for (const sec of this.sectors) sec.setStrokeWeight(w);
}
setStartAngle(degrees) {
this.startAngle = degrees;
}
setSliceColor(index, swColor) {
if (index < 0 || index >= this.sectors.length) return;
this.sectors[index].setFillColor(swColor ? SWColor.copy(swColor) : undefined);
}
setFillAlpha(alpha) {
this.fillAlpha = Math.max(0, Math.min(100, alpha));
for (const sec of this.sectors) sec.setFillAlpha(this.fillAlpha);
}
setN(newN) {
this.n = Math.max(2, Math.round(newN));
this.rotation = 0;
this._buildSlices();
}
setShowCenter(show = true) {
this.shouldShowCenter = show;
}
reset() {
this.rotation = 0;
this.n = this.originalN;
this.startAngle = this.originalStartAngle;
this.thickness = this.originalThickness;
this.colors = this.originalColors.map(c => c ? SWColor.copy(c) : undefined);
this.strokeColor = this.originalStrokeColor
? SWColor.copy(this.originalStrokeColor) : undefined;
this.fillAlpha = this.originalFillAlpha;
this.radius = this.originalRadius;
this._buildSlices();
}
static copy(other) {
const colorsCopy = other.colors.map(c => c ? SWColor.copy(c) : undefined);
const newPie = new SWPie(
new SWPoint(other.center.x, other.center.y),
other.radius,
other.n,
colorsCopy,
other.startAngle,
other.thickness,
other.strokeColor ? SWColor.copy(other.strokeColor) : undefined
);
newPie.rotation = other.rotation;
newPie.shouldShowCenter = other.shouldShowCenter;
return newPie;
}
toString() {
return `SWPie(center=${this.center}, r=${this.radius.toFixed(2)}, ` +
`n=${this.n}, startAngle=${this.startAngle.toFixed(1)}, ` +
`rotation=${this.rotation.toFixed(1)}, sliceTheta=${(360/this.n).toFixed(2)})`;
}
}//end SWPie class