feat(brain): Knapsack DP replacement for optimal bar nesting

This commit is contained in:
2026-08-27 10:44:13 +00:00
parent 80464b9187
commit 261ccd0927
+37 -41
View File
@@ -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);
const toRemoveIds = [];
unusedPieces.sort((a, b) => b.length - a.length);
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 N = unusedPieces.length;
const capacity = bar.remaining;
const requiredSpace = piece.length + kerfSize;
const dp = new Int32Array(capacity + 1);
const keep = new Array(N);
// 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).
for (let i = 0; i < N; i++) {
keep[i] = new Uint8Array(capacity + 1);
const w = unusedPieces[i].length + kerfSize;
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 j = capacity; j >= 0; j--) {
if (j >= w && dp[j - w] + w > dp[j]) {
dp[j] = dp[j - w] + w;
keep[i][j] = 1;
}
}
}
// 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);
let bestW = 0;
for(let j = 0; j <= capacity; j++) {
if(dp[j] > dp[bestW]) bestW = j;
}
let currW = bestW;
const toRemoveIds = [];
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);
}
}
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);
}
}