@pooder/kit 5.3.1 → 6.0.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/extensions/background.js +475 -131
- package/.test-dist/src/extensions/dieline.js +283 -180
- package/.test-dist/src/extensions/dielineShape.js +66 -0
- package/.test-dist/src/extensions/feature.js +388 -303
- package/.test-dist/src/extensions/film.js +133 -74
- package/.test-dist/src/extensions/geometry.js +120 -56
- package/.test-dist/src/extensions/image.js +296 -212
- package/.test-dist/src/extensions/index.js +1 -3
- package/.test-dist/src/extensions/maskOps.js +75 -20
- package/.test-dist/src/extensions/ruler.js +312 -215
- package/.test-dist/src/extensions/sceneLayoutModel.js +9 -3
- package/.test-dist/src/extensions/sceneVisibility.js +3 -10
- package/.test-dist/src/extensions/tracer.js +229 -58
- package/.test-dist/src/extensions/white-ink.js +139 -129
- package/.test-dist/src/services/CanvasService.js +888 -126
- package/.test-dist/src/services/index.js +1 -0
- package/.test-dist/src/services/visibility.js +54 -0
- package/.test-dist/tests/run.js +58 -4
- package/CHANGELOG.md +12 -0
- package/dist/index.d.mts +377 -82
- package/dist/index.d.ts +377 -82
- package/dist/index.js +3920 -2178
- package/dist/index.mjs +3992 -2247
- package/package.json +1 -1
- package/src/extensions/background.ts +631 -145
- package/src/extensions/dieline.ts +280 -187
- package/src/extensions/dielineShape.ts +109 -0
- package/src/extensions/feature.ts +485 -366
- package/src/extensions/film.ts +152 -76
- package/src/extensions/geometry.ts +203 -104
- package/src/extensions/image.ts +319 -238
- package/src/extensions/index.ts +0 -1
- package/src/extensions/ruler.ts +481 -268
- package/src/extensions/sceneLayoutModel.ts +18 -6
- package/src/extensions/white-ink.ts +157 -171
- package/src/services/CanvasService.ts +1126 -140
- package/src/services/index.ts +1 -0
- package/src/services/renderSpec.ts +69 -4
- package/src/services/visibility.ts +78 -0
- package/tests/run.ts +139 -4
- package/.test-dist/src/CanvasService.js +0 -249
- package/.test-dist/src/ViewportSystem.js +0 -75
- package/.test-dist/src/background.js +0 -203
- package/.test-dist/src/bridgeSelection.js +0 -20
- package/.test-dist/src/constraints.js +0 -237
- package/.test-dist/src/dieline.js +0 -818
- package/.test-dist/src/edgeScale.js +0 -12
- package/.test-dist/src/feature.js +0 -826
- package/.test-dist/src/featureComplete.js +0 -32
- package/.test-dist/src/film.js +0 -167
- package/.test-dist/src/geometry.js +0 -506
- package/.test-dist/src/image.js +0 -1250
- package/.test-dist/src/maskOps.js +0 -270
- package/.test-dist/src/mirror.js +0 -104
- package/.test-dist/src/renderSpec.js +0 -2
- package/.test-dist/src/ruler.js +0 -343
- package/.test-dist/src/sceneLayout.js +0 -99
- package/.test-dist/src/sceneLayoutModel.js +0 -196
- package/.test-dist/src/sceneView.js +0 -40
- package/.test-dist/src/sceneVisibility.js +0 -42
- package/.test-dist/src/size.js +0 -332
- package/.test-dist/src/tracer.js +0 -544
- package/.test-dist/src/white-ink.js +0 -829
- package/.test-dist/src/wrappedOffsets.js +0 -33
- package/src/extensions/sceneVisibility.ts +0 -71
|
@@ -2,21 +2,38 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RulerTool = void 0;
|
|
4
4
|
const core_1 = require("@pooder/core");
|
|
5
|
-
const fabric_1 = require("fabric");
|
|
6
|
-
const units_1 = require("../units");
|
|
7
5
|
const sceneLayoutModel_1 = require("./sceneLayoutModel");
|
|
6
|
+
const RULER_LAYER_ID = "ruler-overlay";
|
|
7
|
+
const EXTENSION_LINE_LENGTH = 5;
|
|
8
|
+
const MIN_ARROW_SIZE = 4;
|
|
9
|
+
const THICKNESS_TO_STROKE_WIDTH_RATIO = 20;
|
|
10
|
+
const DEFAULT_THICKNESS = 20;
|
|
11
|
+
const DEFAULT_GAP = 45;
|
|
12
|
+
const DEFAULT_FONT_SIZE = 10;
|
|
13
|
+
const DEFAULT_BACKGROUND_COLOR = "#f0f0f0";
|
|
14
|
+
const DEFAULT_TEXT_COLOR = "#333333";
|
|
15
|
+
const DEFAULT_LINE_COLOR = "#999999";
|
|
16
|
+
const RULER_THICKNESS_MIN = 10;
|
|
17
|
+
const RULER_THICKNESS_MAX = 100;
|
|
18
|
+
const RULER_GAP_MIN = 0;
|
|
19
|
+
const RULER_GAP_MAX = 100;
|
|
20
|
+
const RULER_FONT_SIZE_MIN = 8;
|
|
21
|
+
const RULER_FONT_SIZE_MAX = 24;
|
|
8
22
|
class RulerTool {
|
|
9
23
|
constructor(options) {
|
|
10
24
|
this.id = "pooder.kit.ruler";
|
|
11
25
|
this.metadata = {
|
|
12
26
|
name: "RulerTool",
|
|
13
27
|
};
|
|
14
|
-
this.thickness =
|
|
15
|
-
this.gap =
|
|
16
|
-
this.backgroundColor =
|
|
17
|
-
this.textColor =
|
|
18
|
-
this.lineColor =
|
|
19
|
-
this.fontSize =
|
|
28
|
+
this.thickness = DEFAULT_THICKNESS;
|
|
29
|
+
this.gap = DEFAULT_GAP;
|
|
30
|
+
this.backgroundColor = DEFAULT_BACKGROUND_COLOR;
|
|
31
|
+
this.textColor = DEFAULT_TEXT_COLOR;
|
|
32
|
+
this.lineColor = DEFAULT_LINE_COLOR;
|
|
33
|
+
this.fontSize = DEFAULT_FONT_SIZE;
|
|
34
|
+
this.renderSeq = 0;
|
|
35
|
+
this.numericProps = new Set(["thickness", "gap", "fontSize"]);
|
|
36
|
+
this.specs = [];
|
|
20
37
|
this.onCanvasResized = () => {
|
|
21
38
|
this.updateRuler();
|
|
22
39
|
};
|
|
@@ -28,45 +45,73 @@ class RulerTool {
|
|
|
28
45
|
this.context = context;
|
|
29
46
|
this.canvasService = context.services.get("CanvasService");
|
|
30
47
|
if (!this.canvasService) {
|
|
31
|
-
console.warn("CanvasService not found
|
|
48
|
+
console.warn("[RulerTool] CanvasService not found.");
|
|
32
49
|
return;
|
|
33
50
|
}
|
|
51
|
+
this.renderProducerDisposable?.dispose();
|
|
52
|
+
this.renderProducerDisposable = this.canvasService.registerRenderProducer(this.id, () => ({
|
|
53
|
+
passes: [
|
|
54
|
+
{
|
|
55
|
+
id: RULER_LAYER_ID,
|
|
56
|
+
stack: 950,
|
|
57
|
+
order: 0,
|
|
58
|
+
replace: true,
|
|
59
|
+
visibility: {
|
|
60
|
+
op: "not",
|
|
61
|
+
expr: {
|
|
62
|
+
op: "activeToolIn",
|
|
63
|
+
ids: ["pooder.kit.white-ink"],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
objects: this.specs,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
}), { priority: 400 });
|
|
34
70
|
const configService = context.services.get("ConfigurationService");
|
|
35
71
|
if (configService) {
|
|
36
|
-
|
|
37
|
-
this.thickness = configService.get("ruler.thickness", this.thickness);
|
|
38
|
-
this.gap = configService.get("ruler.gap", this.gap);
|
|
39
|
-
this.backgroundColor = configService.get("ruler.backgroundColor", this.backgroundColor);
|
|
40
|
-
this.textColor = configService.get("ruler.textColor", this.textColor);
|
|
41
|
-
this.lineColor = configService.get("ruler.lineColor", this.lineColor);
|
|
42
|
-
this.fontSize = configService.get("ruler.fontSize", this.fontSize);
|
|
43
|
-
// Listen for changes
|
|
72
|
+
this.syncConfig(configService);
|
|
44
73
|
configService.onAnyChange((e) => {
|
|
45
74
|
let shouldUpdate = false;
|
|
46
75
|
if (e.key.startsWith("ruler.")) {
|
|
47
76
|
const prop = e.key.split(".")[1];
|
|
48
77
|
if (prop && prop in this) {
|
|
49
|
-
this
|
|
78
|
+
if (this.numericProps.has(prop)) {
|
|
79
|
+
this[prop] = this.toFiniteNumber(e.value, this[prop]);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
this[prop] = e.value;
|
|
83
|
+
}
|
|
50
84
|
shouldUpdate = true;
|
|
85
|
+
this.log("config:update", {
|
|
86
|
+
key: e.key,
|
|
87
|
+
raw: e.value,
|
|
88
|
+
normalized: this[prop],
|
|
89
|
+
});
|
|
51
90
|
}
|
|
52
91
|
}
|
|
53
92
|
else if (e.key.startsWith("size.")) {
|
|
54
93
|
shouldUpdate = true;
|
|
94
|
+
this.log("size:update", { key: e.key, value: e.value });
|
|
55
95
|
}
|
|
56
96
|
if (shouldUpdate) {
|
|
57
97
|
this.updateRuler();
|
|
58
98
|
}
|
|
59
99
|
});
|
|
60
100
|
}
|
|
61
|
-
this.createLayer();
|
|
62
101
|
context.eventBus.on("canvas:resized", this.onCanvasResized);
|
|
63
102
|
this.updateRuler();
|
|
64
103
|
}
|
|
65
104
|
deactivate(context) {
|
|
66
105
|
context.eventBus.off("canvas:resized", this.onCanvasResized);
|
|
67
|
-
this.
|
|
106
|
+
this.specs = [];
|
|
107
|
+
this.renderProducerDisposable?.dispose();
|
|
108
|
+
this.renderProducerDisposable = undefined;
|
|
109
|
+
if (this.canvasService) {
|
|
110
|
+
void this.canvasService.flushRenderFromProducers();
|
|
111
|
+
}
|
|
68
112
|
this.canvasService = undefined;
|
|
69
113
|
this.context = undefined;
|
|
114
|
+
this.renderSeq = 0;
|
|
70
115
|
}
|
|
71
116
|
contribute() {
|
|
72
117
|
return {
|
|
@@ -75,43 +120,43 @@ class RulerTool {
|
|
|
75
120
|
id: "ruler.thickness",
|
|
76
121
|
type: "number",
|
|
77
122
|
label: "Thickness",
|
|
78
|
-
min:
|
|
79
|
-
max:
|
|
80
|
-
default:
|
|
123
|
+
min: RULER_THICKNESS_MIN,
|
|
124
|
+
max: RULER_THICKNESS_MAX,
|
|
125
|
+
default: DEFAULT_THICKNESS,
|
|
81
126
|
},
|
|
82
127
|
{
|
|
83
128
|
id: "ruler.gap",
|
|
84
129
|
type: "number",
|
|
85
130
|
label: "Gap",
|
|
86
|
-
min:
|
|
87
|
-
max:
|
|
88
|
-
default:
|
|
131
|
+
min: RULER_GAP_MIN,
|
|
132
|
+
max: RULER_GAP_MAX,
|
|
133
|
+
default: DEFAULT_GAP,
|
|
89
134
|
},
|
|
90
135
|
{
|
|
91
136
|
id: "ruler.backgroundColor",
|
|
92
137
|
type: "color",
|
|
93
138
|
label: "Background Color",
|
|
94
|
-
default:
|
|
139
|
+
default: DEFAULT_BACKGROUND_COLOR,
|
|
95
140
|
},
|
|
96
141
|
{
|
|
97
142
|
id: "ruler.textColor",
|
|
98
143
|
type: "color",
|
|
99
144
|
label: "Text Color",
|
|
100
|
-
default:
|
|
145
|
+
default: DEFAULT_TEXT_COLOR,
|
|
101
146
|
},
|
|
102
147
|
{
|
|
103
148
|
id: "ruler.lineColor",
|
|
104
149
|
type: "color",
|
|
105
150
|
label: "Line Color",
|
|
106
|
-
default:
|
|
151
|
+
default: DEFAULT_LINE_COLOR,
|
|
107
152
|
},
|
|
108
153
|
{
|
|
109
154
|
id: "ruler.fontSize",
|
|
110
155
|
type: "number",
|
|
111
156
|
label: "Font Size",
|
|
112
|
-
min:
|
|
113
|
-
max:
|
|
114
|
-
default:
|
|
157
|
+
min: RULER_FONT_SIZE_MIN,
|
|
158
|
+
max: RULER_FONT_SIZE_MAX,
|
|
159
|
+
default: DEFAULT_FONT_SIZE,
|
|
115
160
|
},
|
|
116
161
|
],
|
|
117
162
|
[core_1.ContributionPointIds.COMMANDS]: [
|
|
@@ -125,11 +170,16 @@ class RulerTool {
|
|
|
125
170
|
lineColor: this.lineColor,
|
|
126
171
|
fontSize: this.fontSize,
|
|
127
172
|
thickness: this.thickness,
|
|
173
|
+
gap: this.gap,
|
|
128
174
|
};
|
|
129
175
|
const newState = { ...oldState, ...theme };
|
|
130
|
-
if (JSON.stringify(newState) === JSON.stringify(oldState))
|
|
176
|
+
if (JSON.stringify(newState) === JSON.stringify(oldState)) {
|
|
131
177
|
return true;
|
|
178
|
+
}
|
|
132
179
|
Object.assign(this, newState);
|
|
180
|
+
this.thickness = this.toFiniteNumber(this.thickness, DEFAULT_THICKNESS);
|
|
181
|
+
this.gap = this.toFiniteNumber(this.gap, DEFAULT_GAP);
|
|
182
|
+
this.fontSize = this.toFiniteNumber(this.fontSize, DEFAULT_FONT_SIZE);
|
|
133
183
|
this.updateRuler();
|
|
134
184
|
return true;
|
|
135
185
|
},
|
|
@@ -137,209 +187,256 @@ class RulerTool {
|
|
|
137
187
|
],
|
|
138
188
|
};
|
|
139
189
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
createLayer() {
|
|
144
|
-
if (!this.canvasService)
|
|
190
|
+
log(step, payload) {
|
|
191
|
+
if (payload) {
|
|
192
|
+
console.debug(`[RulerTool] ${step}`, payload);
|
|
145
193
|
return;
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
194
|
+
}
|
|
195
|
+
console.debug(`[RulerTool] ${step}`);
|
|
196
|
+
}
|
|
197
|
+
syncConfig(configService) {
|
|
198
|
+
this.thickness = this.toFiniteNumber(configService.get("ruler.thickness", this.thickness), DEFAULT_THICKNESS);
|
|
199
|
+
this.gap = Math.max(0, this.toFiniteNumber(configService.get("ruler.gap", this.gap), DEFAULT_GAP));
|
|
200
|
+
this.backgroundColor = configService.get("ruler.backgroundColor", this.backgroundColor);
|
|
201
|
+
this.textColor = configService.get("ruler.textColor", this.textColor);
|
|
202
|
+
this.lineColor = configService.get("ruler.lineColor", this.lineColor);
|
|
203
|
+
this.fontSize = this.toFiniteNumber(configService.get("ruler.fontSize", this.fontSize), DEFAULT_FONT_SIZE);
|
|
204
|
+
this.log("config:loaded", {
|
|
205
|
+
thickness: this.thickness,
|
|
206
|
+
gap: this.gap,
|
|
207
|
+
fontSize: this.fontSize,
|
|
208
|
+
backgroundColor: this.backgroundColor,
|
|
209
|
+
textColor: this.textColor,
|
|
210
|
+
lineColor: this.lineColor,
|
|
158
211
|
});
|
|
159
|
-
canvas.bringObjectToFront(layer);
|
|
160
212
|
}
|
|
161
|
-
|
|
213
|
+
toFiniteNumber(value, fallback) {
|
|
214
|
+
const numeric = Number(value);
|
|
215
|
+
return Number.isFinite(numeric) ? numeric : fallback;
|
|
216
|
+
}
|
|
217
|
+
toSceneDisplayLength(value) {
|
|
162
218
|
if (!this.canvasService)
|
|
163
|
-
return;
|
|
164
|
-
|
|
165
|
-
if (layer) {
|
|
166
|
-
this.canvasService.canvas.remove(layer);
|
|
167
|
-
}
|
|
219
|
+
return value;
|
|
220
|
+
return this.canvasService.toSceneLength(value);
|
|
168
221
|
}
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
222
|
+
formatLengthMm(valueMm, unit) {
|
|
223
|
+
const converted = (0, sceneLayoutModel_1.fromMm)(valueMm, unit);
|
|
224
|
+
const fractionDigits = unit === "in" ? 3 : 2;
|
|
225
|
+
return Number(converted.toFixed(fractionDigits)).toString();
|
|
226
|
+
}
|
|
227
|
+
buildLinePath(start, end) {
|
|
228
|
+
const dx = end.x - start.x;
|
|
229
|
+
const dy = end.y - start.y;
|
|
230
|
+
return `M 0 0 L ${dx} ${dy}`;
|
|
231
|
+
}
|
|
232
|
+
buildStartArrowPath(size) {
|
|
233
|
+
return `M 0 0 L ${size} ${-size / 2} L ${size} ${size / 2} Z`;
|
|
234
|
+
}
|
|
235
|
+
buildEndArrowPath(size) {
|
|
236
|
+
return `M 0 0 L ${-size} ${-size / 2} L ${-size} ${size / 2} Z`;
|
|
237
|
+
}
|
|
238
|
+
createPathSpec(id, pathData, position, options) {
|
|
239
|
+
return {
|
|
240
|
+
id,
|
|
241
|
+
type: "path",
|
|
242
|
+
data: {
|
|
243
|
+
id,
|
|
244
|
+
type: "ruler",
|
|
245
|
+
},
|
|
246
|
+
props: {
|
|
247
|
+
pathData,
|
|
248
|
+
left: position.x,
|
|
249
|
+
top: position.y,
|
|
250
|
+
originX: options.originX ?? "left",
|
|
251
|
+
originY: options.originY ?? "top",
|
|
252
|
+
angle: options.angle ?? 0,
|
|
253
|
+
stroke: options.stroke ?? null,
|
|
254
|
+
fill: options.fill ?? null,
|
|
255
|
+
strokeWidth: options.strokeWidth ?? 1,
|
|
256
|
+
strokeLineCap: options.strokeLineCap ?? "butt",
|
|
257
|
+
selectable: false,
|
|
258
|
+
evented: false,
|
|
259
|
+
excludeFromExport: true,
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
createTextSpec(id, text, position, angle = 0) {
|
|
264
|
+
return {
|
|
265
|
+
id,
|
|
266
|
+
type: "text",
|
|
267
|
+
data: {
|
|
268
|
+
id,
|
|
269
|
+
type: "ruler",
|
|
270
|
+
},
|
|
271
|
+
props: {
|
|
272
|
+
text,
|
|
273
|
+
left: position.x,
|
|
274
|
+
top: position.y,
|
|
275
|
+
angle,
|
|
276
|
+
fontSize: this.toSceneDisplayLength(this.fontSize),
|
|
277
|
+
fill: this.textColor,
|
|
278
|
+
fontFamily: "Arial",
|
|
279
|
+
originX: "center",
|
|
280
|
+
originY: "center",
|
|
281
|
+
backgroundColor: this.backgroundColor,
|
|
282
|
+
selectable: false,
|
|
283
|
+
evented: false,
|
|
284
|
+
excludeFromExport: true,
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
buildRulerSpecs(input) {
|
|
289
|
+
const { left, top, right, bottom, widthLabel, heightLabel } = input;
|
|
290
|
+
const gap = Math.max(0, this.toSceneDisplayLength(this.toFiniteNumber(this.gap, DEFAULT_GAP)));
|
|
291
|
+
const topY = top - gap;
|
|
292
|
+
const leftX = left - gap;
|
|
293
|
+
const arrowSize = Math.max(this.toSceneDisplayLength(MIN_ARROW_SIZE), this.toSceneDisplayLength(this.thickness * 0.3));
|
|
294
|
+
const strokeWidth = Math.max(this.toSceneDisplayLength(1), this.toSceneDisplayLength(this.thickness / THICKNESS_TO_STROKE_WIDTH_RATIO));
|
|
295
|
+
const extensionLength = this.toSceneDisplayLength(EXTENSION_LINE_LENGTH);
|
|
296
|
+
const topLineAngleDeg = 0;
|
|
297
|
+
const leftLineAngleDeg = 90;
|
|
298
|
+
// Keep dimension line inside the arrow heads so it doesn't visually overflow.
|
|
299
|
+
const topMidX = left + (right - left) / 2;
|
|
300
|
+
const leftMidY = top + (bottom - top) / 2;
|
|
301
|
+
const topLineStartX = Math.min(left + arrowSize, topMidX);
|
|
302
|
+
const topLineEndX = Math.max(right - arrowSize, topMidX);
|
|
303
|
+
const leftLineStartY = Math.min(top + arrowSize, leftMidY);
|
|
304
|
+
const leftLineEndY = Math.max(bottom - arrowSize, leftMidY);
|
|
305
|
+
const specs = [];
|
|
306
|
+
specs.push(this.createPathSpec("ruler.top.line", this.buildLinePath({ x: topLineStartX, y: topY }, { x: topLineEndX, y: topY }), { x: topLineStartX, y: topY }, {
|
|
307
|
+
stroke: this.lineColor,
|
|
308
|
+
strokeWidth,
|
|
309
|
+
strokeLineCap: "butt",
|
|
310
|
+
}), this.createPathSpec("ruler.top.arrow.start", this.buildStartArrowPath(arrowSize), { x: left, y: topY }, {
|
|
311
|
+
fill: this.lineColor,
|
|
312
|
+
stroke: this.lineColor,
|
|
313
|
+
strokeWidth: this.toSceneDisplayLength(1),
|
|
314
|
+
originX: "left",
|
|
315
|
+
originY: "center",
|
|
316
|
+
angle: topLineAngleDeg,
|
|
317
|
+
}), this.createPathSpec("ruler.top.arrow.end", this.buildEndArrowPath(arrowSize), { x: right, y: topY }, {
|
|
318
|
+
fill: this.lineColor,
|
|
319
|
+
stroke: this.lineColor,
|
|
320
|
+
strokeWidth: this.toSceneDisplayLength(1),
|
|
188
321
|
originX: "right",
|
|
189
322
|
originY: "center",
|
|
190
|
-
angle:
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
323
|
+
angle: topLineAngleDeg,
|
|
324
|
+
}), this.createPathSpec("ruler.top.ext.start", this.buildLinePath({ x: left, y: topY - extensionLength }, { x: left, y: topY + extensionLength }), { x: left, y: topY - extensionLength }, {
|
|
325
|
+
stroke: this.lineColor,
|
|
326
|
+
strokeWidth: this.toSceneDisplayLength(1),
|
|
327
|
+
}), this.createPathSpec("ruler.top.ext.end", this.buildLinePath({ x: right, y: topY - extensionLength }, { x: right, y: topY + extensionLength }), { x: right, y: topY - extensionLength }, {
|
|
328
|
+
stroke: this.lineColor,
|
|
329
|
+
strokeWidth: this.toSceneDisplayLength(1),
|
|
330
|
+
}), this.createTextSpec("ruler.top.label", widthLabel, {
|
|
331
|
+
x: left + (right - left) / 2,
|
|
332
|
+
y: topY,
|
|
333
|
+
}));
|
|
334
|
+
specs.push(this.createPathSpec("ruler.left.line", this.buildLinePath({ x: leftX, y: leftLineStartY }, { x: leftX, y: leftLineEndY }), { x: leftX, y: leftLineStartY }, {
|
|
335
|
+
stroke: this.lineColor,
|
|
336
|
+
strokeWidth,
|
|
337
|
+
strokeLineCap: "butt",
|
|
338
|
+
}), this.createPathSpec("ruler.left.arrow.start", this.buildStartArrowPath(arrowSize), { x: leftX, y: top }, {
|
|
339
|
+
fill: this.lineColor,
|
|
340
|
+
stroke: this.lineColor,
|
|
341
|
+
strokeWidth: this.toSceneDisplayLength(1),
|
|
203
342
|
originX: "left",
|
|
204
343
|
originY: "center",
|
|
205
|
-
angle:
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
344
|
+
angle: leftLineAngleDeg,
|
|
345
|
+
}), this.createPathSpec("ruler.left.arrow.end", this.buildEndArrowPath(arrowSize), { x: leftX, y: bottom }, {
|
|
346
|
+
fill: this.lineColor,
|
|
347
|
+
stroke: this.lineColor,
|
|
348
|
+
strokeWidth: this.toSceneDisplayLength(1),
|
|
349
|
+
originX: "right",
|
|
350
|
+
originY: "center",
|
|
351
|
+
angle: leftLineAngleDeg,
|
|
352
|
+
}), this.createPathSpec("ruler.left.ext.start", this.buildLinePath({ x: leftX - extensionLength, y: top }, { x: leftX + extensionLength, y: top }), { x: leftX - extensionLength, y: top }, {
|
|
353
|
+
stroke: this.lineColor,
|
|
354
|
+
strokeWidth: this.toSceneDisplayLength(1),
|
|
355
|
+
}), this.createPathSpec("ruler.left.ext.end", this.buildLinePath({ x: leftX - extensionLength, y: bottom }, { x: leftX + extensionLength, y: bottom }), { x: leftX - extensionLength, y: bottom }, {
|
|
356
|
+
stroke: this.lineColor,
|
|
357
|
+
strokeWidth: this.toSceneDisplayLength(1),
|
|
358
|
+
}), this.createTextSpec("ruler.left.label", heightLabel, {
|
|
359
|
+
x: leftX,
|
|
360
|
+
y: top + (bottom - top) / 2,
|
|
361
|
+
}, -90));
|
|
362
|
+
return specs;
|
|
213
363
|
}
|
|
214
364
|
updateRuler() {
|
|
365
|
+
void this.updateRulerAsync();
|
|
366
|
+
}
|
|
367
|
+
async updateRulerAsync() {
|
|
215
368
|
if (!this.canvasService)
|
|
216
369
|
return;
|
|
217
|
-
const layer = this.getLayer();
|
|
218
|
-
if (!layer)
|
|
219
|
-
return;
|
|
220
|
-
layer.remove(...layer.getObjects());
|
|
221
|
-
const { backgroundColor, lineColor, textColor, fontSize } = this;
|
|
222
370
|
const configService = this.context?.services.get("ConfigurationService");
|
|
223
371
|
if (!configService)
|
|
224
372
|
return;
|
|
373
|
+
const seq = ++this.renderSeq;
|
|
225
374
|
const sizeState = (0, sceneLayoutModel_1.readSizeState)(configService);
|
|
226
375
|
const layout = (0, sceneLayoutModel_1.computeSceneLayout)(this.canvasService, sizeState);
|
|
227
|
-
|
|
376
|
+
this.log("render:start", {
|
|
377
|
+
seq,
|
|
378
|
+
unit: sizeState.unit,
|
|
379
|
+
gap: this.gap,
|
|
380
|
+
thickness: this.thickness,
|
|
381
|
+
fontSize: this.fontSize,
|
|
382
|
+
hasLayout: !!layout,
|
|
383
|
+
scale: layout?.scale ?? null,
|
|
384
|
+
});
|
|
385
|
+
if (!layout || layout.scale <= 0) {
|
|
386
|
+
if (seq !== this.renderSeq)
|
|
387
|
+
return;
|
|
388
|
+
this.log("render:skip", { seq, reason: "invalid-layout" });
|
|
389
|
+
this.specs = [];
|
|
390
|
+
await this.canvasService.flushRenderFromProducers();
|
|
228
391
|
return;
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const rulerTop = rulerRect.top;
|
|
238
|
-
const rulerRight = rulerRect.left + rulerRect.width;
|
|
239
|
-
const rulerBottom = rulerRect.top + rulerRect.height;
|
|
240
|
-
// Display Dimensions (Physical)
|
|
241
|
-
const displayWidthMm = useCutAsRuler
|
|
242
|
-
? layout.cutWidthMm
|
|
243
|
-
: layout.trimWidthMm;
|
|
244
|
-
const displayHeightMm = useCutAsRuler
|
|
245
|
-
? layout.cutHeightMm
|
|
246
|
-
: layout.trimHeightMm;
|
|
247
|
-
const displayUnit = sizeState.unit;
|
|
248
|
-
// Ruler Placement Coordinates
|
|
249
|
-
// Top Ruler: Above the top boundary
|
|
250
|
-
const topRulerY = rulerTop - gap;
|
|
251
|
-
const topRulerXStart = rulerLeft;
|
|
252
|
-
const topRulerXEnd = rulerRight;
|
|
253
|
-
// Left Ruler: Left of the left boundary
|
|
254
|
-
const leftRulerX = rulerLeft - gap;
|
|
255
|
-
const leftRulerYStart = rulerTop;
|
|
256
|
-
const leftRulerYEnd = rulerBottom;
|
|
257
|
-
// 1. Top Dimension Line (X-Axis)
|
|
258
|
-
const topDimLine = this.createArrowLine(topRulerXStart, topRulerY, topRulerXEnd, topRulerY, lineColor);
|
|
259
|
-
layer.add(topDimLine);
|
|
260
|
-
// Top Extension Lines
|
|
261
|
-
const extLen = 5;
|
|
262
|
-
layer.add(new fabric_1.Line([
|
|
263
|
-
topRulerXStart,
|
|
264
|
-
topRulerY - extLen,
|
|
265
|
-
topRulerXStart,
|
|
266
|
-
topRulerY + extLen,
|
|
267
|
-
], {
|
|
268
|
-
stroke: lineColor,
|
|
269
|
-
strokeWidth: 1,
|
|
270
|
-
selectable: false,
|
|
271
|
-
evented: false,
|
|
272
|
-
}));
|
|
273
|
-
layer.add(new fabric_1.Line([topRulerXEnd, topRulerY - extLen, topRulerXEnd, topRulerY + extLen], {
|
|
274
|
-
stroke: lineColor,
|
|
275
|
-
strokeWidth: 1,
|
|
276
|
-
selectable: false,
|
|
277
|
-
evented: false,
|
|
278
|
-
}));
|
|
279
|
-
// Top Text (Centered)
|
|
280
|
-
const widthStr = (0, units_1.formatMm)(displayWidthMm, displayUnit);
|
|
281
|
-
const topTextContent = `${widthStr} ${displayUnit}`;
|
|
282
|
-
const topText = new fabric_1.Text(topTextContent, {
|
|
283
|
-
left: topRulerXStart + (rulerRight - rulerLeft) / 2,
|
|
284
|
-
top: topRulerY,
|
|
285
|
-
fontSize: fontSize,
|
|
286
|
-
fill: textColor,
|
|
287
|
-
fontFamily: "Arial",
|
|
288
|
-
originX: "center",
|
|
289
|
-
originY: "center",
|
|
290
|
-
backgroundColor: backgroundColor, // Background mask for readability
|
|
291
|
-
selectable: false,
|
|
292
|
-
evented: false,
|
|
392
|
+
}
|
|
393
|
+
const geometry = (0, sceneLayoutModel_1.buildSceneGeometry)(configService, layout);
|
|
394
|
+
if (geometry.unit !== "px") {
|
|
395
|
+
console.warn("[RulerTool] Unexpected geometry unit.", geometry.unit);
|
|
396
|
+
}
|
|
397
|
+
const centerScene = this.canvasService.toScenePoint({
|
|
398
|
+
x: geometry.x,
|
|
399
|
+
y: geometry.y,
|
|
293
400
|
});
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const heightStr = (0, units_1.formatMm)(displayHeightMm, displayUnit);
|
|
325
|
-
const leftTextContent = `${heightStr} ${displayUnit}`;
|
|
326
|
-
const leftText = new fabric_1.Text(leftTextContent, {
|
|
327
|
-
left: leftRulerX,
|
|
328
|
-
top: leftRulerYStart + (rulerBottom - rulerTop) / 2,
|
|
329
|
-
angle: -90,
|
|
330
|
-
fontSize: fontSize,
|
|
331
|
-
fill: textColor,
|
|
332
|
-
fontFamily: "Arial",
|
|
333
|
-
originX: "center",
|
|
334
|
-
originY: "center",
|
|
335
|
-
backgroundColor: backgroundColor,
|
|
336
|
-
selectable: false,
|
|
337
|
-
evented: false,
|
|
401
|
+
const widthScene = this.canvasService.toSceneLength(geometry.width);
|
|
402
|
+
const heightScene = this.canvasService.toSceneLength(geometry.height);
|
|
403
|
+
const rulerLeft = centerScene.x - widthScene / 2;
|
|
404
|
+
const rulerTop = centerScene.y - heightScene / 2;
|
|
405
|
+
const rulerRight = rulerLeft + widthScene;
|
|
406
|
+
const rulerBottom = rulerTop + heightScene;
|
|
407
|
+
const widthMm = widthScene;
|
|
408
|
+
const heightMm = heightScene;
|
|
409
|
+
const unit = sizeState.unit;
|
|
410
|
+
const widthLabel = `${this.formatLengthMm(widthMm, unit)} ${unit}`;
|
|
411
|
+
const heightLabel = `${this.formatLengthMm(heightMm, unit)} ${unit}`;
|
|
412
|
+
const specs = this.buildRulerSpecs({
|
|
413
|
+
left: rulerLeft,
|
|
414
|
+
top: rulerTop,
|
|
415
|
+
right: rulerRight,
|
|
416
|
+
bottom: rulerBottom,
|
|
417
|
+
widthLabel,
|
|
418
|
+
heightLabel,
|
|
419
|
+
});
|
|
420
|
+
this.log("render:geometry", {
|
|
421
|
+
seq,
|
|
422
|
+
left: rulerLeft,
|
|
423
|
+
top: rulerTop,
|
|
424
|
+
right: rulerRight,
|
|
425
|
+
bottom: rulerBottom,
|
|
426
|
+
widthScene,
|
|
427
|
+
heightScene,
|
|
428
|
+
widthMm,
|
|
429
|
+
heightMm,
|
|
430
|
+
specCount: specs.length,
|
|
338
431
|
});
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
this.
|
|
342
|
-
this.canvasService.
|
|
432
|
+
if (seq !== this.renderSeq)
|
|
433
|
+
return;
|
|
434
|
+
this.specs = specs;
|
|
435
|
+
await this.canvasService.flushRenderFromProducers();
|
|
436
|
+
if (seq !== this.renderSeq)
|
|
437
|
+
return;
|
|
438
|
+
this.canvasService.requestRenderAll();
|
|
439
|
+
this.log("render:done", { seq });
|
|
343
440
|
}
|
|
344
441
|
}
|
|
345
442
|
exports.RulerTool = RulerTool;
|