🌹 SWRose Reference

A SketchWave polar class for representing the mathematical rose curve: r = a·cos(n·θ) or r = a·sin(n·θ)

Back to SWRose Demo

Quick Reference

SWRose is a SketchWave polar curve class representing the classical mathematical rose. The curve is defined by r = a·cos(n·θ) (or the sine variant), sampled over θ ∈ [0, 2π) at SAMPLE_COUNT points and drawn as a closed polygon. When n is odd the rose has n petals; when n is even it has 2n petals. The parameter a sets the petal tip radius.

  • Design Pattern: Polar curve (not composition or inheritance)
  • Equations: r = a·cos(nθ)  or  r = a·sin(nθ)
  • Petal Rule: n odd → n petals; n even → 2n petals
  • Internal Structure: SAMPLE_COUNT = 360 polygon vertices over [0, 2π); naturally closed
  • Dependencies: SWPoint, SWColor, SWGrid, p5.js
  • Key Features: Configurable amplitude (a) and petal count (n), cos/sin toggle, spin animation, bloom animation (oscillating a), draggable center
  • Common Uses: Flower art, mathematical curve exploration, symmetry studies, hypnotic spin animations

Overview

The SWRose class draws a polar rose as a closed polygon. The curve is defined in polar coordinates as:

r = a · cos(n · θ)    (cosine form, default)
r = a · sin(n · θ)    (sine form; rotates the rose by 90/n degrees)
Reading the petal count rule
When n is odd: the rose has exactly n petals.
When n is even: the rose has 2n petals.
Examples: n=3 → 3 petals  |  n=4 → 8 petals  |  n=5 → 5 petals  |  n=6 → 12 petals

Negative Radius Semantics

The polar rose naturally produces negative r values (when cos or sin is negative). SWRose handles these correctly using the standard Cartesian conversion:

x = r · cos(θ)     y = r · sin(θ)

When r < 0, the formula plots the point in the direction opposite to θ, which produces the petal shapes on the other side of the origin. No special handling is required; the math works out automatically.

Cosine vs Sine Form

The cosine form aligns petals symmetrically to the horizontal axis (for odd n, one petal points right). The sine form is a rotation of the cosine form by 90/n degrees. For n=4, the cosine form gives petals at 0°, 90°, 180°, 270° while the sine form rotates the entire rose 22.5°. Both produce the same rose shape.

Rotation

SWRose supports two layers of rotation, both applied about the center point:

  • rotationDeg — Static base rotation set by setRotation(). Persists across frames; survives reset().
  • rotation — Accumulated rotation incremented by rotate(). Starts at 0; cleared by reset().

Effective rotation = rotationDeg + rotation. All angles are CCW positive (user-space convention).

Key Capabilities

  • Polar Rose Curves: Both r=a·cos(nθ) and r=a·sin(nθ) with a single boolean toggle
  • Integer Petal Control: Slide n from 1 to 12+ to explore 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24 petal configurations
  • Spin Animation: Rotate continuously about the center using rotate()
  • Bloom Animation: Oscillate the amplitude (a) sinusoidally using setA() from the sketch layer
  • Fill & Stroke Colors: Independent color pickers and alpha channels
  • Draggable Center: Move the entire rose by repositioning the center SWPoint
  • Dual Coordinate Systems: draw() (screen pixels) or drawOnGrid() (grid user coordinates)

Constructor

new SWRose(center, a, n, strokeColor, fillColor, thickness, useCosine, rotationDeg)

Creates a new SWRose instance. All constructor values are saved as originals for reset().

Parameters
ParameterTypeDefaultDescription
center SWPoint required The origin of the rose in user (grid) coordinates. All petals radiate from this point.
a number required Amplitude. The radius from center to each petal tip. Larger a = bigger petals. Values below 0.01 are clamped.
n number 4 Petal frequency. Odd n → n petals; even n → 2n petals. Integer values 1–12 give the classic rose shapes.
strokeColor SWColor | undefined undefined Outline color. undefined = no stroke drawn.
fillColor SWColor | undefined undefined Fill color for the rose interior. undefined = no fill (transparent). Semi-transparent fill reveals petal overlap regions.
thickness number 2 Stroke weight in pixels.
useCosine boolean true true: r=a·cos(nθ) (petals aligned to axes for even n). false: r=a·sin(nθ) (rotated by 90/n degrees).
rotationDeg number 0 Static base rotation in CCW degrees; applied in addition to accumulated rotation.
Constructor Examples
// 8-petal rose (n=4, even → 2n petals), red stroke, pink fill
const stroke = new SWColor(0, 80, 80, 100, "roseStroke");
const fill   = new SWColor(0, 40, 100, 60, "roseFill");
let rose = new SWRose(new SWPoint(0, 0), 8, 4, stroke, fill, 3);

// 3-petal rose (n=3, odd → n petals), blue, no fill
const blueStroke = new SWColor(220, 80, 80, 100, "blueStroke");
let tri = new SWRose(new SWPoint(0, 0), 6, 3, blueStroke, undefined, 2);

// 5-petal rose, sine form
const gold = new SWColor(40, 90, 90, 100, "gold");
let penta = new SWRose(new SWPoint(0, 0), 7, 5, gold, undefined, 2, false);

// 12-petal rose (n=6, even → 12 petals), pre-rotated 15°
let twelve = new SWRose(new SWPoint(0, 0), 8, 6, stroke, fill, 2, true, 15);

// Stroke-only, single petal (n=1 with cos: traces a circle — useful curiosity)
const dark = new SWColor(0, 0, 20, 100, "dark");
let single = new SWRose(new SWPoint(0, 0), 5, 1, dark);

Properties

center SWPoint

The rose's origin in user (grid) coordinates. All petals radiate from this point. Moving center.x or center.y repositions the entire rose.

rose.center.x = 3; rose.center.y = -2;
a number

Amplitude controlling petal tip radius. The maximum distance from center to any point on the rose equals a. Use setA() to change it.

rose.setA(5); // small petals
rose.setA(9); // large petals
n number

Petal frequency parameter. Odd n gives n petals; even n gives 2n petals. Use setN() to change it.

rose.setN(3); // 3 petals
rose.setN(4); // 8 petals
rose.setN(5); // 5 petals
useCosine boolean

Selects the equation form. true (default) uses r=a·cos(nθ); false uses r=a·sin(nθ), which rotates the rose by 90/n degrees. Use setUseCosine() to toggle.

rose.setUseCosine(false); // switch to sine form
rotationDeg number

Static base rotation in CCW degrees. Set by setRotation(); survives reset().

rose.setRotation(45); // tilt 45° CCW
rotation number

Accumulated rotation in degrees, incremented each frame by rotate(). Starts at 0; cleared by reset().

// Cleared automatically by reset(); read-only in normal use
SWRose.SAMPLE_COUNT static

Number of polygon vertices (default: 360 — one per degree over [0, 2π)). Higher values produce smoother curves; lower values create visible facets. Can be changed globally at any time.

SWRose.SAMPLE_COUNT = 72; // faceted look
SWRose.SAMPLE_COUNT = 720; // ultra-smooth

Methods

Drawing Methods

draw()

Draws the rose in raw screen (pixel) coordinates. The center SWPoint is interpreted as screen pixels. Prefer drawOnGrid() for standard canvas use with an SWGrid.

function draw() {
    background(220);
    rose.draw();
}
drawOnGrid(grid)

Draws the rose mapped through the given SWGrid's coordinate system. The grid handles the y-flip (math up → screen down) and coordinate scaling.

function draw() {
    background(220);
    grid.draw();
    rose.drawOnGrid(grid);
    grid.updateScreenBounds();
}

Rotation Animation

rotate(deltaAngle)

Spins the rose about its center by deltaAngle degrees (CCW+, CW−). Accumulates into this.rotation. Call once per frame in draw() before calling drawOnGrid().

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

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

    background(240);
    grid.draw();
    rose.rotate(SPIN_SPEED * deltaT);  // spin BEFORE drawing
    rose.drawOnGrid(grid);
    grid.updateScreenBounds();
}

Bloom Animation

Bloom — sinusoidal amplitude oscillation

The bloom effect oscillates the amplitude a sinusoidally, making the petals pulse in and out like a flower opening and closing. It is implemented in the sketch layer using setA():

currentA = baseA + depth · sin(2π · speed · t)

baseA is the steady-state amplitude (typically from a slider). depth (Δa) is the oscillation amplitude. speed is the frequency in Hz. The result is clamped to a minimum of 0.1. Bloom and Spin can run simultaneously.

const BLOOM_SPEED = 0.5; // Hz
const BLOOM_DEPTH = 2.0; // Δa (grid units)
let baseA = 8;

function draw() {
    const t       = millis() / 1000;
    const bloomedA = baseA + BLOOM_DEPTH * sin(TWO_PI * BLOOM_SPEED * t);
    rose.setA(max(0.1, bloomedA));
    rose.drawOnGrid(grid);
}

Setter Methods

setA(a)

Updates the amplitude (petal tip radius). Values below 0.01 are clamped. Takes effect immediately on the next draw call.

rose.setA(6);
setN(n)

Updates the petal frequency. Odd n → n petals; even n → 2n petals. Fractional n produces more complex curves.

rose.setN(5); // 5-petal rose
setUseCosine(val)

Switches between cosine (true) and sine (false) form. The sine form is a rotation of the cosine form by 90/n degrees.

rose.setUseCosine(false); // switch to sin form
setStrokeColor(sc)   setFillColor(fc)

Sets the stroke or fill color. Pass an SWColor instance or undefined to remove it.

rose.setStrokeColor(new SWColor(0, 80, 60, 100, "dark"));
rose.setFillColor(undefined); // transparent
setFillAlpha(alpha)   setStrokeAlpha(alpha)

Sets the fill or stroke alpha (0–100) and rebuilds the p5 color object. Requires an existing color to be set first.

