better patterns
All checks were successful
Deploy to Pages / build (push) Successful in 26s

This commit is contained in:
Andras Schmelczer 2026-09-11 22:46:23 +01:00
commit 171fbf1927
9 changed files with 2589 additions and 37 deletions

View file

@ -2,14 +2,16 @@ import { describe, expect, it } from 'vitest';
import { plannerCases } from '../benchmarks/cases';
import {
compareFlooring,
compareCurrentFlooring,
compareSkirting,
flooringMetrics,
skirtingMetrics,
} from '../benchmarks/metrics';
import baseline from '../benchmarks/baseline.json';
import hBaseline from '../benchmarks/h-pattern-baseline.json';
import { planFlooringOptions } from '../src/lib/flooring';
import { planSkirting } from '../src/lib/skirting';
import { defaultProject, rectangle } from '../src/lib/defaults';
import { defaultProject, fireplace, rectangle } from '../src/lib/defaults';
import { buildGeometry, insetPolygon, signedArea } from '../src/lib/geometry';
import type { FlooringPlan, Project } from '../src/lib/types';
@ -118,8 +120,15 @@ describe('measured optimiser quality', () => {
const random = flooringMetrics(options.random, project.flooring);
const before = baseline.find((row) => row.name === name)!;
expect(compareFlooring(cuts, before.flooring)).toBeLessThanOrEqual(0);
expect(compareFlooring(cuts, random)).toBeLessThanOrEqual(0);
expect(compareFlooring(random, cuts, true)).toBeLessThanOrEqual(0);
expect(compareCurrentFlooring(cuts, random)).toBeLessThanOrEqual(0);
expect(compareCurrentFlooring(random, cuts, true)).toBeLessThanOrEqual(0);
const previousAppearance = hBaseline.find((row) => row.name === name)!;
expect(compareCurrentFlooring(random, previousAppearance.random, true)).toBeLessThanOrEqual(
0,
);
expect(random.nearH).toBeLessThanOrEqual(previousAppearance.random.nearH);
// Only this fixture's 600 mm minimum pieces force alternating starts.
if (name !== 'forced-alternation') expect(random.nearH).toBe(0);
validateFlooring(options.cuts, project);
if (options.random !== options.cuts) validateFlooring(options.random, project);
expect(compareSkirting(skirt, before.skirting)).toBeLessThanOrEqual(0);
@ -129,17 +138,20 @@ describe('measured optimiser quality', () => {
);
it('retains the measured improvements across the complete fixed corpus', () => {
const totals = { cuts: 0, randomCuts: 0, pattern: 0, skirtingCuts: 0 };
const totals = { cuts: 0, randomCuts: 0, pattern: 0, nearH: 0, skirtingCuts: 0 };
for (const c of cases) {
const { options, skirt } = resultFor(c);
totals.cuts += options.cuts.cuts;
totals.randomCuts += options.random.cuts;
totals.pattern += flooringMetrics(options.random, c.project.flooring).patternPenalty;
totals.nearH += flooringMetrics(options.random, c.project.flooring).nearH;
totals.skirtingCuts += skirt.cuts;
}
expect(totals.cuts).toBeLessThanOrEqual(1741); // Original: 2177.
expect(totals.randomCuts).toBeLessThanOrEqual(1994);
expect(totals.pattern).toBeLessThanOrEqual(113); // Original: 265.
// Broader H avoidance costs more cuts, while still beating the original 2177 passes.
expect(totals.randomCuts).toBeLessThanOrEqual(2113);
expect(totals.nearH).toBeLessThanOrEqual(6); // Before the visual correction: 557.
expect(totals.pattern).toBeLessThanOrEqual(346); // Retain the separate legacy diagnostic.
expect(totals.skirtingCuts).toBeLessThanOrEqual(1436); // Original: 1447.
});
@ -154,4 +166,18 @@ describe('measured optimiser quality', () => {
expect(plan.cuts).toBe(750);
expect(plan.coveredArea).toBe(4500 * 95000);
});
it.each(Array.from({ length: 10 }, (_, i) => i + 1))(
'avoids broad H joints with independent room seed %i',
(seed) => {
const project = defaultProject();
project.rooms = [
seed % 2 ? rectangle(`holdout-${seed}`) : fireplace(`holdout-${seed}`, 'Fireplace'),
];
const plan = planFlooringOptions(project.rooms, project.flooring, project.tolerance).random;
expect(flooringMetrics(plan, project.flooring).nearH).toBe(0);
expect(plan.boardCount).toBeLessThanOrEqual(seed % 2 ? 76 : 74);
validateFlooring(plan, project);
},
);
});