@pooder/kit 4.1.0 → 4.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/.test-dist/src/CanvasService.js +83 -0
- package/.test-dist/src/ViewportSystem.js +75 -0
- package/.test-dist/src/background.js +203 -0
- package/.test-dist/src/constraints.js +153 -0
- package/.test-dist/src/coordinate.js +74 -0
- package/.test-dist/src/dieline.js +758 -0
- package/.test-dist/src/feature.js +687 -0
- package/.test-dist/src/featureComplete.js +31 -0
- package/.test-dist/src/featureDraft.js +31 -0
- package/.test-dist/src/film.js +167 -0
- package/.test-dist/src/geometry.js +292 -0
- package/.test-dist/src/image.js +421 -0
- package/.test-dist/src/index.js +31 -0
- package/.test-dist/src/mirror.js +104 -0
- package/.test-dist/src/ruler.js +383 -0
- package/.test-dist/src/tracer.js +448 -0
- package/.test-dist/src/units.js +30 -0
- package/.test-dist/src/white-ink.js +310 -0
- package/.test-dist/tests/run.js +60 -0
- package/CHANGELOG.md +6 -0
- package/dist/index.d.mts +50 -5
- package/dist/index.d.ts +50 -5
- package/dist/index.js +544 -297
- package/dist/index.mjs +541 -296
- package/package.json +3 -2
- package/src/CanvasService.ts +7 -0
- package/src/ViewportSystem.ts +92 -0
- package/src/constraints.ts +53 -4
- package/src/dieline.ts +169 -85
- package/src/feature.ts +217 -150
- package/src/featureComplete.ts +45 -0
- package/src/index.ts +1 -0
- package/src/ruler.ts +26 -18
- package/src/units.ts +27 -0
- package/tests/run.ts +81 -0
- package/tsconfig.test.json +15 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pooder/kit",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "Standard plugins for Pooder editor",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
26
|
-
"dev": "tsup src/index.ts --format cjs,esm --dts --watch"
|
|
26
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
27
|
+
"test": "tsc -p tsconfig.test.json && node .test-dist/tests/run.js"
|
|
27
28
|
}
|
|
28
29
|
}
|
package/src/CanvasService.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Canvas, Group, FabricObject } from "fabric";
|
|
2
2
|
import { Service, EventBus } from "@pooder/core";
|
|
3
|
+
import { ViewportSystem } from "./ViewportSystem";
|
|
3
4
|
|
|
4
5
|
export default class CanvasService implements Service {
|
|
5
6
|
public canvas: Canvas;
|
|
7
|
+
public viewport: ViewportSystem;
|
|
6
8
|
private eventBus?: EventBus;
|
|
7
9
|
|
|
8
10
|
constructor(el: HTMLCanvasElement | string | Canvas, options?: any) {
|
|
@@ -14,6 +16,11 @@ export default class CanvasService implements Service {
|
|
|
14
16
|
...options,
|
|
15
17
|
});
|
|
16
18
|
}
|
|
19
|
+
|
|
20
|
+
this.viewport = new ViewportSystem();
|
|
21
|
+
if (this.canvas.width !== undefined && this.canvas.height !== undefined) {
|
|
22
|
+
this.viewport.updateContainer(this.canvas.width, this.canvas.height);
|
|
23
|
+
}
|
|
17
24
|
|
|
18
25
|
if (options?.eventBus) {
|
|
19
26
|
this.setEventBus(options.eventBus);
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Coordinate, Layout, Point, Size } from "./coordinate";
|
|
2
|
+
|
|
3
|
+
export class ViewportSystem {
|
|
4
|
+
private _containerSize: Size = { width: 0, height: 0 };
|
|
5
|
+
private _physicalSize: Size = { width: 0, height: 0 };
|
|
6
|
+
private _padding: number = 0;
|
|
7
|
+
private _layout: Layout = {
|
|
8
|
+
scale: 1,
|
|
9
|
+
offsetX: 0,
|
|
10
|
+
offsetY: 0,
|
|
11
|
+
width: 0,
|
|
12
|
+
height: 0,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
containerSize: Size = { width: 0, height: 0 },
|
|
17
|
+
physicalSize: Size = { width: 0, height: 0 },
|
|
18
|
+
padding: number = 40,
|
|
19
|
+
) {
|
|
20
|
+
this._containerSize = containerSize;
|
|
21
|
+
this._physicalSize = physicalSize;
|
|
22
|
+
this._padding = padding;
|
|
23
|
+
this.updateLayout();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get layout(): Layout {
|
|
27
|
+
return this._layout;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get scale(): number {
|
|
31
|
+
return this._layout.scale;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get offset(): Point {
|
|
35
|
+
return { x: this._layout.offsetX, y: this._layout.offsetY };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
updateContainer(width: number, height: number) {
|
|
39
|
+
if (
|
|
40
|
+
this._containerSize.width === width &&
|
|
41
|
+
this._containerSize.height === height
|
|
42
|
+
)
|
|
43
|
+
return;
|
|
44
|
+
this._containerSize = { width, height };
|
|
45
|
+
this.updateLayout();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
updatePhysical(width: number, height: number) {
|
|
49
|
+
if (this._physicalSize.width === width && this._physicalSize.height === height)
|
|
50
|
+
return;
|
|
51
|
+
this._physicalSize = { width, height };
|
|
52
|
+
this.updateLayout();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
setPadding(padding: number) {
|
|
56
|
+
if (this._padding === padding) return;
|
|
57
|
+
this._padding = padding;
|
|
58
|
+
this.updateLayout();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private updateLayout() {
|
|
62
|
+
this._layout = Coordinate.calculateLayout(
|
|
63
|
+
this._containerSize,
|
|
64
|
+
this._physicalSize,
|
|
65
|
+
this._padding,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
toPixel(value: number): number {
|
|
70
|
+
return value * this._layout.scale;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
toPhysical(value: number): number {
|
|
74
|
+
return this._layout.scale === 0 ? 0 : value / this._layout.scale;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
toPixelPoint(point: Point): Point {
|
|
78
|
+
return {
|
|
79
|
+
x: point.x * this._layout.scale + this._layout.offsetX,
|
|
80
|
+
y: point.y * this._layout.scale + this._layout.offsetY,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Convert screen coordinate (e.g. mouse event) to physical coordinate (relative to content origin)
|
|
85
|
+
toPhysicalPoint(point: Point): Point {
|
|
86
|
+
if (this._layout.scale === 0) return { x: 0, y: 0 };
|
|
87
|
+
return {
|
|
88
|
+
x: (point.x - this._layout.offsetX) / this._layout.scale,
|
|
89
|
+
y: (point.y - this._layout.offsetY) / this._layout.scale,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
package/src/constraints.ts
CHANGED
|
@@ -1,14 +1,29 @@
|
|
|
1
|
-
import { DielineFeature } from "./geometry";
|
|
2
|
-
|
|
3
1
|
export interface ConstraintContext {
|
|
4
2
|
dielineWidth: number;
|
|
5
3
|
dielineHeight: number;
|
|
6
4
|
}
|
|
7
5
|
|
|
6
|
+
export interface ConstraintFeature {
|
|
7
|
+
id: string;
|
|
8
|
+
groupId?: string;
|
|
9
|
+
operation: "add" | "subtract";
|
|
10
|
+
shape: "rect" | "circle";
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
width?: number;
|
|
14
|
+
height?: number;
|
|
15
|
+
radius?: number;
|
|
16
|
+
placement?: "edge" | "internal";
|
|
17
|
+
constraints?: {
|
|
18
|
+
type: string;
|
|
19
|
+
params?: any;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
8
23
|
export type ConstraintHandler = (
|
|
9
24
|
x: number,
|
|
10
25
|
y: number,
|
|
11
|
-
feature:
|
|
26
|
+
feature: ConstraintFeature,
|
|
12
27
|
context: ConstraintContext
|
|
13
28
|
) => { x: number; y: number };
|
|
14
29
|
|
|
@@ -22,7 +37,7 @@ export class ConstraintRegistry {
|
|
|
22
37
|
static apply(
|
|
23
38
|
x: number,
|
|
24
39
|
y: number,
|
|
25
|
-
feature:
|
|
40
|
+
feature: ConstraintFeature,
|
|
26
41
|
context: ConstraintContext
|
|
27
42
|
): { x: number; y: number } {
|
|
28
43
|
if (!feature.constraints || !feature.constraints.type) {
|
|
@@ -153,6 +168,40 @@ const internalConstraint: ConstraintHandler = (x, y, feature, context) => {
|
|
|
153
168
|
return { x: clampedX, y: clampedY };
|
|
154
169
|
};
|
|
155
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Bottom Tangent Strategy (stand protrusion)
|
|
173
|
+
* Forces a feature to be tangent to the dieline bottom edge from outside (below).
|
|
174
|
+
* Params:
|
|
175
|
+
* - gap: number (mm, default 0) extra clearance between dieline and protrusion
|
|
176
|
+
* - confineX: boolean (default true) keep feature within left/right bounds
|
|
177
|
+
*/
|
|
178
|
+
const tangentBottomConstraint: ConstraintHandler = (x, y, feature, context) => {
|
|
179
|
+
const { dielineWidth, dielineHeight } = context;
|
|
180
|
+
const params = feature.constraints?.params || {};
|
|
181
|
+
const gap = params.gap || 0;
|
|
182
|
+
const confineX = params.confineX !== false;
|
|
183
|
+
|
|
184
|
+
const extentY =
|
|
185
|
+
feature.shape === "circle"
|
|
186
|
+
? feature.radius || 0
|
|
187
|
+
: (feature.height || 0) / 2;
|
|
188
|
+
const newY = 1 + (extentY + gap) / dielineHeight;
|
|
189
|
+
|
|
190
|
+
let newX = x;
|
|
191
|
+
if (confineX) {
|
|
192
|
+
const extentX =
|
|
193
|
+
feature.shape === "circle"
|
|
194
|
+
? feature.radius || 0
|
|
195
|
+
: (feature.width || 0) / 2;
|
|
196
|
+
const minX = extentX / dielineWidth;
|
|
197
|
+
const maxX = 1 - extentX / dielineWidth;
|
|
198
|
+
newX = minX > maxX ? 0.5 : Math.max(minX, Math.min(newX, maxX));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return { x: newX, y: newY };
|
|
202
|
+
};
|
|
203
|
+
|
|
156
204
|
// Register built-ins
|
|
157
205
|
ConstraintRegistry.register("edge", edgeConstraint);
|
|
158
206
|
ConstraintRegistry.register("internal", internalConstraint);
|
|
207
|
+
ConstraintRegistry.register("tangent-bottom", tangentBottomConstraint);
|
package/src/dieline.ts
CHANGED
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
import { Path, Pattern } from "fabric";
|
|
9
9
|
import CanvasService from "./CanvasService";
|
|
10
10
|
import { ImageTracer } from "./tracer";
|
|
11
|
-
import {
|
|
11
|
+
import { Unit } from "./coordinate";
|
|
12
|
+
import { parseLengthToMm } from "./units";
|
|
12
13
|
import {
|
|
13
14
|
generateDielinePath,
|
|
14
15
|
generateMaskPath,
|
|
@@ -16,11 +17,11 @@ import {
|
|
|
16
17
|
getPathBounds,
|
|
17
18
|
DielineFeature,
|
|
18
19
|
} from "./geometry";
|
|
19
|
-
import { ConstraintRegistry } from "./constraints";
|
|
20
20
|
|
|
21
21
|
export interface DielineGeometry {
|
|
22
22
|
shape: "rect" | "circle" | "ellipse" | "custom";
|
|
23
|
-
unit:
|
|
23
|
+
unit: "mm";
|
|
24
|
+
displayUnit: Unit;
|
|
24
25
|
x: number;
|
|
25
26
|
y: number;
|
|
26
27
|
width: number;
|
|
@@ -41,7 +42,7 @@ export interface LineStyle {
|
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
export interface DielineState {
|
|
44
|
-
|
|
45
|
+
displayUnit: Unit;
|
|
45
46
|
shape: "rect" | "circle" | "ellipse" | "custom";
|
|
46
47
|
width: number;
|
|
47
48
|
height: number;
|
|
@@ -64,7 +65,7 @@ export class DielineTool implements Extension {
|
|
|
64
65
|
};
|
|
65
66
|
|
|
66
67
|
private state: DielineState = {
|
|
67
|
-
|
|
68
|
+
displayUnit: "mm",
|
|
68
69
|
shape: "rect",
|
|
69
70
|
width: 500,
|
|
70
71
|
height: 500,
|
|
@@ -119,61 +120,138 @@ export class DielineTool implements Extension {
|
|
|
119
120
|
if (configService) {
|
|
120
121
|
// Load initial config
|
|
121
122
|
const s = this.state;
|
|
122
|
-
s.
|
|
123
|
+
s.displayUnit = configService.get("dieline.displayUnit", s.displayUnit);
|
|
123
124
|
s.shape = configService.get("dieline.shape", s.shape);
|
|
124
|
-
s.width =
|
|
125
|
-
|
|
126
|
-
|
|
125
|
+
s.width = parseLengthToMm(
|
|
126
|
+
configService.get("dieline.width", s.width),
|
|
127
|
+
"mm",
|
|
128
|
+
);
|
|
129
|
+
s.height = parseLengthToMm(
|
|
130
|
+
configService.get("dieline.height", s.height),
|
|
131
|
+
"mm",
|
|
132
|
+
);
|
|
133
|
+
s.radius = parseLengthToMm(
|
|
134
|
+
configService.get("dieline.radius", s.radius),
|
|
135
|
+
"mm",
|
|
136
|
+
);
|
|
127
137
|
s.padding = configService.get("dieline.padding", s.padding);
|
|
128
|
-
s.offset =
|
|
129
|
-
|
|
138
|
+
s.offset = parseLengthToMm(
|
|
139
|
+
configService.get("dieline.offset", s.offset),
|
|
140
|
+
"mm",
|
|
141
|
+
);
|
|
142
|
+
|
|
130
143
|
// Main Line
|
|
131
|
-
s.mainLine.width = configService.get(
|
|
132
|
-
|
|
133
|
-
|
|
144
|
+
s.mainLine.width = configService.get(
|
|
145
|
+
"dieline.strokeWidth",
|
|
146
|
+
s.mainLine.width,
|
|
147
|
+
);
|
|
148
|
+
s.mainLine.color = configService.get(
|
|
149
|
+
"dieline.strokeColor",
|
|
150
|
+
s.mainLine.color,
|
|
151
|
+
);
|
|
152
|
+
s.mainLine.dashLength = configService.get(
|
|
153
|
+
"dieline.dashLength",
|
|
154
|
+
s.mainLine.dashLength,
|
|
155
|
+
);
|
|
134
156
|
s.mainLine.style = configService.get("dieline.style", s.mainLine.style);
|
|
135
157
|
|
|
136
158
|
// Offset Line
|
|
137
|
-
s.offsetLine.width = configService.get(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
159
|
+
s.offsetLine.width = configService.get(
|
|
160
|
+
"dieline.offsetStrokeWidth",
|
|
161
|
+
s.offsetLine.width,
|
|
162
|
+
);
|
|
163
|
+
s.offsetLine.color = configService.get(
|
|
164
|
+
"dieline.offsetStrokeColor",
|
|
165
|
+
s.offsetLine.color,
|
|
166
|
+
);
|
|
167
|
+
s.offsetLine.dashLength = configService.get(
|
|
168
|
+
"dieline.offsetDashLength",
|
|
169
|
+
s.offsetLine.dashLength,
|
|
170
|
+
);
|
|
171
|
+
s.offsetLine.style = configService.get(
|
|
172
|
+
"dieline.offsetStyle",
|
|
173
|
+
s.offsetLine.style,
|
|
174
|
+
);
|
|
141
175
|
|
|
142
176
|
s.insideColor = configService.get("dieline.insideColor", s.insideColor);
|
|
143
|
-
s.outsideColor = configService.get(
|
|
144
|
-
|
|
177
|
+
s.outsideColor = configService.get(
|
|
178
|
+
"dieline.outsideColor",
|
|
179
|
+
s.outsideColor,
|
|
180
|
+
);
|
|
181
|
+
s.showBleedLines = configService.get(
|
|
182
|
+
"dieline.showBleedLines",
|
|
183
|
+
s.showBleedLines,
|
|
184
|
+
);
|
|
145
185
|
s.features = configService.get("dieline.features", s.features);
|
|
146
186
|
s.pathData = configService.get("dieline.pathData", s.pathData);
|
|
147
187
|
|
|
148
188
|
// Listen for changes
|
|
149
189
|
configService.onAnyChange((e: { key: string; value: any }) => {
|
|
150
190
|
if (e.key.startsWith("dieline.")) {
|
|
151
|
-
console.log(`[DielineTool] Config change detected: ${e.key} -> ${e.value}`);
|
|
152
|
-
|
|
153
191
|
switch (e.key) {
|
|
154
|
-
case "dieline.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
case "dieline.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
case "dieline.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
case "dieline.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
case "dieline.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
case "dieline.
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
case "dieline.
|
|
192
|
+
case "dieline.displayUnit":
|
|
193
|
+
s.displayUnit = e.value;
|
|
194
|
+
break;
|
|
195
|
+
case "dieline.shape":
|
|
196
|
+
s.shape = e.value;
|
|
197
|
+
break;
|
|
198
|
+
case "dieline.width":
|
|
199
|
+
s.width = parseLengthToMm(e.value, "mm");
|
|
200
|
+
break;
|
|
201
|
+
case "dieline.height":
|
|
202
|
+
s.height = parseLengthToMm(e.value, "mm");
|
|
203
|
+
break;
|
|
204
|
+
case "dieline.radius":
|
|
205
|
+
s.radius = parseLengthToMm(e.value, "mm");
|
|
206
|
+
break;
|
|
207
|
+
case "dieline.padding":
|
|
208
|
+
s.padding = e.value;
|
|
209
|
+
break;
|
|
210
|
+
case "dieline.offset":
|
|
211
|
+
s.offset = parseLengthToMm(e.value, "mm");
|
|
212
|
+
break;
|
|
213
|
+
|
|
214
|
+
case "dieline.strokeWidth":
|
|
215
|
+
s.mainLine.width = e.value;
|
|
216
|
+
break;
|
|
217
|
+
case "dieline.strokeColor":
|
|
218
|
+
s.mainLine.color = e.value;
|
|
219
|
+
break;
|
|
220
|
+
case "dieline.dashLength":
|
|
221
|
+
s.mainLine.dashLength = e.value;
|
|
222
|
+
break;
|
|
223
|
+
case "dieline.style":
|
|
224
|
+
s.mainLine.style = e.value;
|
|
225
|
+
break;
|
|
226
|
+
|
|
227
|
+
case "dieline.offsetStrokeWidth":
|
|
228
|
+
s.offsetLine.width = e.value;
|
|
229
|
+
break;
|
|
230
|
+
case "dieline.offsetStrokeColor":
|
|
231
|
+
s.offsetLine.color = e.value;
|
|
232
|
+
break;
|
|
233
|
+
case "dieline.offsetDashLength":
|
|
234
|
+
s.offsetLine.dashLength = e.value;
|
|
235
|
+
break;
|
|
236
|
+
case "dieline.offsetStyle":
|
|
237
|
+
s.offsetLine.style = e.value;
|
|
238
|
+
break;
|
|
239
|
+
|
|
240
|
+
case "dieline.insideColor":
|
|
241
|
+
s.insideColor = e.value;
|
|
242
|
+
break;
|
|
243
|
+
case "dieline.outsideColor":
|
|
244
|
+
s.outsideColor = e.value;
|
|
245
|
+
break;
|
|
246
|
+
case "dieline.showBleedLines":
|
|
247
|
+
s.showBleedLines = e.value;
|
|
248
|
+
break;
|
|
249
|
+
case "dieline.features":
|
|
250
|
+
s.features = e.value;
|
|
251
|
+
break;
|
|
252
|
+
case "dieline.pathData":
|
|
253
|
+
s.pathData = e.value;
|
|
254
|
+
break;
|
|
177
255
|
}
|
|
178
256
|
this.updateDieline();
|
|
179
257
|
}
|
|
@@ -195,11 +273,11 @@ export class DielineTool implements Extension {
|
|
|
195
273
|
return {
|
|
196
274
|
[ContributionPointIds.CONFIGURATIONS]: [
|
|
197
275
|
{
|
|
198
|
-
id: "dieline.
|
|
276
|
+
id: "dieline.displayUnit",
|
|
199
277
|
type: "select",
|
|
200
|
-
label: "Unit",
|
|
201
|
-
options: ["
|
|
202
|
-
default: s.
|
|
278
|
+
label: "Display Unit",
|
|
279
|
+
options: ["mm", "cm", "in"],
|
|
280
|
+
default: s.displayUnit,
|
|
203
281
|
},
|
|
204
282
|
{
|
|
205
283
|
id: "dieline.shape",
|
|
@@ -211,7 +289,7 @@ export class DielineTool implements Extension {
|
|
|
211
289
|
{
|
|
212
290
|
id: "dieline.width",
|
|
213
291
|
type: "number",
|
|
214
|
-
label: "Width",
|
|
292
|
+
label: "Width (mm)",
|
|
215
293
|
min: 10,
|
|
216
294
|
max: 2000,
|
|
217
295
|
default: s.width,
|
|
@@ -219,7 +297,7 @@ export class DielineTool implements Extension {
|
|
|
219
297
|
{
|
|
220
298
|
id: "dieline.height",
|
|
221
299
|
type: "number",
|
|
222
|
-
label: "Height",
|
|
300
|
+
label: "Height (mm)",
|
|
223
301
|
min: 10,
|
|
224
302
|
max: 2000,
|
|
225
303
|
default: s.height,
|
|
@@ -227,7 +305,7 @@ export class DielineTool implements Extension {
|
|
|
227
305
|
{
|
|
228
306
|
id: "dieline.radius",
|
|
229
307
|
type: "number",
|
|
230
|
-
label: "Corner Radius",
|
|
308
|
+
label: "Corner Radius (mm)",
|
|
231
309
|
min: 0,
|
|
232
310
|
max: 500,
|
|
233
311
|
default: s.radius,
|
|
@@ -242,7 +320,7 @@ export class DielineTool implements Extension {
|
|
|
242
320
|
{
|
|
243
321
|
id: "dieline.offset",
|
|
244
322
|
type: "number",
|
|
245
|
-
label: "Bleed Offset",
|
|
323
|
+
label: "Bleed Offset (mm)",
|
|
246
324
|
min: -100,
|
|
247
325
|
max: 100,
|
|
248
326
|
default: s.offset,
|
|
@@ -343,20 +421,13 @@ export class DielineTool implements Extension {
|
|
|
343
421
|
if (!configService) return;
|
|
344
422
|
|
|
345
423
|
const features = configService.get("dieline.features") || [];
|
|
346
|
-
const dielineWidth = configService.get("dieline.width") || 500;
|
|
347
|
-
const dielineHeight = configService.get("dieline.height") || 500;
|
|
348
424
|
|
|
349
425
|
let changed = false;
|
|
350
426
|
const newFeatures = features.map((f: any) => {
|
|
351
427
|
if (f.groupId === groupId) {
|
|
352
|
-
|
|
353
|
-
dielineWidth,
|
|
354
|
-
dielineHeight,
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
if (f.x !== constrained.x || f.y !== constrained.y) {
|
|
428
|
+
if (f.x !== x || f.y !== y) {
|
|
358
429
|
changed = true;
|
|
359
|
-
return { ...f, x
|
|
430
|
+
return { ...f, x, y };
|
|
360
431
|
}
|
|
361
432
|
}
|
|
362
433
|
return f;
|
|
@@ -494,7 +565,7 @@ export class DielineTool implements Extension {
|
|
|
494
565
|
if (!layer) return;
|
|
495
566
|
|
|
496
567
|
const {
|
|
497
|
-
|
|
568
|
+
displayUnit,
|
|
498
569
|
shape,
|
|
499
570
|
radius,
|
|
500
571
|
offset,
|
|
@@ -505,7 +576,7 @@ export class DielineTool implements Extension {
|
|
|
505
576
|
showBleedLines,
|
|
506
577
|
features,
|
|
507
578
|
} = this.state;
|
|
508
|
-
|
|
579
|
+
const { width, height } = this.state;
|
|
509
580
|
|
|
510
581
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
511
582
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
@@ -513,11 +584,12 @@ export class DielineTool implements Extension {
|
|
|
513
584
|
// Calculate Layout based on Physical Dimensions and Canvas Size
|
|
514
585
|
// Add padding to avoid edge hugging
|
|
515
586
|
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
587
|
+
|
|
588
|
+
// Update Viewport System
|
|
589
|
+
this.canvasService.viewport.setPadding(paddingPx);
|
|
590
|
+
this.canvasService.viewport.updatePhysical(width, height);
|
|
591
|
+
|
|
592
|
+
const layout = this.canvasService.viewport.layout;
|
|
521
593
|
|
|
522
594
|
const scale = layout.scale;
|
|
523
595
|
const cx = layout.offsetX + layout.width / 2;
|
|
@@ -534,7 +606,6 @@ export class DielineTool implements Extension {
|
|
|
534
606
|
|
|
535
607
|
// Scale Features for Geometry Generation
|
|
536
608
|
const absoluteFeatures = (features || []).map((f) => {
|
|
537
|
-
// Scale current unit -> pixels (features share the same unit as the dieline)
|
|
538
609
|
const featureScale = scale;
|
|
539
610
|
|
|
540
611
|
return {
|
|
@@ -709,7 +780,9 @@ export class DielineTool implements Extension {
|
|
|
709
780
|
stroke: mainLine.style === "hidden" ? null : mainLine.color,
|
|
710
781
|
strokeWidth: mainLine.width,
|
|
711
782
|
strokeDashArray:
|
|
712
|
-
mainLine.style === "dashed"
|
|
783
|
+
mainLine.style === "dashed"
|
|
784
|
+
? [mainLine.dashLength, mainLine.dashLength]
|
|
785
|
+
: undefined,
|
|
713
786
|
selectable: false,
|
|
714
787
|
evented: false,
|
|
715
788
|
originX: "left",
|
|
@@ -753,16 +826,26 @@ export class DielineTool implements Extension {
|
|
|
753
826
|
|
|
754
827
|
public getGeometry(): DielineGeometry | null {
|
|
755
828
|
if (!this.canvasService) return null;
|
|
756
|
-
const {
|
|
829
|
+
const {
|
|
830
|
+
displayUnit,
|
|
831
|
+
shape,
|
|
832
|
+
width,
|
|
833
|
+
height,
|
|
834
|
+
radius,
|
|
835
|
+
offset,
|
|
836
|
+
mainLine,
|
|
837
|
+
pathData,
|
|
838
|
+
} = this.state;
|
|
757
839
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
758
840
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
759
841
|
|
|
760
842
|
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
843
|
+
|
|
844
|
+
// Update Viewport System (Ensure it's up to date)
|
|
845
|
+
this.canvasService.viewport.setPadding(paddingPx);
|
|
846
|
+
this.canvasService.viewport.updatePhysical(width, height);
|
|
847
|
+
|
|
848
|
+
const layout = this.canvasService.viewport.layout;
|
|
766
849
|
|
|
767
850
|
const scale = layout.scale;
|
|
768
851
|
const cx = layout.offsetX + layout.width / 2;
|
|
@@ -773,14 +856,14 @@ export class DielineTool implements Extension {
|
|
|
773
856
|
|
|
774
857
|
return {
|
|
775
858
|
shape,
|
|
776
|
-
unit,
|
|
859
|
+
unit: "mm",
|
|
860
|
+
displayUnit,
|
|
777
861
|
x: cx,
|
|
778
862
|
y: cy,
|
|
779
863
|
width: visualWidth,
|
|
780
864
|
height: visualHeight,
|
|
781
865
|
radius: radius * scale,
|
|
782
866
|
offset: offset * scale,
|
|
783
|
-
// Pass scale to help other tools (like FeatureTool) convert units
|
|
784
867
|
scale,
|
|
785
868
|
strokeWidth: mainLine.width,
|
|
786
869
|
pathData,
|
|
@@ -794,16 +877,17 @@ export class DielineTool implements Extension {
|
|
|
794
877
|
if (!userLayer) return null;
|
|
795
878
|
|
|
796
879
|
// 1. Generate Path Data
|
|
797
|
-
const { shape, width, height, radius, features,
|
|
880
|
+
const { shape, width, height, radius, features, pathData } = this.state;
|
|
798
881
|
const canvasW = this.canvasService.canvas.width || 800;
|
|
799
882
|
const canvasH = this.canvasService.canvas.height || 600;
|
|
800
883
|
|
|
801
884
|
const paddingPx = this.resolvePadding(canvasW, canvasH);
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
885
|
+
|
|
886
|
+
// Update Viewport System
|
|
887
|
+
this.canvasService.viewport.setPadding(paddingPx);
|
|
888
|
+
this.canvasService.viewport.updatePhysical(width, height);
|
|
889
|
+
|
|
890
|
+
const layout = this.canvasService.viewport.layout;
|
|
807
891
|
const scale = layout.scale;
|
|
808
892
|
const cx = layout.offsetX + layout.width / 2;
|
|
809
893
|
const cy = layout.offsetY + layout.height / 2;
|