This commit is contained in:
parent
0c0f08b350
commit
171fbf1927
9 changed files with 2589 additions and 37 deletions
|
|
@ -7,6 +7,8 @@ remainders, fireplaces in both directions, room-order permutations, tight and ex
|
|||
stagger limits, fractional stock, large floors, and both supplied angled rooms.
|
||||
`--output=/tmp/results.json` saves a run; `--compare=/tmp/results.json` compares
|
||||
against a previous iteration. Do not overwrite the baseline to hide regressions.
|
||||
`h-pattern-baseline.json` separately freezes both options immediately before the
|
||||
visual H-pattern correction, evaluated using the broader alignment metric below.
|
||||
|
||||
## Fixed metrics
|
||||
|
||||
|
|
@ -14,18 +16,23 @@ against a previous iteration. Do not overwrite the baseline to hide regressions.
|
|||
and additional shaping edges. Skirting includes bevel and coping operations.
|
||||
- **Cut difficulty:** shaping passes, then rip passes, used to break equal saw-count
|
||||
ties. This is an explicit proxy, not an estimate of minutes or tool-specific effort.
|
||||
- **Random appearance:** the existing repetition penalty, evaluated independently
|
||||
- **Legacy repetition:** the original penalty, evaluated independently
|
||||
from the output rows. It penalises nearby repeating joints, similar consecutive
|
||||
steps and staircases, including steps that wrap across a board length. Raw repeat,
|
||||
similar-step and staircase counts are also reported. A lower score is better.
|
||||
similar-step and staircase counts are also reported. This stays unchanged for
|
||||
historical comparisons, but its 3%-of-board-length alignment tolerance missed visible H patterns.
|
||||
- **Near-H row pairs:** joints two rows apart separated by less than one board width,
|
||||
capped at one sixth of the board length. This catches the 40–150 mm misalignments
|
||||
that still look like an H. Current appearance cost is the legacy penalty plus
|
||||
12 per near-H pair, so avoiding an H outweighs several same-direction steps.
|
||||
- **Waste:** purchased stock minus installed material, including kerf. The report
|
||||
gives board counts and waste percentages weighted by purchased stock.
|
||||
- **Validity and runtime:** errors, elapsed time, p95 and maximum case time. Tests
|
||||
separately verify coverage, non-overlapping row bands, minimum sizes, stagger,
|
||||
factory ends, rounded stock consumption, cut accounting, references and input purity.
|
||||
|
||||
Minimise cuts orders `(passes, shaping, rips, pattern penalty, boards)`.
|
||||
Maximise randomness orders `(pattern penalty, passes, shaping, rips, boards)`.
|
||||
Minimise cuts orders `(passes, shaping, rips, appearance cost, boards)`.
|
||||
Maximise randomness orders `(appearance cost, passes, shaping, rips, boards)`.
|
||||
Validity precedes either objective. Skirting orders `(passes, waste)`.
|
||||
|
||||
## Measured results
|
||||
|
|
@ -34,25 +41,52 @@ Totals across the same 84 cases, September 2026:
|
|||
|
||||
| Metric | Original | Minimise cuts | Maximise randomness |
|
||||
| ------------------- | -------: | ------------: | ------------------: |
|
||||
| Flooring saw passes | 2,177 | **1,741** | **1,994** |
|
||||
| Shaping passes | 192 | 184 | 185 |
|
||||
| Rip passes | 414 | 402 | 405 |
|
||||
| Pattern penalty | 265 | 1,349 | **113** |
|
||||
| Flooring boards | 3,561 | 3,537 | 3,579 |
|
||||
| Flooring waste | 8.20% | 7.57% | 8.66% |
|
||||
| Flooring saw passes | 2,177 | **1,741** | **2,113** |
|
||||
| Shaping passes | 192 | 184 | 188 |
|
||||
| Rip passes | 414 | 402 | 409 |
|
||||
| Legacy penalty | 265 | 2,207 | 346 |
|
||||
| Flooring boards | 3,561 | 3,548 | 3,551 |
|
||||
| Flooring waste | 8.20% | 7.85% | 7.94% |
|
||||
| Invalid cases | 0 | 0 | 0 |
|
||||
|
||||
Minimise cuts saves **20.0%** of saw passes. Maximise randomness reduces the pattern
|
||||
penalty **57.4%**, while also saving **8.4%** of saw passes. For random flooring in the
|
||||
default 4,800 × 3,600 mm room, 38 passes become **24** or **30**, respectively; both options use 76
|
||||
Minimise cuts saves **20.0%** of saw passes. Maximise randomness saves **2.9%** of saw
|
||||
passes against the original planner. For random flooring in the default
|
||||
4,800 × 3,600 mm room, 38 passes become **24** or **31**, respectively; both options use 76
|
||||
boards and have approximately 1.2% waste.
|
||||
|
||||
Skirting drops from **1,447 to 1,436 passes**. It uses 521 boards instead of 513:
|
||||
cutting effort now takes precedence over waste, which rises from 9.28% to 10.68%.
|
||||
Eight cases improve and 76 tie.
|
||||
For flooring, 78 cut-priority cases improve and six tie. Appearance priority improves
|
||||
72, ties 11, and uses one extra cut in `rectangle-38` despite the same zero pattern
|
||||
penalty. The bounded search does not establish global optimality.
|
||||
For flooring, 78 cut-priority cases improve and six tie against the original planner.
|
||||
The bounded search does not establish global optimality.
|
||||
|
||||
## H-pattern correction
|
||||
|
||||
The first optimisation reported a 57% lower legacy penalty, but visual inspection
|
||||
showed that the metric was too narrow. The default appearance-priority room still
|
||||
had **11 near-H row pairs**, despite no repeats within the old 36 mm tolerance.
|
||||
The candidate hash also generated almost identical offsets for adjacent candidate
|
||||
numbers. A final bit-mixing step now spreads those candidates across the allowed range.
|
||||
|
||||
| Metric | Cuts before | Cuts now | Random before | Random now |
|
||||
| ------------------------- | ----------: | -------: | ------------: | ---------: |
|
||||
| Near-H row pairs | 515 | 247 | 557 | **6** |
|
||||
| Saw passes | 1,741 | 1,741 | 1,994 | 2,113 |
|
||||
| Boards | 3,537 | 3,548 | 3,579 | 3,551 |
|
||||
| Waste | 7.57% | 7.85% | 8.66% | 7.94% |
|
||||
| Default-room near-H pairs | 17 | 17 | 11 | **0** |
|
||||
| Default-room saw passes | 24 | 24 | 30 | 31 |
|
||||
|
||||
Appearance priority removes **98.9%** of near-H pairs for **6.0%** more cuts than the
|
||||
previous iteration. All six remaining pairs are in `forced-alternation`, whose
|
||||
600 mm minimum pieces force alternating starts. It improves 79 cases and ties five
|
||||
under the corrected objective. Ten additional room seeds also produce zero near-H pairs.
|
||||
|
||||
The tradeoff is explicit: the old direction-run/staircase score increases, and the
|
||||
randomness option uses more cuts. Cuts-first retains its total saw count but three
|
||||
individual cases use extra cuts (`two-rooms-reversed-6`, `angled`, `angled-f8`).
|
||||
It can still show H patterns when avoiding them requires extra cuts; the UI explains
|
||||
this beside the laying-pattern choice. Both frozen baselines remain unchanged.
|
||||
|
||||
## Iterations retained
|
||||
|
||||
|
|
@ -74,6 +108,9 @@ penalty. The bounded search does not establish global optimality.
|
|||
the optimiser.
|
||||
8. Compare exact three-piece skirting fills with the original packing. This improves
|
||||
two cases for a small additional calculation cost.
|
||||
9. Add the board-scale H penalty and mix the offset hash. Search size and geometry
|
||||
constraints stay unchanged. Before/after drawings were checked for the default
|
||||
rectangle, fireplace and large floor, alongside the numeric metrics.
|
||||
|
||||
## Experiments rejected
|
||||
|
||||
|
|
@ -90,5 +127,6 @@ These are indicative timings, not CI thresholds. Ordinary option changes reuse t
|
|||
already calculated plans. A separate 500-row, 2,000-board boundary check completes
|
||||
without an error; temporary stock is repacked before enforcing the board limit.
|
||||
|
||||
Tests enforce the retained aggregate improvements as well as physical invariants.
|
||||
Metric definitions and input cases stay fixed while search strategies change.
|
||||
Tests enforce aggregate cutting budgets, broad H avoidance and physical invariants.
|
||||
The original metrics and cases remain fixed; the new H metric has its own frozen
|
||||
baseline so the correction does not rewrite the earlier results.
|
||||
|
|
|
|||
2438
benchmarks/h-pattern-baseline.json
Normal file
2438
benchmarks/h-pattern-baseline.json
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -6,6 +6,7 @@ export function flooringMetrics(plan: FlooringPlan, s: FlooringSettings) {
|
|||
let ripCuts = 0;
|
||||
let patternPenalty = 0;
|
||||
let repeats = 0;
|
||||
let nearH = 0;
|
||||
let similarSteps = 0;
|
||||
let staircases = 0;
|
||||
for (const piece of plan.pieces) {
|
||||
|
|
@ -32,6 +33,8 @@ export function flooringMetrics(plan: FlooringPlan, s: FlooringSettings) {
|
|||
if (recent.length >= 2) {
|
||||
const movement = step(recent.at(-1)!, phase);
|
||||
const previous = step(recent.at(-2)!, recent.at(-1)!);
|
||||
// Near-aligned joints with one intervening row still read as an H at the scale of the boards.
|
||||
if (Math.abs(step(recent.at(-2)!, phase)) < Math.min(s.width, s.length / 6) - 0.01) nearH++;
|
||||
patternPenalty += Number(Math.abs(Math.abs(movement) - s.length / 2) <= 0.01);
|
||||
for (let distance = 2; distance <= recent.length; distance++) {
|
||||
if (Math.abs(step(recent.at(-distance)!, phase)) <= similar) {
|
||||
|
|
@ -59,6 +62,7 @@ export function flooringMetrics(plan: FlooringPlan, s: FlooringSettings) {
|
|||
ripCuts,
|
||||
patternPenalty,
|
||||
repeats,
|
||||
nearH,
|
||||
similarSteps,
|
||||
staircases,
|
||||
boards: plan.boardCount,
|
||||
|
|
@ -78,7 +82,7 @@ export function skirtingMetrics(plan: SkirtingPlan) {
|
|||
}
|
||||
|
||||
export function compareFlooring(
|
||||
a: ReturnType<typeof flooringMetrics>,
|
||||
a: Omit<ReturnType<typeof flooringMetrics>, 'nearH'>,
|
||||
b: typeof a,
|
||||
random = false,
|
||||
) {
|
||||
|
|
@ -93,6 +97,19 @@ export function compareFlooring(
|
|||
);
|
||||
}
|
||||
|
||||
/** Current appearance score adds broad H alignments; the original metric stays comparable. */
|
||||
export function compareCurrentFlooring(
|
||||
a: ReturnType<typeof flooringMetrics>,
|
||||
b: typeof a,
|
||||
random = false,
|
||||
) {
|
||||
return compareFlooring(
|
||||
{ ...a, patternPenalty: a.patternPenalty + 12 * a.nearH },
|
||||
{ ...b, patternPenalty: b.patternPenalty + 12 * b.nearH },
|
||||
random,
|
||||
);
|
||||
}
|
||||
|
||||
export function compareSkirting(a: ReturnType<typeof skirtingMetrics>, b: typeof a) {
|
||||
return a.errors - b.errors || a.cuts - b.cuts || a.waste - b.waste;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue