Skip to content

Polygon

1. Identity

A polygon is a regular n-sided closed shape whose n vertices lie on a common circle (the circumscribed circle), inscribed about a user-picked centre node, with n equal-length sides and n distance constraints from centre to each vertex that together keep the figure regular under solver edits.

2. When to use it

  • Stator/rotor outline approximations where a regular n-gon is the design intent (3-, 4-, 6-, 8-sided mounting plates, hex magnet pockets).
  • Construction scaffolding for n-fold symmetric layouts where each side later carries its own boundary or material region.
  • Parametric primitive whose size is driven by a variable (=R) so a single named radius drives all n vertices.
  • A quick "polygon-of-radius-R" stamp at a chosen centre with no follow-up clicks.

3. Inputs

  • Tool button: toolbar slot for the regular-polygon icon; sets activeTool === 'polygon' (Canvas/index.tsx:1623, :2346).
  • Keyboard shortcut: not yet wired (no entry in the tool-dispatch table around Canvas/index.tsx:2716).
  • MotorScript builtin: polygon(cx, cy, radius, sides, rotationRad?) (executor/builtins/primitives.ts:375). rotationRad defaults to 0. Throws on non-finite args, sides < 3, or radius <= 0.
  • Command Palette (Cmd+K): not yet wired.
  • Context menu: not yet wired.
  • Reverse codegen: codegenPolygon (codegen.ts:199) emits the matching polygon(...) statement on commit.

4. State machine

The flow is one click + a modal dialog, not two canvas clicks. The dialog gathers sides / radius / rotation atomically.

  1. Click 1 — centre point: any world-space click while activeTool === 'polygon'; snaps to existing nodes/grid via the standard click pipeline. The world coords are stashed in polygonCentre state (Canvas/index.tsx:2346-2347).
    • Preview after step 1: cursor crosshair only; no ghost polygon is rendered (no live radius/rotation rubber-band). The status hint reads "Polygon — centre / Add point" (Canvas/index.tsx:2848).
    • Cancel options: Esc, tool switch, or clicking the dialog's Cancel — all route through setPolygonCentre(null) (Canvas/index.tsx:469, :2897, :4594).
  2. Dialog — parameters: PolygonDialog opens with open = !!polygonCentre (Canvas/index.tsx:4584-4586). Fields:
    • Sides — integer, min 3, default 6.
    • Radius — locale-parsed numeric, must be > 0, default 10. May be a parameter expression (radiusVarRef).
    • Rotation — degrees, default 0; converted to radians before being passed to createPolygon.
    • Preview during dialog: none — the canvas is unchanged until Confirm.
    • Cancel options: dialog Cancel, Esc, or backdrop-close — all call onCancelsetPolygonCentre(null).
  3. Finalize: Submit on the dialog form (PolygonDialog.tsx:42-51) calls addPolygon(cx, cy, radius, sides, rotRad) (Canvas/index.tsx:4590-4592), which delegates to createPolygon in tools/polygon-tool.ts:65. polygonCentre is cleared.

5. Committed state

After Confirm, createPolygon (tools/polygon-tool.ts:65-245) atomically appends:

  • 1 construction Node at (cx, cy) — the centre — unless an existing node already sits within TOL.GEOMETRIC of that point, in which case the existing node is reused and PolygonFeature.centreNodeId is recorded as '' so deletePolygon will not remove a shared user node (tools/polygon-tool.ts:124-143, :217-228).
  • n vertex Nodes on the circumscribed circle at angles rotationRad + k·2π/n for k = 0…n−1 (tools/polygon-tool.ts:107-111).
  • n Segments in order 0→1, 1→2, …, (n−1)→0 (tools/polygon-tool.ts:146-156).
  • (n−1) equal constraints — side-0 against each of side-1…side-(n−1) (tools/polygon-tool.ts:184-197).
  • n distance constraints — centre → each vertex, all carrying the literal radius value, or the valVarRef variable name when the radius came from a parameter expression (tools/polygon-tool.ts:203-214).
  • 1 PolygonFeature record owning all the above ids (scene-model.ts:522-539).
  • 1 MotorScript statement polygon(cx, cy, radius, sides, rotationRad?) via codegenPolygon (codegen.ts:199-208); rotationRad argument omitted when zero.

Total constraint count is 2n − 1. Parametric freedom retained: position (2 DOF) + rotation (1 DOF) + scale (1 DOF) — the centre→vert distance is left unconstrained as the size handle (tools/polygon-tool.ts:20-26).

6. Constraints / interactions

ConstraintBehavior on this primitive
equalAuto-emitted (n−1)× between sides; user may also equate to external segments.
distanceAuto-emitted from centre to each vertex (the size handle); user may smart-dim a single distance to lock size.
horizontal / verticalAccepted on any one side; rotates the whole polygon because the equal-length constraints propagate.
coincidentVertex or centre may be made coincident with external nodes; centre is a real Node and thus a first-class constraint target.
angleAccepted between a polygon side and an external segment; rotates the polygon.

