Quick Reference
SWTwoTonedSquare is a SketchWave composite class for a square visually divided into two differently-colored regions. The integer parameter n (1–4) selects the geometric split pattern. All drawing uses raw p5.js primitives (rect(), beginShape/vertex/endShape) inside a single push/translate/rotate/pop block, so the entire composite always spins and breathes as a unit. The class supports spin, size breathing, and hue-cycling animations.
- Extends: Nothing (standalone composite class)
- Dependencies: SWPoint, SWColor, SWSinusoid, SWGrid, p5.js
- Drawing model: Raw p5.js primitives in local (translated + rotated) coordinate space — no SWRectangle or SWTriangle sub-objects
- Key Features: 4 split patterns, independent Color 1 / Color 2 fills, spin, breathe (size), hue cycling, drag-to-reposition center
- Common Uses: Logo halftones, geometric badges, animated tile designs, pattern exploration
Overview
Drawing Model
SWTwoTonedSquare draws everything inside a single coordinate-transform block:
push()— saves the current transform statetranslate(cx_s, cy_s)— moves the origin to the square's screen-pixel centerrotate(-radians(startAngle + rotation))— applies the total orientation (negated for CCW convention)- Draws the base square (Color 1) with
rectMode(CENTER); rect(0, 0, side, side) - Draws the secondary shape(s) (Color 2) using
rect()orbeginShape/vertex/endShape pop()— restores the transform state
Because all shapes are defined in local coordinates (origin = center, rotation already applied), raw p5.js primitives are simpler and more reliable than constructing sub-objects that would need coordinate re-mapping every time size or rotation changes. There are also no independent outline arcs requiring separate color management — a key reason why SWSector/SWArc sub-objects were appropriate for SWTwoTonedDisk but are not needed here.
Pattern Parameter n
The integer n (1–4) selects how the square is geometrically divided. Color 1 always fills the full base square first; Color 2 is painted on top as the secondary shape(s).
| n | Pattern | Color 2 fills... |
|---|---|---|
| 1 | Horizontal split | The upper half — a rectangle spanning the full width, height = ½ side |
| 2 | Diagonal split | The upper-right right-isosceles triangle; hypotenuse = top-left → bottom-right diagonal; right angle at top-right |
| 3 | Opposite quadrant squares | Two smaller squares (side = ½ main) at the top-left and bottom-right quadrants; inner corners meet at the center |
| 4 | Opposite triangles | Two triangles: one with base on the top side, one with base on the bottom side; both apices meet at the center |
All four patterns share identical spin, breathe, and hue-cycling behavior. You can switch n live — even while the shape is spinning — and the new pattern takes effect immediately on the next frame.
Color Model
The two colors are independent:
- Color 1 (
color1) — fills the entire base square. Set viasetColor1()/setColor1Alpha(). - Color 2 (
color2) — fills the overlaid secondary shape(s). Set viasetColor2()/setColor2Alpha().
Both colors use noStroke() — there are no outline strokes. Alpha values range from 0 (transparent) to 100 (opaque) using p5's HSB 100-scale. Reducing Color 2's alpha lets Color 1 show through in the overlapping region.
Angle and Orientation Convention
SWTwoTonedSquare uses the standard SketchWaveJS math-space angle convention: CCW positive from the +x axis, y increases upward. The effective orientation when drawing is startAngle + rotation, negated to convert to p5's y-down screen space.
// Default orientation: bottom of the square faces down (+y screen).
let sq = new SWTwoTonedSquare(new SWPoint(0, 0), 5);
// startAngle=45: square is tilted 45° CCW.
let sq2 = new SWTwoTonedSquare(new SWPoint(0, 0), 5, 1, color1, color2, 45);
startAngle: static orientation set once at construction or viasetStartAngle()rotation: accumulated spin added by eachrotate(delta)call- Effective orientation =
startAngle + rotation
Typical Workflow
- Create an SWGrid and an SWTwoTonedSquare with center, size, n, Color 1, Color 2, and optional startAngle
- Call
drawOnGrid(grid)each frame insidedraw() - For animations each frame:
- Call
rotate(delta)before draw (takes effect immediately) - Call
breathe(sinusoid, t)to oscillate the side length - For hue cycling, mutate Color 1's HSB values, rebuild its p5 color, and push via
setColor1()
- Call
- Use the elapsed-time pattern for pauseable animations (see Animation Guide)
- Call
reset()to restore all geometry and colors to construction-time originals
Constructor
new SWTwoTonedSquare(center, size, n, color1, color2, startAngle)Creates a new SWTwoTonedSquare. Stores deep copies of colors for reset(). All drawing happens in a single local coordinate frame — no sub-objects are created.
| Parameter | Type | Default | Description |
|---|---|---|---|
center |
SWPoint | required | Center of the square. Can include a display color and pixel size for the center marker. |
size |
number | required | Side length in user units (> 0). Clamped to 0.01 internally. |
n |
number | 1 | Pattern selector: 1, 2, 3, or 4. Out-of-range values are clamped and rounded. |
color1 |
SWColor | undefined | Fill color for the base square. |
color2 |
SWColor | undefined | Fill color for the secondary shape(s) drawn on top. |
startAngle |
number | 0 | Static orientation in degrees CCW from the +x axis. |
// Minimal: just center and size (no colors — nothing visible)
let sq1 = new SWTwoTonedSquare(new SWPoint(0, 0), 5);
// With colors, default n=1 (horizontal split)
let c1 = SWColor.fromHex('#0066CC', 'c1').setAlphaTo(90);
let c2 = SWColor.fromHex('#CC0000', 'c2').setAlphaTo(90);
let sq2 = new SWTwoTonedSquare(new SWPoint(0, 0), 5, 1, c1, c2);
// Diagonal split (n=2) rotated 45°
let sq3 = new SWTwoTonedSquare(new SWPoint(0, 0), 5, 2, c1, c2, 45);
// Styled center marker (dark gray, 8px)
const centerPt = new SWPoint(0, 0, undefined, 8, swDarkGray);
let sq4 = new SWTwoTonedSquare(centerPt, 5, 1, c1, c2, 0);
Properties
center — SWPoint
The center of the square in user coordinates. Drag in the demo to reposition live. Used directly in _drawCore() via grid.userToScreen().
size — number
Current side length in user units. Oscillates when breathing. Set via setSize(s) or breathe(sinusoid, t). Clamped to 0.01.
n — number
Current pattern selector (integer 1–4). Change at any time via setN(n). Takes effect on the next frame.
startAngle — number
Static orientation in degrees CCW from the +x axis. Set once at construction or via setStartAngle(). Does not change when rotate() is called — that accumulates in rotation separately.
rotation — number
Accumulated rotation in degrees (CCW positive). Added to startAngle when rendering. Incremented each frame by rotate(delta). Reset to 0 by reset().
Color 1 Properties
color1 — SWColor
Fill color for the base square. Set via setColor1(swColor). Opacity set via setColor1Alpha(alpha).
Color 2 Properties
color2 — SWColor
Fill color for the secondary shape(s). Set via setColor2(swColor). Opacity set via setColor2Alpha(alpha).
shouldShowCenter — boolean
When true (default), the center SWPoint is rendered as a visible marker. Toggle with setShowCenter(show).
Original-State Properties (used by reset())
Set from constructor arguments; used to restore the square to its initial state. Do not modify directly.
originalSizeoriginalStartAngleoriginalColor1(deep copy of color1 at construction)originalColor2(deep copy of color2 at construction)
Methods
Internal Drawing (private)
_drawCore(cx_s, cy_s, halfS)
Internal draw in local pixel coordinates. Called by both draw() and drawOnGrid() after center and halfS are resolved. Wraps all p5 primitives in a single push / translate / rotate / pop block. Do not call directly.
Drawing
draw()
Draws in screen (pixel) coordinates, using the center's raw x/y as pixel positions and size/2 as the half-side in pixels. Use drawOnGrid() for user-coordinate rendering in demos that use an SWGrid.
drawOnGrid(grid)
Draws mapped through the given SWGrid. Center position is converted via grid.userToScreen() and the half-side is computed as scale × (size/2), where scale is the average of the grid's x and y scales. This is the standard call for all SketchWaveJS demos.
// Typical in draw():
grid.draw();
sq1.drawOnGrid(grid);
Rotation Animation
rotate(deltaAngle)
Increments rotation by deltaAngle degrees (CCW positive, CW negative). Call before draw each frame to spin the square about its center. All pattern shapes rotate together with the base square.
| Parameter | Type | Description |
|---|---|---|
deltaAngle | number | Degrees to add to rotation; negative = clockwise |
// In draw(): spin at 45 deg/sec
sq1.rotate(45 * deltaT); // deltaT = seconds since last frame
sq1.drawOnGrid(grid);
Breathing Animation
breathe(sinusoid, t)
Sets size to the current value of the sinusoid, making the square grow and shrink. All pattern shapes scale uniformly since they are computed from halfS = size/2 each frame. Minimum clamped to 0.01.
| Parameter | Type | Description |
|---|---|---|
sinusoid | SWSinusoid | Configured with desired period and min/max size values |
t | number | Elapsed animated time in seconds |
// Setup (once in setup()):
breatheSinusoid = SWSinusoid.copy(UNIT_SINUSOID);
breatheSinusoid.setPeriod(4);
breatheSinusoid.adjustWaveUsingExtrema(2, 8); // size oscillates 2–8 units
// In draw():
if (shouldBreathe) {
const bt = breatheElapsed + (millis()/1000 - breatheStartTime);
sq1.breathe(breatheSinusoid, bt);
}
Pattern Setter
setN(n)
Changes the pattern selector. Valid values: 1, 2, 3, 4. Out-of-range values are clamped; non-integers are rounded. Takes effect immediately on the next draw call — safe to call mid-animation.
Size Setter
setSize(s)
Sets the side length directly. Clamped to 0.01. Useful when the UI controls size independently of breathing.
Fill Color Methods
setColor1(swColor)
Sets the base square's fill color. Deep-copies the provided SWColor.
setColor2(swColor)
Sets the secondary shape(s) fill color. Deep-copies the provided SWColor.
setColor1Alpha(alpha)
Sets Color 1's alpha, clamped to [0, 100]. Rebuilds the p5 color object without changing hue, saturation, or brightness.
setColor2Alpha(alpha)
Sets Color 2's alpha, clamped to [0, 100]. Rebuilds the p5 color object without changing hue, saturation, or brightness.
Other Setters
setStartAngle(degrees)
Sets the static orientation (CCW from +x axis) without affecting accumulated rotation.
setShowCenter(show)
Shows or hides the center marker point. Pass true (default) to show, false to hide.
Reset
reset()
Restores all animated/slider-driven values — rotation, startAngle, size, color1, color2 — to the originals stored at construction. Does not move the center position or reset n.
Static Methods
static copy(other)
Returns a deep copy of the given SWTwoTonedSquare instance. Deep-copies the center point and both colors. The copy has the same current size, rotation, and n as the original. Throws if the argument is not an SWTwoTonedSquare.
let sqCopy = SWTwoTonedSquare.copy(sq1);
Utility Methods
toString()
Returns a human-readable description of the square's current state.
console.log(sq1.toString());
// → SWTwoTonedSquare(center: SWPoint(0.00, 0.00), size: 5.00, n: 1,
// startAngle: 0°, rotation: 0.0°)
Animation Guide
Elapsed-Time Pattern (Pause/Resume)
All continuous animations use the same elapsed-time pattern: accumulate elapsed seconds while paused, so the wave doesn't jump when you resume.
// Globals:
let shouldBreathe = false;
let breatheStartTime = 0, breatheElapsed = 0;
// Toggle:
function toggleBreathe() {
const t = millis() / 1000;
if (!shouldBreathe) {
breatheStartTime = t;
shouldBreathe = true;
} else {
breatheElapsed += (t - breatheStartTime);
shouldBreathe = false;
}
}
// In draw():
if (shouldBreathe) {
const bt = breatheElapsed + (millis()/1000 - breatheStartTime);
sq1.breathe(breatheSinusoid, bt);
}
Spin Animation
// Globals:
let shouldSpin = false;
let spinSpeed = 45; // degrees per second (CCW)
// In draw():
const t = millis() / 1000;
const deltaT = (prevT > 0) ? (t - prevT) : 0;
prevT = t;
if (shouldSpin) sq1.rotate(spinSpeed * deltaT);
sq1.drawOnGrid(grid);
Hue Cycling (Color 1 and Color 2)
Hue cycling is implemented externally: compute the hue from a sinusoid, rebuild the SWColor's p5 color, then push it to the square. Color 2 gets Color 1's hue plus a fixed offset — the default offset of 180° gives complementary colors.
// In draw() — cycle color1 + color2 hues:
if (shouldCycleHue) {
const ht = hueCycleElapsed + (millis()/1000 - hueCycleStartTime);
// Color 1 hue from sinusoid
color1.h = hueSinusoid.getValue(ht);
color1.col = color(color1.h, color1.s, color1.b, color1.a);
sq1.setColor1(color1);
// Color 2 hue = Color 1 hue + offset (default 180° = complementary)
color2.h = (color1.h + hueOffset) % 360;
color2.col = color(color2.h, color2.s, color2.b, color2.a);
sq1.setColor2(color2);
}
sq1.drawOnGrid(grid);
Running All Animations Simultaneously
// In draw() — spin + hue BEFORE draw; breathe can be before or after:
const t = millis() / 1000;
const deltaT = (prevT > 0) ? (t - prevT) : 0;
prevT = t;
background(bgColor);
if (shouldShowGrid) grid.draw();
// 1. Spin
if (shouldSpin) sq1.rotate(spinSpeed * deltaT);
// 2. Hue cycle
if (shouldCycleHue) {
const ht = hueCycleElapsed + (t - hueCycleStartTime);
color1.h = hueSinusoid.getValue(ht);
color1.col = color(color1.h, color1.s, color1.b, color1.a);
sq1.setColor1(color1);
color2.h = (color1.h + hueOffset) % 360;
color2.col = color(color2.h, color2.s, color2.b, color2.a);
sq1.setColor2(color2);
}
// 3. Breathe
if (shouldBreathe) {
const bt = breatheElapsed + (t - breatheStartTime);
sq1.breathe(breatheSinusoid, bt);
}
// 4. Draw
sq1.drawOnGrid(grid);
Usage Examples
Minimal Square
let sq, grid;
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 c1 = SWColor.fromHex('#0066CC', 'c1').setAlphaTo(90);
const c2 = SWColor.fromHex('#CC0000', 'c2').setAlphaTo(90);
sq = new SWTwoTonedSquare(new SWPoint(0, 0), 5, 1, c1, c2);
}
function draw() {
background(240, 5, 93);
grid.draw();
sq.drawOnGrid(grid);
}
Switching Patterns Live
// Switch to the diagonal split while the shape may already be spinning:
sq.setN(2); // takes effect next frame — safe at any time
Spinning Square (Time-Based)
let prevT = 0;
const SPIN_SPEED = 60; // degrees per second
function draw() {
const t = millis() / 1000;
const deltaT = (prevT > 0) ? (t - prevT) : 0;
prevT = t;
background(240, 5, 93);
grid.draw();
sq.rotate(SPIN_SPEED * deltaT);
sq.drawOnGrid(grid);
}
Breathing Size
let breatheSin;
let breatheStart = 0, breatheElapsed = 0, shouldBreathe = false;
function setup() {
// ...square construction...
breatheSin = SWSinusoid.copy(UNIT_SINUSOID);
breatheSin.setPeriod(4);
breatheSin.adjustWaveUsingExtrema(2, 8);
}
function draw() {
const t = millis() / 1000;
// ...
if (shouldBreathe) {
sq.breathe(breatheSin, breatheElapsed + (t - breatheStart));
}
sq.drawOnGrid(grid);
}
function keyPressed() {
if (key === 'b') {
const t = millis() / 1000;
if (!shouldBreathe) { breatheStart = t; shouldBreathe = true; }
else { breatheElapsed += t - breatheStart; shouldBreathe = false; }
}
if (key === 'r') { sq.reset(); breatheElapsed = 0; }
}
Loading Dependencies
<!-- p5.js -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.6.0/lib/p5.js"></script>
<!-- SketchWaveJS classes (order matters) -->
<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/swTwoTonedSquare.js"></script>
<!-- Your sketch -->
<script src="sketches/yourSketch.js"></script>
Note: swSector.js, swArc.js, swRectangle.js,
and swTriangle.js are not needed — SWTwoTonedSquare
uses only raw p5.js drawing primitives internally.
Source Code
The full swTwoTonedSquare.js source code is shown below for reference.
/*
File: swTwoTonedSquare.js
Date: 2026-03-29
Author: klp
App: SketchWaveTNT2026-03-19-Stg7
Purpose: SWTwoTonedSquare class for SketchWaveJS
Comment(s):
SWTwoTonedSquare is a composite shape: a square visually divided into two
differently-colored regions. The division type is selected by the integer
parameter n (1–4):
n = 1 Horizontal split — base square (color1) + a rectangle covering
the upper half (color2).
n = 2 Diagonal split — base square (color1) + a right-isosceles triangle
(color2) whose hypotenuse runs along the top-left → bottom-right
diagonal. The right angle sits at the top-right corner.
n = 3 Opposite quadrant squares — base square (color1) + two smaller
squares (color2, side = ½ main side) placed in the top-left and
bottom-right quadrants. Their inner corners touch the center.
n = 4 Opposite triangles — base square (color1) + two triangles (color2)
whose bases are the top and bottom sides of the square and whose
apex meets at the center.
Animations (same conventions as the rest of the SketchWaveJS family):
rotate(deltaAngle) — spin the entire composite about its center (CCW +, deg)
breathe(sinusoid, t) — oscillate the overall side length using an SWSinusoid
Color model:
color1 — fills the base square
color2 — fills the overlaid secondary shape(s)
Drawing model:
All shapes are drawn with raw p5.js primitives (rect, beginShape/vertex/endShape)
inside a single push/translate/rotate/pop block. This is the right choice because
all coordinates are defined in local space (origin = center, rotation applied), so
raw primitives need no extra coordinate mapping. No SWRectangle or SWTriangle
sub-objects are used — adding them would require re-deriving sub-shape positions
whenever size or rotation changes, with no benefit for this geometry.
Angle convention:
User / math space: CCW positive, y increases upward.
p5 / screen space: CW positive, y increases downward.
→ rotate() negates the angle when calling p5's rotate() in _drawCore().
Dependencies: p5.js, SWColor, SWPoint, SWGrid
*/
console.log("[swTwoTonedSquare.js] SWTwoTonedSquare class loaded.");
class SWTwoTonedSquare {
/**
* @param {SWPoint} center - Center of the square (user coordinates)
* @param {number} size - Side length in user units (> 0)
* @param {number} [n=1] - Pattern selector: 1, 2, 3, or 4
* @param {SWColor} [color1] - Base (background) fill color
* @param {SWColor} [color2] - Secondary shape fill color
* @param {number} [startAngle=0] - Static orientation in degrees (CCW from +x)
*/
constructor(center, size, n = 1,
color1 = undefined, color2 = undefined,
startAngle = 0) {
this.center = center;
this.size = Math.max(0.01, size);
this.n = Math.max(1, Math.min(4, Math.round(n)));
this.startAngle = startAngle;
this.rotation = 0; // accumulated spin (degrees)
this.color1 = color1 ? SWColor.copy(color1) : undefined;
this.color2 = color2 ? SWColor.copy(color2) : undefined;
// Originals stored at construction time for reset()
this.originalSize = this.size;
this.originalStartAngle = startAngle;
this.originalColor1 = color1 ? SWColor.copy(color1) : undefined;
this.originalColor2 = color2 ? SWColor.copy(color2) : undefined;
this.shouldShowCenter = true;
}//end constructor
// ─── Drawing ───────────────────────────────────────────────────────────────
/**
* Internal draw in local pixel coordinates.
* push / translate(cx_s, cy_s) / rotate wraps all shape calls so the
* entire composite spins as a unit.
*
* @param {number} cx_s - Center x in screen pixels
* @param {number} cy_s - Center y in screen pixels
* @param {number} halfS - Half the side length in screen pixels
*/
_drawCore(cx_s, cy_s, halfS) {
const totalAngle = this.startAngle + this.rotation;
push();
translate(cx_s, cy_s);
// Negate: CCW user convention → visually CCW in p5's y-down space
rotate(-radians(totalAngle));
noStroke();
// ── Base square ──────────────────────────────────────────────────────
if (this.color1 && this.color1.col) {
fill(this.color1.col);
} else {
noFill();
}
rectMode(CENTER);
rect(0, 0, halfS * 2, halfS * 2);
// ── Secondary shape(s) ───────────────────────────────────────────────
if (this.color2 && this.color2.col) {
fill(this.color2.col);
} else {
noFill();
}
if (this.n === 1) {
// ── n=1: upper rectangle (top half of the square) ────────────────
// p5 y-down: "upper" = negative y. Top-half center is at y = -halfS/2.
rectMode(CENTER);
rect(0, -halfS / 2, halfS * 2, halfS);
} else if (this.n === 2) {
// ── n=2: right-isosceles triangle, hypotenuse = main diagonal ────
// Diagonal runs top-left (-halfS,-halfS) to bottom-right (halfS,halfS).
// Right angle sits at top-right (halfS, -halfS).
beginShape();
vertex(-halfS, -halfS); // top-left (45° angle)
vertex( halfS, -halfS); // top-right (right angle vertex)
vertex( halfS, halfS); // bottom-right (45° angle)
endShape(CLOSE);
} else if (this.n === 3) {
// ── n=3: two quadrant squares in opposite corners ────────────────
// Each sub-square has side = halfS; its inner corner touches (0,0).
rectMode(CENTER);
rect(-halfS / 2, -halfS / 2, halfS, halfS); // top-left quadrant
rect( halfS / 2, halfS / 2, halfS, halfS); // bottom-right quadrant
} else if (this.n === 4) {
// ── n=4: two triangles on top and bottom sides ───────────────────
// Each triangle has its base on one full side; apex is the center (0,0).
beginShape();
vertex(-halfS, -halfS); // top-left
vertex( halfS, -halfS); // top-right
vertex(0, 0); // center (apex)
endShape(CLOSE);
beginShape();
vertex(-halfS, halfS); // bottom-left
vertex( halfS, halfS); // bottom-right
vertex(0, 0); // center (apex)
endShape(CLOSE);
}
pop();
}//end _drawCore
/**
* Draws in screen (pixel) coordinates.
* center.x / center.y are treated as screen pixels; size as screen pixels.
*/
draw() {
const halfS = this.size / 2;
this._drawCore(this.center.x, this.center.y, halfS);
if (this.shouldShowCenter && this.center && this.center.draw) {
this.center.draw();
}
}//end draw
/**
* Draws mapped through an SWGrid (user → screen).
* @param {SWGrid} grid
*/
drawOnGrid(grid) {
const { x: cx_s, y: cy_s } = grid.userToScreen(this.center.x, this.center.y);
const scale = (grid.xScale + grid.yScale) / 2;
const halfS = scale * (this.size / 2);
this._drawCore(cx_s, cy_s, halfS);
if (this.shouldShowCenter && this.center && this.center.drawOnGrid) {
this.center.drawOnGrid(grid);
}
}//end drawOnGrid
// ─── Spin animation ─────────────────────────────────────────────────────────
/**
* Increments the accumulated rotation by deltaAngle degrees (CCW positive).
* Call once per frame before drawOnGrid() to spin the shape.
* @param {number} deltaAngle
*/
rotate(deltaAngle) {
this.rotation += deltaAngle;
}//end rotate
// ─── Breathe animation ──────────────────────────────────────────────────────
/**
* Modulates the overall side length using a sinusoid.
* @param {SWSinusoid} sinusoid
* @param {number} t - Elapsed time in seconds
*/
breathe(sinusoid, t) {
this.size = Math.max(0.01, sinusoid.getValue(t));
}//end breathe
// ─── Setters ────────────────────────────────────────────────────────────────
setSize(s) {
this.size = Math.max(0.01, s);
}//end setSize
setN(n) {
this.n = Math.max(1, Math.min(4, Math.round(n)));
}//end setN
setColor1(swColor) {
this.color1 = swColor ? SWColor.copy(swColor) : undefined;
}//end setColor1
setColor2(swColor) {
this.color2 = swColor ? SWColor.copy(swColor) : undefined;
}//end setColor2
/**
* Updates color1 alpha without changing hue/sat/bri.
* @param {number} alpha - 0–100
*/
setColor1Alpha(alpha) {
if (!this.color1) return;
this.color1.a = Math.max(0, Math.min(100, alpha));
this.color1.col = color(this.color1.h, this.color1.s, this.color1.b, this.color1.a);
}//end setColor1Alpha
/**
* Updates color2 alpha without changing hue/sat/bri.
* @param {number} alpha - 0–100
*/
setColor2Alpha(alpha) {
if (!this.color2) return;
this.color2.a = Math.max(0, Math.min(100, alpha));
this.color2.col = color(this.color2.h, this.color2.s, this.color2.b, this.color2.a);
}//end setColor2Alpha
setStartAngle(degrees) {
this.startAngle = degrees;
}//end setStartAngle
setShowCenter(show = true) {
this.shouldShowCenter = show;
}//end setShowCenter
// ─── Reset ──────────────────────────────────────────────────────────────────
/**
* Resets all animated / slider-driven values to construction-time originals.
* Does NOT move the center position.
*/
reset() {
this.rotation = 0;
this.startAngle = this.originalStartAngle;
this.size = this.originalSize;
if (this.originalColor1) this.color1 = SWColor.copy(this.originalColor1);
if (this.originalColor2) this.color2 = SWColor.copy(this.originalColor2);
}//end reset
// ─── Static copy ────────────────────────────────────────────────────────────
/**
* Returns a deep copy of the given SWTwoTonedSquare instance.
* @param {SWTwoTonedSquare} other
* @returns {SWTwoTonedSquare}
*/
static copy(other) {
if (!(other instanceof SWTwoTonedSquare)) {
throw new Error('SWTwoTonedSquare.copy: argument must be an SWTwoTonedSquare instance');
}
const sq = new SWTwoTonedSquare(
SWPoint.copy(other.center),
other.originalSize,
other.n,
other.color1,
other.color2,
other.originalStartAngle
);
sq.size = other.size;
sq.startAngle = other.startAngle;
sq.rotation = other.rotation;
sq.shouldShowCenter = other.shouldShowCenter;
return sq;
}//end copy
// ─── toString ───────────────────────────────────────────────────────────────
toString() {
return `SWTwoTonedSquare(center: ${this.center.toString()}, ` +
`size: ${this.size.toFixed(2)}, n: ${this.n}, ` +
`startAngle: ${this.startAngle}°, rotation: ${this.rotation.toFixed(1)}°)`;
}//end toString
}//end class SWTwoTonedSquare
// export default SWTwoTonedSquare;