Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

blank: proper barrel hinge with moving attachment point

Replaces fixed-pivot rotation with physically correct barrel hinge:
- Barrel center at (hh + lidThick/2, 0), radius R = hh + lidThick/2
- Attachment point traces semicircular arc from base top-back to bottom-back
- 2:1 gear ratio: lid rotates 2π while barrel sweeps π
- At 0°: screen face flush on base top (4 front points at y=0)
- At 2π: outer face flush on base bottom (4 back points at y=2*hh)
- Natural clearance built into barrel radius — no gap hack needed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+38 -39
+38 -39
system/public/aesthetic.computer/disks/blank.mjs
··· 139 139 140 140 // Dimensions from spec: 293mm × 207mm × 19.9mm 141 141 const hw = 1.44, hh = 0.07, hd = 1.0; 142 - const lidThick = hh * 0.9; // lid noticeably thinner than base (display panel) 143 - const gap = hh * 0.6; // visible gap at hinge for clearance 142 + const lidThick = hh * 0.9; // lid thinner than base (display panel) 143 + 144 + // Barrel hinge: attachment point travels a semicircular arc 145 + // from base top-back (closed) to base bottom-back (tablet). 146 + // Barrel radius = half total stack height (base + lid clearance) 147 + const R = hh + lidThick / 2; 144 148 145 - // Pivot point: at the hinge seam between base and lid (y=0, z=0) 146 - // When flat at 180°, base extends forward (+z) and lid extends backward (-z) 147 - // symmetrically — looks level from any camera angle. 148 - const pivotY = 0; 149 - const pivotZ = 0; 149 + // Attachment point on the barrel circle 150 + // hingeAngle 0 → top-back (0, 0), hingeAngle 2π → bottom-back (2*hh, 0) 151 + const attachY = R * (1 - cos(hingeAngle / 2)); 152 + const attachZ = R * sin(hingeAngle / 2); 150 153 151 - // Base slab: extends forward from pivot 152 - // y: 0 (top) to 2*hh (bottom), z: 0 to 2*hd 154 + // Base slab: y 0 (top) to 2*hh (bottom), z 0 (back) to 2*hd (front) 153 155 const base = [ 154 156 [-hw, 0, 0], [hw, 0, 0], [hw, 2 * hh, 0], [-hw, 2 * hh, 0], 155 157 [-hw, 0, 2 * hd], [hw, 0, 2 * hd], [hw, 2 * hh, 2 * hd], [-hw, 2 * hh, 2 * hd], 156 158 ]; 157 159 160 + // Lid local: screen face at y=0, outer face at y=-lidThick 161 + // z from 0 (hinge edge) to 2*hd (front edge) 158 162 const cosH = cos(hingeAngle), sinH = sin(hingeAngle); 159 - 160 - // Lid centered on base midpoint (y=hh) so both halves align when flat 161 - // At 180°: y flips around pivot → lid centered at y=hh, same as base center 162 - const lidMid = -hh; // flips to +hh at 180° to match base midpoint 163 163 const lidLocal = [ 164 - [-hw, lidMid + lidThick / 2, gap], [hw, lidMid + lidThick / 2, gap], 165 - [hw, lidMid - lidThick / 2, gap], [-hw, lidMid - lidThick / 2, gap], 166 - [-hw, lidMid + lidThick / 2, 2 * hd], [hw, lidMid + lidThick / 2, 2 * hd], 167 - [hw, lidMid - lidThick / 2, 2 * hd], [-hw, lidMid - lidThick / 2, 2 * hd], 164 + [-hw, 0, 0], [hw, 0, 0], 165 + [hw, -lidThick, 0], [-hw, -lidThick, 0], 166 + [-hw, 0, 2 * hd], [hw, 0, 2 * hd], 167 + [hw, -lidThick, 2 * hd], [-hw, -lidThick, 2 * hd], 168 168 ]; 169 169 170 - // Rotate lid around pivot (y=0, z=0 in local = pivot point) 170 + // Lid transform: rotate by hingeAngle (2:1 vs barrel sweep), translate to attachment 171 171 const lid = lidLocal.map(([lx, ly, lz]) => { 172 172 const ry = ly * cosH - lz * sinH; 173 173 const rz = ly * sinH + lz * cosH; 174 - return [lx, ry + pivotY, rz + pivotZ]; 174 + return [lx, attachY + ry, attachZ + rz]; 175 175 }); 176 176 177 - // Hinge barrels — wider than tall, rotate WITH the lid 178 - const barrelW = 0.28; // wide 179 - const barrelH = hh * 1.6; // shorter than wide 177 + // Hinge barrels — centered at barrel center, rotate at barrel sweep rate 178 + const barrelCY = R; // = hh + lidThick/2 179 + const barrelCZ = 0; 180 + const barrelW = 0.28; 181 + const barrelH = hh * 1.6; 180 182 const barrelD = hh * 1.4; 181 183 const barrelPositions = [-hw * 0.65, hw * 0.65]; 184 + const barrelSweep = hingeAngle / 2; 185 + const cB = cos(barrelSweep), sB = sin(barrelSweep); 182 186 const hingeVerts = []; 183 187 for (const bx of barrelPositions) { 184 188 const bw = barrelW / 2, bh = barrelH / 2, bd = barrelD / 2; 185 - // Barrel centered on base midpoint (y=hh) 186 189 const barrelLocal = [ 187 - [bx - bw, hh - bh, -bd], [bx + bw, hh - bh, -bd], 188 - [bx + bw, hh + bh, -bd], [bx - bw, hh + bh, -bd], 189 - [bx - bw, hh - bh, bd], [bx + bw, hh - bh, bd], 190 - [bx + bw, hh + bh, bd], [bx - bw, hh + bh, bd], 190 + [bx - bw, -bh, -bd], [bx + bw, -bh, -bd], 191 + [bx + bw, bh, -bd], [bx - bw, bh, -bd], 192 + [bx - bw, -bh, bd], [bx + bw, -bh, bd], 193 + [bx + bw, bh, bd], [bx - bw, bh, bd], 191 194 ]; 192 - // Rotate barrels with the lid (half the lid angle — they sit at the joint) 193 - const halfA = hingeAngle * 0.5; 194 - const cH = cos(halfA), sH = sin(halfA); 195 195 hingeVerts.push(barrelLocal.map(([vx, vy, vz]) => { 196 - const ry = vy * cH - vz * sH; 197 - const rz = vy * sH + vz * cH; 198 - return [vx, ry + pivotY, rz + pivotZ]; 196 + const ry = vy * cB - vz * sB; 197 + const rz = vy * sB + vz * cB; 198 + return [vx, barrelCY + ry, barrelCZ + rz]; 199 199 })); 200 200 } 201 201 ··· 363 363 const inset = 0.15; 364 364 const bezelInset = 0.08; 365 365 const hingeInset = 0.35; // larger inset at hinge end (away from base) 366 - // Screen is on the INNER face of the lid (y = 0 in lid local). 367 - const screenY = lidMid + lidThick / 2 + 0.002; 366 + // Screen is on the INNER face of the lid (y=0 in lid local). 367 + const screenY = 0.002; 368 368 const screenTL = [-hw + inset, screenY, 2 * hd - inset]; 369 369 const screenTR = [hw - inset, screenY, 2 * hd - inset]; 370 370 const screenBL = [-hw + inset, screenY, hingeInset]; 371 371 const screenBR = [hw - inset, screenY, hingeInset]; 372 372 373 373 // Bezel corners (slightly larger than screen) 374 - const bezelY = lidMid + lidThick / 2 + 0.001; 375 - // Screen faces toward base (positive y direction from lid) 374 + const bezelY = 0.001; 376 375 const bezelTL = [-hw + bezelInset, bezelY, 2 * hd - bezelInset]; 377 376 const bezelTR = [hw - bezelInset, bezelY, 2 * hd - bezelInset]; 378 377 const bezelBL = [-hw + bezelInset, bezelY, hingeInset - 0.07]; 379 378 const bezelBR = [hw - bezelInset, bezelY, hingeInset - 0.07]; 380 379 381 - // Same transform as lid vertices (rotate around barrel center) 380 + // Same transform as lid vertices (barrel hinge) 382 381 const hingeXform = ([lx, ly, lz]) => { 383 382 const ry = ly * cosH - lz * sinH; 384 383 const rz = ly * sinH + lz * cosH; 385 - return [lx, ry + pivotY, rz + pivotZ]; 384 + return [lx, attachY + ry, attachZ + rz]; 386 385 }; 387 386 const sTL = hingeXform(screenTL), sTR = hingeXform(screenTR); 388 387 const sBL = hingeXform(screenBL), sBR = hingeXform(screenBR);