♣ 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)
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 < 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)
| Feature | Local y (defaults) | Description |
|---|---|---|
| Top lobe top | spread + radius = +4.5 | Highest point of the club |
| Top lobe center | +spread = +2.0 | Center of the top circle |
| Lower lobe centers | −spread/2 = −1.0 | Center of bottom-left and bottom-right circles |
| Lobe bottoms | −(spread/2 + radius) = −3.5 | Stem attachment point |
| Stem bottom | −(spread/2 + radius) − stemLength = −5.5 | Foot 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)
| Parameter | Type | Default | Description |
|---|---|---|---|
center | SWPoint | required | Center origin in user (grid) coordinates; the equilateral triangle is centered here |
radius | number | 2.5 | Lobe circle radius in grid units |
spread | number | 2.0 | Distance from center to each lobe center. Reduce to tighten overlap; increase to separate circles |
stemLength | number | 2.0 | Stem trapezoid height (grid units) |
stemWidth | number | 3.5 | Stem foot width (grid units); neck = stemWidth × 0.22 |
fillColor | SWColor | undefined | Fill color; undefined = no fill |
strokeColor | SWColor | undefined | Stroke color; undefined = no stroke |
thickness | number | 2 | Stroke weight in pixels |
rotationDeg | number | 0 | Static 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
| Property | Type | Description |
|---|---|---|
center | SWPoint | Center origin. Drag to reposition; center.shouldShow controls dot visibility |
radius | number | Lobe circle radius in grid units. Change during Breathe animation. |
spread | number | Distance from center to each lobe center. Decrease for tighter overlap. |
stemLength | number | Stem trapezoid height in grid units |
stemWidth | number | Stem foot width in grid units |
fillColor | SWColor | Fill color (undefined = no fill) |
strokeColor | SWColor | Stroke color (undefined = no outline) |
thickness | number | Stroke weight in pixels |
rotationDeg | number | Static base rotation (CCW degrees) |
rotation | number | Accumulated spin rotation (degrees); cleared by reset() |
Static Constant
| Property | Value | Description |
|---|---|---|
SWClub.ROOT3_2 | √3/2 ≈ 0.866 | Pre-computed constant used for equilateral triangle geometry |
Methods
Drawing
| Method | Description |
|---|---|
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
| Method | Parameters | Description |
|---|---|---|
rotate(deltaAngle) |
deltaAngle: degrees (CCW+, CW−) |
Accumulates spin rotation. All lobes + stem rotate together. Call each frame: club.rotate(speed * deltaT) |
Setters
| Method | Parameter | Description |
|---|---|---|
setRadius(r) | number > 0 | Sets lobe circle radius. Use during Breathe animation. |
setSpread(s) | number > 0 | Sets lobe center distance from origin. |
setStemLength(l) | number > 0 | Sets stem trapezoid height. |
setStemWidth(w) | number > 0 | Sets stem foot width. Neck = w × 0.22. |
setRotation(deg) | number | Sets static base rotation (CCW degrees). Does not clear accumulated rotation. |
setFillColor(fc) | SWColor | Replaces fill color (deep copy stored). |
setStrokeColor(sc) | SWColor | Replaces stroke color (deep copy stored). |
setStrokeWeight(w) | number | Sets stroke thickness in pixels. |
setFillAlpha(alpha) | 0–100 | Updates fill opacity and rebuilds the p5 color object. |
setStrokeAlpha(alpha) | 0–100 | Updates stroke opacity and rebuilds the p5 color object. |
Reset & Utility
| Method | Description |
|---|---|
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