⌂ SWPolygon Reference

A SketchWave class for representing a closed polygon defined by an arbitrary ordered set of SWPoint vertices

SWPolygon Creation SWPolygon Modification

Quick Reference

SWPolygon is a SketchWave class that represents a closed polygon defined by an ordered array of SWPoint vertices. Any number of vertices from 3 to any practical limit may be used. The polygon automatically computes its perimeter, area (shoelace formula), centroid, convexity, and simplicity (simple vs. complex/self-intersecting). It draws itself through an SWGrid coordinate system and supports dynamic fill/stroke styling, scale-from-centroid, and vertex dragging.

  • Design Pattern: Vertex-list polygon (not parametric or composite)
  • Internal Structure: Array of SWPoint vertices; centroid maintained as a drawable SWPoint; original-vertex snapshot for scale/reset
  • Dependencies: SWPoint, SWColor, SWGrid, p5.js
  • Key Features: Arbitrary vertex count (≥ 3), shoelace area, convexity and simplicity detection, fill/stroke styling, drawOnGrid(), scale-from-centroid, horizontal translation, vertex drag support
  • Common Uses: Interactive polygon construction, geometric art, irregular shape drawing, classroom polygon explorer

Overview

The SWPolygon class takes an ordered list of SWPoint vertices and draws them as a closed filled/stroked polygon using p5.js's beginShape()/vertex()/endShape(CLOSE) pattern. Vertices are stored by reference, which means external mutations (e.g., dragging a point in the demo) are immediately reflected in subsequent drawOnGrid() calls.

Simple vs. Complex Polygons
A simple polygon has no two non-adjacent edges that cross each other — the boundary never self-intersects. A complex (self-intersecting) polygon has edges that cross, producing a star-like or butterfly appearance. isSimple detects this in O(n²) time using a parametric segment-intersection test.

Vertex Storage and Mutation

SWPolygon stores the same SWPoint objects passed to the constructor — it does not deep-copy them. This is intentional: the demo's drag logic mutates pt.x / pt.y directly, and the polygon redraws with updated positions automatically. A separate originalVertices snapshot (plain {x, y} objects) is kept for the scale and reset operations.

Centroid

The centroid is the arithmetic mean of all vertex positions — the geometric center of the vertex set (not the area centroid for irregular polygons). It is stored as a full SWPoint so it can be drawn, labeled, and dragged just like any vertex. The centroid is used as the pivot point for scaleAboutCentroid() and for the demo's spin animation.

Key Capabilities

  • Arbitrary Vertex Count: Any polygon from a triangle (3 vertices) upwards
  • Live Geometry: perimeter, area, isConvex, and isSimple are computed fresh each time they are read — always reflect current vertex positions
  • Drawable Centroid: Centroid stored as an SWPoint; optional visibility (showCentroid)
  • Scale from Centroid: scaleAboutCentroid(factor) uniformly scales all vertices relative to the centroid without moving it
  • Horizontal Translation: horizShiftBy(xInc) shifts every vertex and both original snapshots together
  • Fill & Stroke: Independent fill color (SWColor with alpha) and stroke color/weight; strokeWeight = 0 suppresses the border completely
  • Dual Coordinate Systems: Draw in screen pixels (draw()) or grid user coordinates (drawOnGrid())

Typical Workflow

  1. Place SWPoint objects on the canvas (or build them programmatically)
  2. Create fill and stroke SWColor instances
  3. Construct new SWPolygon(vertices, fillColor, options)
  4. Style the centroid point as desired (myPolygon.centroid.strokeColor = swMedGreen etc.)
  5. Call myPolygon.drawOnGrid(grid) each frame in the p5 draw() loop
  6. After dragging vertices, call updateCentroid(grid) and optionally rebuildOriginalsAtScale(scale)

Constructor

new SWPolygon(vertices, fillColor, options)

Creates a new SWPolygon. Vertices are stored by reference. An originalVertices snapshot is captured immediately for use by scaleAboutCentroid() and reset(). The centroid SWPoint is built from the vertex mean.

