Quick Reference
SWSpiral is a SketchWave parametric class that represents an Archimedean spiral defined by the polar equation r = k·θ/π, where k controls the spacing between spiral arms and revolutions sets how many full turns the spiral traces. The drawn shape is a closed polygon — the spiral arc sampled at SAMPLE_COUNT × revolutions points, connected back to the center with a straight closing line. SWSpiral is not a composite class; all drawing, animation, and styling is handled directly.
- Design Pattern: Parametric curve (not composition or inheritance)
- Equation: r = k·θ/π — Archimedean spiral with linear arm spacing
- Internal Structure: Polar coordinates sampled at
SAMPLE_COUNT = 120points per revolution, closed back to center - Dependencies: SWPoint, SWColor, SWGrid, p5.js
- Key Features: Configurable arm spacing (k), revolution count, CW/CCW direction, fill & stroke colors with independent alpha, spin animation, draggable center
- Common Uses: Spiral art, nautilus/galaxy shapes, hypnotic animations, geometric pattern exploration, mathematical curve illustration
Overview
The SWSpiral class draws an Archimedean spiral as a closed polygon. The spiral is fully defined by a center point, growth coefficient k, and the number of revolutions. In polar coordinates, the curve is:
r = k · θ / π (θ in radians, r in user-space units)
At θ = π (half-turn, 180°):
r = k — the radius equals k units.At θ = 2π (one full turn):
r = 2k.After n full turns:
r = 2kn.So k is precisely the radius at the half-turn point, and also the gap between successive arms measured at the half-turn crossing.
Fill Semantics
The spiral is drawn as a closed polygon: the arc from the center outward to the tip, then a straight line back to the center. When a fill color is set, p5.js fills the entire interior of this polygon using non-zero winding. With multiple revolutions the fill creates a layered effect — the regions between arms take on the fill color, which shows through most visibly when the fill is semi-transparent.
To highlight the space between spiral arms, use a semi-transparent fill (opacity 40–70%) combined with a distinct stroke color.
Winding Direction
Set clockwise = false (default) for a CCW spiral that opens to the right. Set clockwise = true to mirror the winding direction. Both produce an Archimedean spiral; the direction only affects which way the arms wind outward.
Rotation
SWSpiral supports two layers of rotation, both applied about the center point:
rotationDeg— Static base rotation set bysetRotation(). Persists across frames; survivesreset().rotation— Accumulated rotation incremented byrotate(). Starts at 0; cleared byreset().
Effective rotation = rotationDeg + rotation. All angles are CCW positive (user-space convention).
Key Capabilities
- Archimedean Spiral: Mathematically precise r = k·θ/π; evenly spaced arms
- Configurable Arm Spacing:
kdirectly controls how far apart each revolution is - CW / CCW Winding: Toggle the direction of the spiral without rebuilding
- Spin Animation: Rotate continuously about the center using
rotate() - Fill & Stroke Colors: Independent color pickers and alpha channels for stroke (outline) and fill (interior)
- Draggable Center: Move the entire spiral by repositioning the center SWPoint
- Dual Coordinate Systems: Draw in screen pixels (
draw()) or grid user coordinates (drawOnGrid())
Constructor
new SWSpiral(center, k, revolutions, strokeColor, fillColor, thickness, clockwise, rotationDeg)Creates a new SWSpiral instance. All constructor values are saved as originals for reset().
| Parameter | Type | Default | Description |
|---|---|---|---|
center |
SWPoint | required | The origin of the spiral in user (grid) coordinates. The spiral starts here (r = 0 at θ = 0) and winds outward. |
k |
number | required | Growth coefficient. Controls the distance between successive arms (r = k at the half-turn). Larger k = wider spacing. |
revolutions |
number | 3 | Number of full 360° turns to draw. Must be > 0; values below 0.25 are clamped to 0.25. |
strokeColor |
SWColor | undefined | undefined | Spiral outline color. undefined = no stroke drawn. |
fillColor |
SWColor | undefined | undefined | Fill color for the spiral interior polygon. undefined = no fill (transparent interior). |
thickness |
number | 2 | Stroke weight in pixels. |
clockwise |
boolean | false | Winding direction. false = CCW (default, opens to the right); true = CW (mirrored). |
rotationDeg |
number | 0 | Static base rotation in CCW degrees; applied in addition to accumulated rotation. |
// Basic blue spiral, 3 turns, CCW
const stroke = new SWColor(220, 70, 60, 100, "spiralStroke");
const fill = new SWColor(200, 40, 90, 60, "spiralFill");
let spiral = new SWSpiral(new SWPoint(0, 0), 1.5, 3, stroke, fill, 2);
// Tight spiral with many turns (small k, many revolutions)
const tStroke = new SWColor(0, 80, 80, 100, "tightStroke");
let tight = new SWSpiral(new SWPoint(0, 0), 0.5, 6, tStroke, undefined, 1);
// Clockwise spiral, no fill, thick stroke
const cwStroke = new SWColor(30, 90, 90, 100, "cwStroke");
let cw = new SWSpiral(new SWPoint(2, 1), 2.0, 2, cwStroke, undefined, 3, true);
// Pre-rotated spiral (45° tilt at construction)
let tilted = new SWSpiral(new SWPoint(0, 0), 1.5, 3, stroke, fill, 2, false, 45);
// Stroke-only spiral with default revolutions
const minStroke = new SWColor(0, 0, 20, 100, "dark");
let minimal = new SWSpiral(new SWPoint(0, 0), 1.0, undefined, minStroke);
Properties
center SWPointThe spiral's origin in user (grid) coordinates. The spiral always starts here (r = 0) and winds outward. Moving center.x or center.y repositions the entire spiral.
spiral.center.x = 3; spiral.center.y = -2;k numberGrowth coefficient controlling arm spacing. At θ = π, r = k. After n full turns, the outer radius is 2kn. Larger k = wider spiral arms. Use setK() to change it.
spiral.setK(0.5); // tight spiral
spiral.setK(3.0); // wide spiral
revolutions numberNumber of full turns the spiral draws. Values below 0.25 are automatically clamped to 0.25. Use setRevolutions() to change it.
spiral.setRevolutions(5); // 5 full turns
spiral.setRevolutions(0.5); // half a turn only
clockwise booleanWinding direction of the spiral. false (default) = CCW (mathematically positive direction); true = CW (mirrored). Use setClockwise() to toggle.
spiral.setClockwise(true); // wind clockwise
spiral.setClockwise(false); // wind counter-clockwise
rotationDeg numberStatic base rotation in CCW degrees. Set by setRotation(); survives reset(). Added to the accumulated rotation to produce the effective drawing rotation.
spiral.setRotation(45); // tilt 45° CCWrotation numberAccumulated rotation in degrees, incremented each frame by rotate(). Starts at 0; cleared by reset().
// Cleared automatically by reset(); read-only in normal useSWSpiral.SAMPLE_COUNT staticNumber of arc sample points per full revolution (default: 120). The total number of polygon vertices equals SAMPLE_COUNT × revolutions + 1 (plus the closing center point). Higher values produce smoother curves; lower values reveal the polygon facets. Can be set at any time.
SWSpiral.SAMPLE_COUNT = 24; // faceted, angular look
SWSpiral.SAMPLE_COUNT = 180; // very smooth
Methods
Drawing Methods
draw()Draws the spiral in raw screen (pixel) coordinates. The center SWPoint is interpreted as screen pixels. Prefer drawOnGrid() for standard canvas use with an SWGrid.
function draw() {
background(220);
spiral.draw();
}
drawOnGrid(grid)Draws the spiral mapped through the given SWGrid's coordinate system. This is the standard method; the grid handles converting user-space coordinates to screen pixels and the y-flip (math up → screen down).
function draw() {
background(220);
grid.draw();
spiral.drawOnGrid(grid);
}
Rotation Animation
rotate(deltaAngle)Spins the spiral about its center by deltaAngle degrees (CCW+, CW−). Accumulates into this.rotation. Call once per frame in draw() before calling drawOnGrid().
// Spin at 45°/second
const SPIN_SPEED = 45;
let prevT = 0;
function draw() {
const t = millis() / 1000;
const deltaT = prevT > 0 ? t - prevT : 0;
prevT = t;
spiral.rotate(SPIN_SPEED * deltaT); // call BEFORE drawOnGrid
spiral.drawOnGrid(grid);
}
Breathe Animation
The breathe effect is implemented in the sketch layer using setK().
Each frame, k is driven by a sine wave centered on a base value:
currentK = baseK + depth · sin(2π · speed · t)
baseK is the steady-state value (typically from a slider). depth (Δk) is the amplitude of oscillation. speed is the frequency in Hz (cycles per second). The value is clamped to a minimum of 0.05 so the spiral never collapses. Breathe and Spin can run simultaneously.
// Breathe at 0.3 Hz with Δk = 0.3
const BREATHE_SPEED = 0.3; // Hz
const BREATHE_DEPTH = 0.3; // Δk
let baseK = 0.8;
function draw() {
const t = millis() / 1000;
const breathedK = baseK + BREATHE_DEPTH * sin(TWO_PI * BREATHE_SPEED * t);
spiral.setK(max(0.05, breathedK));
spiral.drawOnGrid(grid);
}
Setter Methods
setK(k)Updates the growth coefficient. Takes effect immediately on the next draw call.
spiral.setK(2.5);setRevolutions(r)Updates the number of full turns. Values below 0.25 are clamped to 0.25.
spiral.setRevolutions(4);setClockwise(cw)Sets the winding direction. true = CW; false = CCW.
spiral.setClockwise(true); // wind clockwisesetStrokeColor(sc) setFillColor(fc)Sets the stroke or fill color. Pass an SWColor instance or undefined to remove the color.
spiral.setStrokeColor(new SWColor(240, 80, 60, 100, "dark"));
spiral.setFillColor(undefined); // transparent fill
setFillAlpha(alpha) setStrokeAlpha(alpha)Sets the fill or stroke alpha (0–100) and rebuilds the p5 color object. Requires an existing fill/stroke color to be set first.
spiral.setFillAlpha(40); // 40% opacity fill
spiral.setStrokeAlpha(100); // fully opaque stroke
setStrokeWeight(w)Sets the stroke thickness in pixels.
spiral.setStrokeWeight(4);setRotation(deg)Sets the static base rotation in CCW degrees. Does not affect the accumulated rotation.
spiral.setRotation(90); // rotates the spiral 90° CCW from defaultReset & Utility Methods
reset()Restores all animated and slider-driven properties to their original constructor values. Clears accumulated spin rotation. Does not move the center position.
spiral.reset(); // back to factory defaultsstatic SWSpiral.copy(other)Creates a deep copy of the given SWSpiral, preserving all current and original state including rotation accumulation.
const copy = SWSpiral.copy(spiral);toString()Returns a human-readable string describing the spiral's current state.
console.log(spiral.toString());
// "SWSpiral(center=SWPoint(x:0, y:0), k=1.50, revolutions=3.00, direction=CCW, rotationDeg=0.0, rotation=0.0)"
Code Examples
Minimal sketch (spiral on a grid)
let grid, spiral;
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 stroke = new SWColor(220, 70, 60, 100, "s");
const fill = new SWColor(200, 40, 90, 60, "f");
spiral = new SWSpiral(new SWPoint(0, 0), 1.5, 3, stroke, fill, 2);
}
function draw() {
background(240);
grid.draw();
spiral.drawOnGrid(grid);
grid.updateScreenBounds();
}
Spinning spiral
let prevT = 0;
const SPIN_SPEED = 60; // degrees per second
function setup() { /* ... create grid and spiral ... */ }
function draw() {
const t = millis() / 1000;
const deltaT = prevT > 0 ? t - prevT : 0;
prevT = t;
background(240);
grid.draw();
spiral.rotate(SPIN_SPEED * deltaT); // spin BEFORE drawing
spiral.drawOnGrid(grid);
grid.updateScreenBounds();
}
Changing direction at runtime
function keyPressed() {
if (key === 'c') {
spiral.setClockwise(!spiral.clockwise); // flip direction
}
if (key === 'r') {
spiral.reset(); // back to factory defaults
}
}
Using a color picker with SWColor.fromHex()
// In your HTML:
// <input type="color" id="strokePicker" value="#3366bb">
// <input type="range" id="alphaSlider" min="0" max="100" value="100">
const picker = document.getElementById('strokePicker');
const alpha = document.getElementById('alphaSlider');
picker.addEventListener('input', () => {
const col = SWColor.fromHex(picker.value, Number(alpha.value), 'spiralStroke');
spiral.setStrokeColor(col);
});
alpha.addEventListener('input', () => {
const col = SWColor.fromHex(picker.value, Number(alpha.value), 'spiralStroke');
spiral.setStrokeColor(col);
});
Required script tags (in dependency order)
<script src="https://cdn.jsdelivr.net/npm/p5@1.6.0/lib/p5.js"></script>
<!-- SketchWaveJS classes in dependency order -->
<script src="../shapeClasses/swColor.js"></script>
<script src="../shapeClasses/swPoint.js"></script>
<script src="../shapeClasses/swGrid.js"></script>
<script src="../shapeClasses/swSpiral.js"></script>
<!-- Your sketch -->
<script src="../sketches/yourSketch.js"></script>
Design Notes
- Outer radius grows with both k and revolutions: The outermost point is at
r = 2knwhere n = revolutions. With k=1.5 and 3 revolutions, r_max = 9 — this fits neatly in a 10-unit grid. With larger k or more revolutions, the spiral will extend beyond the visible grid area. - SAMPLE_COUNT controls smoothness per revolution: The total polygon vertex count is
SAMPLE_COUNT × revolutions. The default of 120 per revolution gives very smooth curves; reducing it to 12–24 creates a faceted polygon look. - Fill color applies to the full interior polygon: p5.js fills the closed polygon formed by the spiral arc and the straight closing line back to center. This is not a true "between-arms" fill — it fills the enclosed area of the whole shape. Use semi-transparent fill for the most natural appearance.
- Rotation is applied in polar → Cartesian conversion: The rotation is applied to the computed (x, y) displacement from center before converting to screen coordinates, which correctly rotates the entire spiral about its center.
- reset() does not move the center: This allows the center to be dragged and repositioned interactively without losing the position on reset.
Source Code
The complete SWSpiral class implementation:
Show/Hide Source Code
/*
File: swSpiral.js
Date: 2026-04-27
Author: klp
App: SketchWaveTNT2026-04-21-Stg8
Purpose: SWSpiral class for SketchWaveJS
SWSpiral represents an Archimedean spiral defined by the polar equation
r = k · θ / π in a coordinate system centered at a given center point:
center (SWPoint) — the origin of the spiral (where θ = 0, r = 0)
k (number) — growth coefficient controlling arm spacing:
r = k · θ / π
At θ = π (half turn) : r = k
At θ = 2π (one full turn): r = 2k
After n full turns : r = 2kn
revolutions (number) — how many full 360° turns the spiral traces (must be > 0)
clockwise (boolean) — if true, spiral winds clockwise; if false, CCW (default)
In Cartesian (user-space, y-up):
x = r · cos(θ) y = r · sin(θ) for CCW
x = r · cos(θ) y = −r · sin(θ) for CW (same as θ → −θ)
The drawn shape is a closed polygon: the spiral arc sampled at
SAMPLE_COUNT × revolutions points from the center outward to the tip,
then a straight line back to the center. Fill is applied to the
enclosed interior; stroke is drawn along the full outline.
Fill semantics:
The fill color covers the interior of the spiral polygon. With multiple
revolutions the p5.js non-zero-winding fill naturally shades the regions
between the arms, giving the appearance of the fill color showing through
the gaps in the spiral stroke. Use a semi-transparent fill for the best
visual layering effect.
Rotation:
rotationDeg — static base rotation (CCW degrees), set by setRotation().
Persists across frames; survives reset().
rotation — accumulated rotation (degrees), incremented by rotate().
Starts at 0; reset() returns it to 0.
Effective rotation = rotationDeg + rotation. All rotation is CCW positive.
All rotation is applied about the CENTER point.
Angle convention (same as all SketchWaveJS classes):
User space: CCW positive, y increases upward (standard math/Cartesian).
p5 screen: CW positive, y increases downward.
SWSpiral handles the y-flip internally; always pass CCW degrees.
Dependencies: p5.js, SWColor, SWPoint, SWGrid.
*/
console.log("[swSpiral.js] SWSpiral class loaded.");
class SWSpiral {
static SAMPLE_COUNT = 120; // sample points per full revolution
/**
* @param {SWPoint} center - Center of the spiral in user (grid) coordinates
* @param {number} k - Growth coefficient (r = k·θ/π). Larger = wider arm spacing.
* @param {number} [revolutions=3] - Number of full turns to draw (must be > 0)
* @param {SWColor} [strokeColor] - Spiral line / border color (undefined = no stroke)
* @param {SWColor} [fillColor] - Fill color for spiral interior (undefined = no fill)
* @param {number} [thickness=2] - Stroke weight in pixels
* @param {boolean} [clockwise=false]- Wind clockwise (true) or counter-clockwise (false)
* @param {number} [rotationDeg=0] - Static base rotation in CCW degrees
*/
constructor(center, k, revolutions = 3,
strokeColor = undefined, fillColor = undefined,
thickness = 2, clockwise = false, rotationDeg = 0) {
this.center = center;
this.k = k;
this.revolutions = Math.max(0.25, revolutions);
this.strokeColor = strokeColor ? SWColor.copy(strokeColor) : undefined;
this.fillColor = fillColor ? SWColor.copy(fillColor) : undefined;
this.thickness = thickness;
this.clockwise = clockwise;
this.rotationDeg = rotationDeg;
this.rotation = 0; // accumulated via rotate(); cleared by reset()
// ── Originals for reset() ──────────────────────────────────────────────
this.originalK = k;
this.originalRevolutions = this.revolutions;
this.originalStrokeColor = strokeColor ? SWColor.copy(strokeColor) : undefined;
this.originalFillColor = fillColor ? SWColor.copy(fillColor) : undefined;
this.originalThickness = thickness;
this.originalClockwise = clockwise;
this.originalRotationDeg = rotationDeg;
this.showClosingLine = false; // default: no line from tip back to center
this.originalShowClosingLine = false;
}//end constructor
// ── Internal helpers ──────────────────────────────────────────────────────
/** @returns {number} Total effective rotation in degrees. */
_totalRotDeg() { return this.rotationDeg + this.rotation; }
/**
* Rotates a local math-space displacement (lx, ly) by totalRotation degrees (CCW+).
* @returns {{ x: number, y: number }} rotated displacement in math-space
*/
_rotateLocal(lx, ly) {
const rad = this._totalRotDeg() * Math.PI / 180;
const cosR = Math.cos(rad);
const sinR = Math.sin(rad);
return {
x: lx * cosR - ly * sinR,
y: lx * sinR + ly * cosR,
};
}
/**
* Builds an array of { x, y } positions in user (math) space.
* The final entry is the center point, closing the polygon back to the origin.
* @returns {{ x: number, y: number }[]}
*/
_buildUserPts() {
const cx = this.center.x;
const cy = this.center.y;
const maxTheta = 2 * Math.PI * this.revolutions;
const totalSteps = Math.max(3, Math.round(SWSpiral.SAMPLE_COUNT * this.revolutions));
const dir = this.clockwise ? -1 : 1;
const pts = [];
for (let i = 0; i <= totalSteps; i++) {
const theta = (i / totalSteps) * maxTheta;
const r = (this.k * theta) / Math.PI;
const lx = r * Math.cos(dir * theta);
const ly = r * Math.sin(dir * theta);
const rot = this._rotateLocal(lx, ly);
pts.push({ x: cx + rot.x, y: cy + rot.y });
}
// Close back to center so the polygon fills correctly
pts.push({ x: cx, y: cy });
return pts;
}
/**
* Builds screen-space { x, y } points using the given SWGrid.
* grid.userToScreen() handles the math-space ↔ screen y-flip.
*/
_buildScreenPtsGrid(grid) {
return this._buildUserPts().map(p => grid.userToScreen(p.x, p.y));
}
/**
* Builds screen-space { x, y } points for draw() (no grid).
* The math-space y-displacement is negated to produce correct screen-space y (down).
*/
_buildScreenPtsDirect() {
const cx = this.center.x;
const cy = this.center.y;
const maxTheta = 2 * Math.PI * this.revolutions;
const totalSteps = Math.max(3, Math.round(SWSpiral.SAMPLE_COUNT * this.revolutions));
const dir = this.clockwise ? -1 : 1;
const pts = [];
for (let i = 0; i <= totalSteps; i++) {
const theta = (i / totalSteps) * maxTheta;
const r = (this.k * theta) / Math.PI;
const lx = r * Math.cos(dir * theta);
const ly = r * Math.sin(dir * theta);
const rot = this._rotateLocal(lx, ly);
pts.push({ x: cx + rot.x, y: cy - rot.y }); // y-flip for screen
}
// Close back to center
pts.push({ x: cx, y: cy });
return pts;
}
/**
* Executes two-pass draw: fill (always closed) then stroke (closed only when showClosingLine).
* screenPts includes the explicit center closing point at the end.
*/
_drawShape(screenPts) {
// ── Fill pass: always close so the interior fills correctly ──────────────
if (this.fillColor && this.fillColor.col) {
fill(this.fillColor.col);
noStroke();
beginShape();
for (const sp of screenPts) vertex(sp.x, sp.y);
endShape(CLOSE);
}
// ── Stroke pass: close (tip→center) only when showClosingLine is true ────
if (this.strokeColor && this.strokeColor.col) {
noFill();
stroke(this.strokeColor.col);
strokeWeight(this.thickness);
if (this.showClosingLine) {
// Draw full polygon including the closing line tip→center
beginShape();
for (const sp of screenPts) vertex(sp.x, sp.y);
endShape(CLOSE);
} else {
// Draw only the spiral arc — omit the explicit closing center point
beginShape();
const arcPts = screenPts.slice(0, -1);
for (const sp of arcPts) vertex(sp.x, sp.y);
endShape();
}
}
noStroke();
noFill();
strokeWeight(1);
}
// ── Drawing ───────────────────────────────────────────────────────────────
/**
* Draws the spiral in raw screen (pixel) coordinates.
* Prefer drawOnGrid() for standard canvas use.
*/
draw() {
const screenPts = this._buildScreenPtsDirect();
this._drawShape(screenPts);
if (this.center && this.center.draw) {
this.center.draw(this.strokeColor);
}
}//end draw
/**
* Draws the spiral mapped through the given SWGrid's coordinate system.
* This is the standard drawing method to use in a p5.js draw() loop.
* @param {SWGrid} grid
*/
drawOnGrid(grid) {
const screenPts = this._buildScreenPtsGrid(grid);
this._drawShape(screenPts);
if (this.center && this.center.drawOnGrid) {
this.center.drawOnGrid(grid, this.strokeColor);
}
}//end drawOnGrid
// ── Animation ─────────────────────────────────────────────────────────────
/**
* Spins the spiral about its center by deltaAngle degrees (CCW+, CW−).
* Accumulates into this.rotation. Call once per frame in draw().
* @param {number} deltaAngle degrees per frame (typically speed × deltaT)
*/
rotate(deltaAngle) { this.rotation += deltaAngle; }
// ── Setters ───────────────────────────────────────────────────────────────
setK(k) { this.k = k; }
setRevolutions(r) { this.revolutions = Math.max(0.25, r); }
setClockwise(cw) { this.clockwise = cw; }
/** Sets the static base rotation in CCW degrees. Does not affect accumulated rotation. */
setRotation(deg) { this.rotationDeg = deg; }
setStrokeColor(sc) { this.strokeColor = sc ? SWColor.copy(sc) : undefined; }
setFillColor(fc) { this.fillColor = fc ? SWColor.copy(fc) : undefined; }
setStrokeWeight(w) { this.thickness = w; }
setShowClosingLine(val) { this.showClosingLine = val; }
/**
* Sets the fill alpha (0–100) and rebuilds the p5 color object.
* @param {number} alpha 0 = transparent, 100 = opaque
*/
setFillAlpha(alpha) {
if (this.fillColor) {
this.fillColor.a = Math.max(0, Math.min(100, alpha));
this.fillColor.col = color(
this.fillColor.h, this.fillColor.s,
this.fillColor.b, this.fillColor.a
);
}
}
/**
* Sets the stroke alpha (0–100) and rebuilds the p5 color object.
* @param {number} alpha 0 = transparent, 100 = opaque
*/
setStrokeAlpha(alpha) {
if (this.strokeColor) {
this.strokeColor.a = Math.max(0, Math.min(100, alpha));
this.strokeColor.col = color(
this.strokeColor.h, this.strokeColor.s,
this.strokeColor.b, this.strokeColor.a
);
}
}
// ── Reset & Utility ───────────────────────────────────────────────────────
/**
* Restores all animated/slider-driven properties to original constructor values.
* Clears accumulated spin rotation. Does NOT move the center position.
*/
reset() {
this.k = this.originalK;
this.revolutions = this.originalRevolutions;
this.clockwise = this.originalClockwise;
this.rotationDeg = this.originalRotationDeg;
this.rotation = 0;
this.thickness = this.originalThickness;
this.strokeColor = this.originalStrokeColor
? SWColor.copy(this.originalStrokeColor) : undefined;
this.fillColor = this.originalFillColor
? SWColor.copy(this.originalFillColor) : undefined;
this.showClosingLine = this.originalShowClosingLine;
}//end reset
/**
* Creates a deep copy of the given SWSpiral, preserving all current and original state.
* @param {SWSpiral} other
* @returns {SWSpiral}
*/
static copy(other) {
const c = new SWSpiral(
SWPoint.copy(other.center),
other.originalK,
other.originalRevolutions,
other.originalStrokeColor,
other.originalFillColor,
other.originalThickness,
other.originalClockwise,
other.originalRotationDeg
);
c.k = other.k;
c.revolutions = other.revolutions;
c.clockwise = other.clockwise;
c.rotationDeg = other.rotationDeg;
c.rotation = other.rotation;
c.showClosingLine = other.showClosingLine;
return c;
}//end copy
toString() {
const dir = this.clockwise ? 'CW' : 'CCW';
return `SWSpiral(center=${this.center}, k=${this.k.toFixed(2)}, ` +
`revolutions=${this.revolutions.toFixed(2)}, direction=${dir}, ` +
`rotationDeg=${this.rotationDeg.toFixed(1)}, rotation=${this.rotation.toFixed(1)})`;
}
}//end SWSpiral class