Spaces:
Sleeping
Sleeping
| /** | |
| * engine/optimizer.js | |
| * ืื ืืข ืืืืคืืืืฆืื (Imposition Engine) - V2 (Hardened) | |
| * ==================================== | |
| * ืชืคืงืื: ืืืฉื ืืชืืืืช ืืื ืืืืืืช ื ืื ืกืืช ืืืืืืื SRA3. | |
| * ืชืืงื: ืืืกืคืช Epsilon Guard ืืื ืืขืช ืืืชืื ืฉืืื ืืืื Floating Point (ืืืฉื ืืื ืข ื-2.99999998 ืืืคืื ื-2). | |
| */ | |
| const MACHINE_SPECS = { | |
| // HP Indigo 7K specs (SRA3) | |
| sheetWidth: 320, // mm | |
| sheetHeight: 450, // mm | |
| margins: 5, // mm (safety margin for gripper/bleed) | |
| gutter: 2 // mm (space between cuts) | |
| }; | |
| /** | |
| * ืคืื ืงืฆืืืช ืขืืจ ืืืืืงื ืืืื ืช ืืคื ื ืฉืืืืืช Floating Point | |
| */ | |
| function safeFloorDivision(total, part) { | |
| if (part <= 0) return 0; | |
| // Number.EPSILON ืืืคื ืืืืืง ืขื ืืืกืคืจืื ืฉืืืจืืื ื-0.000000001 | |
| return Math.floor((total / part) + Number.EPSILON); | |
| } | |
| /** | |
| * ืืืฉืื ืคืจืืกื ืืืคืืืืืืช (Best Fit) | |
| * @param {number} prodWidth - ืจืืื ืืืฆืจ ืื"ื | |
| * @param {number} prodHeight - ืืืื ืืืฆืจ ืื"ื | |
| * @returns {object} { ups, layout, efficiency } | |
| */ | |
| function calculateImposition(prodWidth, prodHeight) { | |
| // ืฉืื ื ืื ืืืืคืกื (ืืืืจ ืืคืืชืช ืฉืืื ืืืื ื) | |
| const safeW = MACHINE_SPECS.sheetWidth - (MACHINE_SPECS.margins * 2); | |
| const safeH = MACHINE_SPECS.sheetHeight - (MACHINE_SPECS.margins * 2); | |
| // ืืื ื ืืคื ื ืงืื ืื ืชืงืื | |
| if (!prodWidth || !prodHeight || prodWidth <= 0 || prodHeight <= 0) { | |
| return { ups: 0, layout: 'error', efficiency: 0 }; | |
| } | |
| // ืืืคืฆืื ื': ืืฉืจ (Portrait) | |
| const fitW_A = safeFloorDivision(safeW, prodWidth + MACHINE_SPECS.gutter); | |
| const fitH_A = safeFloorDivision(safeH, prodHeight + MACHINE_SPECS.gutter); | |
| const total_A = fitW_A * fitH_A; | |
| // ืืืคืฆืื ื': ืืกืืื (Landscape) | |
| const fitW_B = safeFloorDivision(safeW, prodHeight + MACHINE_SPECS.gutter); | |
| const fitH_B = safeFloorDivision(safeH, prodWidth + MACHINE_SPECS.gutter); | |
| const total_B = fitW_B * fitH_B; | |
| // ืืืืจืช ืืื ืฆื (ืืืคื ื ืื ืกืื ืืืชืจ?) | |
| const maxUps = Math.max(total_A, total_B); | |
| const bestLayout = total_A >= total_B ? 'portrait' : 'landscape'; | |
| // ืืืคืื ืืืื ืื ืืืืฆืจ ืืืื ืืื (ืืื ื ืขื ืืืขืจืืช ืืงืจืฉ ืขื ืืืืงื ืืืคืก) | |
| if (maxUps === 0) { | |
| return { ups: 0, layout: 'error', efficiency: "0.0%" }; | |
| } | |
| // ืืืฉืื ืืืื ื ืืฆืื ืื ืืืจ (Efficiency) - ืืขืจื: ืืืืฉื ืืื ืฉืื ืืจืืื ืืืืื ื ืชืืืื | |
| const usedArea = maxUps * prodWidth * prodHeight; | |
| const totalArea = MACHINE_SPECS.sheetWidth * MACHINE_SPECS.sheetHeight; | |
| const efficiencyNum = ((usedArea / totalArea) * 100).toFixed(1); | |
| return { | |
| ups: maxUps, | |
| layout: bestLayout, | |
| efficiency: efficiencyNum + "%" | |
| }; | |
| } | |
| module.exports = { calculateImposition }; |