Parameters
Parameter Type Default Description
vertices SWPoint[] required Ordered array of SWPoint vertices. Minimum 3. Stored by reference.
fillColor SWColor required Fill color for the polygon interior (SWColor with HSB + alpha).
options.strokeColor SWColor | undefined undefined Border (outline) color. undefined = no stroke drawn.
options.strokeWeight number 2 Border thickness in pixels. Set to 0 to suppress the border entirely.
options.showVertices boolean true Whether to draw the SWPoint dots at each vertex position.
options.showCentroid boolean false Whether to draw the centroid SWPoint.
Constructor Examples
// Triangle with fill and stroke
const v1 = new SWPoint(-4, -3, undefined, 12, swRed,   "P1");
const v2 = new SWPoint( 4, -3, undefined, 12, swBlue,  "P2");
const v3 = new SWPoint( 0,  4, undefined, 12, swGreen, "P3");

const fill   = new SWColor(200, 60, 90, 75, "polyFill");
const border = new SWColor(210, 80, 50, 100, "polyStroke");

const tri = new SWPolygon([v1, v2, v3], fill, {
    strokeColor:  border,
    strokeWeight: 2,
    showVertices: true,
    showCentroid: true
});

// Pentagon — no border (strokeWeight = 0)
const penta = new SWPolygon(pentaVerts, fill, { strokeWeight: 0 });

// Stroke-only (transparent fill — note: SWPolygon always needs a fillColor object,
// so pass a near-transparent color for a stroke-only look)
const transparent = new SWColor(0, 0, 100, 0, "noFill");
const outline = new SWPolygon(verts, transparent, {
    strokeColor:  border,
    strokeWeight: 3,
    showVertices: false
});

Properties

Direct read/write properties. Mutate these to restyle the polygon without recreating it.

vertices SWPoint[]

The live array of vertex SWPoints. Stored by reference — mutations to individual points are automatically reflected when the polygon is next drawn. Do not replace the array reference; mutate .x / .y on the SWPoints directly.

myPolygon.vertices[0].x = 5; // reposition first vertex
fillColor SWColor

The fill color for the polygon interior. Replace to change the fill. The alpha channel of the SWColor controls fill transparency.

myPolygon.fillColor = new SWColor(120, 60, 85, 50, "greenFill");
strokeColor SWColor | undefined

The border (outline) color. Set to undefined or use strokeWeight = 0 to suppress the border.

myPolygon.strokeColor = new SWColor(0, 0, 20, 100, "darkBorder");
myPolygon.strokeColor = undefined; // no border
strokeWeight number

Border thickness in pixels. A value of 0 suppresses the border entirely (calls noStroke() internally) regardless of the strokeColor setting.

myPolygon.strokeWeight = 4; // thick border
myPolygon.strokeWeight = 0; // no border
showVertices boolean

Whether to draw the SWPoint dot markers at each vertex. When true, each vertex's own drawOnGrid() is called after the polygon shape is drawn, so vertices appear on top of the fill.

myPolygon.showVertices = false; // hide vertex dots
showCentroid boolean

Whether to draw the centroid SWPoint. The centroid updates live as vertices are dragged. Style the centroid independently via myPolygon.centroid.strokeColor etc.

myPolygon.showCentroid = true;
myPolygon.centroid.strokeColor = swMedGreen;
myPolygon.centroid.strokeWeight = 6;
myPolygon.centroid.setLabel("G");
centroid SWPoint

The centroid stored as a full SWPoint — drawable, labelable, and draggable. Its position is the arithmetic mean of all vertex positions. Updated automatically by _refreshCentroid() (called inside scaleAboutCentroid(), horizShiftBy(), and reset()). Call updateCentroid(grid) after externally dragging vertices.

console.log(`Centroid: (${myPolygon.centroid.x.toFixed(2)}, ${myPolygon.centroid.y.toFixed(2)})`);

Computed Getters

Read-only computed properties. Recalculated fresh on every access — always reflect the current vertex positions.

vertexCount number

The number of vertices in the polygon. Equivalent to vertices.length.

console.log(`${myPolygon.vertexCount}-sided polygon`);
perimeter number

The total perimeter: the sum of all edge lengths including the closing edge from the last vertex back to the first. Computed using Math.hypot(). Reported in user-space units.

console.log(`Perimeter: ${myPolygon.perimeter.toFixed(2)} units`);
area number

