@pooder/kit 5.3.0 → 5.4.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/CHANGELOG.md +12 -0
- package/dist/index.d.mts +249 -36
- package/dist/index.d.ts +249 -36
- package/dist/index.js +2374 -1049
- package/dist/index.mjs +2375 -1050
- package/package.json +1 -1
- package/src/extensions/background.ts +178 -85
- package/src/extensions/dieline.ts +1149 -1030
- package/src/extensions/dielineShape.ts +109 -0
- package/src/extensions/feature.ts +482 -366
- package/src/extensions/film.ts +148 -76
- package/src/extensions/geometry.ts +210 -44
- package/src/extensions/image.ts +244 -114
- package/src/extensions/ruler.ts +471 -268
- package/src/extensions/sceneLayoutModel.ts +28 -6
- package/src/extensions/sceneVisibility.ts +3 -10
- package/src/extensions/tracer.ts +1019 -980
- package/src/extensions/white-ink.ts +284 -231
- package/src/services/CanvasService.ts +543 -11
- package/src/services/renderSpec.ts +37 -2
- 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/coordinate.js +0 -74
- package/.test-dist/src/dieline.js +0 -818
- package/.test-dist/src/edgeScale.js +0 -12
- package/.test-dist/src/extensions/background.js +0 -203
- package/.test-dist/src/extensions/bridgeSelection.js +0 -20
- package/.test-dist/src/extensions/constraints.js +0 -237
- package/.test-dist/src/extensions/dieline.js +0 -828
- package/.test-dist/src/extensions/edgeScale.js +0 -12
- package/.test-dist/src/extensions/feature.js +0 -825
- package/.test-dist/src/extensions/featureComplete.js +0 -32
- package/.test-dist/src/extensions/film.js +0 -167
- package/.test-dist/src/extensions/geometry.js +0 -545
- package/.test-dist/src/extensions/image.js +0 -1529
- package/.test-dist/src/extensions/index.js +0 -30
- package/.test-dist/src/extensions/maskOps.js +0 -279
- package/.test-dist/src/extensions/mirror.js +0 -104
- package/.test-dist/src/extensions/ruler.js +0 -345
- package/.test-dist/src/extensions/sceneLayout.js +0 -96
- package/.test-dist/src/extensions/sceneLayoutModel.js +0 -196
- package/.test-dist/src/extensions/sceneVisibility.js +0 -62
- package/.test-dist/src/extensions/size.js +0 -331
- package/.test-dist/src/extensions/tracer.js +0 -538
- package/.test-dist/src/extensions/white-ink.js +0 -1190
- package/.test-dist/src/extensions/wrappedOffsets.js +0 -33
- 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/index.js +0 -18
- 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/services/CanvasService.js +0 -249
- package/.test-dist/src/services/ViewportSystem.js +0 -76
- package/.test-dist/src/services/index.js +0 -24
- package/.test-dist/src/services/renderSpec.js +0 -2
- package/.test-dist/src/size.js +0 -332
- package/.test-dist/src/tracer.js +0 -544
- package/.test-dist/src/units.js +0 -30
- package/.test-dist/src/white-ink.js +0 -829
- package/.test-dist/src/wrappedOffsets.js +0 -33
- package/.test-dist/tests/run.js +0 -94
package/src/extensions/image.ts
CHANGED
|
@@ -14,8 +14,10 @@ import {
|
|
|
14
14
|
Pattern,
|
|
15
15
|
Point,
|
|
16
16
|
} from "fabric";
|
|
17
|
-
import { CanvasService, RenderObjectSpec } from "../services";
|
|
18
|
-
import {
|
|
17
|
+
import { CanvasService, RenderLayoutRect, RenderObjectSpec } from "../services";
|
|
18
|
+
import { isDielineShape, normalizeShapeStyle } from "./dielineShape";
|
|
19
|
+
import type { DielineShape, DielineShapeStyle } from "./dielineShape";
|
|
20
|
+
import { generateDielinePath, getPathBounds } from "./geometry";
|
|
19
21
|
import {
|
|
20
22
|
buildSceneGeometry,
|
|
21
23
|
computeSceneLayout,
|
|
@@ -64,11 +66,11 @@ interface FrameVisualConfig {
|
|
|
64
66
|
outerBackground: string;
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
type DielineShape = "rect" | "circle" | "ellipse" | "custom";
|
|
68
69
|
type ShapeOverlayShape = Exclude<DielineShape, "custom">;
|
|
69
70
|
|
|
70
71
|
interface SceneGeometryLike {
|
|
71
72
|
shape: DielineShape;
|
|
73
|
+
shapeStyle: DielineShapeStyle;
|
|
72
74
|
radius: number;
|
|
73
75
|
offset: number;
|
|
74
76
|
}
|
|
@@ -134,6 +136,9 @@ export class ImageTool implements Extension {
|
|
|
134
136
|
private dirtyTrackerDisposable?: { dispose(): void };
|
|
135
137
|
private cropShapeHatchPattern?: Pattern;
|
|
136
138
|
private cropShapeHatchPatternColor?: string;
|
|
139
|
+
private cropShapeHatchPatternKey?: string;
|
|
140
|
+
private overlaySpecs: RenderObjectSpec[] = [];
|
|
141
|
+
private renderProducerDisposable?: { dispose: () => void };
|
|
137
142
|
|
|
138
143
|
activate(context: ExtensionContext) {
|
|
139
144
|
this.context = context;
|
|
@@ -142,6 +147,16 @@ export class ImageTool implements Extension {
|
|
|
142
147
|
console.warn("CanvasService not found for ImageTool");
|
|
143
148
|
return;
|
|
144
149
|
}
|
|
150
|
+
this.renderProducerDisposable?.dispose();
|
|
151
|
+
this.renderProducerDisposable = this.canvasService.registerRenderProducer(
|
|
152
|
+
this.id,
|
|
153
|
+
() => ({
|
|
154
|
+
rootLayerSpecs: {
|
|
155
|
+
[IMAGE_OVERLAY_LAYER_ID]: this.overlaySpecs,
|
|
156
|
+
},
|
|
157
|
+
}),
|
|
158
|
+
{ priority: 300 },
|
|
159
|
+
);
|
|
145
160
|
|
|
146
161
|
context.eventBus.on("tool:activated", this.onToolActivated);
|
|
147
162
|
context.eventBus.on("object:modified", this.onObjectModified);
|
|
@@ -202,13 +217,14 @@ export class ImageTool implements Extension {
|
|
|
202
217
|
this.dirtyTrackerDisposable = undefined;
|
|
203
218
|
this.cropShapeHatchPattern = undefined;
|
|
204
219
|
this.cropShapeHatchPatternColor = undefined;
|
|
220
|
+
this.cropShapeHatchPatternKey = undefined;
|
|
221
|
+
this.overlaySpecs = [];
|
|
205
222
|
|
|
206
223
|
this.clearRenderedImages();
|
|
224
|
+
this.renderProducerDisposable?.dispose();
|
|
225
|
+
this.renderProducerDisposable = undefined;
|
|
207
226
|
if (this.canvasService) {
|
|
208
|
-
void this.canvasService.
|
|
209
|
-
IMAGE_OVERLAY_LAYER_ID,
|
|
210
|
-
[],
|
|
211
|
-
);
|
|
227
|
+
void this.canvasService.flushRenderFromProducers();
|
|
212
228
|
this.canvasService = undefined;
|
|
213
229
|
}
|
|
214
230
|
this.context = undefined;
|
|
@@ -766,47 +782,41 @@ export class ImageTool implements Extension {
|
|
|
766
782
|
return { left: 0, top: 0, width: 0, height: 0 };
|
|
767
783
|
}
|
|
768
784
|
|
|
769
|
-
return {
|
|
785
|
+
return this.canvasService.toSceneRect({
|
|
770
786
|
left: layout.cutRect.left,
|
|
771
787
|
top: layout.cutRect.top,
|
|
772
788
|
width: layout.cutRect.width,
|
|
773
789
|
height: layout.cutRect.height,
|
|
774
|
-
};
|
|
790
|
+
});
|
|
775
791
|
}
|
|
776
792
|
|
|
777
|
-
private
|
|
778
|
-
if (!this.
|
|
779
|
-
|
|
780
|
-
|
|
793
|
+
private getFrameRectScreen(frame?: FrameRect): FrameRect {
|
|
794
|
+
if (!this.canvasService) {
|
|
795
|
+
return { left: 0, top: 0, width: 0, height: 0 };
|
|
796
|
+
}
|
|
797
|
+
return this.canvasService.toScreenRect(frame || this.getFrameRect());
|
|
798
|
+
}
|
|
781
799
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
if (
|
|
793
|
-
!Number.isFinite(width) ||
|
|
794
|
-
!Number.isFinite(height) ||
|
|
795
|
-
!Number.isFinite(left) ||
|
|
796
|
-
!Number.isFinite(top)
|
|
797
|
-
) {
|
|
798
|
-
return null;
|
|
799
|
-
}
|
|
800
|
+
private toLayoutSceneRect(rect: FrameRect): RenderLayoutRect {
|
|
801
|
+
return {
|
|
802
|
+
left: rect.left,
|
|
803
|
+
top: rect.top,
|
|
804
|
+
width: rect.width,
|
|
805
|
+
height: rect.height,
|
|
806
|
+
space: "scene",
|
|
807
|
+
};
|
|
808
|
+
}
|
|
800
809
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
+
private async resolveDefaultFitArea(): Promise<DielineFitArea | null> {
|
|
811
|
+
if (!this.canvasService) return null;
|
|
812
|
+
const frame = this.getFrameRect();
|
|
813
|
+
if (frame.width <= 0 || frame.height <= 0) return null;
|
|
814
|
+
return {
|
|
815
|
+
width: Math.max(1, frame.width),
|
|
816
|
+
height: Math.max(1, frame.height),
|
|
817
|
+
left: frame.left + frame.width / 2,
|
|
818
|
+
top: frame.top + frame.height / 2,
|
|
819
|
+
};
|
|
810
820
|
}
|
|
811
821
|
|
|
812
822
|
private async fitImageToDefaultArea(id: string) {
|
|
@@ -818,13 +828,14 @@ export class ImageTool implements Extension {
|
|
|
818
828
|
return;
|
|
819
829
|
}
|
|
820
830
|
|
|
821
|
-
const
|
|
822
|
-
const
|
|
831
|
+
const viewport = this.canvasService.getSceneViewportRect();
|
|
832
|
+
const canvasW = Math.max(1, viewport.width || 0);
|
|
833
|
+
const canvasH = Math.max(1, viewport.height || 0);
|
|
823
834
|
await this.fitImageToArea(id, {
|
|
824
835
|
width: canvasW,
|
|
825
836
|
height: canvasH,
|
|
826
|
-
left: canvasW / 2,
|
|
827
|
-
top: canvasH / 2,
|
|
837
|
+
left: viewport.left + canvasW / 2,
|
|
838
|
+
top: viewport.top + canvasH / 2,
|
|
828
839
|
});
|
|
829
840
|
}
|
|
830
841
|
|
|
@@ -929,19 +940,24 @@ export class ImageTool implements Extension {
|
|
|
929
940
|
|
|
930
941
|
private toSceneGeometryLike(raw: any): SceneGeometryLike | null {
|
|
931
942
|
const shape = raw?.shape;
|
|
932
|
-
if (
|
|
933
|
-
shape !== "rect" &&
|
|
934
|
-
shape !== "circle" &&
|
|
935
|
-
shape !== "ellipse" &&
|
|
936
|
-
shape !== "custom"
|
|
937
|
-
) {
|
|
943
|
+
if (!isDielineShape(shape)) {
|
|
938
944
|
return null;
|
|
939
945
|
}
|
|
940
946
|
|
|
941
|
-
const
|
|
942
|
-
const
|
|
947
|
+
const radiusRaw = Number(raw?.radius);
|
|
948
|
+
const offsetRaw = Number(raw?.offset);
|
|
949
|
+
const unit = typeof raw?.unit === "string" ? raw.unit : "px";
|
|
950
|
+
const radius =
|
|
951
|
+
unit === "scene" || !this.canvasService
|
|
952
|
+
? radiusRaw
|
|
953
|
+
: this.canvasService.toSceneLength(radiusRaw);
|
|
954
|
+
const offset =
|
|
955
|
+
unit === "scene" || !this.canvasService
|
|
956
|
+
? offsetRaw
|
|
957
|
+
: this.canvasService.toSceneLength(offsetRaw);
|
|
943
958
|
return {
|
|
944
959
|
shape,
|
|
960
|
+
shapeStyle: normalizeShapeStyle(raw?.shapeStyle),
|
|
945
961
|
radius: Number.isFinite(radius) ? radius : 0,
|
|
946
962
|
offset: Number.isFinite(offset) ? offset : 0,
|
|
947
963
|
};
|
|
@@ -1008,9 +1024,12 @@ export class ImageTool implements Extension {
|
|
|
1008
1024
|
color = "rgba(255, 0, 0, 0.6)",
|
|
1009
1025
|
): Pattern | undefined {
|
|
1010
1026
|
if (typeof document === "undefined") return undefined;
|
|
1027
|
+
const sceneScale = this.canvasService?.getSceneScale() || 1;
|
|
1028
|
+
const cacheKey = `${color}::${sceneScale.toFixed(6)}`;
|
|
1011
1029
|
if (
|
|
1012
1030
|
this.cropShapeHatchPattern &&
|
|
1013
|
-
this.cropShapeHatchPatternColor === color
|
|
1031
|
+
this.cropShapeHatchPatternColor === color &&
|
|
1032
|
+
this.cropShapeHatchPatternKey === cacheKey
|
|
1014
1033
|
) {
|
|
1015
1034
|
return this.cropShapeHatchPattern;
|
|
1016
1035
|
}
|
|
@@ -1043,8 +1062,18 @@ export class ImageTool implements Extension {
|
|
|
1043
1062
|
// @ts-ignore: Fabric Pattern accepts canvas source here.
|
|
1044
1063
|
repetition: "repeat",
|
|
1045
1064
|
});
|
|
1065
|
+
// Scene specs are scaled to screen by CanvasService; keep hatch density in screen pixels.
|
|
1066
|
+
(pattern as any).patternTransform = [
|
|
1067
|
+
1 / sceneScale,
|
|
1068
|
+
0,
|
|
1069
|
+
0,
|
|
1070
|
+
1 / sceneScale,
|
|
1071
|
+
0,
|
|
1072
|
+
0,
|
|
1073
|
+
];
|
|
1046
1074
|
this.cropShapeHatchPattern = pattern;
|
|
1047
1075
|
this.cropShapeHatchPatternColor = color;
|
|
1076
|
+
this.cropShapeHatchPatternKey = cacheKey;
|
|
1048
1077
|
return pattern;
|
|
1049
1078
|
}
|
|
1050
1079
|
|
|
@@ -1062,6 +1091,7 @@ export class ImageTool implements Extension {
|
|
|
1062
1091
|
}
|
|
1063
1092
|
|
|
1064
1093
|
const shape = sceneGeometry.shape as ShapeOverlayShape;
|
|
1094
|
+
const shapeStyle = sceneGeometry.shapeStyle;
|
|
1065
1095
|
const inset = 0;
|
|
1066
1096
|
const shapeWidth = Math.max(1, frame.width);
|
|
1067
1097
|
const shapeHeight = Math.max(1, frame.height);
|
|
@@ -1072,6 +1102,7 @@ export class ImageTool implements Extension {
|
|
|
1072
1102
|
frameWidth: frame.width,
|
|
1073
1103
|
frameHeight: frame.height,
|
|
1074
1104
|
offset: sceneGeometry.offset,
|
|
1105
|
+
shapeStyle,
|
|
1075
1106
|
inset,
|
|
1076
1107
|
shapeWidth,
|
|
1077
1108
|
shapeHeight,
|
|
@@ -1097,6 +1128,7 @@ export class ImageTool implements Extension {
|
|
|
1097
1128
|
x: frame.width / 2,
|
|
1098
1129
|
y: frame.height / 2,
|
|
1099
1130
|
features: [],
|
|
1131
|
+
shapeStyle,
|
|
1100
1132
|
canvasWidth: frame.width,
|
|
1101
1133
|
canvasHeight: frame.height,
|
|
1102
1134
|
};
|
|
@@ -1116,6 +1148,9 @@ export class ImageTool implements Extension {
|
|
|
1116
1148
|
|
|
1117
1149
|
const patternFill = this.getCropShapeHatchPattern();
|
|
1118
1150
|
const hatchFill = patternFill || "rgba(255, 0, 0, 0.22)";
|
|
1151
|
+
const shapeBounds = getPathBounds(shapePathData);
|
|
1152
|
+
const hatchBounds = getPathBounds(hatchPathData);
|
|
1153
|
+
const frameRect = this.toLayoutSceneRect(frame);
|
|
1119
1154
|
const hatchPathLength = hatchPathData.length;
|
|
1120
1155
|
const shapePathLength = shapePathData.length;
|
|
1121
1156
|
const specs: RenderObjectSpec[] = [
|
|
@@ -1123,10 +1158,16 @@ export class ImageTool implements Extension {
|
|
|
1123
1158
|
id: "image.cropShapeHatch",
|
|
1124
1159
|
type: "path",
|
|
1125
1160
|
data: { id: "image.cropShapeHatch", zIndex: 5 },
|
|
1161
|
+
layout: {
|
|
1162
|
+
reference: "custom",
|
|
1163
|
+
referenceRect: frameRect,
|
|
1164
|
+
alignX: "start",
|
|
1165
|
+
alignY: "start",
|
|
1166
|
+
offsetX: hatchBounds.x,
|
|
1167
|
+
offsetY: hatchBounds.y,
|
|
1168
|
+
},
|
|
1126
1169
|
props: {
|
|
1127
1170
|
pathData: hatchPathData,
|
|
1128
|
-
left: frame.left,
|
|
1129
|
-
top: frame.top,
|
|
1130
1171
|
originX: "left",
|
|
1131
1172
|
originY: "top",
|
|
1132
1173
|
fill: hatchFill,
|
|
@@ -1143,15 +1184,21 @@ export class ImageTool implements Extension {
|
|
|
1143
1184
|
id: "image.cropShapePath",
|
|
1144
1185
|
type: "path",
|
|
1145
1186
|
data: { id: "image.cropShapePath", zIndex: 6 },
|
|
1187
|
+
layout: {
|
|
1188
|
+
reference: "custom",
|
|
1189
|
+
referenceRect: frameRect,
|
|
1190
|
+
alignX: "start",
|
|
1191
|
+
alignY: "start",
|
|
1192
|
+
offsetX: shapeBounds.x,
|
|
1193
|
+
offsetY: shapeBounds.y,
|
|
1194
|
+
},
|
|
1146
1195
|
props: {
|
|
1147
1196
|
pathData: shapePathData,
|
|
1148
|
-
left: frame.left,
|
|
1149
|
-
top: frame.top,
|
|
1150
1197
|
originX: "left",
|
|
1151
1198
|
originY: "top",
|
|
1152
1199
|
fill: "rgba(0,0,0,0)",
|
|
1153
1200
|
stroke: "rgba(255, 0, 0, 0.9)",
|
|
1154
|
-
strokeWidth: 1,
|
|
1201
|
+
strokeWidth: this.canvasService?.toSceneLength(1) ?? 1,
|
|
1155
1202
|
selectable: false,
|
|
1156
1203
|
evented: false,
|
|
1157
1204
|
excludeFromExport: true,
|
|
@@ -1168,6 +1215,8 @@ export class ImageTool implements Extension {
|
|
|
1168
1215
|
fillRule: "evenodd",
|
|
1169
1216
|
shapePathLength,
|
|
1170
1217
|
hatchPathLength,
|
|
1218
|
+
shapeBounds,
|
|
1219
|
+
hatchBounds,
|
|
1171
1220
|
hatchFillType:
|
|
1172
1221
|
hatchFill && typeof hatchFill === "object" ? "pattern" : "color",
|
|
1173
1222
|
ids: specs.map((spec) => spec.id),
|
|
@@ -1241,6 +1290,30 @@ export class ImageTool implements Extension {
|
|
|
1241
1290
|
};
|
|
1242
1291
|
}
|
|
1243
1292
|
|
|
1293
|
+
private toScreenObjectProps(props: Record<string, any>): Record<string, any> {
|
|
1294
|
+
if (!this.canvasService) return props;
|
|
1295
|
+
const next = { ...props };
|
|
1296
|
+
if (Number.isFinite(next.left) || Number.isFinite(next.top)) {
|
|
1297
|
+
const mapped = this.canvasService.toScreenPoint({
|
|
1298
|
+
x: Number.isFinite(next.left) ? Number(next.left) : 0,
|
|
1299
|
+
y: Number.isFinite(next.top) ? Number(next.top) : 0,
|
|
1300
|
+
});
|
|
1301
|
+
if (Number.isFinite(next.left)) next.left = mapped.x;
|
|
1302
|
+
if (Number.isFinite(next.top)) next.top = mapped.y;
|
|
1303
|
+
}
|
|
1304
|
+
const sceneScale = this.canvasService.getSceneScale();
|
|
1305
|
+
const sx = Number.isFinite(next.scaleX) ? Number(next.scaleX) : 1;
|
|
1306
|
+
const sy = Number.isFinite(next.scaleY) ? Number(next.scaleY) : 1;
|
|
1307
|
+
next.scaleX = sx * sceneScale;
|
|
1308
|
+
next.scaleY = sy * sceneScale;
|
|
1309
|
+
return next;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
private toSceneObjectScale(value: number): number {
|
|
1313
|
+
if (!this.canvasService) return value;
|
|
1314
|
+
return value / this.canvasService.getSceneScale();
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1244
1317
|
private getCurrentSrc(obj: any): string | undefined {
|
|
1245
1318
|
if (!obj) return undefined;
|
|
1246
1319
|
if (typeof obj.getSrc === "function") return obj.getSrc();
|
|
@@ -1300,9 +1373,10 @@ export class ImageTool implements Extension {
|
|
|
1300
1373
|
this.rememberSourceSize(render.src, obj);
|
|
1301
1374
|
const sourceSize = this.getSourceSize(render.src, obj);
|
|
1302
1375
|
const props = this.computeCanvasProps(render, sourceSize, frame);
|
|
1376
|
+
const screenProps = this.toScreenObjectProps(props);
|
|
1303
1377
|
|
|
1304
1378
|
obj.set({
|
|
1305
|
-
...
|
|
1379
|
+
...screenProps,
|
|
1306
1380
|
data: {
|
|
1307
1381
|
...(obj.data || {}),
|
|
1308
1382
|
id: item.id,
|
|
@@ -1384,26 +1458,52 @@ export class ImageTool implements Extension {
|
|
|
1384
1458
|
return [];
|
|
1385
1459
|
}
|
|
1386
1460
|
|
|
1387
|
-
const
|
|
1388
|
-
const
|
|
1461
|
+
const viewport = this.canvasService.getSceneViewportRect();
|
|
1462
|
+
const canvasW = viewport.width || 0;
|
|
1463
|
+
const canvasH = viewport.height || 0;
|
|
1464
|
+
const canvasLeft = viewport.left || 0;
|
|
1465
|
+
const canvasTop = viewport.top || 0;
|
|
1389
1466
|
const visual = this.getFrameVisualConfig();
|
|
1467
|
+
const strokeWidthScene = this.canvasService.toSceneLength(
|
|
1468
|
+
visual.strokeWidth,
|
|
1469
|
+
);
|
|
1470
|
+
const dashLengthScene = this.canvasService.toSceneLength(visual.dashLength);
|
|
1390
1471
|
|
|
1391
|
-
const frameLeft = Math.max(
|
|
1392
|
-
|
|
1472
|
+
const frameLeft = Math.max(
|
|
1473
|
+
canvasLeft,
|
|
1474
|
+
Math.min(canvasLeft + canvasW, frame.left),
|
|
1475
|
+
);
|
|
1476
|
+
const frameTop = Math.max(
|
|
1477
|
+
canvasTop,
|
|
1478
|
+
Math.min(canvasTop + canvasH, frame.top),
|
|
1479
|
+
);
|
|
1393
1480
|
const frameRight = Math.max(
|
|
1394
1481
|
frameLeft,
|
|
1395
|
-
Math.min(canvasW, frame.left + frame.width),
|
|
1482
|
+
Math.min(canvasLeft + canvasW, frame.left + frame.width),
|
|
1396
1483
|
);
|
|
1397
1484
|
const frameBottom = Math.max(
|
|
1398
1485
|
frameTop,
|
|
1399
|
-
Math.min(canvasH, frame.top + frame.height),
|
|
1486
|
+
Math.min(canvasTop + canvasH, frame.top + frame.height),
|
|
1400
1487
|
);
|
|
1401
1488
|
const visibleFrameH = Math.max(0, frameBottom - frameTop);
|
|
1402
1489
|
|
|
1403
|
-
const topH = frameTop;
|
|
1404
|
-
const bottomH = Math.max(0, canvasH - frameBottom);
|
|
1405
|
-
const leftW = frameLeft;
|
|
1406
|
-
const rightW = Math.max(0, canvasW - frameRight);
|
|
1490
|
+
const topH = Math.max(0, frameTop - canvasTop);
|
|
1491
|
+
const bottomH = Math.max(0, canvasTop + canvasH - frameBottom);
|
|
1492
|
+
const leftW = Math.max(0, frameLeft - canvasLeft);
|
|
1493
|
+
const rightW = Math.max(0, canvasLeft + canvasW - frameRight);
|
|
1494
|
+
const viewportRect = this.toLayoutSceneRect({
|
|
1495
|
+
left: canvasLeft,
|
|
1496
|
+
top: canvasTop,
|
|
1497
|
+
width: canvasW,
|
|
1498
|
+
height: canvasH,
|
|
1499
|
+
});
|
|
1500
|
+
const visibleFrameBandRect = this.toLayoutSceneRect({
|
|
1501
|
+
left: canvasLeft,
|
|
1502
|
+
top: frameTop,
|
|
1503
|
+
width: canvasW,
|
|
1504
|
+
height: visibleFrameH,
|
|
1505
|
+
});
|
|
1506
|
+
const frameRect = this.toLayoutSceneRect(frame);
|
|
1407
1507
|
const shapeOverlay = this.buildCropShapeOverlaySpecs(frame, sceneGeometry);
|
|
1408
1508
|
|
|
1409
1509
|
const mask: RenderObjectSpec[] = [
|
|
@@ -1411,13 +1511,17 @@ export class ImageTool implements Extension {
|
|
|
1411
1511
|
id: "image.cropMask.top",
|
|
1412
1512
|
type: "rect",
|
|
1413
1513
|
data: { id: "image.cropMask.top", zIndex: 1 },
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1514
|
+
layout: {
|
|
1515
|
+
reference: "custom",
|
|
1516
|
+
referenceRect: viewportRect,
|
|
1517
|
+
alignX: "start",
|
|
1518
|
+
alignY: "start",
|
|
1519
|
+
width: "100%",
|
|
1418
1520
|
height: topH,
|
|
1419
|
-
|
|
1420
|
-
|
|
1521
|
+
},
|
|
1522
|
+
props: {
|
|
1523
|
+
originX: "left",
|
|
1524
|
+
originY: "top",
|
|
1421
1525
|
fill: visual.outerBackground,
|
|
1422
1526
|
selectable: false,
|
|
1423
1527
|
evented: false,
|
|
@@ -1427,13 +1531,17 @@ export class ImageTool implements Extension {
|
|
|
1427
1531
|
id: "image.cropMask.bottom",
|
|
1428
1532
|
type: "rect",
|
|
1429
1533
|
data: { id: "image.cropMask.bottom", zIndex: 2 },
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1534
|
+
layout: {
|
|
1535
|
+
reference: "custom",
|
|
1536
|
+
referenceRect: viewportRect,
|
|
1537
|
+
alignX: "start",
|
|
1538
|
+
alignY: "end",
|
|
1539
|
+
width: "100%",
|
|
1434
1540
|
height: bottomH,
|
|
1435
|
-
|
|
1436
|
-
|
|
1541
|
+
},
|
|
1542
|
+
props: {
|
|
1543
|
+
originX: "left",
|
|
1544
|
+
originY: "top",
|
|
1437
1545
|
fill: visual.outerBackground,
|
|
1438
1546
|
selectable: false,
|
|
1439
1547
|
evented: false,
|
|
@@ -1443,13 +1551,17 @@ export class ImageTool implements Extension {
|
|
|
1443
1551
|
id: "image.cropMask.left",
|
|
1444
1552
|
type: "rect",
|
|
1445
1553
|
data: { id: "image.cropMask.left", zIndex: 3 },
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1554
|
+
layout: {
|
|
1555
|
+
reference: "custom",
|
|
1556
|
+
referenceRect: visibleFrameBandRect,
|
|
1557
|
+
alignX: "start",
|
|
1558
|
+
alignY: "start",
|
|
1449
1559
|
width: leftW,
|
|
1450
|
-
height:
|
|
1451
|
-
|
|
1452
|
-
|
|
1560
|
+
height: "100%",
|
|
1561
|
+
},
|
|
1562
|
+
props: {
|
|
1563
|
+
originX: "left",
|
|
1564
|
+
originY: "top",
|
|
1453
1565
|
fill: visual.outerBackground,
|
|
1454
1566
|
selectable: false,
|
|
1455
1567
|
evented: false,
|
|
@@ -1459,13 +1571,17 @@ export class ImageTool implements Extension {
|
|
|
1459
1571
|
id: "image.cropMask.right",
|
|
1460
1572
|
type: "rect",
|
|
1461
1573
|
data: { id: "image.cropMask.right", zIndex: 4 },
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1574
|
+
layout: {
|
|
1575
|
+
reference: "custom",
|
|
1576
|
+
referenceRect: visibleFrameBandRect,
|
|
1577
|
+
alignX: "end",
|
|
1578
|
+
alignY: "start",
|
|
1465
1579
|
width: rightW,
|
|
1466
|
-
height:
|
|
1467
|
-
|
|
1468
|
-
|
|
1580
|
+
height: "100%",
|
|
1581
|
+
},
|
|
1582
|
+
props: {
|
|
1583
|
+
originX: "left",
|
|
1584
|
+
originY: "top",
|
|
1469
1585
|
fill: visual.outerBackground,
|
|
1470
1586
|
selectable: false,
|
|
1471
1587
|
evented: false,
|
|
@@ -1477,22 +1593,26 @@ export class ImageTool implements Extension {
|
|
|
1477
1593
|
id: "image.cropFrame",
|
|
1478
1594
|
type: "rect",
|
|
1479
1595
|
data: { id: "image.cropFrame", zIndex: 7 },
|
|
1596
|
+
layout: {
|
|
1597
|
+
reference: "custom",
|
|
1598
|
+
referenceRect: frameRect,
|
|
1599
|
+
alignX: "start",
|
|
1600
|
+
alignY: "start",
|
|
1601
|
+
width: "100%",
|
|
1602
|
+
height: "100%",
|
|
1603
|
+
},
|
|
1480
1604
|
props: {
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
width: frame.width,
|
|
1484
|
-
height: frame.height,
|
|
1485
|
-
originX: "center",
|
|
1486
|
-
originY: "center",
|
|
1605
|
+
originX: "left",
|
|
1606
|
+
originY: "top",
|
|
1487
1607
|
fill: visual.innerBackground,
|
|
1488
1608
|
stroke:
|
|
1489
1609
|
visual.strokeStyle === "hidden"
|
|
1490
1610
|
? "rgba(0,0,0,0)"
|
|
1491
1611
|
: visual.strokeColor,
|
|
1492
|
-
strokeWidth: visual.strokeStyle === "hidden" ? 0 :
|
|
1612
|
+
strokeWidth: visual.strokeStyle === "hidden" ? 0 : strokeWidthScene,
|
|
1493
1613
|
strokeDashArray:
|
|
1494
1614
|
visual.strokeStyle === "dashed"
|
|
1495
|
-
? [
|
|
1615
|
+
? [dashLengthScene, dashLengthScene]
|
|
1496
1616
|
: undefined,
|
|
1497
1617
|
selectable: false,
|
|
1498
1618
|
evented: false,
|
|
@@ -1548,10 +1668,8 @@ export class ImageTool implements Extension {
|
|
|
1548
1668
|
if (seq !== this.renderSeq) return;
|
|
1549
1669
|
|
|
1550
1670
|
const overlaySpecs = this.buildOverlaySpecs(frame, sceneGeometry);
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
overlaySpecs,
|
|
1554
|
-
);
|
|
1671
|
+
this.overlaySpecs = overlaySpecs;
|
|
1672
|
+
await this.canvasService.flushRenderFromProducers();
|
|
1555
1673
|
this.syncImageZOrder(renderItems);
|
|
1556
1674
|
const overlayCanvasCount = this.getOverlayObjects().length;
|
|
1557
1675
|
|
|
@@ -1584,8 +1702,12 @@ export class ImageTool implements Extension {
|
|
|
1584
1702
|
const center = target.getCenterPoint
|
|
1585
1703
|
? target.getCenterPoint()
|
|
1586
1704
|
: new Point(target.left ?? 0, target.top ?? 0);
|
|
1705
|
+
const centerScene = this.canvasService
|
|
1706
|
+
? this.canvasService.toScenePoint({ x: center.x, y: center.y })
|
|
1707
|
+
: { x: center.x, y: center.y };
|
|
1587
1708
|
|
|
1588
1709
|
const objectScale = Number.isFinite(target?.scaleX) ? target.scaleX : 1;
|
|
1710
|
+
const objectScaleScene = this.toSceneObjectScale(objectScale || 1);
|
|
1589
1711
|
|
|
1590
1712
|
const workingItem = this.workingItems.find((item) => item.id === id);
|
|
1591
1713
|
const sourceKey = workingItem?.sourceUrl || workingItem?.url || "";
|
|
@@ -1593,10 +1715,10 @@ export class ImageTool implements Extension {
|
|
|
1593
1715
|
const coverScale = this.getCoverScale(frame, sourceSize);
|
|
1594
1716
|
|
|
1595
1717
|
const updates: Partial<ImageItem> = {
|
|
1596
|
-
left: this.clampNormalized((
|
|
1597
|
-
top: this.clampNormalized((
|
|
1718
|
+
left: this.clampNormalized((centerScene.x - frame.left) / frame.width),
|
|
1719
|
+
top: this.clampNormalized((centerScene.y - frame.top) / frame.height),
|
|
1598
1720
|
angle: Number.isFinite(target.angle) ? target.angle : 0,
|
|
1599
|
-
scale: Math.max(0.05,
|
|
1721
|
+
scale: Math.max(0.05, objectScaleScene / coverScale),
|
|
1600
1722
|
};
|
|
1601
1723
|
|
|
1602
1724
|
this.focusedImageId = id;
|
|
@@ -1691,7 +1813,7 @@ export class ImageTool implements Extension {
|
|
|
1691
1813
|
const frame = this.getFrameRect();
|
|
1692
1814
|
const coverScale = this.getCoverScale(frame, source);
|
|
1693
1815
|
|
|
1694
|
-
const currentScale = obj.scaleX || 1;
|
|
1816
|
+
const currentScale = this.toSceneObjectScale(obj.scaleX || 1);
|
|
1695
1817
|
const zoom = Math.max(0.05, currentScale / coverScale);
|
|
1696
1818
|
|
|
1697
1819
|
const updated: Partial<ImageItem> = {
|
|
@@ -1739,16 +1861,21 @@ export class ImageTool implements Extension {
|
|
|
1739
1861
|
Math.max(1, area.height) / Math.max(1, source.height),
|
|
1740
1862
|
);
|
|
1741
1863
|
|
|
1742
|
-
const
|
|
1743
|
-
const
|
|
1864
|
+
const viewport = this.canvasService.getSceneViewportRect();
|
|
1865
|
+
const canvasW = viewport.width || 1;
|
|
1866
|
+
const canvasH = viewport.height || 1;
|
|
1744
1867
|
|
|
1745
1868
|
const areaLeftInput = area.left ?? 0.5;
|
|
1746
1869
|
const areaTopInput = area.top ?? 0.5;
|
|
1747
1870
|
|
|
1748
1871
|
const areaLeftPx =
|
|
1749
|
-
areaLeftInput <= 1.5
|
|
1872
|
+
areaLeftInput <= 1.5
|
|
1873
|
+
? viewport.left + areaLeftInput * canvasW
|
|
1874
|
+
: areaLeftInput;
|
|
1750
1875
|
const areaTopPx =
|
|
1751
|
-
areaTopInput <= 1.5
|
|
1876
|
+
areaTopInput <= 1.5
|
|
1877
|
+
? viewport.top + areaTopInput * canvasH
|
|
1878
|
+
: areaTopInput;
|
|
1752
1879
|
|
|
1753
1880
|
const updates: Partial<ImageItem> = {
|
|
1754
1881
|
scale: Math.max(0.05, desiredScale / baseCover),
|
|
@@ -1794,6 +1921,8 @@ export class ImageTool implements Extension {
|
|
|
1794
1921
|
this.normalizeItem({
|
|
1795
1922
|
...item,
|
|
1796
1923
|
url,
|
|
1924
|
+
// Keep original source for next image-tool session editing,
|
|
1925
|
+
// and use committedUrl as non-image-tools render source.
|
|
1797
1926
|
sourceUrl,
|
|
1798
1927
|
committedUrl: url,
|
|
1799
1928
|
}),
|
|
@@ -1825,7 +1954,8 @@ export class ImageTool implements Extension {
|
|
|
1825
1954
|
throw new Error("image-ids-required");
|
|
1826
1955
|
}
|
|
1827
1956
|
|
|
1828
|
-
const
|
|
1957
|
+
const frameScene = this.getFrameRect();
|
|
1958
|
+
const frame = this.getFrameRectScreen(frameScene);
|
|
1829
1959
|
const multiplier = Math.max(1, options.multiplier ?? 2);
|
|
1830
1960
|
const format: "png" | "jpeg" = options.format === "jpeg" ? "jpeg" : "png";
|
|
1831
1961
|
|