Interactions with related features:

  • Fillet: a polygon corner can be filleted; the fillet replaces the corner node with the tangent-point pair and a fillet arc. The polygon's distance/equal constraints continue to reference the original vertex nodes.
  • Crossing: a polygon side may participate in a crossing with another segment — standard segment behaviour.
  • Modify → Move/Rotate: dragging any vertex or the centre node is permitted; the regular-polygon constraints will keep the shape regular as the solver re-runs.
  • Modify → Delete: deleting the feature removes the n segments, all 2n−1 owned constraints, and the centre node only when the polygon created it (centreNodeId !== '') and no sibling polygon's constraints still reference it (tools/polygon-tool.ts:259-296).
  • Copy & Array: replicatePolygons (transforms.ts:961-1073) clones the feature record under applyClone and applyCloneMirror. All vertex nodes of the source must be in the selection or the feature handle is dropped (segments are still cloned as loose primitives). The cloned polygon re-emits its own (n−1) equal + n distance constraints; the centre node is materialised at the transformed centre (deduped against any existing node).

7. Failure modes

createPolygon is total — every failure path returns the input scene unchanged with empty result ids; no toast, no partial geometry, no rollback needed:

  • n < 3 (or non-integer, or n > 1000): early return (tools/polygon-tool.ts:95-104). The PolygonDialog rejects submission first (PolygonDialog.tsx:47), so this only surfaces via the MotorScript builtin, which throws (executor/builtins/primitives.ts:379).
  • radius ≤ 0: same early return; dialog rejects (PolygonDialog.tsx:48); builtin throws (executor/builtins/primitives.ts:380).
  • Non-finite cx/cy/radius/rotationRad/sides: early return (tools/polygon-tool.ts:77-90); builtin throws (executor/builtins/primitives.ts:377).
  • Centre lookup miss after addNode (should not happen — defensive): early return (tools/polygon-tool.ts:134-143).
  • Vertex node lookup miss after addSegment dedup (defensive; the Euclidean tolerance fix in I-A-3 closed the original window): early return (tools/polygon-tool.ts:164-174).
  • Solver divergence: the parametric is well-posed by construction (position + rotation + size free), so divergence typically indicates an over-constraint from external user constraints. Standard solver-failure UI applies — failed ids surface in failedConstraintIds.

8. Figures

              • v1
             ╱   ╲
          v2•     •v0     ← rotationRad = 0 places v0 on +X from centre
            │  •  │       ← centre node C (construction, real Node)
          v3•     •v5
             ╲   ╱        ── distance(C, v0) is the free size handle
              • v4        ── distance(C, v_k) == distance(C, v0) for k=1..5
                          ── equal(side_0, side_k) for k=1..5

Figure 1: Committed hexagon (n=6) at centre C with radius line C→v0 highlighted. Capture conditions: empty scene, click centre at (0, 0), dialog Sides=6, Radius=10, Rotation=0.

centrePolygonDialogsides: 6radius: 50rotation: 0°Confirm

Figure 1: PolygonDialog after click 1 — centre node placed on canvas, modal dialog gathers sides, radius, and rotation atomically.

5 × equal(side_0, side_k) + 6 × distance(C, v_k)C

Figure 2: Committed hexagon — 6 vertex nodes, 1 centre node, 6 sides; constraint glyphs mark the 5 equal-side ties and the 6 centre-to-vertex distance constraints.

9. Known bugs

None known. > Note: PolygonFeature doc-comment in scene-model.ts:532-537 says "next sides entries: distance from centre to each vertex" but also parenthetically states "(actually: (sides−1) distance constraints …)". The implementation in tools/polygon-tool.ts:203-214 emits sides distance constraints (one per vertex, including vert). The first bullet of the comment is correct; the parenthetical is stale. Worth a docstring cleanup but not a behavioral bug.

10. Class API

In the current model, polygons are class instances rather than plain objects. Like RectFeature, PolygonFeature is a thin owner — it holds id tuples only; the physical records live in the Scene-level Map indices.

  • Class file: app/src/lib/fea2d/model/PolygonFeature.ts
  • Constructor: new PolygonFeature(id, sides: number, segmentIds: readonly SegmentId[], nodeIds: readonly NodeId[], centreNodeId: NodeId | '', constraintIds: readonly ConstraintId[]) — validates sides >= 3 and integer; segmentIds.length === sides; nodeIds.length === sides; permits constraintIds.length === 0 (legacy fixtures) or === 2 * sides - 1 (authored from the polygon tool). centreNodeId === '' represents "no owned centre".
  • Snapshot type: PolygonFeatureSnapshot (app/src/lib/fea2d/model/snapshots/PolygonFeature.ts).
  • Lifecycle methods:
    • toJSON: PolygonFeatureSnapshot
    • static fromJSON(snap, _scene?): PolygonFeature
    • clone: PolygonFeature
    • equals(other: SceneEntity): boolean
  • Polymorphic surface (SceneFeature):
    • referencesEntity(id: string): boolean — true when id matches the feature id, the (optional) centre node, or any owned segment / node / constraint id.
    • isStale(_scene: unknown): boolean — returns false (delete + regenerate, no re-eval).

Public fields are readonly: PolygonFeature is a thin owner — every mutation is delete + regenerate.

motordevs studio — geometry editor specification