feat(brain): Knapsack DP replacement for optimal bar nesting
This commit is contained in:
+37
-41
@@ -495,14 +495,13 @@ function showFeedback(message, type) {
|
|||||||
showToast(message, type);
|
showToast(message, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== ALGORITMO FFD AVANÇADO =====
|
// ===== ALGORITMO OTIMIZADO AVANÇADO (Programação Dinâmica / Knapsack) =====
|
||||||
function optimizeCutting() {
|
function optimizeCutting() {
|
||||||
if (availableBars.length === 0 || demandPieces.length === 0) {
|
if (availableBars.length === 0 || demandPieces.length === 0) {
|
||||||
showToast('Adicione barras e peças', 'warning');
|
showToast("Adicione barras e peças", "warning");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expandir peças com ID único global
|
|
||||||
const expandedPieces = [];
|
const expandedPieces = [];
|
||||||
let uniqueIdCounter = 0;
|
let uniqueIdCounter = 0;
|
||||||
demandPieces.forEach(p => {
|
demandPieces.forEach(p => {
|
||||||
@@ -511,14 +510,12 @@ function optimizeCutting() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// FFD em cada barra disponível
|
|
||||||
const usedBars = [];
|
const usedBars = [];
|
||||||
const unusedPieces = [...expandedPieces];
|
let unusedPieces = [...expandedPieces];
|
||||||
|
|
||||||
// Ordenar barras por comprimento (maior para menor) para tentar usar as maiores primeiro?
|
const sortedBarTypes = [...availableBars].sort((a, b) => b.length - a.length);
|
||||||
// O código original não ordenava, seguia a ordem de inserção. Manteremos assim por enquanto.
|
|
||||||
|
|
||||||
for (let barType of availableBars) {
|
for (let barType of sortedBarTypes) {
|
||||||
for (let barCopy = 0; barCopy < barType.qty; barCopy++) {
|
for (let barCopy = 0; barCopy < barType.qty; barCopy++) {
|
||||||
if (unusedPieces.length === 0) break;
|
if (unusedPieces.length === 0) break;
|
||||||
|
|
||||||
@@ -531,48 +528,47 @@ function optimizeCutting() {
|
|||||||
isSimulated: barType.isSimulated || false
|
isSimulated: barType.isSimulated || false
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tentar encaixar peças (FFD)
|
unusedPieces.sort((a, b) => b.length - a.length);
|
||||||
// Ordenar peças restantes por tamanho decrescente
|
|
||||||
const sorted = [...unusedPieces].sort((a, b) => b.length - a.length);
|
|
||||||
const toRemoveIds = [];
|
|
||||||
|
|
||||||
for (let piece of sorted) {
|
const N = unusedPieces.length;
|
||||||
// Check if piece fits considering kerf loss for this cut
|
const capacity = bar.remaining;
|
||||||
// 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;
|
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?
|
for (let i = 0; i < N; i++) {
|
||||||
// If we cut it, we lose the kerf. So we need remaining >= piece.length + kerf?
|
keep[i] = new Uint8Array(capacity + 1);
|
||||||
// Or does the kerf come from the "waste"?
|
const w = unusedPieces[i].length + kerfSize;
|
||||||
// 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) {
|
for (let j = capacity; j >= 0; j--) {
|
||||||
bar.pieces.push(piece);
|
if (j >= w && dp[j - w] + w > dp[j]) {
|
||||||
bar.remaining -= requiredSpace;
|
dp[j] = dp[j - w] + w;
|
||||||
toRemoveIds.push(piece.uniqueId);
|
keep[i][j] = 1;
|
||||||
} 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.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remover peças colocadas usando ID único
|
let bestW = 0;
|
||||||
for (let uid of toRemoveIds) {
|
for(let j = 0; j <= capacity; j++) {
|
||||||
const idx = unusedPieces.findIndex(p => p.uniqueId === uid);
|
if(dp[j] > dp[bestW]) bestW = j;
|
||||||
if (idx !== -1) unusedPieces.splice(idx, 1);
|
}
|
||||||
|
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);
|
usedBars.push(bar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user