Overview
SWDiskStack is a Core composite class built from SWDisk objects. You provide a bigDisk, a smallDisk, and a step count. SWDiskStack then creates an internal layered stack that linearly blends:
- center location (x, y)
- radius
- border thickness
- fill and stroke color (HSBA)
Result: a layered disk-stack transition shape that can be drawn directly or on an SWGrid.
Constructor
let diskStack = new SWDiskStack(bigDisk, smallDisk, steps);
// Required endpoint disks
let bigDisk = new SWDisk(new SWPoint(-3, 2), 6, 2, swCyan, swBlue);
let smallDisk = new SWDisk(new SWPoint(3, -2), 2, 2, swPaleYellow, swBrown);
// Minimum steps is clamped to 2
let diskStack = new SWDiskStack(bigDisk, smallDisk, 18);
Properties
bigDisk
Starting endpoint disk (copied internally for safe composition).
Starting endpoint disk (copied internally for safe composition).
smallDisk
Ending endpoint disk (copied internally for safe composition).
Ending endpoint disk (copied internally for safe composition).
steps
Number of blended layers (integer, clamped to at least 2).
Number of blended layers (integer, clamped to at least 2).
layers
Internal array of interpolated SWDisk objects used for rendering.
Internal array of interpolated SWDisk objects used for rendering.
Methods
Configuration
setSteps(n)
Update layer count and rebuild interpolation.
Update layer count and rebuild interpolation.
setBigDisk(disk)
Replace the big endpoint and rebuild.
Replace the big endpoint and rebuild.
setSmallDisk(disk)
Replace the small endpoint and rebuild.
Replace the small endpoint and rebuild.
setEndpoints(bigDisk, smallDisk)
Replace both endpoints and rebuild.
Replace both endpoints and rebuild.
Rendering
draw()
Draw all layers in screen coordinates.
Draw all layers in screen coordinates.
drawOnGrid(grid)
Draw all layers in grid/user coordinates.
Draw all layers in grid/user coordinates.
Utility
getLayerCount()
Return current layer count.
Return current layer count.
toString()
Return summary text for debugging.
Return summary text for debugging.
Usage Examples
Example 1: Basic Disk Stack On Grid
let grid;
let diskStack;
function setup() {
createCanvas(500, 500);
colorMode(HSB, 360, 100, 100, 100);
initializeSWColors();
grid = new SWGrid({ UL: new SWPoint(-10, 10), LR: new SWPoint(10, -10) });
let big = new SWDisk(new SWPoint(-3, 2), 6, 2, new SWColor(190, 85, 95, 75), new SWColor(210, 80, 45, 100));
let small = new SWDisk(new SWPoint(3, -2), 2, 2, new SWColor(45, 35, 98, 100), new SWColor(25, 85, 70, 100));
diskStack = new SWDiskStack(big, small, 20);
}
function draw() {
background(0, 0, 95);
grid.draw();
diskStack.drawOnGrid(grid);
}
Example 2: Animate Endpoints
// Move endpoints, then rebuild each frame
function draw() {
background(0, 0, 95);
grid.draw();
big.center.x = -3 + 2 * sin(frameCount * 0.02);
small.center.y = -2 + 1.5 * cos(frameCount * 0.03);
diskStack.setEndpoints(big, small);
diskStack.drawOnGrid(grid);
}
Example 3: Dynamic Layer Resolution
// Increase/decrease smoothness with keyboard
function keyPressed() {
if (key === '+') diskStack.setSteps(diskStack.getLayerCount() + 1);
if (key === '-') diskStack.setSteps(diskStack.getLayerCount() - 1);
}
Integration Notes
Load scripts in dependency order:
<script src="https://cdn.jsdelivr.net/npm/p5@1.6.0/lib/p5.js"></script>
<script src="../shapeClasses/swColor.js"></script>
<script src="../shapeClasses/swPoint.js"></script>
<script src="../shapeClasses/swGrid.js"></script>
<script src="../shapeClasses/swDisk.js"></script>
<script src="../shapeClasses/swDiskStack.js"></script>
<script src="../sketches/yourSketch.js"></script>
Source Code
The complete SWDiskStack class implementation:
Show/Hide Source Code
/*
File: swDiskStack.js
Date: 2026-05-28
Author: klp + Copilot
Purpose: SWDiskStack class for SketchWaveJS
An SWDiskStack is a stack of interpolated SWDisk instances that transition
from a big endpoint disk to a small endpoint disk.
Dependencies:
- p5.js
- SWColor
- SWPoint
- SWDisk
*/
console.log("[swDiskStack.js] SWDiskStack class loaded.");
class SWDiskStack {
/**
* @param {SWDisk} bigDisk - Starting disk in the stack
* @param {SWDisk} smallDisk - Ending disk in the stack
* @param {number} [steps=12] - Number of disks in the blended stack
*/
constructor(bigDisk, smallDisk, steps = 12) {
this.bigDisk = SWDiskStack.copyDisk(bigDisk);
this.smallDisk = SWDiskStack.copyDisk(smallDisk);
this.steps = SWDiskStack.clampSteps(steps);
this.layers = [];
this.rebuildLayers();
}//end constructor
static clampSteps(n) {
return Math.max(2, Math.round(Number(n) || 2));
}//end clampSteps
static copyDisk(disk) {
if (!(disk instanceof SWDisk)) {
throw new Error("SWDiskStack expects SWDisk endpoints.");
}
const centerCopy = SWPoint.copy(disk.center);
const fillCopy = disk.fillColor ? SWColor.copy(disk.fillColor) : undefined;
const strokeCopy = disk.strokeColor ? SWColor.copy(disk.strokeColor) : undefined;
const copy = new SWDisk(centerCopy, disk.radius, disk.thickness, fillCopy, strokeCopy);
copy.setShowCenter(!!disk.shouldShowCenter);
return copy;
}//end copyDisk
static lerpNumber(a, b, t) {
return a + (b - a) * t;
}//end lerpNumber
static lerpHue(h1, h2, t) {
const d = ((h2 - h1 + 540) % 360) - 180;
let out = h1 + d * t;
out = ((out % 360) + 360) % 360;
return out;
}//end lerpHue
static lerpColor(c1, c2, t, name = "orbColor") {
if (!c1 && !c2) return undefined;
if (!c1) return SWColor.copy(c2);
if (!c2) return SWColor.copy(c1);
const h = SWDiskStack.lerpHue(c1.h, c2.h, t);
const s = SWDiskStack.lerpNumber(c1.s, c2.s, t);
const b = SWDiskStack.lerpNumber(c1.b, c2.b, t);
const a = SWDiskStack.lerpNumber(c1.a, c2.a, t);
return new SWColor(h, s, b, a, name);
}//end lerpColor
setSteps(n) {
this.steps = SWDiskStack.clampSteps(n);
this.rebuildLayers();
}//end setSteps
setBigDisk(disk) {
this.bigDisk = SWDiskStack.copyDisk(disk);
this.rebuildLayers();
}//end setBigDisk
setSmallDisk(disk) {
this.smallDisk = SWDiskStack.copyDisk(disk);
this.rebuildLayers();
}//end setSmallDisk
setEndpoints(bigDisk, smallDisk) {
this.bigDisk = SWDiskStack.copyDisk(bigDisk);
this.smallDisk = SWDiskStack.copyDisk(smallDisk);
this.rebuildLayers();
}//end setEndpoints
rebuildLayers() {
this.layers = [];
const n = this.steps;
for (let i = 0; i < n; i++) {
const t = (n === 1) ? 0 : i / (n - 1);
const cx = SWDiskStack.lerpNumber(this.bigDisk.center.x, this.smallDisk.center.x, t);
const cy = SWDiskStack.lerpNumber(this.bigDisk.center.y, this.smallDisk.center.y, t);
const radius = SWDiskStack.lerpNumber(this.bigDisk.radius, this.smallDisk.radius, t);
const thickness = SWDiskStack.lerpNumber(this.bigDisk.thickness, this.smallDisk.thickness, t);
const fillColor = SWDiskStack.lerpColor(this.bigDisk.fillColor, this.smallDisk.fillColor, t, `diskStackFill_${i}`);
const strokeColor = SWDiskStack.lerpColor(this.bigDisk.strokeColor, this.smallDisk.strokeColor, t, `diskStackStroke_${i}`);
const center = new SWPoint(cx, cy, undefined, 1, strokeColor);
const layerDisk = new SWDisk(center, radius, thickness, fillColor, strokeColor);
layerDisk.setShowCenter(false);
this.layers.push(layerDisk);
}
}//end rebuildLayers
draw() {
for (const layer of this.layers) {
layer.draw();
}
}//end draw
drawOnGrid(grid) {
for (const layer of this.layers) {
layer.drawOnGrid(grid);
}
}//end drawOnGrid
getLayerCount() {
return this.layers.length;
}//end getLayerCount
toString() {
return `SWDiskStack(steps: ${this.steps}, bigRadius: ${this.bigDisk.radius}, smallRadius: ${this.smallDisk.radius}, layers: ${this.layers.length})`;
}//end toString
}//end class SWDiskStack