fix(web): match AMS slots by color globally, not position-first

This commit is contained in:
Pavulon87
2026-07-14 13:37:28 +02:00
parent b80b81b51c
commit dcfb8f8123

View File

@@ -2926,29 +2926,85 @@ function openFilamentDialog(slots){
var dr=ar[0]-br[0], dg=ar[1]-br[1], db=ar[2]-br[2];
return (dr*dr + dg*dg + db*db);
}
var defaultSlotByPaint={};
var usedDefaultSlot={};
channels.forEach(function(gc,i){
// Globale, farboptimale Zuordnung Kanal -> Slot.
// Anforderungen: 1) Material muss passen (harter Filter, wie bisher)
// 2) Farbe so nah wie möglich
// 3) Position spielt praktisch keine Rolle mehr
// Ein rein gieriger Ansatz (Kanal für Kanal den "nächsten" Slot wählen)
// kann bei knappen/mehrdeutigen Slots suboptimale Gesamtlösungen liefern
// (z.B. Kanal A "verbraucht" den Slot, der eigentlich viel besser zu
// Kanal B gepasst hätte). Da ein AMS in der Praxis nur wenige Slots hat
// (üblich: bis zu 4), lohnt sich Brute-Force über alle möglichen
// Zuordnungen mit Pruning - liefert garantiert das global beste Ergebnis,
// ganz ohne einen vollen Hungarian-Algorithmus implementieren zu müssen.
function _bestAssignment(slotsPerChannel){
var n=slotsPerChannel.length;
var assign=new Array(n).fill(-1);
// Sicherheitsnetz für unrealistisch viele Kanäle: gieriger Fallback
// (kleinster Farbabstand zuerst bedient), um exponentielle Laufzeit zu
// vermeiden. Greift in der Praxis nie, ein AMS hat max. wenige Slots.
if(n>8){
var used={};
var order=slotsPerChannel.map(function(_,idx){return idx;}).sort(function(a,b){
var da=slotsPerChannel[a].length?slotsPerChannel[a][0].dist:Infinity;
var db=slotsPerChannel[b].length?slotsPerChannel[b][0].dist:Infinity;
return da-db;
});
order.forEach(function(i){
var cands=slotsPerChannel[i];
if(!cands.length)return;
var pick=cands.find(function(c){return !used[c.slot_index];})||cands[0];
assign[i]=pick.slot_index;
used[pick.slot_index]=true;
});
return assign;
}
var bestCost=Infinity, bestAssign=assign.slice(), used={};
(function backtrack(idx,cost){
if(cost>=bestCost)return; // Pruning: kann eh nicht mehr besser werden
if(idx===n){ bestCost=cost; bestAssign=assign.slice(); return; }
var candidates=slotsPerChannel[idx];
if(!candidates.length){
assign[idx]=-1;
backtrack(idx+1,cost);
return;
}
var placedFree=false;
for(var k=0;k<candidates.length;k++){
var c=candidates[k];
if(used[c.slot_index])continue;
placedFree=true;
used[c.slot_index]=true;
assign[idx]=c.slot_index;
backtrack(idx+1,cost+c.dist);
used[c.slot_index]=false;
}
if(!placedFree){
// keine freien kompatiblen Slots mehr -> Mehrfachbelegung des
// farblich besten kompatiblen Slots zulassen (besser als leer)
var best=candidates[0];
assign[idx]=best.slot_index;
backtrack(idx+1,cost+best.dist);
}
assign[idx]=-1;
})(0,0);
return bestAssign;
}
var slotsPerChannel=channels.map(function(gc){
var compatible=_amsSlots.filter(function(s){
return _materialsCompatible(gc.material, s.material);
});
if(!compatible.length){
defaultSlotByPaint[i]=-1;
return;
}
var ranked=compatible.slice().sort(function(a,b){
var da=Math.abs((a.slot_index||0)-i), db=Math.abs((b.slot_index||0)-i);
if(da!==db)return da-db;
var ca=_colorDist(gc.color_hex, a.color_hex), cb=_colorDist(gc.color_hex, b.color_hex);
if(ca!==cb)return ca-cb;
return (a.slot_index||0)-(b.slot_index||0);
});
var chosen=ranked.find(function(s){return !usedDefaultSlot[s.slot_index];}) || ranked[0];
defaultSlotByPaint[i]=chosen?chosen.slot_index:-1;
if(chosen) usedDefaultSlot[chosen.slot_index]=1;
return compatible.map(function(s){
return {slot_index:s.slot_index, dist:_colorDist(gc.color_hex, s.color_hex)};
}).sort(function(a,b){ return a.dist-b.dist; });
});
var _assignResult=_bestAssignment(slotsPerChannel);
var defaultSlotByPaint={};
channels.forEach(function(gc,i){ defaultSlotByPaint[i]=_assignResult[i]; });
if(!_amsSlots.length){
body.innerHTML='<p style="color:var(--txt2);font-size:13px;text-align:center;padding:16px 0">'+T.fd_no_slots_msg.replace('{br}','<br>')+'</p>';
@@ -3402,4 +3458,4 @@ function loadPrinterTab(){
}).catch(function(e){
if(grid)grid.innerHTML='<div style="color:var(--err);font-size:13px;padding:20px">Fehler: '+e+'</div>';
});
}
}