The interior area computed with the shoelace (Gauss) formula: |∑(xᵢ·yᵣ − xᵣ·yᵢ)| / 2. Returns the absolute value, so vertex winding order (CW vs. CCW) does not matter. For self-intersecting (complex) polygons, this returns the algebraic net area rather than the true visual area.

console.log(`Area: ${myPolygon.area.toFixed(2)} sq. units`);
isConvex boolean

true if no interior angle exceeds 180°. Detected using the cross-product sign test: if all consecutive edge cross-products have the same sign, the polygon is convex. Returns false as soon as any sign change is found (concave vertex). Also returns false for self-intersecting polygons, since they are never convex by definition.

console.log(myPolygon.isConvex ? "Convex" : "Concave");
isSimple boolean

true if no two non-adjacent edges cross each other. Uses a parametric segment-intersection test with strict inequalities so shared adjacent-edge endpoints do not count as intersections. Returns false the moment any crossing is found. O(n²) in vertex count. For a self-intersecting (complex) polygon, this returns false.

A polygon is simple if its boundary never crosses itself. Star polygons (pentagrams etc.) and butterfly shapes are complex. Both the shoelace area and the convexity test give unexpected results for complex polygons.
console.log(myPolygon.isSimple ? "Simple" : "Complex (self-intersecting)");

Methods

Core Drawing Methods

draw()

Draws the polygon using the raw screen-pixel coordinates stored directly in each SWPoint (v.x, v.y). Use this only when working without a SWGrid. For standard canvas use, prefer drawOnGrid().

function draw() {
    background(220);
    myPolygon.draw(); // SWPoint coords treated as screen pixels
}
drawOnGrid(grid)

Maps every vertex through grid.userToScreen(v.x, v.y) and draws the polygon in the SWGrid's coordinate system. This is the standard drawing method. Vertices, centroid, fill, and stroke are all handled automatically based on the current property values.

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

Centroid Methods

updateCentroid(grid)

Recomputes the centroid position from the current vertex positions and optionally runs labelProximityCheck(grid) on the centroid label. Call this after externally dragging one or more vertices.

// After dragging a vertex in mouseDragged():
myPolygon.updateCentroid(grid);

Scale and Transform Methods

scaleAboutCentroid(factor)

Scales all vertices proportionally from the originalCentroid position by factor. A factor of 1.0 restores original size; 2.0 doubles it; 0.5 halves it. Uses the originalVertices snapshot so repeated calls to the same factor always produce the same result (no drift). The centroid position is preserved.

// Scale slider handler (0.2 → 3.0):
myPolygon.scaleAboutCentroid(parseFloat(scaleSlider.value));
rebuildOriginalsAtScale(scale)

Re-anchors the originalVertices snapshot to the current vertex positions (divided back by scale), so the scale slider continues to work correctly after a vertex has been dragged. Call this after a drag operation completes (e.g., in mouseReleased()) when a polygon and scale slider coexist.

// After releasing a dragged vertex:
if (myPolygon) {
    myPolygon.rebuildOriginalsAtScale(currentScale);
    myPolygon.updateCentroid(grid);
}
horizShiftBy(xInc)

Shifts every vertex and both the originalVertices snapshot and originalCentroid by xInc user units in the x direction. Useful for repositioning the entire polygon without recreating it. The centroid SWPoint is also refreshed.

myPolygon.horizShiftBy(2);  // move 2 units right
myPolygon.horizShiftBy(-1); // move 1 unit left

Reset Method

reset()

Restores all vertices to their stored originalVertices positions (captured at construction or last re-anchored by rebuildOriginalsAtScale()). Also refreshes the centroid. Does not affect fill/stroke colors or the scale slider state.

// Reset button handler:
myPolygon.reset();

Utility Methods

toString()

Returns a human-readable summary string including vertex count, perimeter, area, and centroid coordinates.

console.log(myPolygon.toString());
// "SWPolygon(vertices:5, perimeter:28.34, area:19.50, centroid:(0.60,0.80))"

Usage Examples

Example 1: Basic Triangle

