Commit Graph

2 Commits

Author SHA1 Message Date
m3ultra
18099c8e6f Align sail lane to contracts.js; free blown corners so they flog
Rebased onto M0 and reconciled against the real spine. checkContract
('sailRig') now conforms and js/tests/b.test.js runs 28 asserts green.

Contract fixes:
  - anchor.sway(t) is the ABSOLUTE position, not an offset (thanks A —
    I had it adding sway to pos, which would have flung every
    tree-anchored corner to double its coordinates).
  - events is an Emitter emitting {type, corner}, not a drained array.
  - coverageOver() rects are centre+size, matching world.gardenBed. It
    consumes world.sunDir directly: a hit along sunDir means shaded.
  - START_BUDGET/SPARE_COST/HARDWARE/FIXED_DT now come from contracts.js
    rather than being redeclared here.

Bug: a corner that blew was marked broken but never had its mass
returned, so invMass stayed 0 and the "blown" corner sat welded in
mid-air — no flogging, and the sail silently went dead. PLAN3D §5-B
wants flogging emergent from the freed node, so _checkFailure now frees
it. The cascade test missed this because it called _repin() by hand;
the new test drives a real overload failure instead and asserts the
corner tears 2 m off its anchor and keeps moving.

Tension dial remapped from the prototype's rest/tension to a real
pre-strain. rest/tension asks for 17% strain at dial 1.2 and 29% at 1.4
— stretching an 18 m sail by three metres — and put 68 kN on a corner of
the yard's biggest quad with no wind blowing. At 0.10 strain-per-dial it
swings a 5x5 rig's peak load 2.1x loose-to-tight and redlines a 192 m2
quad at 8.3 kN drum-tight, which is punishing and correct.

HARDWARE ratings retuned in contracts.js to real newtons per the
standing note there that Lane B owns these numbers. Costs and tier shape
untouched; $80 still buys rated hardware on at most 2 of 4 corners.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:55:37 +10:00
m3ultra
06ec4cbea2 Add 3D sail cloth sim with per-face wind and XPBD corner loads
Verlet cloth on a bilinear patch between 4 anchors, N=10 grid,
structural/shear/bend constraints, 5 relaxation iterations at a fixed
1/60 substep. Wind is applied per FACE so hypar twist genuinely sheds
load rather than being cosmetic.

Two deviations from PLAN3D worth flagging:

- Load is read from each constraint's XPBD Lagrange multiplier, not from
  FABRIC_K * leftover-stretch. After a fixed iteration count the leftover
  stretch is solver error, not fabric strain, so the naive reading came
  out ~50x hot (60 kN peaks on a 5x5 m sail). The multiplier is the real
  constraint impulse, which the statics assert confirms by balancing the
  corner reactions against the applied wind to 8%.

- Wind uses a signed square (d*|d|) rather than clamp(d)^2, so the
  leeward face is pushed too. A sail is double-sided.

The sim core deliberately does not import three.js: it runs headless
under node today, stays allocation-free in the hot loop, and replays
bit-for-bit. createSailView() pulls three in lazily for rendering.

Loads land in real newtons (~1-4 kN on a 5x5 m sail in a 34 m/s storm),
so hardware ratings are real working load limits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:55:37 +10:00