SketchWaveJS — Rhombus (Diamond) Shape Class
SWDiamond is a SketchWaveJS Extended class that represents a rhombus (diamond) shape by extending SWKite. It is defined by a center SWPoint, a horizontal half-diagonal (diagX), and a single vertical half-diagonal (diagY). Internally, SWDiamond calls super(center, diagX, diagY, diagY, fillColor, options) — passing diagY for both diagTop and diagBottom, ensuring all four sides remain equal.
A diamond is a rhombus — the special symmetric case of a kite where diagTop equals diagBottom. When diagX === diagY, it becomes a square rotated 45°.
All drawing, breathing, rotation, hit-detection, and vertex methods are inherited from SWKite.
| Property | Default | Notes |
|---|---|---|
diagX | 9.0 | Half horizontal diagonal (user units) |
diagY | 6.0 | Half vertical diagonal (user units) — aspect ratio 1.5 |
fillColor | white (#ffffff) | HSB: (0, 0, 100, 80) |
strokeColor | dark gray | HSB: (0, 0, 35, 100) |
strokeWeight | 3 | Border thickness in pixels |
rotation | 0 | Degrees CCW |
| Parameter | Type | Required | Description |
|---|---|---|---|
center | SWPoint | Yes | Center point of the diamond (user coords) |
diagX | number | Yes | Half-length of horizontal diagonal (user units) |
diagY | number | Yes | Half-length of vertical diagonal (user units) |
fillColor | SWColor | Yes | Interior fill color |
options | Object | No | See options table below |
| Key | Type | Default | Description |
|---|---|---|---|
strokeColor | SWColor | black | Border color |
strokeWeight | number | 3 | Border thickness (px) |
showCenter | boolean | false | Draw center SWPoint |
rotation | number | 0 | Initial rotation in degrees CCW |
const center = new SWPoint(0, 0);
const fillColor = new SWColor(0, 0, 100, 80, "diamondFill"); // white, 80% opacity
const borderColor = new SWColor(0, 0, 35, 100, "diamondStroke"); // dark gray
const d = new SWDiamond(center, 9, 6, fillColor, {
strokeColor: borderColor,
strokeWeight: 3,
showCenter: false,
rotation: 0
});
These are stored directly on the instance and can be read or set freely.
| Property | Type | Description |
|---|---|---|
center | SWPoint | Center point (user coords). Can be repositioned by setting .x / .y. |
diagX | number | Current half horizontal diagonal (may differ from originalDiagX during breathing). |
diagY | number | Current half vertical diagonal. |
fillColor | SWColor | Interior fill color. |
strokeColor | SWColor | Border color. |
strokeWeight | number | Border thickness in pixels. |
showCenter | boolean | Whether to draw the center SWPoint. |
rotation | number | Current rotation in degrees CCW. Set this directly to rotate. |
originalDiagX | number | Starting diagX — used by breathe() and reset(). |
originalDiagY | number | Starting diagY — used by breathe() and reset(). |
originalCenter | SWPoint | Starting center position — used by reset(). |
Read-only. Calculated from current diagX and diagY.
Area of the rhombus using the diagonal formula: area = 2 × diagX × diagY.
Derivation: full diagonals are 2·diagX and 2·diagY; area = (d₁ × d₂) / 2 = (2·diagX × 2·diagY) / 2 = 2·diagX·diagY.
Perimeter = 4 × sideLength. All four sides of a rhombus are equal.
Length of one side: √(diagX² + diagY²). Uses the Pythagorean theorem on the right triangle formed by the half-diagonals.
diagX / diagY. Values > 1 mean a wider diamond; values < 1 mean a taller diamond.
Alias for aspectRatio — exposed for semantic clarity when discussing diagonal proportions.
True when diagX ≈ diagY within 1% tolerance. At that point the diamond is a square rotated 45°.
Scales diagX and/or diagY using sinusoid multipliers. Pass null to leave a diagonal unchanged (holds at original value).
| Param | Type | Description |
|---|---|---|
sinX | SWSinusoid | null | Sinusoid for diagX scaling. null = hold at originalDiagX. |
sinY | SWSinusoid | null | Sinusoid for diagY scaling. null = hold at originalDiagY. |
t | number | Current time in seconds. |
// Uniform breathing (same sinusoid for both)
const sin = new SWSinusoid(0.4, Math.PI, 1.2, 0);
diamond.breathe(sin, sin, millis() / 1000);
// Horizontal only
diamond.breathe(sin, null, t);
// Reset (stops breathing, restores originals)
diamond.breathe(null, null, 0);
Restores diagX, diagY, and center.x/y to their original (pre-animation) values.
Draw the diamond in pixel space (no grid conversion). Use when you have pixel coordinates directly. Center .x/.y are treated as screen pixels.
Draw the diamond mapped through a SWGrid. Center coordinates are converted from user space to screen space. diagX is scaled by grid.xScale and diagY by grid.yScale.
diamond.drawOnGrid(grid);
Draw the four diamond tip vertices as circular dots on the grid.
| Param | Type | Default | Description |
|---|---|---|---|
grid | SWGrid | — | Grid for coordinate conversion |
dotSize | number | 8 | Diameter of each dot (pixels) |
dotColor | SWColor | blue | Fill color for the dots |
Toggle display of the center SWPoint. Equivalent to setting diamond.showCenter = show.
Returns the four diamond tips in user coordinates (un-rotated). Useful for geometric calculations.
const v = diamond.getVerticesUserCoords();
console.log(v.top); // { x: cx, y: cy + diagY }
console.log(v.right); // { x: cx + diagX, y: cy }
console.log(v.bottom); // { x: cx, y: cy - diagY }
console.log(v.left); // { x: cx - diagX, y: cy }
Returns true if the screen point (px, py) is within tolerance pixels of the diamond's center. Used for drag detection in p5's mousePressed().
function mousePressed() {
if (diamond.centerContainsPoint(mouseX, mouseY, grid, 15)) {
isDraggingCenter = true;
}
}
Returns a human-readable summary: SWDiamond(center: (x, y), diagX: n, diagY: n, area: n, rotation: n°).
SWDiamond extends SWKite. The following properties and methods come directly from SWKite with no override needed, because the rhombus math is already correct when diagTop === diagBottom.
| Name | Type | Notes |
|---|---|---|
draw() | method | Draws using p5 quad() with push/translate/rotate/pop |
drawOnGrid(grid) | method | Maps through SWGrid coordinate transform |
drawVerticesOnGrid(grid) | method | Draws four tip dots with rotation applied |
breathe(sinX, sinY, t) | method | Scales diagTop/diagBottom; diagY getter keeps them equal |
reset() | method | Restores originalDiagX, originalDiagTop/Bottom, and center |
getVerticesUserCoords() | method | Returns { top, right, bottom, left } in user coords |
setShowCenter(show) | method | Toggles center SWPoint visibility |
centerContainsPoint(px,py,grid,tol) | method | Drag hit detection within tolerance pixels of center |
area | getter | = diagX×(diagTop+diagBottom) = 2×diagX×diagY for rhombus ✓ |
perimeter | getter | = 2×(topSide+bottomSide) = 4×√(diagX²+diagY²) for rhombus ✓ |
isRhombus | getter | Always true for SWDiamond |
topSideLength, bottomSideLength | getter | Both equal sideLength in a rhombus |
For full SWKite documentation, see the SWKite Reference.
// In p5.js setup():
colorMode(HSB, 360, 100, 100, 100);
const grid = new SWGrid({ UL: new SWPoint(-12, 10), LR: new SWPoint(12, -10) });
const center = new SWPoint(0, 0);
const fill = new SWColor(0, 0, 100, 80, "white");
const border = new SWColor(0, 0, 35, 100, "darkGray");
const diamond = new SWDiamond(center, 9, 6, fill, {
strokeColor: border,
strokeWeight: 3
});
// In p5.js draw():
grid.draw();
diamond.drawOnGrid(grid);
const sinU = new SWSinusoid(0.4, Math.PI, 1.2, -Math.PI / 6);
// In draw():
const t = millis() / 1000;
diamond.breathe(sinU, sinU, t);
diamond.drawOnGrid(grid);
let totalDeg = 0;
let lastTime = 0;
// In draw():
const now = millis();
const delta = now - lastTime;
lastTime = now;
totalDeg += 45 * delta / 1000; // 45°/sec
diamond.rotation = totalDeg;
diamond.drawOnGrid(grid);
let isDragging = false;
function mousePressed() {
if (diamond.centerContainsPoint(mouseX, mouseY, grid, 15)) isDragging = true;
}
function mouseDragged() {
if (isDragging) {
const u = grid.screenToUser(mouseX, mouseY);
diamond.center.x = diamond.originalCenter.x = u.x;
diamond.center.y = diamond.originalCenter.y = u.y;
}
}
function mouseReleased() { isDragging = false; }
push()/translate()/rotate()/pop() pattern means the underlying center, diagX, and diagY values are never affected by rotation. getVerticesUserCoords() returns un-rotated vertex positions.
HSB 0, 0, 35) instead of using the auto-darker algorithm. This ensures visibility against a light canvas.
isSquare uses a 1% relative tolerance. For pixel-level comparisons, check Math.abs(diagX - diagY) < epsilon directly.
getVerticesUserCoords() and drawVerticesOnGrid().
swKite.js before swDiamond.js:
<script src="swKite.js"></script>
<script src="swDiamond.js"></script>
class SWKite extends SWDiamond). After research, we confirmed the correct mathematical relationship: a rhombus is a special case of a kite, so SWDiamond should extend SWKite — not the reverse. We refactored accordingly. This is also an illustration of the Liskov Substitution Principle: wherever an SWKite is expected, an SWDiamond (rhombus) can always substitute, because every rhombus is a kite.
Complete source for swDiamond.js:
/*
File: swDiamond.js
Date: 2026-04-23
Author: klp + GitHub Copilot
Workspace: SketchWaveTNT2026-04-21-Stg8
Purpose: SWDiamond class for SketchWaveJS
Comment(s):
SWDiamond: A rhombus (diamond) shape extending SWKite.
A rhombus is a special case of a kite where diagTop === diagBottom === diagY,
making all four sides equal. Passes diagY as both diagTop and diagBottom to SWKite.
All four sides are equal; the shape is a square rotated 45° when diagX === diagY.
Breathing and rotation are inherited from SWKite.
Defaults: white fill, dark gray stroke, aspect ratio 1.5 (diagX=9, diagY=6).
TEACHABLE MOMENT (2026-04-24): Refactored from standalone to extend SWKite because
geometrically a rhombus is a special case of a kite, not the reverse.
Dependencies: p5.js, SWColor, SWPoint, SWGrid, SWSinusoid, SWKite
*/
console.log("[swDiamond.js] SWDiamond class loaded.");
class SWDiamond extends SWKite {
/**
* @param {SWPoint} center - Center point (SWPoint instance)
* @param {number} diagX - Half-length of horizontal diagonal (user units)
* @param {number} diagY - Half-length of vertical diagonal (user units)
* Sets both diagTop and diagBottom for rhombus constraint.
* @param {SWColor} fillColor - Fill color (SWColor instance)
* @param {Object} [options]
* strokeColor : SWColor — border color (optional)
* strokeWeight : number — border thickness (default 3)
* showCenter : boolean — show center SWPoint (default false)
* rotation : number — initial rotation in degrees CCW (default 0)
*/
constructor(center, diagX, diagY, fillColor, options = {}) {
// A rhombus is a kite where diagTop === diagBottom === diagY
super(center, diagX, diagY, diagY, fillColor, options);
}//end constructor
// ── diagY compatibility getter / setter ───────────────────────────────
get diagY() { return this.diagTop; }
set diagY(v) { this.diagTop = v; this.diagBottom = v; }
get originalDiagY() { return this.originalDiagTop; }
set originalDiagY(v) { this.originalDiagTop = v; this.originalDiagBottom = v; }
// ── Rhombus-specific computed properties ──────────────────────────────
/** Side length — all four sides equal; alias for topSideLength */
get sideLength() { return this.topSideLength; }//end sideLength
/** Aspect ratio: diagX / diagY */
get aspectRatio() {
return this.diagY !== 0 ? this.diagX / this.diagY : 0;
}//end aspectRatio
/** Diagonal ratio: diagX / diagY (alias for aspectRatio) */
get diagonalRatio() { return this.aspectRatio; }//end diagonalRatio
/** isSquare — true when diagX ≈ diagY (within 1% tolerance) */
get isSquare() {
if (this.diagY === 0) return false;
return Math.abs(this.diagX - this.diagY) / Math.max(this.diagX, this.diagY) < 0.01;
}//end isSquare
// ── Inherited from SWKite ─────────────────────────────────────────────
// draw(), drawOnGrid(), drawVerticesOnGrid()
// breathe(), reset(), getVerticesUserCoords()
// setShowCenter(), centerContainsPoint()
// area, perimeter, isRhombus, topSideLength, bottomSideLength
toString() {
return `SWDiamond(center: (${this.center.x.toFixed(2)}, ${this.center.y.toFixed(2)}), ` +
`diagX: ${this.diagX.toFixed(2)}, diagY: ${this.diagY.toFixed(2)}, ` +
`area: ${this.area.toFixed(2)}, rotation: ${this.rotation.toFixed(1)}°)`;
}//end toString
}//end class SWDiamond
// export default SWDiamond;