import type { FlooringPiece, FlooringPlan, FlooringSettings, SkirtingPiece, SkirtingPlan, } from './types'; const sameLength = (a: number, b: number) => Math.abs(a - b) < 0.01; /** A ready offcut still needs a placement code; only fresh, uncut whole boards are omitted. */ export const flooringCuts = (plan: FlooringPlan) => plan.pieces.filter((piece) => piece.cut || piece.reused); export function isSkirtingCut(piece: SkirtingPiece, plan: SkirtingPlan): boolean { const stock = plan.stocks.find((candidate) => candidate.id === piece.stockId); return ( !stock || !sameLength(piece.length, stock.length) || Math.abs(piece.leftMitre) > 0.001 || Math.abs(piece.rightMitre) > 0.001 ); } export const skirtingCuts = (plan: SkirtingPlan) => plan.pieces.filter((piece) => isSkirtingCut(piece, plan)); export function uncutFlooring(plan: FlooringPlan, settings: FlooringSettings) { const uncut = plan.pieces.filter((piece) => !piece.cut); const wholeBoards = uncut.filter( (piece) => sameLength(piece.length, settings.length) && sameLength(piece.width, settings.width), ).length; return { wholeBoards, readyOffcuts: uncut.length - wholeBoards }; } export function uncutSkirting(plan: SkirtingPlan): { length: number; count: number }[] { const counts = new Map(); for (const piece of plan.pieces) { if (!isSkirtingCut(piece, plan)) counts.set(piece.length, (counts.get(piece.length) ?? 0) + 1); } return [...counts].sort(([a], [b]) => a - b).map(([length, count]) => ({ length, count })); } /** Ready remnants need placement instructions, never another trimming instruction. */ export function readyOffcutInstructions(piece: FlooringPiece): string { const ends = piece.factoryLeft && piece.factoryRight ? 'both' : piece.factoryLeft ? 'left' : piece.factoryRight ? 'right' : 'neither'; return `Use offcut; no new cut. Required factory short ends: ${ends}.`; } /** Keep every cut from a purchased board together, in allocation order across rooms. */ function groupByStock(pieces: T[], stocks: { id: string }[]): T[][] { const groups = new Map(stocks.map((stock) => [stock.id, []])); for (const piece of pieces) { const group = groups.get(piece.stockId); if (group) group.push(piece); else groups.set(piece.stockId, [piece]); } return [...groups.values()].filter((group) => group.length > 0); } export const groupFlooringCuts = (plan: FlooringPlan) => groupByStock(flooringCuts(plan), plan.stocks); export const groupSkirtingCuts = (plan: SkirtingPlan) => groupByStock(skirtingCuts(plan), plan.stocks);