Compare commits

...

2 commits

Author SHA1 Message Date
dlawler489
122f9d53ce Merge any-colour slot assignment order
All checks were successful
build-and-push / docker (push) Successful in 15s
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:40:37 +10:00
dlawler489
1581f767fa Fill "any colour" slots last, not first
Found on the live library: setting slot 2 of a four-colour bookmark to "any
colour" made it take the one Jade White spool, leaving slot 4 — which asks for
#FFFFFF — with nothing, and the verdict stuck at "needs 3 colours" when it
should have improved to 2.

The assignment sorts candidate pairs by colour distance and scored a colourless
slot 0, a perfect match, so it always claimed a spool ahead of a slot that
needed that exact colour. Colourless slots now sort last and take whatever is
left over.

Fixed in both the server and the browser copy, which mirror each other, and
pinned by a test built from the reported case.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 13:27:58 +10:00
3 changed files with 22 additions and 2 deletions

View file

@ -68,7 +68,9 @@ function assignSpools(filaments, spools) {
for (const slot of slots) {
for (const spool of shelf) {
if (spool.material !== slot.material) continue;
if (slot.colour === null) { pairs.push({ slot: slot.index, spool: spool.key, d: 0 }); continue; }
// A slot that takes any colour is filled last, from whatever is left:
// scoring it 0 would let it claim a spool an exact-match slot needs.
if (slot.colour === null) { pairs.push({ slot: slot.index, spool: spool.key, d: Number.MAX_SAFE_INTEGER }); continue; }
const d = colourDistance(slot.colour, spool.colour);
if (d !== null && d <= COLOUR_TOLERANCE) pairs.push({ slot: slot.index, spool: spool.key, d });
}

View file

@ -328,7 +328,9 @@ function printability(m) {
for (const slot of slots) {
for (const spool of shelf) {
if (spool.material !== slot.material) continue;
if (slot.colour === null) { pairs.push({ slot: slot.index, spool: spool.key, d: 0 }); continue; }
// Filled last, from what's left: scoring it 0 would let an "any colour"
// slot take the spool an exact-match slot needs.
if (slot.colour === null) { pairs.push({ slot: slot.index, spool: spool.key, d: Number.MAX_SAFE_INTEGER }); continue; }
const d = colourDistance(slot.colour, spool.colour);
if (d !== null && d <= COLOUR_TOLERANCE) pairs.push({ slot: slot.index, spool: spool.key, d });
}

View file

@ -130,3 +130,19 @@ test('a slot with no colour is filled by any spool of that material', () => {
assert.equal(filaments.isPrintable([{ type: 'PLA', colour: null }, { type: 'PLA', colour: null }],
[{ id: 1, material: 'PLA', colour_hex: '#123456', colour: 'Anything', remaining_g: 200 }]), false);
});
test('an "any colour" slot takes what’s left, not what another slot needs', () => {
// Reported from the live library: editing slot 2 of a bookmark to "any
// colour" stole the one white spool, leaving the slot that actually wanted
// #FFFFFF empty and the verdict no better than before.
const shelf = [{ id: 1, material: 'PLA', colour_hex: '#FFFFFF', colour: 'Jade White', remaining_g: 1000 }];
const slots = filaments.assignSpools([{ type: 'PLA', colour: null }, { type: 'PLA', colour: '#FFFFFF' }], shelf);
assert.equal(slots[1].filled, true, 'the slot that asks for white gets the white spool');
assert.equal(slots[1].by, 'Jade White');
assert.equal(slots[0].filled, false, 'the any-colour slot goes without');
// With two spools, both are filled and the exact match still gets white.
const two = shelf.concat({ id: 2, material: 'PLA', colour_hex: '#123456', colour: 'Anything', remaining_g: 500 });
const both = filaments.assignSpools([{ type: 'PLA', colour: null }, { type: 'PLA', colour: '#FFFFFF' }], two);
assert.deepEqual(both.map((s) => s.by), ['Anything', 'Jade White']);
});