diff --git a/lib/filaments.js b/lib/filaments.js index ce429e4..07eaaf6 100644 --- a/lib/filaments.js +++ b/lib/filaments.js @@ -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 }); } diff --git a/public/app.js b/public/app.js index 5e2e352..eab9936 100644 --- a/public/app.js +++ b/public/app.js @@ -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 }); } diff --git a/test/filaments.test.js b/test/filaments.test.js index d2e26b5..4ae4b66 100644 --- a/test/filaments.test.js +++ b/test/filaments.test.js @@ -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']); +});