SWDiamond Class Reference

SketchWaveJS — Rhombus (Diamond) Shape Class

Overview

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.

Dependencies: p5.js, SWColor, SWPoint, SWGrid, SWSinusoid, SWKite
PropertyDefaultNotes
diagX9.0Half horizontal diagonal (user units)
diagY6.0Half vertical diagonal (user units) — aspect ratio 1.5
fillColorwhite (#ffffff)HSB: (0, 0, 100, 80)
strokeColordark grayHSB: (0, 0, 35, 100)
strokeWeight3Border thickness in pixels
rotation0Degrees CCW

Constructor

new SWDiamond(center, diagX, diagY, fillColor, options)
ParameterTypeRequiredDescription
centerSWPointYesCenter point of the diamond (user coords)
diagXnumberYesHalf-length of horizontal diagonal (user units)
diagYnumberYesHalf-length of vertical diagonal (user units)
fillColorSWColorYesInterior fill color
optionsObjectNoSee options table below

Options Object

KeyTypeDefaultDescription
strokeColorSWColorblackBorder color
strokeWeightnumber3Border thickness (px)
showCenterbooleanfalseDraw center SWPoint
rotationnumber0Initial 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
});

Own Properties

These are stored directly on the instance and can be read or set freely.

PropertyTypeDescription
centerSWPointCenter point (user coords). Can be repositioned by setting .x / .y.
diagXnumberCurrent half horizontal diagonal (may differ from originalDiagX during breathing).
diagYnumberCurrent half vertical diagonal.
fillColorSWColorInterior fill color.
strokeColorSWColorBorder color.
strokeWeightnumberBorder thickness in pixels.
showCenterbooleanWhether to draw the center SWPoint.
rotationnumberCurrent rotation in degrees CCW. Set this directly to rotate.
originalDiagXnumberStarting diagX — used by breathe() and reset().
originalDiagYnumberStarting diagY — used by breathe() and reset().
originalCenterSWPointStarting center position — used by reset().

Computed Properties (Getters)

Read-only. Calculated from current diagX and diagY.

getterarea → number

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.

getterperimeter → number

Perimeter = 4 × sideLength. All four sides of a rhombus are equal.

gettersideLength → number

Length of one side: √(diagX² + diagY²). Uses the Pythagorean theorem on the right triangle formed by the half-diagonals.

getteraspectRatio → number

diagX / diagY. Values > 1 mean a wider diamond; values < 1 mean a taller diamond.

getterdiagonalRatio → number

Alias for aspectRatio — exposed for semantic clarity when discussing diagonal proportions.

getterisSquare → boolean

True when diagX ≈ diagY within 1% tolerance. At that point the diamond is a square rotated 45°.

Animation Methods

methodbreathe(sinX, sinY, t)
diamond.breathe(sinX, sinY, t)

Scales diagX and/or diagY using sinusoid multipliers. Pass null to leave a diagonal unchanged (holds at original value).

ParamTypeDescription
sinXSWSinusoid | nullSinusoid for diagX scaling. null = hold at originalDiagX.
sinYSWSinusoid | nullSinusoid for diagY scaling. null = hold at originalDiagY.
tnumberCurrent 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);
methodreset()
diamond.reset()

Restores diagX, diagY, and center.x/y to their original (pre-animation) values.

Drawing Methods

methoddraw()
diamond.draw()

Draw the diamond in pixel space (no grid conversion). Use when you have pixel coordinates directly. Center .x/.y are treated as screen pixels.

methoddrawOnGrid(grid)
diamond.drawOnGrid(grid)

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);
methoddrawVerticesOnGrid(grid, dotSize, dotColor)
diamond.drawVerticesOnGrid(grid, dotSize = 8, dotColor)

Draw the four diamond tip vertices as circular dots on the grid.

ParamTypeDefaultDescription
gridSWGridGrid for coordinate conversion
dotSizenumber8Diameter of each dot (pixels)
dotColorSWColorblueFill color for the dots
methodsetShowCenter(show)
diamond.setShowCenter(show)

Toggle display of the center SWPoint. Equivalent to setting diamond.showCenter = show.

Utility Methods

methodgetVerticesUserCoords()
diamond.getVerticesUserCoords() → { top, right, bottom, left }

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 }
methodcenterContainsPoint(px, py, grid, tolerance)
diamond.centerContainsPoint(px, py, grid, tolerance = 12) → boolean

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;
    }
}
methodtoString()
diamond.toString() → string

Returns a human-readable summary: SWDiamond(center: (x, y), diagX: n, diagY: n, area: n, rotation: n°).

Inherited from SWKite

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.

NameTypeNotes
draw()methodDraws using p5 quad() with push/translate/rotate/pop
drawOnGrid(grid)methodMaps through SWGrid coordinate transform
drawVerticesOnGrid(grid)methodDraws four tip dots with rotation applied
breathe(sinX, sinY, t)methodScales diagTop/diagBottom; diagY getter keeps them equal
reset()methodRestores originalDiagX, originalDiagTop/Bottom, and center
getVerticesUserCoords()methodReturns { top, right, bottom, left } in user coords
setShowCenter(show)methodToggles center SWPoint visibility
centerContainsPoint(px,py,grid,tol)methodDrag hit detection within tolerance pixels of center
areagetter= diagX×(diagTop+diagBottom) = 2×diagX×diagY for rhombus ✓
perimetergetter= 2×(topSide+bottomSide) = 4×√(diagX²+diagY²) for rhombus ✓
isRhombusgetterAlways true for SWDiamond
topSideLength, bottomSideLengthgetterBoth equal sideLength in a rhombus

For full SWKite documentation, see the SWKite Reference.

Examples

Basic Usage

// 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);

Breathing (Uniform)

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);

Spinning Diamond

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);

Drag to Reposition

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; }

Tips & Notes

Rotation is visual-only. The push()/translate()/rotate()/pop() pattern means the underlying center, diagX, and diagY values are never affected by rotation. getVerticesUserCoords() returns un-rotated vertex positions.
White fill + dark border default. When the fill color is near-white (saturation < 5%, brightness > 90%), the demo automatically assigns a dark gray border (HSB 0, 0, 35) instead of using the auto-darker algorithm. This ensures visibility against a light canvas.
isSquare tolerance. isSquare uses a 1% relative tolerance. For pixel-level comparisons, check Math.abs(diagX - diagY) < epsilon directly.
No foci. Unlike SWEllipse, SWDiamond has no focal points. The closest analog is the four tip vertices, accessible via getVerticesUserCoords() and drawVerticesOnGrid().
Extends SWKite. SWDiamond is no longer standalone — it extends SWKite. Script load order matters: load swKite.js before swDiamond.js:
<script src="swKite.js"></script>
<script src="swDiamond.js"></script>
📚 Teachable Moment (2026-04-24). We originally built SWKite as a subclass of SWDiamond (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.

Source Code

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;