@lucablockltd/ultimate-packaging 1.0.3 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AGENTS_README.md +570 -119
- package/dist/index.d.mts +129 -23
- package/dist/index.d.ts +129 -23
- package/dist/index.js +2142 -654
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2139 -655
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/AGENTS_README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
## Quick Start
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
|
-
npm install
|
|
9
|
+
npm install @lucablockltd/ultimate-packaging
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
Requires `react >= 18.0.0` and `react-dom >= 18.0.0`.
|
|
@@ -18,13 +18,13 @@ Requires `react >= 18.0.0` and `react-dom >= 18.0.0`.
|
|
|
18
18
|
Use `DIE_LINE_LAYOUT` — one component for ALL models. No switch-case needed.
|
|
19
19
|
|
|
20
20
|
```tsx
|
|
21
|
-
import { DIE_LINE_LAYOUT, modelList } from "ultimate-packaging";
|
|
22
|
-
import type { DieLineLayoutRef } from "ultimate-packaging";
|
|
21
|
+
import { DIE_LINE_LAYOUT, modelList } from "@lucablockltd/ultimate-packaging";
|
|
22
|
+
import type { DieLineLayoutRef } from "@lucablockltd/ultimate-packaging";
|
|
23
23
|
|
|
24
24
|
const ref = useRef<DieLineLayoutRef>(null);
|
|
25
25
|
|
|
26
26
|
// Get default attributes for any model
|
|
27
|
-
const model = modelList.find(m => m.id === "BECF-1010A")!;
|
|
27
|
+
const model = modelList.find((m) => m.id === "BECF-1010A")!;
|
|
28
28
|
|
|
29
29
|
<DIE_LINE_LAYOUT
|
|
30
30
|
ref={ref}
|
|
@@ -33,18 +33,18 @@ const model = modelList.find(m => m.id === "BECF-1010A")!;
|
|
|
33
33
|
unit="mm"
|
|
34
34
|
mode="DIE_LINE"
|
|
35
35
|
isShowDimensions={true}
|
|
36
|
-
|
|
36
|
+
/>;
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
### DIE_LINE_LAYOUT Props
|
|
40
40
|
|
|
41
|
-
| Prop
|
|
42
|
-
|
|
43
|
-
| `modelId`
|
|
44
|
-
| `attributes`
|
|
45
|
-
| `unit`
|
|
46
|
-
| `mode`
|
|
47
|
-
| `isShowDimensions` | `boolean`
|
|
41
|
+
| Prop | Type | Required | Default | Description |
|
|
42
|
+
| ------------------ | ---------------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------ |
|
|
43
|
+
| `modelId` | `string` | YES | — | One of: `"BECF-1010A"`, `"BECF-1030A"`, `"BECF-1040A"`, `"BECF-11D01"`, `"BECF-12101"`, `"BECF-12109"` |
|
|
44
|
+
| `attributes` | `object` | YES | — | Box dimensions (see Model Attributes below) |
|
|
45
|
+
| `unit` | `"mm" \| "cm" \| "in"` | no | `"mm"` | Display unit for dimension labels |
|
|
46
|
+
| `mode` | `ModelMode` | no | `"DIE_LINE"` | `"DIE_LINE"` = interactive canvas, `"AUTO_LAYOUT"` = raw SVG |
|
|
47
|
+
| `isShowDimensions` | `boolean` | no | `false` | Show dimension annotation lines |
|
|
48
48
|
|
|
49
49
|
### DIE_LINE_LAYOUT Ref Methods
|
|
50
50
|
|
|
@@ -55,7 +55,10 @@ const ref = useRef<DieLineLayoutRef>(null);
|
|
|
55
55
|
ref.current.getAttributes(); // → { length: 60, width: 25, ... }
|
|
56
56
|
|
|
57
57
|
// Export as PNG image
|
|
58
|
-
const blob = await ref.current.exportImage({
|
|
58
|
+
const blob = await ref.current.exportImage({
|
|
59
|
+
isShowDimension: true,
|
|
60
|
+
originalSize: true,
|
|
61
|
+
});
|
|
59
62
|
|
|
60
63
|
// Export dimensions as PDF
|
|
61
64
|
const pdf = await ref.current.exportDimension();
|
|
@@ -66,68 +69,350 @@ const pdf = await ref.current.exportDimension();
|
|
|
66
69
|
## 2. Auto-Layout (Pack Dielines on Paper)
|
|
67
70
|
|
|
68
71
|
Use `AUTO_LAYOUT` to automatically arrange dieline pieces on paper sheets.
|
|
72
|
+
The engine calculates the optimal rotation and grid placement to maximize pieces per sheet.
|
|
69
73
|
|
|
70
|
-
|
|
71
|
-
import { AUTO_LAYOUT, calculateAutoLayout } from "ultimate-packaging";
|
|
72
|
-
import type { AutoLayoutRef, AutoLayoutConfig, AutoLayoutResult } from "ultimate-packaging";
|
|
74
|
+
### AutoLayoutConfig — Full Reference
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
`AutoLayoutConfig` has **3 required fields** and **5 optional print-setting fields**.
|
|
77
|
+
If you omit any optional field, the system uses a sensible default automatically.
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
interface AutoLayoutConfig {
|
|
81
|
+
// ─── REQUIRED ───────────────────────────────────────────────
|
|
82
|
+
papers: AutoLayoutPaper[]; // At least 1 paper size to test
|
|
83
|
+
model: AutoLayoutModel; // Which box/bag model and its dimensions
|
|
84
|
+
quantity: number; // Total pieces needed (e.g. 1000)
|
|
85
|
+
|
|
86
|
+
// ─── OPTIONAL (print settings) ──────────────────────────────
|
|
87
|
+
// You can omit ALL of these — the system will use defaults.
|
|
88
|
+
layoutDistance?: number; // default: 3 — mm gap between piece cut-lines
|
|
89
|
+
spacing?: number; // default: 5 — mm margin from paper edges
|
|
90
|
+
griper?: number; // default: 10 — mm machine gripper zone
|
|
91
|
+
colorbarHeight?: number; // default: 5 — mm height of color test strip
|
|
92
|
+
isShowColorbar?: boolean; // default: true — whether to show colorbar
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### Required Field #1: `papers` — Paper sizes to test
|
|
97
|
+
|
|
98
|
+
An array of paper sizes. The engine will calculate layout for EVERY paper and rank them by efficiency.
|
|
99
|
+
Each paper needs 4 fields — all required:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
interface AutoLayoutPaper {
|
|
103
|
+
paperId: string; // Unique ID (any string, e.g. "1", "paper-a4", "custom-1")
|
|
104
|
+
paperName: string; // Display name (e.g. "20x28\"", "A4", "25x36\"")
|
|
105
|
+
paperWidth: number; // Width in millimeters
|
|
106
|
+
paperHeight: number; // Height in millimeters
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Common paper sizes (inches → mm, multiply by 25.4):**
|
|
111
|
+
|
|
112
|
+
| Name | Inches | paperWidth (mm) | paperHeight (mm) |
|
|
113
|
+
| -------- | -------- | --------------- | ----------------- |
|
|
114
|
+
| 20x28" | 20 × 28 | 508 | 711.2 |
|
|
115
|
+
| 25x36" | 25 × 36 | 635 | 914.4 |
|
|
116
|
+
| 25x12" | 25 × 12 | 635 | 304.8 |
|
|
117
|
+
| 23x35" | 23 × 35 | 584.2 | 889 |
|
|
118
|
+
|
|
119
|
+
**Example:**
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
const papers: AutoLayoutPaper[] = [
|
|
123
|
+
{ paperId: "1", paperName: '20x28"', paperWidth: 508, paperHeight: 711.2 },
|
|
124
|
+
{ paperId: "2", paperName: '25x36"', paperWidth: 635, paperHeight: 914.4 },
|
|
125
|
+
];
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### Required Field #2: `model` — Which packaging model and its dimensions
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
interface AutoLayoutModel {
|
|
132
|
+
modelId: string; // Must be one of the supported model IDs (see below)
|
|
133
|
+
attributes: Record<string, number>; // Dimensions in mm — different per model
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Supported `modelId` values and their required `attributes`:**
|
|
138
|
+
|
|
139
|
+
| modelId | Type | Required attributes |
|
|
140
|
+
| ------------ | ----------------------- | ---------------------------------------------------------------- |
|
|
141
|
+
| `BECF-1010A` | Tuck End Box Type A | `length`, `width`, `height`, `glueArea`, `dustFlap`, `tuckFlap` |
|
|
142
|
+
| `BECF-1030A` | Tuck End Box Type C | `length`, `width`, `height`, `glueArea`, `dustFlap`, `tuckFlap` |
|
|
143
|
+
| `BECF-1040A` | Tuck End Box Type B | `length`, `width`, `height`, `glueArea`, `dustFlap`, `tuckFlap` |
|
|
144
|
+
| `BECF-11D01` | Standard Box | `length`, `width`, `height`, `flapHeight`, `glueArea` |
|
|
145
|
+
| `BECF-12101` | Carton Bag / Shopping Bag Type A | `length`, `width`, `height`, `glueArea` |
|
|
146
|
+
| `BECF-12109` | Carton Bag / Shopping Bag Type B | `length`, `width`, `height`, `glueArea` |
|
|
147
|
+
|
|
148
|
+
**Example — Tuck End Box:**
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
const model: AutoLayoutModel = {
|
|
152
|
+
modelId: "BECF-1010A",
|
|
153
|
+
attributes: {
|
|
154
|
+
length: 60, // box length (mm)
|
|
155
|
+
width: 25, // box width (mm)
|
|
156
|
+
height: 100, // box height (mm)
|
|
157
|
+
glueArea: 15, // glue flap width (mm)
|
|
158
|
+
dustFlap: 15, // dust flap extension (mm)
|
|
159
|
+
tuckFlap: 15, // tuck flap height (mm)
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Example — Standard Box:**
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
const model: AutoLayoutModel = {
|
|
168
|
+
modelId: "BECF-11D01",
|
|
169
|
+
attributes: {
|
|
170
|
+
length: 100,
|
|
171
|
+
width: 50,
|
|
172
|
+
height: 150,
|
|
173
|
+
flapHeight: 50,
|
|
174
|
+
glueArea: 13,
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Example — Carton Bag:**
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
const model: AutoLayoutModel = {
|
|
183
|
+
modelId: "BECF-12101",
|
|
184
|
+
attributes: {
|
|
185
|
+
length: 100, // A — front/back panel width (mm)
|
|
186
|
+
width: 50, // B — side panel / gusset (mm)
|
|
187
|
+
height: 150, // C — bag body height (mm)
|
|
188
|
+
glueArea: 13, // glue flap width (mm)
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
```
|
|
76
192
|
|
|
193
|
+
**Tip:** You can use pre-defined default attributes instead of hardcoding:
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
import {
|
|
197
|
+
BECF_1010A_DEFAULT_ATTRIBUTES,
|
|
198
|
+
BECF_12101_DEFAULT_ATTRIBUTES,
|
|
199
|
+
} from "@lucablockltd/ultimate-packaging";
|
|
200
|
+
|
|
201
|
+
const model: AutoLayoutModel = {
|
|
202
|
+
modelId: "BECF-1010A",
|
|
203
|
+
attributes: { ...BECF_1010A_DEFAULT_ATTRIBUTES },
|
|
204
|
+
};
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### Required Field #3: `quantity` — Total pieces needed
|
|
208
|
+
|
|
209
|
+
A positive integer. The engine calculates how many sheets are needed to produce this quantity.
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
quantity: 1000 // need 1000 pieces total
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
#### Optional Print Settings (all have defaults)
|
|
216
|
+
|
|
217
|
+
These 5 fields control the printing press layout. **You can omit any or all of them.**
|
|
218
|
+
|
|
219
|
+
| Field | Type | Default | What it does |
|
|
220
|
+
| ---------------- | --------- | ------- | ------------------------------------------------------------------------------------------------ |
|
|
221
|
+
| `layoutDistance` | `number` | `3` | Gap between piece cut-lines in mm. Creates an offset contour around each piece (half on each side). Set to `0` for pieces touching edge-to-edge. |
|
|
222
|
+
| `spacing` | `number` | `5` | Margin from all paper edges in mm. Reserved border where no pieces are placed. |
|
|
223
|
+
| `griper` | `number` | `10` | Machine gripper zone depth in mm. Area reserved for the printing press feed mechanism. Placed on the bottom edge (landscape) or left edge (portrait). |
|
|
224
|
+
| `colorbarHeight` | `number` | `5` | Height of the color test strip in mm. A row of colored squares for print registration, placed on the opposite side of the gripper. |
|
|
225
|
+
| `isShowColorbar` | `boolean` | `true` | Whether to show the colorbar. If `false`, `colorbarHeight` is ignored and that space becomes usable for pieces. |
|
|
226
|
+
|
|
227
|
+
### Minimal Config Example (only required fields)
|
|
228
|
+
|
|
229
|
+
This is the **simplest possible config** — all print settings use defaults:
|
|
230
|
+
|
|
231
|
+
```tsx
|
|
77
232
|
const config: AutoLayoutConfig = {
|
|
78
233
|
papers: [
|
|
79
|
-
{ paperId: "1", paperName:
|
|
80
|
-
{ paperId: "2", paperName: "25x36\"", paperWidth: 635, paperHeight: 914.4 },
|
|
234
|
+
{ paperId: "1", paperName: '20x28"', paperWidth: 508, paperHeight: 711.2 },
|
|
81
235
|
],
|
|
82
236
|
model: {
|
|
83
237
|
modelId: "BECF-1010A",
|
|
84
238
|
attributes: { length: 60, width: 25, height: 100, glueArea: 15, dustFlap: 15, tuckFlap: 15 },
|
|
85
239
|
},
|
|
86
240
|
quantity: 1000,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
colorbarHeight
|
|
241
|
+
// layoutDistance → defaults to 3
|
|
242
|
+
// spacing → defaults to 5
|
|
243
|
+
// griper → defaults to 10
|
|
244
|
+
// colorbarHeight → defaults to 5
|
|
245
|
+
// isShowColorbar → defaults to true
|
|
246
|
+
};
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Full Config Example (all fields explicit)
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
const config: AutoLayoutConfig = {
|
|
253
|
+
papers: [
|
|
254
|
+
{ paperId: "1", paperName: '20x28"', paperWidth: 508, paperHeight: 711.2 },
|
|
255
|
+
{ paperId: "2", paperName: '25x36"', paperWidth: 635, paperHeight: 914.4 },
|
|
256
|
+
],
|
|
257
|
+
model: {
|
|
258
|
+
modelId: "BECF-1010A",
|
|
259
|
+
attributes: {
|
|
260
|
+
length: 60,
|
|
261
|
+
width: 25,
|
|
262
|
+
height: 100,
|
|
263
|
+
glueArea: 15,
|
|
264
|
+
dustFlap: 15,
|
|
265
|
+
tuckFlap: 15,
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
quantity: 1000,
|
|
269
|
+
layoutDistance: 3,
|
|
270
|
+
spacing: 5,
|
|
271
|
+
griper: 10,
|
|
272
|
+
colorbarHeight: 5,
|
|
91
273
|
isShowColorbar: true,
|
|
92
274
|
};
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Using the AUTO_LAYOUT Component (React)
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
import { AUTO_LAYOUT } from "@lucablockltd/ultimate-packaging";
|
|
281
|
+
import type {
|
|
282
|
+
AutoLayoutRef,
|
|
283
|
+
AutoLayoutConfig,
|
|
284
|
+
AutoLayoutResult,
|
|
285
|
+
} from "@lucablockltd/ultimate-packaging";
|
|
286
|
+
|
|
287
|
+
const ref = useRef<AutoLayoutRef>(null);
|
|
288
|
+
const [result, setResult] = useState<AutoLayoutResult | null>(null);
|
|
93
289
|
|
|
94
|
-
<AUTO_LAYOUT ref={ref} config={config} onResult={setResult}
|
|
290
|
+
<AUTO_LAYOUT ref={ref} config={config} onResult={setResult} />;
|
|
95
291
|
```
|
|
96
292
|
|
|
97
293
|
### AUTO_LAYOUT Props
|
|
98
294
|
|
|
99
|
-
| Prop
|
|
100
|
-
|
|
101
|
-
| `config`
|
|
102
|
-
| `onResult`
|
|
103
|
-
| `onModifiedPapers` | `(papers: AutoLayoutPaperResult[]) => void`
|
|
295
|
+
| Prop | Type | Required | Default | Description |
|
|
296
|
+
| ------------------ | -------------------------------------------- | -------- | ---------------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
297
|
+
| `config` | `AutoLayoutConfig \| null` | YES | — | Layout configuration. Pass `null` to clear/hide the layout. |
|
|
298
|
+
| `onResult` | `(result: AutoLayoutResult \| null) => void` | no | — | Callback fired when layout calculation completes. Receives the full result object. |
|
|
299
|
+
| `onModifiedPapers` | `(papers: AutoLayoutPaperResult[]) => void` | no | — | Callback fired when user manually drags/modifies piece positions on the layout. |
|
|
300
|
+
| `isShowSummary` | `boolean` | no | `true` | Show/hide the summary stats panel above each paper (pieces/sheet, waste %, etc.) |
|
|
301
|
+
| `isShowAction` | `AutoLayoutAction[]` | no | all actions shown | Which action buttons to show. Values: `"EXPORT_SHEET"`, `"EXPORT_PDF"`, `"MODIFY_LAYOUT"`. Omit to show all. |
|
|
104
302
|
|
|
105
303
|
### AUTO_LAYOUT Ref Methods
|
|
106
304
|
|
|
305
|
+
Access these via `ref.current` after the component mounts:
|
|
306
|
+
|
|
107
307
|
```tsx
|
|
108
|
-
|
|
109
|
-
|
|
308
|
+
const ref = useRef<AutoLayoutRef>(null);
|
|
309
|
+
|
|
310
|
+
// 1. Get the full calculation result
|
|
311
|
+
const result = ref.current.getResult();
|
|
312
|
+
// → AutoLayoutResult | null
|
|
110
313
|
|
|
111
|
-
// Export layout
|
|
112
|
-
const
|
|
314
|
+
// 2. Export a specific paper's layout as PNG image
|
|
315
|
+
const pngBlob = await ref.current.exportImage(0); // 0 = first paper index
|
|
316
|
+
// → Blob (image/png)
|
|
113
317
|
|
|
114
|
-
// Export layout as PDF
|
|
115
|
-
const
|
|
318
|
+
// 3. Export a specific paper's layout as PDF
|
|
319
|
+
const pdfBlob = await ref.current.exportPdf(0);
|
|
320
|
+
// → Blob (application/pdf)
|
|
116
321
|
|
|
117
|
-
//
|
|
322
|
+
// 4. Export the dieline pattern as PNG
|
|
323
|
+
const dielineBlob = await ref.current.exportDielineImage();
|
|
324
|
+
// → Blob (image/png)
|
|
325
|
+
|
|
326
|
+
// 5. Get packed result — includes images as File objects (useful for upload/FormData)
|
|
118
327
|
const packed = await ref.current.getPackedResult(0);
|
|
119
|
-
// → {
|
|
328
|
+
// → AutoLayoutPackedResult {
|
|
329
|
+
// ...all AutoLayoutPaperResult fields,
|
|
330
|
+
// quantity: number,
|
|
331
|
+
// dielineFile: File, // dieline pattern PNG
|
|
332
|
+
// layoutFile: File, // layout arrangement PNG
|
|
333
|
+
// }
|
|
120
334
|
```
|
|
121
335
|
|
|
122
|
-
### Programmatic Calculation (No UI)
|
|
336
|
+
### Programmatic Calculation (No UI / No React)
|
|
337
|
+
|
|
338
|
+
Use `calculateAutoLayout()` to compute layout without rendering any component.
|
|
339
|
+
Same config, same result — just no visual output.
|
|
123
340
|
|
|
124
341
|
```tsx
|
|
125
|
-
import { calculateAutoLayout } from "ultimate-packaging";
|
|
342
|
+
import { calculateAutoLayout } from "@lucablockltd/ultimate-packaging";
|
|
343
|
+
import type { AutoLayoutConfig, AutoLayoutResult } from "@lucablockltd/ultimate-packaging";
|
|
344
|
+
|
|
345
|
+
const config: AutoLayoutConfig = {
|
|
346
|
+
papers: [
|
|
347
|
+
{ paperId: "A", paperName: '20x28"', paperWidth: 508, paperHeight: 711.2 },
|
|
348
|
+
{ paperId: "B", paperName: '25x36"', paperWidth: 635, paperHeight: 914.4 },
|
|
349
|
+
],
|
|
350
|
+
model: {
|
|
351
|
+
modelId: "BECF-1010A",
|
|
352
|
+
attributes: { length: 60, width: 25, height: 100, glueArea: 15, dustFlap: 15, tuckFlap: 15 },
|
|
353
|
+
},
|
|
354
|
+
quantity: 1000,
|
|
355
|
+
// print settings are optional — defaults will be used
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const result: AutoLayoutResult = calculateAutoLayout(config);
|
|
359
|
+
|
|
360
|
+
// Result structure:
|
|
361
|
+
// result.recommendedPaperId → "B" (best paper by least total area)
|
|
362
|
+
// result.quantity → 1000
|
|
363
|
+
// result.totalProduced → 1008 (may exceed quantity due to full sheets)
|
|
364
|
+
// result.totalSheets → total sheets across all papers
|
|
365
|
+
// result.remainingNeeded → 0 (if all produced)
|
|
366
|
+
// result.papers → AutoLayoutPaperResult[] (one per paper, sorted best-first)
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### AutoLayoutResult — Output Reference
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
interface AutoLayoutResult {
|
|
373
|
+
papers: AutoLayoutPaperResult[]; // Results per paper size, sorted by efficiency (best first)
|
|
374
|
+
quantity: number; // Original requested quantity
|
|
375
|
+
totalProduced: number; // Total pieces produced (may exceed quantity)
|
|
376
|
+
totalSheets: number; // Total sheets needed across all papers
|
|
377
|
+
remainingNeeded: number; // Shortfall (0 if quantity is met)
|
|
378
|
+
recommendedPaperId: string | null; // paperId of the most efficient paper (least total area)
|
|
379
|
+
}
|
|
126
380
|
|
|
127
|
-
|
|
128
|
-
//
|
|
381
|
+
interface AutoLayoutPaperResult {
|
|
382
|
+
paperId: string; // Matches the input paper's paperId
|
|
383
|
+
paperName: string; // Display name
|
|
384
|
+
paperWidth: number; // mm
|
|
385
|
+
paperHeight: number; // mm
|
|
386
|
+
producedPerSheet: number; // How many pieces fit on ONE sheet
|
|
387
|
+
totalSheets: number; // Sheets needed for the requested quantity
|
|
388
|
+
totalProduced: number; // producedPerSheet × totalSheets
|
|
389
|
+
excessCount: number; // Overproduction beyond quantity
|
|
390
|
+
wastePercent: number; // Unused paper area percentage (0–100)
|
|
391
|
+
independentSheets: number; // Sheets needed if ONLY this paper is used
|
|
392
|
+
independentTotalArea: number; // Total paper area in mm² if only this paper
|
|
393
|
+
placements: AutoLayoutPlacement[]; // Position of each piece on the sheet
|
|
394
|
+
gripperSide: GripperSide; // Which edge the gripper is on
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
interface AutoLayoutPlacement {
|
|
398
|
+
x: number; // mm from paper left edge
|
|
399
|
+
y: number; // mm from paper top edge
|
|
400
|
+
rotation: number; // 0 | 90 | 180 | 270 degrees
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
type GripperSide = "top" | "bottom" | "left" | "right";
|
|
404
|
+
// Automatically determined: landscape paper → "bottom", portrait → "left"
|
|
129
405
|
```
|
|
130
406
|
|
|
407
|
+
### Layout Algorithm Summary
|
|
408
|
+
|
|
409
|
+
1. The engine tries all 4 rotations (0°, 90°, 180°, 270°) for each piece on each paper
|
|
410
|
+
2. For each rotation, it packs pieces in a grid within the **usable bounds**
|
|
411
|
+
3. **Usable bounds** = paper area minus: `spacing` (all edges) + `griper` (one edge) + `colorbarHeight` (opposite edge)
|
|
412
|
+
4. The rotation that fits the **most pieces** wins
|
|
413
|
+
5. Papers are sorted by `independentTotalArea` (ascending) — the paper that uses the least total area to fulfill quantity is ranked #1
|
|
414
|
+
6. `recommendedPaperId` = the #1 ranked paper
|
|
415
|
+
|
|
131
416
|
---
|
|
132
417
|
|
|
133
418
|
## 3. Available Models & Attributes
|
|
@@ -135,13 +420,13 @@ const result = calculateAutoLayout(config);
|
|
|
135
420
|
### Get All Models
|
|
136
421
|
|
|
137
422
|
```tsx
|
|
138
|
-
import { modelList } from "ultimate-packaging";
|
|
423
|
+
import { modelList } from "@lucablockltd/ultimate-packaging";
|
|
139
424
|
|
|
140
|
-
modelList.forEach(model => {
|
|
141
|
-
console.log(model.id);
|
|
142
|
-
console.log(model.nameEN);
|
|
143
|
-
console.log(model.nameTH);
|
|
144
|
-
console.log(model.dimension);
|
|
425
|
+
modelList.forEach((model) => {
|
|
426
|
+
console.log(model.id); // "BECF-1010A"
|
|
427
|
+
console.log(model.nameEN); // "TUCK END BOXES TYPE A"
|
|
428
|
+
console.log(model.nameTH); // "กล่องฝาเสียบ ก้นเสียบ A"
|
|
429
|
+
console.log(model.dimension); // ["DIE_LINE"]
|
|
145
430
|
console.log(model.attributes); // { length: 60, width: 25, ... }
|
|
146
431
|
});
|
|
147
432
|
```
|
|
@@ -152,36 +437,57 @@ modelList.forEach(model => {
|
|
|
152
437
|
|
|
153
438
|
```typescript
|
|
154
439
|
interface TuckEndBoxAttributes {
|
|
155
|
-
length: number;
|
|
156
|
-
width: number;
|
|
157
|
-
height: number;
|
|
158
|
-
glueArea: number;
|
|
159
|
-
dustFlap: number;
|
|
160
|
-
tuckFlap: number;
|
|
440
|
+
length: number; // box length (mm)
|
|
441
|
+
width: number; // box width (mm)
|
|
442
|
+
height: number; // box height (mm)
|
|
443
|
+
glueArea: number; // glue flap width (mm)
|
|
444
|
+
dustFlap: number; // dust flap extension (mm)
|
|
445
|
+
tuckFlap: number; // tuck flap height (mm)
|
|
161
446
|
}
|
|
162
447
|
```
|
|
163
448
|
|
|
164
|
-
| Model
|
|
165
|
-
|
|
166
|
-
| BECF-1010A | 60
|
|
167
|
-
| BECF-1030A | 105
|
|
168
|
-
| BECF-1040A | 56
|
|
449
|
+
| Model | length | width | height | glueArea | dustFlap | tuckFlap |
|
|
450
|
+
| ---------- | ------ | ----- | ------ | -------- | -------- | -------- |
|
|
451
|
+
| BECF-1010A | 60 | 25 | 100 | 15 | 15 | 15 |
|
|
452
|
+
| BECF-1030A | 105 | 50 | 155 | 15 | 25 | 14 |
|
|
453
|
+
| BECF-1040A | 56 | 56 | 150 | 15 | 30 | 14 |
|
|
169
454
|
|
|
170
455
|
#### Standard Box (BECF-11D01)
|
|
171
456
|
|
|
172
457
|
```typescript
|
|
173
458
|
interface StandardBoxAttributes {
|
|
174
|
-
length: number;
|
|
175
|
-
width: number;
|
|
176
|
-
height: number;
|
|
459
|
+
length: number; // box length (mm)
|
|
460
|
+
width: number; // box width (mm)
|
|
461
|
+
height: number; // box height (mm)
|
|
177
462
|
flapHeight: number; // top/bottom flap height (mm)
|
|
178
|
-
glueArea: number;
|
|
463
|
+
glueArea: number; // glue flap width (mm)
|
|
179
464
|
}
|
|
180
465
|
```
|
|
181
466
|
|
|
182
|
-
| Model
|
|
183
|
-
|
|
184
|
-
| BECF-11D01 | 100
|
|
467
|
+
| Model | length | width | height | flapHeight | glueArea |
|
|
468
|
+
| ---------- | ------ | ----- | ------ | ---------- | -------- |
|
|
469
|
+
| BECF-11D01 | 100 | 50 | 150 | 50 | 13 |
|
|
470
|
+
|
|
471
|
+
#### Carton Bags / Shopping Bags (BECF-12101, BECF-12109)
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
interface CartonBagAttributes {
|
|
475
|
+
length: number; // A — bag length / front-back panel width (mm)
|
|
476
|
+
width: number; // B — bag width / side panel gusset (mm)
|
|
477
|
+
height: number; // C — bag body height (mm)
|
|
478
|
+
glueArea: number; // glue flap width (mm)
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
Auto-calculated values (not user inputs):
|
|
483
|
+
|
|
484
|
+
- `D` — top flap height = min(C - 2 - B/2, 40)
|
|
485
|
+
- `DIP` — bottom glue tab height (15mm for 12101, 13mm for 12109)
|
|
486
|
+
|
|
487
|
+
| Model | length | width | height | glueArea |
|
|
488
|
+
| ------------------- | ------ | ----- | ------ | -------- |
|
|
489
|
+
| BECF-12101 (Type A) | 100 | 50 | 150 | 13 |
|
|
490
|
+
| BECF-12109 (Type B) | 100 | 50 | 150 | 13 |
|
|
185
491
|
|
|
186
492
|
### Default Attribute Constants
|
|
187
493
|
|
|
@@ -191,7 +497,9 @@ import {
|
|
|
191
497
|
BECF_1030A_DEFAULT_ATTRIBUTES,
|
|
192
498
|
BECF_1040A_DEFAULT_ATTRIBUTES,
|
|
193
499
|
BECF_11D01_DEFAULT_ATTRIBUTES,
|
|
194
|
-
|
|
500
|
+
BECF_12101_DEFAULT_ATTRIBUTES,
|
|
501
|
+
BECF_12109_DEFAULT_ATTRIBUTES,
|
|
502
|
+
} from "@lucablockltd/ultimate-packaging";
|
|
195
503
|
```
|
|
196
504
|
|
|
197
505
|
---
|
|
@@ -202,21 +510,21 @@ import {
|
|
|
202
510
|
|
|
203
511
|
```typescript
|
|
204
512
|
interface AutoLayoutConfig {
|
|
205
|
-
papers: AutoLayoutPaper[];
|
|
206
|
-
model: AutoLayoutModel;
|
|
207
|
-
quantity: number;
|
|
208
|
-
layoutDistance
|
|
209
|
-
spacing
|
|
210
|
-
griper
|
|
211
|
-
colorbarHeight
|
|
212
|
-
isShowColorbar
|
|
513
|
+
papers: AutoLayoutPaper[]; // paper sizes to try
|
|
514
|
+
model: AutoLayoutModel; // { modelId, attributes }
|
|
515
|
+
quantity: number; // total pieces needed
|
|
516
|
+
layoutDistance?: number; // mm between piece cut-lines (default: 3)
|
|
517
|
+
spacing?: number; // mm margin on paper sides (default: 5)
|
|
518
|
+
griper?: number; // mm machine gripper zone (default: 10)
|
|
519
|
+
colorbarHeight?: number; // mm colorbar strip (default: 5)
|
|
520
|
+
isShowColorbar?: boolean; // show colorbar on layout (default: true)
|
|
213
521
|
}
|
|
214
522
|
|
|
215
523
|
interface AutoLayoutPaper {
|
|
216
524
|
paperId: string;
|
|
217
525
|
paperName: string;
|
|
218
|
-
paperWidth: number;
|
|
219
|
-
paperHeight: number;
|
|
526
|
+
paperWidth: number; // mm
|
|
527
|
+
paperHeight: number; // mm
|
|
220
528
|
}
|
|
221
529
|
```
|
|
222
530
|
|
|
@@ -224,7 +532,7 @@ interface AutoLayoutPaper {
|
|
|
224
532
|
|
|
225
533
|
```typescript
|
|
226
534
|
interface AutoLayoutResult {
|
|
227
|
-
papers: AutoLayoutPaperResult[];
|
|
535
|
+
papers: AutoLayoutPaperResult[]; // results per paper size
|
|
228
536
|
quantity: number;
|
|
229
537
|
totalProduced: number;
|
|
230
538
|
totalSheets: number;
|
|
@@ -237,20 +545,20 @@ interface AutoLayoutPaperResult {
|
|
|
237
545
|
paperName: string;
|
|
238
546
|
paperWidth: number;
|
|
239
547
|
paperHeight: number;
|
|
240
|
-
producedPerSheet: number;
|
|
548
|
+
producedPerSheet: number; // pieces per sheet
|
|
241
549
|
totalSheets: number;
|
|
242
550
|
totalProduced: number;
|
|
243
|
-
excessCount: number;
|
|
244
|
-
wastePercent: number;
|
|
245
|
-
independentSheets: number;
|
|
246
|
-
independentTotalArea: number;
|
|
551
|
+
excessCount: number; // overproduction
|
|
552
|
+
wastePercent: number; // paper waste %
|
|
553
|
+
independentSheets: number; // sheets if using only this paper
|
|
554
|
+
independentTotalArea: number; // total paper area (mm²)
|
|
247
555
|
placements: AutoLayoutPlacement[];
|
|
248
|
-
gripperSide: GripperSide;
|
|
556
|
+
gripperSide: GripperSide; // "top" | "bottom" | "left" | "right"
|
|
249
557
|
}
|
|
250
558
|
|
|
251
559
|
interface AutoLayoutPlacement {
|
|
252
|
-
x: number;
|
|
253
|
-
y: number;
|
|
560
|
+
x: number; // mm from paper left
|
|
561
|
+
y: number; // mm from paper top
|
|
254
562
|
rotation: number; // 0 | 90 | 180 | 270
|
|
255
563
|
}
|
|
256
564
|
```
|
|
@@ -260,8 +568,8 @@ interface AutoLayoutPlacement {
|
|
|
260
568
|
```typescript
|
|
261
569
|
interface AutoLayoutPackedResult extends AutoLayoutPaperResult {
|
|
262
570
|
quantity: number;
|
|
263
|
-
dielineFile: File;
|
|
264
|
-
layoutFile: File;
|
|
571
|
+
dielineFile: File; // dieline pattern image
|
|
572
|
+
layoutFile: File; // layout arrangement image
|
|
265
573
|
}
|
|
266
574
|
```
|
|
267
575
|
|
|
@@ -275,15 +583,17 @@ type ModelMode = "DIE_LINE" | "3D" | "AUTO_LAYOUT";
|
|
|
275
583
|
|
|
276
584
|
## 5. Theme Configuration
|
|
277
585
|
|
|
586
|
+
`configurePackaging()` must be called **ONCE before rendering** any packaging components. All fields are optional — only override what you need.
|
|
587
|
+
|
|
278
588
|
```tsx
|
|
279
|
-
import { configurePackaging } from "ultimate-packaging";
|
|
589
|
+
import { configurePackaging } from "@lucablockltd/ultimate-packaging";
|
|
280
590
|
|
|
281
|
-
// Call ONCE at app startup, BEFORE rendering any components
|
|
282
591
|
configurePackaging({
|
|
592
|
+
fontDimensionSize: 4.2, // dimension label font size (default: 4.2)
|
|
283
593
|
modelTheme: {
|
|
284
594
|
colorBackground: "#FAFAFA",
|
|
285
|
-
colorDieLine: "#FF0000",
|
|
286
|
-
colorFoldLine: "#00FF00",
|
|
595
|
+
colorDieLine: "#FF0000", // cut lines
|
|
596
|
+
colorFoldLine: "#00FF00", // crease lines
|
|
287
597
|
},
|
|
288
598
|
autoLayoutTheme: {
|
|
289
599
|
colorBackground: "#F5F5F5",
|
|
@@ -299,6 +609,95 @@ configurePackaging({
|
|
|
299
609
|
});
|
|
300
610
|
```
|
|
301
611
|
|
|
612
|
+
### Where to call `configurePackaging()` per framework
|
|
613
|
+
|
|
614
|
+
#### Vite / CRA (Client-side SPA)
|
|
615
|
+
|
|
616
|
+
Call in `main.tsx` before `createRoot().render()`:
|
|
617
|
+
|
|
618
|
+
```tsx
|
|
619
|
+
// main.tsx
|
|
620
|
+
import { configurePackaging } from "@lucablockltd/ultimate-packaging";
|
|
621
|
+
|
|
622
|
+
configurePackaging({ modelTheme: { colorDieLine: "#FF0000" } });
|
|
623
|
+
|
|
624
|
+
createRoot(document.getElementById("root")!).render(<App />);
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
#### Next.js (App Router)
|
|
628
|
+
|
|
629
|
+
Create a client-side provider component and use it in the root layout:
|
|
630
|
+
|
|
631
|
+
```tsx
|
|
632
|
+
// components/PackagingProvider.tsx
|
|
633
|
+
"use client";
|
|
634
|
+
|
|
635
|
+
import { configurePackaging } from "@lucablockltd/ultimate-packaging";
|
|
636
|
+
|
|
637
|
+
// Runs once on module load (client-side only)
|
|
638
|
+
configurePackaging({
|
|
639
|
+
modelTheme: { colorDieLine: "#FF0000" },
|
|
640
|
+
autoLayoutTheme: { colorDistanceLine: "#0088FF" },
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
export default function PackagingProvider({
|
|
644
|
+
children,
|
|
645
|
+
}: {
|
|
646
|
+
children: React.ReactNode;
|
|
647
|
+
}) {
|
|
648
|
+
return <>{children}</>;
|
|
649
|
+
}
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
```tsx
|
|
653
|
+
// app/layout.tsx
|
|
654
|
+
import PackagingProvider from "@/components/PackagingProvider";
|
|
655
|
+
|
|
656
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
657
|
+
return (
|
|
658
|
+
<html>
|
|
659
|
+
<body>
|
|
660
|
+
<PackagingProvider>{children}</PackagingProvider>
|
|
661
|
+
</body>
|
|
662
|
+
</html>
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
> **Why a `"use client"` provider?** — `configurePackaging()` sets a global variable. In Next.js App Router, server components run on the server where this global has no effect. Using `"use client"` ensures the config runs in the browser before any packaging components render.
|
|
668
|
+
|
|
669
|
+
#### Next.js (Pages Router)
|
|
670
|
+
|
|
671
|
+
Call in `_app.tsx`:
|
|
672
|
+
|
|
673
|
+
```tsx
|
|
674
|
+
// pages/_app.tsx
|
|
675
|
+
import { configurePackaging } from "@lucablockltd/ultimate-packaging";
|
|
676
|
+
import type { AppProps } from "next/app";
|
|
677
|
+
|
|
678
|
+
configurePackaging({
|
|
679
|
+
modelTheme: { colorDieLine: "#FF0000" },
|
|
680
|
+
autoLayoutTheme: { colorDistanceLine: "#0088FF" },
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
export default function App({ Component, pageProps }: AppProps) {
|
|
684
|
+
return <Component {...pageProps} />;
|
|
685
|
+
}
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
#### Remix
|
|
689
|
+
|
|
690
|
+
Call in `root.tsx` or a client-only module:
|
|
691
|
+
|
|
692
|
+
```tsx
|
|
693
|
+
// app/root.tsx
|
|
694
|
+
import { configurePackaging } from "@lucablockltd/ultimate-packaging";
|
|
695
|
+
|
|
696
|
+
configurePackaging({
|
|
697
|
+
modelTheme: { colorDieLine: "#FF0000" },
|
|
698
|
+
});
|
|
699
|
+
```
|
|
700
|
+
|
|
302
701
|
---
|
|
303
702
|
|
|
304
703
|
## 6. Complete Imports Cheat Sheet
|
|
@@ -306,15 +705,17 @@ configurePackaging({
|
|
|
306
705
|
```tsx
|
|
307
706
|
// Components
|
|
308
707
|
import {
|
|
309
|
-
DIE_LINE_LAYOUT,
|
|
310
|
-
AUTO_LAYOUT,
|
|
311
|
-
MODEL_BECF_1010A,
|
|
708
|
+
DIE_LINE_LAYOUT, // Recommended: auto-switches by modelId
|
|
709
|
+
AUTO_LAYOUT, // Auto-layout packing on paper
|
|
710
|
+
MODEL_BECF_1010A, // Direct model components (if needed)
|
|
312
711
|
MODEL_BECF_1030A,
|
|
313
712
|
MODEL_BECF_1040A,
|
|
314
713
|
MODEL_BECF_11D01,
|
|
315
|
-
|
|
714
|
+
MODEL_BECF_12101,
|
|
715
|
+
MODEL_BECF_12109,
|
|
716
|
+
Colorbar, // Sub-components for custom rendering
|
|
316
717
|
Gripper,
|
|
317
|
-
} from "ultimate-packaging";
|
|
718
|
+
} from "@lucablockltd/ultimate-packaging";
|
|
318
719
|
|
|
319
720
|
// Types
|
|
320
721
|
import type {
|
|
@@ -329,8 +730,11 @@ import type {
|
|
|
329
730
|
AutoLayoutPlacement,
|
|
330
731
|
AutoLayoutPaper,
|
|
331
732
|
AutoLayoutModel,
|
|
733
|
+
AutoLayoutAction,
|
|
332
734
|
TuckEndBoxAttributes,
|
|
333
735
|
StandardBoxAttributes,
|
|
736
|
+
CartonBagAttributes,
|
|
737
|
+
CartonBag12109Attributes,
|
|
334
738
|
ModelMode,
|
|
335
739
|
PackagingModel,
|
|
336
740
|
GripperSide,
|
|
@@ -338,24 +742,26 @@ import type {
|
|
|
338
742
|
ModelThemeConfig,
|
|
339
743
|
AutoLayoutThemeConfig,
|
|
340
744
|
UltimatePackagingConfig,
|
|
341
|
-
} from "ultimate-packaging";
|
|
745
|
+
} from "@lucablockltd/ultimate-packaging";
|
|
342
746
|
|
|
343
747
|
// Data & Constants
|
|
344
748
|
import {
|
|
345
|
-
modelList,
|
|
749
|
+
modelList, // PackagingModel[] — all models with defaults
|
|
346
750
|
BECF_1010A_DEFAULT_ATTRIBUTES,
|
|
347
751
|
BECF_1030A_DEFAULT_ATTRIBUTES,
|
|
348
752
|
BECF_1040A_DEFAULT_ATTRIBUTES,
|
|
349
753
|
BECF_11D01_DEFAULT_ATTRIBUTES,
|
|
754
|
+
BECF_12101_DEFAULT_ATTRIBUTES,
|
|
755
|
+
BECF_12109_DEFAULT_ATTRIBUTES,
|
|
350
756
|
MODEL_THEME_CONFIG,
|
|
351
757
|
AUTO_LAYOUT_THEME_CONFIG,
|
|
352
|
-
} from "ultimate-packaging";
|
|
758
|
+
} from "@lucablockltd/ultimate-packaging";
|
|
353
759
|
|
|
354
760
|
// Functions
|
|
355
761
|
import {
|
|
356
|
-
configurePackaging,
|
|
357
|
-
calculateAutoLayout,
|
|
358
|
-
} from "ultimate-packaging";
|
|
762
|
+
configurePackaging, // Set global theme
|
|
763
|
+
calculateAutoLayout, // Compute layout without UI
|
|
764
|
+
} from "@lucablockltd/ultimate-packaging";
|
|
359
765
|
```
|
|
360
766
|
|
|
361
767
|
---
|
|
@@ -365,8 +771,8 @@ import {
|
|
|
365
771
|
### Pattern A: Display a Dieline with Controls
|
|
366
772
|
|
|
367
773
|
```tsx
|
|
368
|
-
import { DIE_LINE_LAYOUT, modelList } from "ultimate-packaging";
|
|
369
|
-
import type { DieLineLayoutRef } from "ultimate-packaging";
|
|
774
|
+
import { DIE_LINE_LAYOUT, modelList } from "@lucablockltd/ultimate-packaging";
|
|
775
|
+
import type { DieLineLayoutRef } from "@lucablockltd/ultimate-packaging";
|
|
370
776
|
|
|
371
777
|
function DielineEditor() {
|
|
372
778
|
const ref = useRef<DieLineLayoutRef>(null);
|
|
@@ -375,13 +781,20 @@ function DielineEditor() {
|
|
|
375
781
|
|
|
376
782
|
const handleModelChange = (id: string) => {
|
|
377
783
|
setModelId(id);
|
|
378
|
-
setAttrs(modelList.find(m => m.id === id)!.attributes);
|
|
784
|
+
setAttrs(modelList.find((m) => m.id === id)!.attributes);
|
|
379
785
|
};
|
|
380
786
|
|
|
381
787
|
return (
|
|
382
788
|
<>
|
|
383
|
-
<select
|
|
384
|
-
|
|
789
|
+
<select
|
|
790
|
+
value={modelId}
|
|
791
|
+
onChange={(e) => handleModelChange(e.target.value)}
|
|
792
|
+
>
|
|
793
|
+
{modelList.map((m) => (
|
|
794
|
+
<option key={m.id} value={m.id}>
|
|
795
|
+
{m.nameEN}
|
|
796
|
+
</option>
|
|
797
|
+
))}
|
|
385
798
|
</select>
|
|
386
799
|
<DIE_LINE_LAYOUT ref={ref} modelId={modelId} attributes={attrs} />
|
|
387
800
|
</>
|
|
@@ -392,16 +805,37 @@ function DielineEditor() {
|
|
|
392
805
|
### Pattern B: Auto-Layout with Export
|
|
393
806
|
|
|
394
807
|
```tsx
|
|
395
|
-
import { AUTO_LAYOUT } from "ultimate-packaging";
|
|
396
|
-
import type {
|
|
808
|
+
import { AUTO_LAYOUT } from "@lucablockltd/ultimate-packaging";
|
|
809
|
+
import type {
|
|
810
|
+
AutoLayoutRef,
|
|
811
|
+
AutoLayoutConfig,
|
|
812
|
+
AutoLayoutResult,
|
|
813
|
+
} from "@lucablockltd/ultimate-packaging";
|
|
397
814
|
|
|
398
815
|
function LayoutPlanner() {
|
|
399
816
|
const ref = useRef<AutoLayoutRef>(null);
|
|
400
817
|
const [result, setResult] = useState<AutoLayoutResult | null>(null);
|
|
401
818
|
|
|
402
819
|
const config: AutoLayoutConfig = {
|
|
403
|
-
papers: [
|
|
404
|
-
|
|
820
|
+
papers: [
|
|
821
|
+
{
|
|
822
|
+
paperId: "1",
|
|
823
|
+
paperName: '20x28"',
|
|
824
|
+
paperWidth: 508,
|
|
825
|
+
paperHeight: 711.2,
|
|
826
|
+
},
|
|
827
|
+
],
|
|
828
|
+
model: {
|
|
829
|
+
modelId: "BECF-1010A",
|
|
830
|
+
attributes: {
|
|
831
|
+
length: 60,
|
|
832
|
+
width: 25,
|
|
833
|
+
height: 100,
|
|
834
|
+
glueArea: 15,
|
|
835
|
+
dustFlap: 15,
|
|
836
|
+
tuckFlap: 15,
|
|
837
|
+
},
|
|
838
|
+
},
|
|
405
839
|
quantity: 500,
|
|
406
840
|
layoutDistance: 3,
|
|
407
841
|
spacing: 5,
|
|
@@ -420,7 +854,12 @@ function LayoutPlanner() {
|
|
|
420
854
|
return (
|
|
421
855
|
<>
|
|
422
856
|
<AUTO_LAYOUT ref={ref} config={config} onResult={setResult} />
|
|
423
|
-
{result &&
|
|
857
|
+
{result && (
|
|
858
|
+
<p>
|
|
859
|
+
Best: {result.recommendedPaperId} —{" "}
|
|
860
|
+
{result.papers[0].producedPerSheet} pcs/sheet
|
|
861
|
+
</p>
|
|
862
|
+
)}
|
|
424
863
|
<button onClick={handleExport}>Export</button>
|
|
425
864
|
</>
|
|
426
865
|
);
|
|
@@ -430,14 +869,24 @@ function LayoutPlanner() {
|
|
|
430
869
|
### Pattern C: Headless Calculation (No React)
|
|
431
870
|
|
|
432
871
|
```tsx
|
|
433
|
-
import { calculateAutoLayout } from "ultimate-packaging";
|
|
872
|
+
import { calculateAutoLayout } from "@lucablockltd/ultimate-packaging";
|
|
434
873
|
|
|
435
874
|
const result = calculateAutoLayout({
|
|
436
875
|
papers: [
|
|
437
|
-
{ paperId: "A", paperName:
|
|
438
|
-
{ paperId: "B", paperName:
|
|
876
|
+
{ paperId: "A", paperName: '20x28"', paperWidth: 508, paperHeight: 711.2 },
|
|
877
|
+
{ paperId: "B", paperName: '25x36"', paperWidth: 635, paperHeight: 914.4 },
|
|
439
878
|
],
|
|
440
|
-
model: {
|
|
879
|
+
model: {
|
|
880
|
+
modelId: "BECF-1030A",
|
|
881
|
+
attributes: {
|
|
882
|
+
length: 105,
|
|
883
|
+
width: 50,
|
|
884
|
+
height: 155,
|
|
885
|
+
glueArea: 15,
|
|
886
|
+
dustFlap: 25,
|
|
887
|
+
tuckFlap: 14,
|
|
888
|
+
},
|
|
889
|
+
},
|
|
441
890
|
quantity: 1000,
|
|
442
891
|
layoutDistance: 3,
|
|
443
892
|
spacing: 5,
|
|
@@ -447,8 +896,10 @@ const result = calculateAutoLayout({
|
|
|
447
896
|
});
|
|
448
897
|
|
|
449
898
|
console.log(result.recommendedPaperId); // "B"
|
|
450
|
-
result.papers.forEach(p => {
|
|
451
|
-
console.log(
|
|
899
|
+
result.papers.forEach((p) => {
|
|
900
|
+
console.log(
|
|
901
|
+
`${p.paperName}: ${p.producedPerSheet} pcs/sheet, ${p.wastePercent}% waste`,
|
|
902
|
+
);
|
|
452
903
|
});
|
|
453
904
|
```
|
|
454
905
|
|