feat(brain): Knapsack DP replacement for optimal bar nesting
This commit is contained in:
+42
-46
@@ -495,14 +495,13 @@ function showFeedback(message, type) {
|
||||
showToast(message, type);
|
||||
}
|
||||
|
||||
// ===== ALGORITMO FFD AVANÇADO =====
|
||||
// ===== ALGORITMO OTIMIZADO AVANÇADO (Programação Dinâmica / Knapsack) =====
|
||||
function optimizeCutting() {
|
||||
if (availableBars.length === 0 || demandPieces.length === 0) {
|
||||
showToast('Adicione barras e peças', 'warning');
|
||||
showToast("Adicione barras e peças", "warning");
|
||||
return;
|
||||
}
|
||||
|
||||
// Expandir peças com ID único global
|
||||
const expandedPieces = [];
|
||||
let uniqueIdCounter = 0;
|
||||
demandPieces.forEach(p => {
|
||||
@@ -511,14 +510,12 @@ function optimizeCutting() {
|
||||
}
|
||||
});
|
||||
|
||||
// FFD em cada barra disponível
|
||||
const usedBars = [];
|
||||
const unusedPieces = [...expandedPieces];
|
||||
let unusedPieces = [...expandedPieces];
|
||||
|
||||
// Ordenar barras por comprimento (maior para menor) para tentar usar as maiores primeiro?
|
||||
// O código original não ordenava, seguia a ordem de inserção. Manteremos assim por enquanto.
|
||||
const sortedBarTypes = [...availableBars].sort((a, b) => b.length - a.length);
|
||||
|
||||
for (let barType of availableBars) {
|
||||
for (let barType of sortedBarTypes) {
|
||||
for (let barCopy = 0; barCopy < barType.qty; barCopy++) {
|
||||
if (unusedPieces.length === 0) break;
|
||||
|
||||
@@ -531,48 +528,47 @@ function optimizeCutting() {
|
||||
isSimulated: barType.isSimulated || false
|
||||
};
|
||||
|
||||
// Tentar encaixar peças (FFD)
|
||||
// Ordenar peças restantes por tamanho decrescente
|
||||
const sorted = [...unusedPieces].sort((a, b) => b.length - a.length);
|
||||
unusedPieces.sort((a, b) => b.length - a.length);
|
||||
|
||||
const N = unusedPieces.length;
|
||||
const capacity = bar.remaining;
|
||||
|
||||
const dp = new Int32Array(capacity + 1);
|
||||
const keep = new Array(N);
|
||||
|
||||
for (let i = 0; i < N; i++) {
|
||||
keep[i] = new Uint8Array(capacity + 1);
|
||||
const w = unusedPieces[i].length + kerfSize;
|
||||
|
||||
for (let j = capacity; j >= 0; j--) {
|
||||
if (j >= w && dp[j - w] + w > dp[j]) {
|
||||
dp[j] = dp[j - w] + w;
|
||||
keep[i][j] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let bestW = 0;
|
||||
for(let j = 0; j <= capacity; j++) {
|
||||
if(dp[j] > dp[bestW]) bestW = j;
|
||||
}
|
||||
let currW = bestW;
|
||||
|
||||
const toRemoveIds = [];
|
||||
|
||||
for (let piece of sorted) {
|
||||
// Check if piece fits considering kerf loss for this cut
|
||||
// We assume each piece consumes its length + kerf
|
||||
// Exception: The very last piece in a bar might not strictly need a kerf if it's the end,
|
||||
// but usually in cutting processes, you cut the piece out, so kerf is consumed.
|
||||
// User requirement: "consumo adicional de cada corte... sera de mais 2mm para cada corte"
|
||||
|
||||
const requiredSpace = piece.length + kerfSize;
|
||||
|
||||
// However, we need to be careful. If remaining is EXACTLY piece.length, can we cut it?
|
||||
// If we cut it, we lose the kerf. So we need remaining >= piece.length + kerf?
|
||||
// Or does the kerf come from the "waste"?
|
||||
// Usually: Bar 6000. Piece 1000. Kerf 3.
|
||||
// Cut 1: Consumes 1003. Remaining 4997.
|
||||
// So yes, we treat piece length as (length + kerf).
|
||||
|
||||
if (bar.remaining >= requiredSpace) {
|
||||
bar.pieces.push(piece);
|
||||
bar.remaining -= requiredSpace;
|
||||
toRemoveIds.push(piece.uniqueId);
|
||||
} else if (bar.remaining >= piece.length && bar.remaining < requiredSpace) {
|
||||
// Edge case: Fits exactly or with less than kerf remaining?
|
||||
// If I have 1000mm remaining and need 1000mm piece.
|
||||
// If I cut, I destroy 3mm. So I need 1003mm to get a 1000mm piece?
|
||||
// Yes, usually. Unless it's the raw end of the bar, but we can't assume that.
|
||||
// Let's stick to the rule: consumption = length + kerf.
|
||||
// If bar.remaining < length + kerf, we can't cut it.
|
||||
for (let i = N - 1; i >= 0; i--) {
|
||||
if (keep[i][currW] === 1) {
|
||||
const selectedPiece = unusedPieces[i];
|
||||
bar.pieces.push(selectedPiece);
|
||||
const w = selectedPiece.length + kerfSize;
|
||||
bar.remaining -= w;
|
||||
currW -= w;
|
||||
toRemoveIds.push(selectedPiece.uniqueId);
|
||||
}
|
||||
}
|
||||
|
||||
// Remover peças colocadas usando ID único
|
||||
for (let uid of toRemoveIds) {
|
||||
const idx = unusedPieces.findIndex(p => p.uniqueId === uid);
|
||||
if (idx !== -1) unusedPieces.splice(idx, 1);
|
||||
}
|
||||
|
||||
if (bar.pieces.length > 0) {
|
||||
if (toRemoveIds.length > 0) {
|
||||
const removeSet = new Set(toRemoveIds);
|
||||
unusedPieces = unusedPieces.filter(p => !removeSet.has(p.uniqueId));
|
||||
usedBars.push(bar);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user