let grid;
let tri;

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 v1 = new SWPoint(-5, -3, undefined, 10, swRed,   "A");
    const v2 = new SWPoint( 5, -3, undefined, 10, swBlue,  "B");
    const v3 = new SWPoint( 0,  5, undefined, 10, swGreen, "C");

    const fill   = new SWColor(200, 55, 90, 70, "triFill");
    const border = new SWColor(210, 80, 50, 100, "triStroke");

    tri = new SWPolygon([v1, v2, v3], fill, {
        strokeColor:  border,
        strokeWeight: 2,
        showVertices: true,
        showCentroid: true
    });

    // Style centroid
    tri.centroid.strokeColor  = swMedGreen;
    tri.centroid.strokeWeight = 8;
    tri.centroid.setLabel("G");
    tri.centroid.setLabelAbove(15);
}

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

Example 2: Reading Geometric Properties

// After creating myPolygon:
console.log(`Vertices:  ${myPolygon.vertexCount}`);
console.log(`Perimeter: ${myPolygon.perimeter.toFixed(2)}`);
console.log(`Area:      ${myPolygon.area.toFixed(2)}`);
console.log(`Centroid:  (${myPolygon.centroid.x.toFixed(2)}, ${myPolygon.centroid.y.toFixed(2)})`);
console.log(`Convex:    ${myPolygon.isConvex}`);
console.log(`Simple:    ${myPolygon.isSimple}`);

Example 3: Scale Slider

let currentScale = 1.0;

// HTML slider calls this:
function scalePolygon(factor) {
    if (!myPolygon) return;
    currentScale = factor;
    myPolygon.scaleAboutCentroid(factor);
    redraw();
}

// After user drags a vertex, re-anchor so slider still works:
function mouseReleased() {
    if (draggedPoint && myPolygon) {
        myPolygon.rebuildOriginalsAtScale(currentScale);
        myPolygon.updateCentroid(grid);
    }
    draggedPoint = null;
}

Example 4: Detecting Simple vs. Complex in Real Time

// In draw() — update a label when edges cross:
function draw() {
    background(220);
    grid.draw();
    myPolygon.drawOnGrid(grid);

    // Highlight border red when self-intersecting
    if (!myPolygon.isSimple) {
        myPolygon.strokeColor = new SWColor(0, 90, 90, 100, "warnStroke");
    } else {
        myPolygon.strokeColor = originalStrokeColor;
    }
}

Example 5: Programmatic Polygon (Regular Pentagon)

// Build a regular pentagon using trigonometry (no SWRegularPolygon needed)
function buildRegularPolygon(cx, cy, radius, sides) {
    const pts = [];
    for (let i = 0; i < sides; i++) {
        const angle = (TWO_PI / sides) * i - HALF_PI; // start at top
        pts.push(new SWPoint(
            cx + radius * cos(angle),
            cy + radius * sin(angle),
            undefined, 10, swBlue, `P${i + 1}`
        ));
    }
    return pts;
}

const penta = new SWPolygon(
    buildRegularPolygon(0, 0, 5, 5),
    new SWColor(50, 70, 90, 60, "pentaFill"),
    { strokeColor: new SWColor(50, 80, 50, 100, "pentaStroke"), strokeWeight: 2 }
);

Example 6: Christmas Tree Polygon

// Classic Christmas tree silhouette — a simple 11-vertex polygon
// (stacked triangular tiers + trunk)
function buildChristmasTree() {
    const pts = [
        // Top tip
        new SWPoint( 0,   8),
        // Upper tier
        new SWPoint( 2,   5), new SWPoint( 1,   5),
        new SWPoint( 3,   2), new SWPoint( 2,   2),
        new SWPoint( 4.5,-1), new SWPoint(-4.5,-1),
        new SWPoint(-2,   2), new SWPoint(-3,   2),
        new SWPoint(-1,   5), new SWPoint(-2,   5),
    ].map((p, i) => new SWPoint(p.x, p.y, undefined, 10, swMedGreen, `T${i+1}`));

    return new SWPolygon(pts,
        new SWColor(130, 70, 55, 100, "treeFill"),
        { strokeColor: new SWColor(130, 80, 30, 100, "treeBorder"), strokeWeight: 2 }
    );
}

Best Practices

1. Vertex Ordering

  • Place vertices in a consistent winding order (all CCW or all CW); mixing causes the shoelace area to give unexpected values
  • For interactive demos, the order is determined by the sequence in which the user clicks — this is fine as long as the area display is treated as approximate

