Skip to content

Lamp smooth mode is broken for contracting tiers (radius decreasing) — pieces vanish in 3D, and profile reflow destructively collapses drawn polygons #92

Description

@dompm

The math bug

For a frustum tier unrolled to an annulus, the slant distances are L = R / sin(α). For a contracting tier (top radius > bottom radius… i.e. Rt > Rb going down, e.g. the tulip profile's last tier 1200→1000), L_top > L_bot. Three places assume L_top < L_bot:

  1. lampGeometry.ts:287if (d < m.L_top - 0.5 || d > m.L_bot + 0.5) continue; — for a contracting tier every interior point satisfies d < L_top - 0.5, so patternToSurface returns null for the entire tier. Example: profile [{r:800,y:0},{r:400,y:1000}] → L_top≈2154, L_bot≈1077, valid d∈[1077,2154] is entirely rejected.
  2. lampGeometry.ts:291, lampGeometry.ts:502, Lamp3DPreview.tsx:241v = (d - L_top) / Math.max(1e-6, L_bot - L_top): the legitimately negative denominator (numerator and denominator both negative → v would be correct) is clamped to 1e-6, yielding v ≈ −5×10⁸, then clamped to 0 — every vertex collapses onto the tier's top ring.
  3. angleRel = atan2(bisectorSign*dx, bisectorSign*dy) (lampGeometry.ts:288/503, Lamp3DPreview.tsx:242) makes theta01 decrease with pattern-x for contracting tiers but increase for cylinders/expanding tiers → contracting tiers render horizontally mirrored relative to the rest of the lamp.

Why it's destructive, not just cosmetic

patternToSurfaceRobust has the same v bug, and reflowLampPoints (used live by LampProfileDialog.tsx:88-125 and on confirm) runs drawn polygons through it. Changing the profile with a contracting tier collapses the user's polygons onto an arc (zero area) — committed into project state, autosaved 500 ms later. Undo is the only recovery, and only if noticed immediately.

Repro

  1. Pick the tulip profile (last tier contracts) and set the facet slider to Smooth.
  2. Pieces in the contracting tier vanish from the 3D preview (vertexTo3D → null → skip at Lamp3DPreview.tsx:301-315) or collapse onto the top ring.
  3. Edit the profile → drawn pieces in that tier are flattened to arcs in the 2D pattern itself.

Related degenerate-tier bugs (same file)

  • Two consecutive profile points with equal y and r (easy to create — the editor's alignment snap at LampProfileDialog.tsx:416-420 sets exact values, and commit's sort at line 176 doesn't dedupe) produce a height = 0 cylinder tier → (py - m.topY) / m.height is 0/0 → NaN → Math.max(0, Math.min(1, NaN)) is NaN → silently corrupts the position buffer (lampGeometry.ts:279, Lamp3DPreview.tsx:235).
  • Profile points are only sorted on commit (LampProfileDialog.tsx:176), so live preview during editing feeds negative-height tiers to layout, and a profile drawn out of order commits a geometry the user never previewed.
  • A nearly-flat tier (sinα→1) unrolls to a θ≈2π annulus that overlaps the tiers stacked above/below in the 2D layout; patternToSurface (lampGeometry.ts:273) returns the first containing tier, so pieces drawn in the overlap silently map to the wrong tier — and the flat pattern is ambiguous/uncuttable there.

(For the record: the faceted trapezoid math and the expanding-tier unroll constants — θ = 2π·sinα, L = R/sinα — are correct; modeling each tier as a developable frustum is the right approach. It's specifically the contracting-tier orientation and the degenerate cases that are broken.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions