import { describe, expect, it } from 'vitest'; import { defaultProject } from '../src/lib/defaults'; import { flooringCuts, groupFlooringCuts } from '../src/lib/cut-list'; import { planFlooring } from '../src/lib/flooring'; import type { FlooringPiece, FlooringPlan } from '../src/lib/types'; function piece(id: string, changes: Partial = {}): FlooringPiece { return { id, roomId: 'room-1', row: 1, column: 1, polygon: [], length: 600, width: 190, stockId: 'B1', reused: false, cut: true, shaped: false, template: [], factoryLeft: true, factoryRight: false, ripEdge: null, ...changes, }; } function plan(pieces: FlooringPiece[]): FlooringPlan { return { pieces, rows: [], stocks: [], boardCount: 0, cuts: 0, wasteArea: 0, wastePercent: 0, reusedCount: 0, coveredArea: 0, issues: [], }; } describe('flooring cut batches', () => { it('combines matching cuts across rows and stocks, preserving every placement exactly once', () => { const pieces = [ piece('F1'), piece('F2', { row: 4, stockId: 'B4', reused: true }), piece('F3', { cut: false, length: 1200 }), piece('F4', { cut: false, reused: true }), piece('F5', { roomId: 'room-2' }), ]; const source = plan(pieces), before = structuredClone(source); const groups = groupFlooringCuts(source); expect(groups.map((group) => group.map((p) => p.id))).toEqual([['F1', 'F2'], ['F4'], ['F5']]); expect( groups .flat() .map((p) => p.id) .sort(), ).toEqual( flooringCuts(source) .map((p) => p.id) .sort(), ); expect(source).toEqual(before); expect(groupFlooringCuts(source)).toEqual(groups); }); it('keeps different factory ends, rip edges and thousandth-millimetre dimensions separate', () => { const groups = groupFlooringCuts( plan([ piece('F1'), piece('F2', { factoryLeft: false, factoryRight: true }), piece('F3', { ripEdge: 'top' }), piece('F4', { ripEdge: 'bottom' }), piece('F5', { length: 600.001 }), piece('F6', { width: 190.001 }), ]), ); expect(groups).toHaveLength(6); }); it('groups shapes only when their complete measured templates match', () => { const template = [ { x: 0, y: 0 }, { x: 600, y: 0 }, { x: 600, y: 190 }, { x: 0, y: 100 }, ]; const groups = groupFlooringCuts( plan([ piece('F1', { shaped: true, template }), piece('F2', { shaped: true, template: structuredClone(template) }), piece('F3', { shaped: true, template: [...template.slice(0, 3), { x: 0, y: 100.001 }] }), piece('F4'), ]), ); expect(groups.map((group) => group.map((p) => p.id))).toEqual([['F1', 'F2'], ['F3'], ['F4']]); }); it('reduces the default room to a few batches while keeping all cut and offcut codes', () => { const project = defaultProject(); const floor = planFlooring(project.rooms, project.flooring, project.tolerance); const groups = groupFlooringCuts(floor); expect(groups.length).toBeLessThanOrEqual(7); expect(groups.length).toBeLessThan(flooringCuts(floor).length / 2); expect( groups .flat() .map((p) => p.id) .sort(), ).toEqual( flooringCuts(floor) .map((p) => p.id) .sort(), ); expect(new Set(groups.flat().map((p) => p.id)).size).toBe(flooringCuts(floor).length); }); });