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>
This commit is contained in:
dlawler489 2026-09-17 13:27:58 +10:00
parent e8b68e1149
commit 1581f767fa
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 slot of slots) {
for (const spool of shelf) { for (const spool of shelf) {
if (spool.material !== slot.material) continue; 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); const d = colourDistance(slot.colour, spool.colour);
if (d !== null && d <= COLOUR_TOLERANCE) pairs.push({ slot: slot.index, spool: spool.key, d }); 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 slot of slots) {
for (const spool of shelf) { for (const spool of shelf) {
if (spool.material !== slot.material) continue; 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); const d = colourDistance(slot.colour, spool.colour);
if (d !== null && d <= COLOUR_TOLERANCE) pairs.push({ slot: slot.index, spool: spool.key, d }); 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 }], 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); [{ 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']);
});