rose.setFillAlpha(40); // 40% opacity fill
rose.setStrokeAlpha(100); // fully opaque stroke
setStrokeWeight(w)

Sets the stroke thickness in pixels.

rose.setStrokeWeight(4);
setRotation(deg)

Sets the static base rotation in CCW degrees. Does not affect the accumulated rotation.

rose.setRotation(30); // tilt 30° CCW from default

Reset & Utility Methods

reset()

Restores all animated and slider-driven properties to their original constructor values. Clears accumulated spin rotation. Does not move the center position.

rose.reset(); // back to factory defaults
static SWRose.copy(other)

Creates a deep copy of the given SWRose, preserving all current and original state including rotation accumulation.

const copy = SWRose.copy(rose);
toString()

Returns a human-readable string describing the rose's current state, including the petal count for integer n.

console.log(rose.toString());
// "SWRose(center=SWPoint(x:0, y:0), a=8.00, n=4 (8 petals), formula=r=8.00·cos(4·θ), rotationDeg=0.0, rotation=0.0)"

Code Examples

Minimal sketch (rose on a grid)

let grid, rose;

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 stroke = new SWColor(0, 80, 70, 100, "s");
    const fill   = new SWColor(0, 40, 100, 60, "f");
    rose = new SWRose(new SWPoint(0, 0), 8, 4, stroke, fill, 3);
}

function draw() {
    background(240);
    grid.draw();
    rose.drawOnGrid(grid);
    grid.updateScreenBounds();
}

Spinning rose

let prevT = 0;
const SPIN_SPEED = 60; // degrees per second

function setup() { /* ... create grid and rose ... */ }

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

    background(240);
    grid.draw();
    rose.rotate(SPIN_SPEED * deltaT);   // spin BEFORE drawing
    rose.drawOnGrid(grid);
    grid.updateScreenBounds();
}

Exploring n values at runtime

function keyPressed() {
    if (key === 'ArrowUp')   { rose.setN(rose.n + 1); }
    if (key === 'ArrowDown') { rose.setN(Math.max(1, rose.n - 1)); }
    if (key === 'c')         { rose.setUseCosine(!rose.useCosine); }
    if (key === 'r')         { rose.reset(); }
}

Bloom (pulsing petals)

const BLOOM_SPEED = 0.5; // Hz
const BLOOM_DEPTH = 2.0; // Δa
let baseA = 8;

function draw() {
    const t = millis() / 1000;
    const bloomedA = baseA + BLOOM_DEPTH * sin(TWO_PI * BLOOM_SPEED * t);
    rose.setA(max(0.1, bloomedA));
    rose.drawOnGrid(grid);
}

Using a color picker with SWColor.fromHex()

const picker = document.getElementById('strokePicker');
const alpha  = document.getElementById('alphaSlider');

picker.addEventListener('input', () => {
    const col = SWColor.fromHex(picker.value, Number(alpha.value), 'roseStroke');
    rose.setStrokeColor(col);
});

alpha.addEventListener('input', () => {
    const col = SWColor.fromHex(picker.value, Number(alpha.value), 'roseStroke');
    rose.setStrokeColor(col);
});

Required script tags (in dependency order)

<script src="https://cdn.jsdelivr.net/npm/p5@1.6.0/lib/p5.js"></script>

<!-- SketchWaveJS classes in dependency order -->
<script src="../shapeClasses/swColor.js"></script>
<script src="../shapeClasses/swPoint.js"></script>
<script src="../shapeClasses/swGrid.js"></script>
<script src="../shapeClasses/swRose.js"></script>

<!-- Your sketch -->
<script src="../sketches/yourSketch.js"></script>

Design Notes

  • Petal count doubles for even n: Because r = a·cos(nθ) for even n is negative over half the [0, 2π] range, those negative-r segments fold back through the origin and produce petals interleaved with the positive-r petals, doubling the count to 2n.
  • n=1 (cosine) is a circle, not a petal: r = a·cos(θ) in Cartesian is the circle x² + y² = ax, a circle of radius a/2 centered at (a/2, 0). n=1 with sine gives the same circle shifted vertically. Both are valid but not the typical "rose" shape.
  • SAMPLE_COUNT = 360 is per full sweep, not per petal: For n=12 (24 petals), each petal gets ~15 samples — smooth at normal canvas sizes. Increase to 720 for very fine detail or large canvas renders.
  • Fill with semi-transparency reveals petal overlaps: When multiple petals overlap near the center (especially for large n), a semi-transparent fill shows the layering beautifully.
  • reset() does not move the center: This allows dragging and repositioning without losing position on reset, consistent with SWSpiral and other SketchWaveJS polar classes.
  • Fractional n produces multi-loop "rhodonea" curves: For example, n=3/2 (enter 1.5) traces a 3-petal figure over a larger theta range. For non-integer n, the curve will not close within [0, 2π]; increase SAMPLE_COUNT or extend the sweep externally for best results.

Source Code

The complete SWRose class implementation:

Show/Hide Source Code
// Loading source code...