[object Object]

← back to Quadrille Showroom

auto-save: 2026-07-02T11:47:40 (2 files) — public/js/rack.js public/js/showroom.js

e7f3b0f0a5753ac435adb24d29d88b824b1dbe36 · 2026-07-02 11:47:44 -0700 · Steve Abrams

Files touched

Diff

commit e7f3b0f0a5753ac435adb24d29d88b824b1dbe36
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Jul 2 11:47:44 2026 -0700

    auto-save: 2026-07-02T11:47:40 (2 files) — public/js/rack.js public/js/showroom.js
---
 public/js/rack.js     | 43 +++++++++++++++++++++++++++++++++++++------
 public/js/showroom.js | 15 +++++++++++++--
 2 files changed, 50 insertions(+), 8 deletions(-)

diff --git a/public/js/rack.js b/public/js/rack.js
index 6782ec1..a25aa83 100644
--- a/public/js/rack.js
+++ b/public/js/rack.js
@@ -51,6 +51,15 @@
   // The continuous `indexOffset` (Chunk I carousel) shifts every board by the same
   // fractional number of slots so the rack conveyor-slides as one piece.
   function arcAngleFor(i, n, cfg) {
+    // LINEAR mode (Steve 2026-07-02): NO semicircular arc — the wings hang on a
+    // STRAIGHT rail centred against the back wall, one per peg-hole, 2" apart.
+    // Here "angle" degenerates to the board's X position on that straight rail so
+    // railPointFor/faceCentreYaw stay the only consumers (call sites unchanged).
+    if (cfg && cfg.layout === 'linear') {
+      const pitch = (cfg.pegPitchM || 0.0508) * (cfg.sliverPitchScale || 1); // 2" default
+      const run = (n > 1 ? (n - 1) * pitch : 0);
+      return -run / 2 + (i + indexOffset) * pitch;   // centred on x=0
+    }
     const span = cfg.arcSpanDeg * Math.PI / 180;
     const pitch = (n > 1 ? span / (n - 1) : span) * (cfg.sliverPitchScale || 1);
     const centre = (typeof cfg.arcCentreDeg === 'number' ? cfg.arcCentreDeg : 90) * Math.PI / 180;
@@ -67,6 +76,12 @@
 
   // World-space rail point for a given arc angle. The board hangs from this point.
   function railPointFor(angle, cfg) {
+    // LINEAR mode: `angle` IS the board's X on the straight back-wall rail; Z is
+    // fixed just in front of the back wall, the rail runs flat (tangentY = 0).
+    if (cfg && cfg.layout === 'linear') {
+      const z = (typeof cfg.backWallZ === 'number') ? cfg.backWallZ : -2.87;
+      return { x: angle, z, tangentY: 0 };
+    }
     const R = cfg.arcRadius, cx = cfg.arcCenter.x, cz = cfg.arcCenter.z;
     const x = cx + Math.cos(angle) * R;
     const z = cz + Math.sin(angle) * R;
@@ -79,6 +94,9 @@
   // (cx-x, cz-z); its Three.js Y-rotation (atan2(dx, dz)) orients the +Z face
   // of the plane to point that way.
   function faceCentreYaw(angle, cfg) {
+    // LINEAR mode: every board faces the viewer dead-on (+Z), so the open hero is
+    // flat to the room and the closed neighbours rake off that shared front pose.
+    if (cfg && cfg.layout === 'linear') return 0;
     const R = cfg.arcRadius, cx = cfg.arcCenter.x, cz = cfg.arcCenter.z;
     const x = cx + Math.cos(angle) * R, z = cz + Math.sin(angle) * R;
     return Math.atan2(cx - x, cz - z);
@@ -95,7 +113,12 @@
     const angle = arcAngleFor(i, n, cfg);
     const rp = railPointFor(angle, cfg);
     const openYaw = faceCentreYaw(angle, cfg);
-    const rake = revealToRake(reveal);
+    const linear = !!(cfg && cfg.layout === 'linear');
+    // LINEAR pegboard: a gentler rake so the wings lie NEAR-FLAT against the back
+    // wall (each showing a ~2" sliver of the next). They lean FORWARD off the wall
+    // (closedYaw uses −rake below) so a front board occludes its left neighbour —
+    // never raking back THROUGH the wall (the arc's 73° rake did, clipping them).
+    const rake = revealToRake(reveal) * (linear ? 0.34 : 1);
 
     // BASE rail position (no open-forward offset). flipUpdate adds the forward push for
     // the open hero ON TOP of this base, so the next relayout (carousel shift) re-reads a
@@ -104,10 +127,16 @@
     // Inward unit normal toward the arc centre (cx,cz) — the direction a board pushes when
     // it opens so its FACE comes forward toward the room centre / viewer, clearing the
     // raked neighbour slivers + frames out from in front of its pattern.
-    const cx = cfg.arcCenter.x, cz = cfg.arcCenter.z;
-    let inx = cx - rp.x, inz = cz - rp.z;
-    const ilen = Math.hypot(inx, inz) || 1;
-    ud.inX = inx / ilen; ud.inZ = inz / ilen;
+    if (cfg && cfg.layout === 'linear') {
+      // Straight rail: the hero comes forward off the back wall toward the viewer
+      // (+Z), no lateral drift — so it stays over its own peg slot as it opens.
+      ud.inX = 0; ud.inZ = 1;
+    } else {
+      const cx = cfg.arcCenter.x, cz = cfg.arcCenter.z;
+      let inx = cx - rp.x, inz = cz - rp.z;
+      const ilen = Math.hypot(inx, inz) || 1;
+      ud.inX = inx / ilen; ud.inZ = inz / ilen;
+    }
     // Half the board face width (face sits +halfWidth from the left hinge) — used by
     // flipUpdate to re-centre the open hero's face on the slot centre.
     ud.faceHalf = (cfg && cfg.boardWidthM ? cfg.boardWidthM : 0.762) / 2;
@@ -116,7 +145,9 @@
     ud.arcAngle = angle;
     ud.openYaw = openYaw;
     ud.rake = rake;
-    ud.closedYaw = openYaw + rake;     // raked CCW about the hinge → packed
+    // Arc rakes CCW (+) into the fan; the straight rail rakes FORWARD (−) off the
+    // wall so packed neighbours occlude to a sliver without clipping the back wall.
+    ud.closedYaw = openYaw + (linear ? -rake : rake);
     if (typeof ud.flip !== 'number') ud.flip = 0;           // 0 closed → 1 open
     pivot.rotation.y = ud.closedYaw + (ud.openYaw - ud.closedYaw) * ud.flip;
   }
diff --git a/public/js/showroom.js b/public/js/showroom.js
index 4a08208..1b4af41 100644
--- a/public/js/showroom.js
+++ b/public/js/showroom.js
@@ -33,7 +33,15 @@ const CONFIG = {
     arcSpanDeg: 96,
     arcCentreDeg: -90,               // sweep centred on -Z → boards sit in FRONT of the viewer
     arcCenter: { x: 0.0, z: 0.5 },   // arc centre just BEHIND the centred viewer → concave bank faces them
-    sliverPitchScale: 1.0            // multiplier on the angular pitch (reveal slider can thin)
+    sliverPitchScale: 1.0,           // multiplier on the pitch (reveal slider can thin)
+    // ---- STRAIGHT BACK-WALL RAIL (Steve 2026-07-02) ----
+    // Steve killed the semicircular "dry-cleaner" arc: the wings now hang on a
+    // STRAIGHT brass rail centred against the back wall, one wing per peg-hole,
+    // 2" (0.0508m) apart, packed so each closed wing shows a ~2" colour sliver;
+    // click one and it swings forward off the wall to the dead-on open hero.
+    layout: 'linear',
+    pegPitchM: 0.0508,               // 2" between peg-holes
+    backWallZ: -(6.10 / 2) + 0.18    // just in front of the back wall (z = -3.05 + 0.18)
   },
   // Camera FIXED at room centre, eye height ~1.6m, facing the wing bank (toward arc centre).
   // startPos.z pulled back to +0.7 (Steve 2026-06-30: the centred eye at z=0 sat ~2.4m from
@@ -1973,7 +1981,10 @@ async function loadProducts() {
   // mirrored sample book centred on the spawn's forward axis. The avatar spawns dead-centre,
   // stationary, facing it — NO auto-fly. [ ] / N-P page the catalog.
   if (BOOK_MODE) {
-    if (currentWallGroup) currentWallGroup.visible = false;   // no arc in the book view
+    // Steve 2026-07-02: KEEP the straight back-wall wing bank visible on load — all the
+    // other wings hang as ~2" colour slivers across the back wall — AND keep the centred
+    // open book in front (the "open boards on both sides" he likes). Was hidden before.
+    if (currentWallGroup) currentWallGroup.visible = true;
     buildCenterBook();
     setCenterBook(Math.floor((products.slice(0, windowSize).length) / 2));
     document.body.classList.add('book-mode');

← a156052 5x sweep 1: keep bottom-corner panels clear of the centre pa  ·  back to Quadrille Showroom  ·  chore: macstudio3 migration — reconcile from mac2 + repoint 17535f5 →