♣ SWClub Reference

Three-Circle Club Symbol — Geometric Class — SketchWaveJS

♣ Club Mathematics

Three-Circle Construction

SWClub draws the playing-card club symbol (♣) by compositing three overlapping circles (the “lobes”) arranged at the vertices of an equilateral triangle, plus a filled trapezoid stem at the bottom. This is a purely geometric construction — no parametric curves are used.

Lobe Center Positions (Local Space)

Top lobe: (lx, ly) = (0, +spread)
Bottom-left lobe: (lx, ly) = (−spread · √3/2, −spread/2)
Bottom-right lobe: (lx, ly) = (+spread · √3/2, −spread/2)

The three centers form an equilateral triangle. Each vertex is at distance spread from the origin, and they are separated by 120° in angle.

Overlap Geometry

Two adjacent lobe centers are separated by distance: d = spread × √3. The circles overlap when d < 2 × radius, i.e., when:

spread × √3 < 2 × radius
spread < radius × 2 / √3 ≈ radius × 1.155

With defaults (radius=2.5, spread=2.0): adjacent centers are ≈ 3.46 apart, while the sum of radii = 5.0, giving ≈ 1.54 units of overlap — enough to create the traditional solid silhouette.

Vertical Extent (Local Space)

FeatureLocal y (defaults)Description
Top lobe topspread + radius = +4.5Highest point of the club
Top lobe center+spread = +2.0Center of the top circle
Lower lobe centers−spread/2 = −1.0Center of bottom-left and bottom-right circles
Lobe bottoms−(spread/2 + radius) = −3.5Stem attachment point
Stem bottom−(spread/2 + radius) − stemLength = −5.5Foot of the stem

Stem Geometry

The stem is a filled trapezoid with:

  • Attachment y = −spread/2 − radius (bottom of lower lobes) — analytically computed, no iteration needed
  • Neck width = stemWidth × 0.22 (narrow top)
  • Foot width = stemWidth (wider base)
  • Height = stemLength (extends downward)

Rotation

All three lobe centers + the stem are rotated together around center using a 2D rotation matrix. Both rotationDeg (static base) and rotation (accumulated spin) contribute to the total effective rotation.

Constructor

new SWClub(center, radius, spread, stemLength, stemWidth, fillColor, strokeColor, thickness, rotationDeg)
ParameterTypeDefaultDescription
centerSWPointrequiredCenter origin in user (grid) coordinates; the equilateral triangle is centered here
radiusnumber2.5Lobe circle radius in grid units
spreadnumber2.0Distance from center to each lobe center. Reduce to tighten overlap; increase to separate circles
stemLengthnumber2.0Stem trapezoid height (grid units)
stemWidthnumber3.5Stem foot width (grid units); neck = stemWidth × 0.22
fillColorSWColorundefinedFill color; undefined = no fill
strokeColorSWColorundefinedStroke color; undefined = no stroke
thicknessnumber2Stroke weight in pixels
rotationDegnumber0Static base rotation (CCW degrees); preserved across reset()
// Minimal construction (all defaults)
const center = new SWPoint(0, 0, undefined, 8,
    new SWColor(120, 60, 25, 100));
const fill   = SWColor.fromHex('#2a5a2a', 100, 'fill');
const stroke = SWColor.fromHex('#1a3a1a', 100, 'stroke');
const club   = new SWClub(center, 2.5, 2.0, 2.0, 3.5,
                            fill, stroke, 2, 0);
club.drawOnGrid(grid);

Properties

Live Properties

PropertyTypeDescription
centerSWPointCenter origin. Drag to reposition; center.shouldShow controls dot visibility
radiusnumberLobe circle radius in grid units. Change during Breathe animation.
spreadnumberDistance from center to each lobe center. Decrease for tighter overlap.
stemLengthnumberStem trapezoid height in grid units
stemWidthnumberStem foot width in grid units
fillColorSWColorFill color (undefined = no fill)
strokeColorSWColorStroke color (undefined = no outline)
thicknessnumberStroke weight in pixels
rotationDegnumberStatic base rotation (CCW degrees)
rotationnumberAccumulated spin rotation (degrees); cleared by reset()

Static Constant

PropertyValueDescription
SWClub.ROOT3_2√3/2 ≈ 0.866Pre-computed constant used for equilateral triangle geometry

Methods

Drawing

MethodDescription
draw() Draws in raw pixel (screen) coordinates. Prefer drawOnGrid() for standard use.
drawOnGrid(grid) Draws three lobes + stem mapped through the given SWGrid. Handles y-flip automatically. Use this in the p5.js draw loop.

Animation

MethodParametersDescription
rotate(deltaAngle) deltaAngle: degrees (CCW+, CW−) Accumulates spin rotation. All lobes + stem rotate together. Call each frame: club.rotate(speed * deltaT)

Setters

MethodParameterDescription
setRadius(r)number > 0Sets lobe circle radius. Use during Breathe animation.
setSpread(s)number > 0Sets lobe center distance from origin.
setStemLength(l)number > 0Sets stem trapezoid height.
setStemWidth(w)number > 0Sets stem foot width. Neck = w × 0.22.
setRotation(deg)numberSets static base rotation (CCW degrees). Does not clear accumulated rotation.
setFillColor(fc)SWColorReplaces fill color (deep copy stored).
setStrokeColor(sc)SWColorReplaces stroke color (deep copy stored).
setStrokeWeight(w)numberSets stroke thickness in pixels.
setFillAlpha(alpha)0–100Updates fill opacity and rebuilds the p5 color object.
setStrokeAlpha(alpha)0–100Updates stroke opacity and rebuilds the p5 color object.

Reset & Utility

MethodDescription
reset() Restores radius, spread, stemLength, stemWidth, colors, and thickness to constructor values. Clears accumulated spin rotation. Does not move center.
SWClub.copy(other) (static) Returns a deep copy of other (including center SWPoint, colors, and accumulated rotation).

Code Examples

1. Basic Setup (p5.js global mode)

let grid, club;

function setup() {
    createCanvas(400, 400);
    colorMode(HSB, 360, 100, 100, 100);
    initializeSWColors();

    grid = new SWGrid({ UL: new SWPoint(-8, 8), LR: new SWPoint(8, -8) });

    const center = new SWPoint(0, 0, undefined, 8,
        new SWColor(120, 60, 25, 100));
    const fill   = SWColor.fromHex('#2a5a2a', 100, 'fill');
    const stroke = SWColor.fromHex('#1a3a1a', 100, 'stroke');
    club = new SWClub(center, 2.5, 2.0, 2.0, 3.5, fill, stroke, 2, 0);
}

function draw() {
    background(0, 0, 93);
    grid.draw();
    club.drawOnGrid(grid);
    grid.updateScreenBounds();
}

2. Spin Animation

let prevT = 0;
const SPIN_SPEED = 45; // degrees per second (CCW)

function draw() {
    const t = millis() / 1000;
    const deltaT = (prevT > 0) ? (t - prevT) : 0;
    prevT = t;

    background(0, 0, 93);
    grid.draw();
    club.rotate(SPIN_SPEED * deltaT);
    club.drawOnGrid(grid);
    grid.updateScreenBounds();
}

3. Breathe Animation (Radius Oscillation)

const BASE_RADIUS     = 2.5;
const BREATHE_SPEED   = 0.5;   // Hz
const BREATHE_AMOUNT  = 0.5;   // grid units of radius oscillation

function draw() {
    const t = millis() / 1000;

    background(0, 0, 93);
    grid.draw();

    // When radius < spread/√3, circles separate — watch them re-merge!
    const r = BASE_RADIUS + BREATHE_AMOUNT * Math.sin(2 * Math.PI * BREATHE_SPEED * t);
    club.setRadius(Math.max(0.1, r));

    club.drawOnGrid(grid);
    grid.updateScreenBounds();
}

4. Tighten Overlap for Filled Silhouette

// Tighter spread — circles heavily overlap → solid club silhouette
club.setSpread(1.5);    // spread < radius × 1.155 = 2.89
club.setRadius(2.5);

// Wider spread — circles just touching → clover-like shape
club.setSpread(2.9);    // near the critical overlap boundary

5. Copy and Reset

// Deep copy
const copy = SWClub.copy(club);

// Reset to constructor values
club.reset();
// center preserved; radius, spread, rotation are restored

Source Code

Show / Hide swClub.js source
// Source will be loaded here — see shapeClasses/swClub.js
↑ Top