All files / web/src/components/create/abacus abacus-fabrication.ts

100% Statements 71/71
100% Branches 1/1
100% Functions 1/1
100% Lines 71/71

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 721x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 4x 4x 4x 4x 4x 4x 4x  
// Abacus Studio — the fabrication axis + the fabrication-neutral intent seam
// (Gitea epic #5, CP2).
//
// An AbacusDesign says WHAT abacus the user wants; it does not say HOW that
// becomes a physical object. The "how" is a separate axis — FabricationKind —
// and the two realizers (the stick-on ArUco paper marker sheet, the FDM 3D
// print) are two projections of ONE shared design.
//
// `intentOf` is the seam between them: the fabrication-neutral core both
// realizers agree on (column count, per-column colors, corner-marker geometry),
// DERIVED from the design so the paper output can never silently drift from the
// printed one. The FDM realizer additionally consumes the full params (printer
// profile, clearance, OpenSCAD knobs); the paper realizer needs only this
// intent. Pure and framework-free — no React, no three, no geometry.
 
import type { AbacusDesign, ResolvedColors } from './abacus-design'
 
// The output axis. 'paper' = stick-on ArUco corner markers for an abacus you
// already own — the express lane, loads none of the 3D stack. 'fdm' = a printed
// frame with the markers baked in. These two are the whole axis.
export type FabricationKind = 'paper' | 'fdm'
 
export interface FabricationTarget {
  kind: FabricationKind
}
 
// 3D print is the default: the studio leads with its most compelling output — a
// spinnable printed frame — rather than opening on the utilitarian marker sheet.
// This is a deliberate product trade: the no-3D-printer majority now pays the
// three.js/OpenSCAD download on first paint, in exchange for the exciting path
// being the star instead of hidden behind a control. Paper stays one tap away
// and, when selected, still mounts none of the 3D stack (the express lane and
// its code-split are unchanged — see the express-lane-isolation guard).
export const DEFAULT_FABRICATION: FabricationTarget = { kind: 'fdm' }
 
// One source of truth for the axis chooser's presentation, so the label/icon
// can't fork across the surfaces that render it (left rail today, more later).
export const FABRICATION_OPTIONS: ReadonlyArray<{
  kind: FabricationKind
  label: string
  icon: string
}> = [
  { kind: 'paper', label: 'Paper markers', icon: '🏷️' },
  { kind: 'fdm', label: '3D print', icon: '🖨️' },
]
 
// The four-corner ArUco geometry both realizers place. `sizeMm` is the printed
// edge length of one marker; `enabled` is whether the design carries markers at
// all (a plain abacus can opt out).
export interface MarkerIntent {
  enabled: boolean
  sizeMm: number
}
 
// The fabrication-neutral core of a design: everything a realizer needs that is
// independent of HOW the abacus is made. Never authored — always derived — so
// the paper and FDM realizations stay in lockstep with the one shared design.
export interface AbacusIntent {
  columns: number
  resolvedColors: ResolvedColors
  markers: MarkerIntent
}
 
export function intentOf(design: AbacusDesign): AbacusIntent {
  const { params, resolvedColors } = design
  return {
    columns: params.cols,
    resolvedColors,
    markers: { enabled: params.show_markers, sizeMm: params.marker_mm },
  }
}