2. Drag + Scale Coexistence

  • scaleAboutCentroid() uses originalVertices as its reference, so the scale slider always produces a deterministic result from the original shape
  • After a vertex drag, call rebuildOriginalsAtScale(currentScale) to re-anchor the snapshot — otherwise the next scale slider movement will "snap" vertices back to pre-drag positions
// Correct pattern after a vertex drag completes:
myPolygon.rebuildOriginalsAtScale(currentScale);
myPolygon.updateCentroid(grid);

3. strokeWeight = 0 for No Border

  • Setting strokeWeight = 0 is the correct way to suppress the border — it calls noStroke() inside the draw methods
  • Setting strokeColor = undefined also suppresses the border, but keeping strokeWeight at 0 is the more explicit choice when using the demo slider (minimum value 0)

4. Complex Polygon Caveats

  • The shoelace area gives the algebraic net area for self-intersecting polygons, not the total visual area. For classroom purposes, note that this result is only meaningful for simple polygons.
  • The convexity test always returns false for complex polygons (a self-intersecting polygon cannot be convex).
  • The centroid (vertex mean) of a complex polygon still returns a valid point, but it may lie outside all the visible regions of the shape.

5. Geometric Properties Update Timing

  • All getters (perimeter, area, isConvex, isSimple) recompute on every access — they are safe to call in the draw loop
  • The centroid SWPoint (myPolygon.centroid) is not automatically refreshed when you mutate vertices directly; call updateCentroid(grid) explicitly after vertex drags

Integration with Other SketchWave Classes

Script Loading Order

SWPolygon depends on SWColor, SWPoint, and SWGrid:

<!-- p5.js library -->
<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/swPolygon.js"></script>

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

Working with SWPoint

SWPolygon uses SWPoint in two ways:

  • The vertices array holds the polygon's corner points — each SWPoint can have its own color, label, and setDraggable() state for interactive demos
  • The centroid property is also a full SWPoint, built from the vertex mean, with its own independent styling and label

Working with SWColor

SWPolygon uses SWColor for fill and stroke in HSB mode:

  • The fill color's alpha channel controls transparency independently of the stroke
  • Colors are stored by reference (not copied at construction) — replace the fillColor property to change the fill without recreating the polygon

Comparing SWPolygon and SWRegularPolygon

Both classes draw closed filled polygons, but they differ significantly:

Feature SWPolygon SWRegularPolygon
Vertex placement Arbitrary — user-defined SWPoints Computed — equally spaced on circumradius
Shape variety Any simple or complex polygon Regular n-gons only (equilateral & equiangular)
Vertex dragging Yes — each vertex can be dragged independently No — shape is always regular
Area formula Shoelace (Gauss) Geometric (apothem × perimeter / 2)
Convexity Detected dynamically (isConvex) Always convex (by definition)
Self-intersection Detected dynamically (isSimple) Never self-intersects
Rounded corners No Yes (Catmull-Rom mode)
Spin animation Via p5 push/translate/rotate/pop in sketch Built-in rotate() method

Source Code

The complete SWPolygon class source is in shapeClasses/swPolygon.js.

 (click to expand / collapse source)
/*
File: swPolygon.js
Date: 2026-04-22
Author: klp
Workspace: SketchWaveTNT2026-04-21-Stg8
Purpose: SWPolygon class for SketchWaveJS

Represents a closed polygon defined by an ordered array of SWPoint vertices.
Vertices are stored by reference — external mutations are reflected immediately
in subsequent draw calls.

Computed getters (recalculated on every access):
  perimeter  — sum of all edge lengths (including closing edge)
  area       — shoelace (Gauss) formula; absolute value; valid for simple polygons
  vertexCount — vertices.length
  isConvex   — cross-product sign consistency test
  isSimple   — pairwise segment-intersection test for non-adjacent edges

Centroid:
  Stored as a drawable SWPoint at the arithmetic mean of vertex positions.
  Refreshed by _refreshCentroid() (called inside manipulation methods).
  Call updateCentroid(grid) after externally dragging vertices.

Scaling:
  scaleAboutCentroid(factor) — scales from originalCentroid using originalVertices snapshot.
  rebuildOriginalsAtScale(scale) — re-anchors snapshot after a drag.

Dependencies: p5.js, SWColor, SWPoint, SWGrid.
*/