kritzel-stencil 0.0.160 → 0.0.162
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/cjs/{default-text-tool.config-D10FksvZ.js → default-line-tool.config-D1Ns0NmM.js} +3156 -929
- package/dist/cjs/default-line-tool.config-D1Ns0NmM.js.map +1 -0
- package/dist/cjs/index.cjs.js +131 -126
- package/dist/cjs/index.cjs.js.map +1 -1
- package/dist/cjs/kritzel-brush-style.cjs.entry.js +1 -1
- package/dist/cjs/kritzel-brush-style.entry.cjs.js.map +1 -1
- package/dist/cjs/kritzel-color_22.cjs.entry.js +538 -198
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/stencil.cjs.js +1 -1
- package/dist/collection/classes/core/core.class.js +140 -3
- package/dist/collection/classes/core/core.class.js.map +1 -1
- package/dist/collection/classes/core/reviver.class.js +8 -0
- package/dist/collection/classes/core/reviver.class.js.map +1 -1
- package/dist/collection/classes/core/store.class.js +8 -0
- package/dist/collection/classes/core/store.class.js.map +1 -1
- package/dist/collection/classes/handlers/line-handle.handler.js +383 -0
- package/dist/collection/classes/handlers/line-handle.handler.js.map +1 -0
- package/dist/collection/classes/handlers/move.handler.js +2 -2
- package/dist/collection/classes/handlers/move.handler.js.map +1 -1
- package/dist/collection/classes/handlers/resize.handler.js +42 -34
- package/dist/collection/classes/handlers/resize.handler.js.map +1 -1
- package/dist/collection/classes/handlers/rotation.handler.js +12 -8
- package/dist/collection/classes/handlers/rotation.handler.js.map +1 -1
- package/dist/collection/classes/managers/anchor.manager.js +874 -0
- package/dist/collection/classes/managers/anchor.manager.js.map +1 -0
- package/dist/collection/classes/managers/cursor.manager.js +117 -0
- package/dist/collection/classes/managers/cursor.manager.js.map +1 -0
- package/dist/collection/classes/objects/base-object.class.js +4 -2
- package/dist/collection/classes/objects/base-object.class.js.map +1 -1
- package/dist/collection/classes/objects/line.class.js +564 -0
- package/dist/collection/classes/objects/line.class.js.map +1 -0
- package/dist/collection/classes/objects/selection-group.class.js +4 -0
- package/dist/collection/classes/objects/selection-group.class.js.map +1 -1
- package/dist/collection/classes/registries/icon-registry.class.js +7 -1
- package/dist/collection/classes/registries/icon-registry.class.js.map +1 -1
- package/dist/collection/classes/tools/line-tool.class.js +172 -0
- package/dist/collection/classes/tools/line-tool.class.js.map +1 -0
- package/dist/collection/classes/tools/selection-tool.class.js +41 -8
- package/dist/collection/classes/tools/selection-tool.class.js.map +1 -1
- package/dist/collection/components/core/kritzel-cursor-trail/kritzel-cursor-trail.js +1 -1
- package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +11 -2
- package/dist/collection/components/core/kritzel-editor/kritzel-editor.js.map +1 -1
- package/dist/collection/components/core/kritzel-engine/kritzel-engine.css +0 -14
- package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +163 -21
- package/dist/collection/components/core/kritzel-engine/kritzel-engine.js.map +1 -1
- package/dist/collection/components/shared/kritzel-brush-style/kritzel-brush-style.css +0 -1
- package/dist/collection/components/shared/kritzel-color/kritzel-color.js +2 -2
- package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.css +1 -1
- package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.js +1 -1
- package/dist/collection/components/shared/kritzel-dropdown/kritzel-dropdown.css +1 -1
- package/dist/collection/components/shared/kritzel-font/kritzel-font.js +1 -1
- package/dist/collection/components/shared/kritzel-font-family/kritzel-font-family.css +1 -1
- package/dist/collection/components/shared/kritzel-font-family/kritzel-font-family.js +1 -1
- package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.css +1 -1
- package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.js +1 -1
- package/dist/collection/components/shared/kritzel-menu/kritzel-menu.js +1 -1
- package/dist/collection/components/shared/kritzel-menu-item/kritzel-menu-item.css +1 -2
- package/dist/collection/components/shared/kritzel-menu-item/kritzel-menu-item.js +2 -2
- package/dist/collection/components/shared/kritzel-portal/kritzel-portal.js +1 -1
- package/dist/collection/components/shared/kritzel-split-button/kritzel-split-button.css +1 -1
- package/dist/collection/components/shared/kritzel-split-button/kritzel-split-button.js +1 -1
- package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.css +1 -1
- package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.js +1 -1
- package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js +4 -4
- package/dist/collection/components/ui/kritzel-context-menu/kritzel-context-menu.css +1 -2
- package/dist/collection/components/ui/kritzel-context-menu/kritzel-context-menu.js +1 -1
- package/dist/collection/components/ui/kritzel-control-brush-config/kritzel-control-brush-config.css +1 -1
- package/dist/collection/components/ui/kritzel-control-brush-config/kritzel-control-brush-config.js +2 -2
- package/dist/collection/components/ui/kritzel-control-text-config/kritzel-control-text-config.css +1 -1
- package/dist/collection/components/ui/kritzel-control-text-config/kritzel-control-text-config.js +2 -2
- package/dist/collection/components/ui/kritzel-controls/kritzel-controls.css +4 -4
- package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +3 -3
- package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.css +1 -1
- package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.js +1 -1
- package/dist/collection/configs/default-engine-config.js +10 -0
- package/dist/collection/configs/default-engine-config.js.map +1 -1
- package/dist/collection/configs/default-line-tool.config.js +34 -0
- package/dist/collection/configs/default-line-tool.config.js.map +1 -0
- package/dist/collection/helpers/cursor.helper.js +58 -0
- package/dist/collection/helpers/cursor.helper.js.map +1 -0
- package/dist/collection/helpers/geometry.helper.js +42 -0
- package/dist/collection/helpers/geometry.helper.js.map +1 -1
- package/dist/collection/index.js +6 -0
- package/dist/collection/index.js.map +1 -1
- package/dist/collection/interfaces/anchor.interface.js +2 -0
- package/dist/collection/interfaces/anchor.interface.js.map +1 -0
- package/dist/collection/interfaces/arrow-head.interface.js +2 -0
- package/dist/collection/interfaces/arrow-head.interface.js.map +1 -0
- package/dist/collection/interfaces/engine-state.interface.js.map +1 -1
- package/dist/collection/interfaces/line-options.interface.js +2 -0
- package/dist/collection/interfaces/line-options.interface.js.map +1 -0
- package/dist/collection/interfaces/toolbar-control.interface.js.map +1 -1
- package/dist/components/index.js +4 -4
- package/dist/components/kritzel-brush-style.js +3 -3
- package/dist/components/kritzel-brush-style.js.map +1 -1
- package/dist/components/kritzel-color-palette.js +1 -1
- package/dist/components/kritzel-color.js +1 -1
- package/dist/components/kritzel-context-menu.js +1 -1
- package/dist/components/kritzel-control-brush-config.js +1 -1
- package/dist/components/kritzel-control-text-config.js +1 -1
- package/dist/components/kritzel-controls.js +1 -1
- package/dist/components/kritzel-cursor-trail.js +1 -1
- package/dist/components/kritzel-dropdown.js +1 -1
- package/dist/components/kritzel-editor.js +64 -23
- package/dist/components/kritzel-editor.js.map +1 -1
- package/dist/components/kritzel-engine.js +1 -1
- package/dist/components/kritzel-font-family.js +1 -1
- package/dist/components/kritzel-font-size.js +1 -1
- package/dist/components/kritzel-font.js +1 -1
- package/dist/components/kritzel-icon.js +1 -1
- package/dist/components/kritzel-menu-item.js +1 -1
- package/dist/components/kritzel-menu.js +1 -1
- package/dist/components/kritzel-portal.js +1 -1
- package/dist/components/kritzel-split-button.js +1 -1
- package/dist/components/kritzel-stroke-size.js +1 -1
- package/dist/components/kritzel-tooltip.js +1 -1
- package/dist/components/kritzel-utility-panel.js +1 -1
- package/dist/components/kritzel-workspace-manager.js +1 -1
- package/dist/components/{p-TdCTkEu0.js → p-7_lwv0zQ.js} +7 -7
- package/dist/components/p-7_lwv0zQ.js.map +1 -0
- package/dist/components/{p-CTP479Lf.js → p-9Fzdviju.js} +6 -6
- package/dist/components/{p-CTP479Lf.js.map → p-9Fzdviju.js.map} +1 -1
- package/dist/components/{p-DDmSxM5f.js → p-B6r22FSC.js} +5 -5
- package/dist/components/p-B6r22FSC.js.map +1 -0
- package/dist/components/{p-CLt3HMl6.js → p-B_3OZeom.js} +3 -3
- package/dist/components/{p-CLt3HMl6.js.map → p-B_3OZeom.js.map} +1 -1
- package/dist/components/{p-CIXPLjCu.js → p-BdwB-S9G.js} +3 -3
- package/dist/components/p-BdwB-S9G.js.map +1 -0
- package/dist/components/p-BixlbUD7.js +104 -0
- package/dist/components/p-BixlbUD7.js.map +1 -0
- package/dist/components/{p-D1uj4A4F.js → p-Brd9SxWS.js} +5 -5
- package/dist/components/p-Brd9SxWS.js.map +1 -0
- package/dist/components/{p-Ddh40W3x.js → p-CDteBYm9.js} +9 -9
- package/dist/components/p-CDteBYm9.js.map +1 -0
- package/dist/components/{p-BgznZoBH.js → p-CFH6XRL5.js} +5 -5
- package/dist/components/p-CFH6XRL5.js.map +1 -0
- package/dist/components/{p-B4kxkVe-.js → p-CRGwaUcp.js} +5 -5
- package/dist/components/p-CRGwaUcp.js.map +1 -0
- package/dist/components/{p-uuRJU2R1.js → p-Ck4lGnmt.js} +3 -3
- package/dist/components/{p-uuRJU2R1.js.map → p-Ck4lGnmt.js.map} +1 -1
- package/dist/components/{p-CK6no3mi.js → p-CkD1PQQX.js} +6 -6
- package/dist/components/{p-CK6no3mi.js.map → p-CkD1PQQX.js.map} +1 -1
- package/dist/components/{p-BQg4YML7.js → p-Cqr0Bah5.js} +12 -12
- package/dist/components/p-Cqr0Bah5.js.map +1 -0
- package/dist/components/{p-DTHqEUDc.js → p-CuhOrcET.js} +2861 -390
- package/dist/components/p-CuhOrcET.js.map +1 -0
- package/dist/components/{p-DAfkuR8U.js → p-CvLFRlQU.js} +5 -5
- package/dist/components/p-CvLFRlQU.js.map +1 -0
- package/dist/components/{p-1lIHoOlH.js → p-DKwJJuFb.js} +19 -19
- package/dist/components/p-DKwJJuFb.js.map +1 -0
- package/dist/components/{p-CsA9M6me.js → p-DZ7kxJUx.js} +8 -8
- package/dist/components/p-DZ7kxJUx.js.map +1 -0
- package/dist/components/{p-D4yvhd1d.js → p-LAsVgL2e.js} +4 -4
- package/dist/components/{p-D4yvhd1d.js.map → p-LAsVgL2e.js.map} +1 -1
- package/dist/components/{p-D5Wq4x4r.js → p-OFrACpZf.js} +3 -3
- package/dist/components/{p-D5Wq4x4r.js.map → p-OFrACpZf.js.map} +1 -1
- package/dist/components/{p-C2sWlNsJ.js → p-dMCB4tJA.js} +5 -5
- package/dist/components/p-dMCB4tJA.js.map +1 -0
- package/dist/components/{p-CBYBurdY.js → p-sokRZ7Vn.js} +49 -5
- package/dist/components/p-sokRZ7Vn.js.map +1 -0
- package/dist/esm/{default-text-tool.config-DzqpOikl.js → default-line-tool.config-C35m-d1Y.js} +3150 -930
- package/dist/esm/default-line-tool.config-C35m-d1Y.js.map +1 -0
- package/dist/esm/index.js +2 -2
- package/dist/esm/kritzel-brush-style.entry.js +1 -1
- package/dist/esm/kritzel-brush-style.entry.js.map +1 -1
- package/dist/esm/kritzel-color_22.entry.js +457 -117
- package/dist/esm/loader.js +1 -1
- package/dist/esm/stencil.js +1 -1
- package/dist/stencil/index.esm.js +1 -1
- package/dist/stencil/kritzel-brush-style.entry.esm.js.map +1 -1
- package/dist/stencil/p-385bab97.entry.js +2 -0
- package/dist/stencil/{p-d702c5af.entry.js.map → p-385bab97.entry.js.map} +1 -1
- package/dist/stencil/p-C35m-d1Y.js +2 -0
- package/dist/stencil/p-C35m-d1Y.js.map +1 -0
- package/dist/stencil/p-d142ef46.entry.js +10 -0
- package/dist/stencil/p-d142ef46.entry.js.map +1 -0
- package/dist/stencil/stencil.esm.js +1 -1
- package/dist/types/classes/core/core.class.d.ts +18 -0
- package/dist/types/classes/core/store.class.d.ts +3 -0
- package/dist/types/classes/handlers/line-handle.handler.d.ts +34 -0
- package/dist/types/classes/managers/anchor.manager.d.ts +160 -0
- package/dist/types/classes/managers/cursor.manager.d.ts +43 -0
- package/dist/types/classes/objects/line.class.d.ts +98 -0
- package/dist/types/classes/tools/line-tool.class.d.ts +17 -0
- package/dist/types/classes/tools/selection-tool.class.d.ts +4 -0
- package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +4 -2
- package/dist/types/components.d.ts +7 -5
- package/dist/types/configs/default-line-tool.config.d.ts +2 -0
- package/dist/types/helpers/cursor.helper.d.ts +22 -0
- package/dist/types/helpers/geometry.helper.d.ts +10 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/interfaces/anchor.interface.d.ts +137 -0
- package/dist/types/interfaces/arrow-head.interface.d.ts +26 -0
- package/dist/types/interfaces/engine-state.interface.d.ts +15 -0
- package/dist/types/interfaces/line-options.interface.d.ts +21 -0
- package/dist/types/interfaces/toolbar-control.interface.d.ts +17 -1
- package/package.json +1 -1
- package/dist/cjs/default-text-tool.config-D10FksvZ.js.map +0 -1
- package/dist/components/p-1lIHoOlH.js.map +0 -1
- package/dist/components/p-B4kxkVe-.js.map +0 -1
- package/dist/components/p-BQg4YML7.js.map +0 -1
- package/dist/components/p-BgznZoBH.js.map +0 -1
- package/dist/components/p-Bhtn9qay.js +0 -98
- package/dist/components/p-Bhtn9qay.js.map +0 -1
- package/dist/components/p-C2sWlNsJ.js.map +0 -1
- package/dist/components/p-CBYBurdY.js.map +0 -1
- package/dist/components/p-CIXPLjCu.js.map +0 -1
- package/dist/components/p-CsA9M6me.js.map +0 -1
- package/dist/components/p-D1uj4A4F.js.map +0 -1
- package/dist/components/p-DAfkuR8U.js.map +0 -1
- package/dist/components/p-DDmSxM5f.js.map +0 -1
- package/dist/components/p-DTHqEUDc.js.map +0 -1
- package/dist/components/p-Ddh40W3x.js.map +0 -1
- package/dist/components/p-TdCTkEu0.js.map +0 -1
- package/dist/esm/default-text-tool.config-DzqpOikl.js.map +0 -1
- package/dist/stencil/p-5475442e.entry.js +0 -10
- package/dist/stencil/p-5475442e.entry.js.map +0 -1
- package/dist/stencil/p-DzqpOikl.js +0 -2
- package/dist/stencil/p-DzqpOikl.js.map +0 -1
- package/dist/stencil/p-d702c5af.entry.js +0 -2
package/dist/cjs/{default-text-tool.config-D10FksvZ.js → default-line-tool.config-D1Ns0NmM.js}
RENAMED
|
@@ -81,6 +81,48 @@ class KritzelGeometryHelper {
|
|
|
81
81
|
const u = -((p1a.x - p2a.x) * (p1b.y - p1a.y) - (p1a.y - p2a.y) * (p1b.x - p1a.x)) / det;
|
|
82
82
|
return t >= 0 && t <= 1 && u >= 0 && u <= 1;
|
|
83
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Finds the intersection point between a line segment and a line segment.
|
|
86
|
+
* Returns the intersection point or null if no intersection.
|
|
87
|
+
*/
|
|
88
|
+
static getLineIntersectionPoint(p1a, p1b, p2a, p2b) {
|
|
89
|
+
const det = (p1b.x - p1a.x) * (p2b.y - p2a.y) - (p1b.y - p1a.y) * (p2b.x - p2a.x);
|
|
90
|
+
if (det === 0) {
|
|
91
|
+
return null; // Lines are parallel
|
|
92
|
+
}
|
|
93
|
+
const t = ((p2a.x - p1a.x) * (p2b.y - p2a.y) - (p2a.y - p1a.y) * (p2b.x - p2a.x)) / det;
|
|
94
|
+
const u = -((p1a.x - p2a.x) * (p1b.y - p1a.y) - (p1a.y - p2a.y) * (p1b.x - p1a.x)) / det;
|
|
95
|
+
if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
|
|
96
|
+
return {
|
|
97
|
+
x: p1a.x + t * (p1b.x - p1a.x),
|
|
98
|
+
y: p1a.y + t * (p1b.y - p1a.y)
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Finds the closest intersection point between a line segment (from lineStart to lineEnd)
|
|
105
|
+
* and a polygon. Returns the intersection point closest to lineStart, or null if no intersection.
|
|
106
|
+
*/
|
|
107
|
+
static getLinePolygonIntersection(lineStart, lineEnd, polygon) {
|
|
108
|
+
const points = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
|
|
109
|
+
let closestIntersection = null;
|
|
110
|
+
let closestDistance = Infinity;
|
|
111
|
+
for (let i = 0; i < points.length; i++) {
|
|
112
|
+
const edgeStart = points[i];
|
|
113
|
+
const edgeEnd = points[(i + 1) % points.length];
|
|
114
|
+
const intersection = this.getLineIntersectionPoint(lineStart, lineEnd, edgeStart, edgeEnd);
|
|
115
|
+
if (intersection) {
|
|
116
|
+
const distance = Math.sqrt(Math.pow(intersection.x - lineStart.x, 2) +
|
|
117
|
+
Math.pow(intersection.y - lineStart.y, 2));
|
|
118
|
+
if (distance < closestDistance) {
|
|
119
|
+
closestDistance = distance;
|
|
120
|
+
closestIntersection = intersection;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return closestIntersection;
|
|
125
|
+
}
|
|
84
126
|
}
|
|
85
127
|
|
|
86
128
|
class KritzelBaseObject {
|
|
@@ -189,10 +231,10 @@ class KritzelBaseObject {
|
|
|
189
231
|
return this.rotation * (180 / Math.PI);
|
|
190
232
|
}
|
|
191
233
|
get centerX() {
|
|
192
|
-
return this.translateX + this.totalWidth / 2;
|
|
234
|
+
return this.translateX + this.totalWidth / 2 / this.scale;
|
|
193
235
|
}
|
|
194
236
|
get centerY() {
|
|
195
|
-
return this.translateY + this.totalHeight / 2;
|
|
237
|
+
return this.translateY + this.totalHeight / 2 / this.scale;
|
|
196
238
|
}
|
|
197
239
|
constructor() {
|
|
198
240
|
this.id = this.generateId();
|
|
@@ -262,6 +304,8 @@ class KritzelBaseObject {
|
|
|
262
304
|
this.translateX = x;
|
|
263
305
|
this.translateY = y;
|
|
264
306
|
this._core.store.state.objects.update(this);
|
|
307
|
+
// Update any lines that are anchored to this object (after position is updated)
|
|
308
|
+
this._core.anchorManager.updateAnchorsForObject(this.id);
|
|
265
309
|
}
|
|
266
310
|
rotate(value) {
|
|
267
311
|
this.rotation = value;
|
|
@@ -14454,6 +14498,568 @@ class KritzelImage extends KritzelBaseObject {
|
|
|
14454
14498
|
}
|
|
14455
14499
|
}
|
|
14456
14500
|
|
|
14501
|
+
class KritzelLine extends KritzelBaseObject {
|
|
14502
|
+
__class__ = 'KritzelLine';
|
|
14503
|
+
startX;
|
|
14504
|
+
startY;
|
|
14505
|
+
endX;
|
|
14506
|
+
endY;
|
|
14507
|
+
controlX;
|
|
14508
|
+
controlY;
|
|
14509
|
+
stroke;
|
|
14510
|
+
strokeWidth;
|
|
14511
|
+
scale = 1;
|
|
14512
|
+
options;
|
|
14513
|
+
/** Anchor connection for the start point of the line */
|
|
14514
|
+
startAnchor;
|
|
14515
|
+
/** Anchor connection for the end point of the line */
|
|
14516
|
+
endAnchor;
|
|
14517
|
+
/** Arrow head configuration for start and end of line */
|
|
14518
|
+
arrows;
|
|
14519
|
+
isVisible = true;
|
|
14520
|
+
isDebugInfoVisible = true;
|
|
14521
|
+
isCompleted = false;
|
|
14522
|
+
_adjustedPoints = null;
|
|
14523
|
+
get d() {
|
|
14524
|
+
if (this.controlX !== undefined && this.controlY !== undefined) {
|
|
14525
|
+
return `M ${this.startX} ${this.startY} Q ${this.controlX} ${this.controlY} ${this.endX} ${this.endY}`;
|
|
14526
|
+
}
|
|
14527
|
+
return `M ${this.startX} ${this.startY} L ${this.endX} ${this.endY}`;
|
|
14528
|
+
}
|
|
14529
|
+
get viewBox() {
|
|
14530
|
+
return `${this.x} ${this.y} ${this.width} ${this.height}`;
|
|
14531
|
+
}
|
|
14532
|
+
constructor(config) {
|
|
14533
|
+
super();
|
|
14534
|
+
this.options = config;
|
|
14535
|
+
this.startX = config?.startX ?? 0;
|
|
14536
|
+
this.startY = config?.startY ?? 0;
|
|
14537
|
+
this.endX = config?.endX ?? 0;
|
|
14538
|
+
this.endY = config?.endY ?? 0;
|
|
14539
|
+
this.controlX = config?.controlX;
|
|
14540
|
+
this.controlY = config?.controlY;
|
|
14541
|
+
this.translateX = config?.translateX ?? 0;
|
|
14542
|
+
this.translateY = config?.translateY ?? 0;
|
|
14543
|
+
this.scale = config?.scale ?? 1;
|
|
14544
|
+
this.strokeWidth = config?.strokeWidth ?? 4;
|
|
14545
|
+
this.stroke = config?.stroke ?? '#000000';
|
|
14546
|
+
this.startAnchor = config?.startAnchor;
|
|
14547
|
+
this.endAnchor = config?.endAnchor;
|
|
14548
|
+
this.arrows = config?.arrows;
|
|
14549
|
+
this.updateDimensions();
|
|
14550
|
+
}
|
|
14551
|
+
static create(core, options) {
|
|
14552
|
+
const object = new KritzelLine();
|
|
14553
|
+
object._core = core;
|
|
14554
|
+
object.id = object.generateId();
|
|
14555
|
+
object.workspaceId = core.store.state.activeWorkspace.id;
|
|
14556
|
+
object.options = options;
|
|
14557
|
+
object.startX = options?.startX ?? 0;
|
|
14558
|
+
object.startY = options?.startY ?? 0;
|
|
14559
|
+
object.endX = options?.endX ?? 0;
|
|
14560
|
+
object.endY = options?.endY ?? 0;
|
|
14561
|
+
object.controlX = options?.controlX;
|
|
14562
|
+
object.controlY = options?.controlY;
|
|
14563
|
+
object.translateX = options?.translateX ?? 0;
|
|
14564
|
+
object.translateY = options?.translateY ?? 0;
|
|
14565
|
+
object.scale = options?.scale ?? 1;
|
|
14566
|
+
object.strokeWidth = options?.strokeWidth ?? 4;
|
|
14567
|
+
object.stroke = options?.stroke ?? '#000000';
|
|
14568
|
+
object.startAnchor = options?.startAnchor;
|
|
14569
|
+
object.endAnchor = options?.endAnchor;
|
|
14570
|
+
object.arrows = options?.arrows;
|
|
14571
|
+
object.zIndex = core.store.currentZIndex;
|
|
14572
|
+
object.updateDimensions();
|
|
14573
|
+
return object;
|
|
14574
|
+
}
|
|
14575
|
+
resize(x, y, width, height) {
|
|
14576
|
+
if (width <= 1 || height <= 1) {
|
|
14577
|
+
return;
|
|
14578
|
+
}
|
|
14579
|
+
const scaleX = width / this.width;
|
|
14580
|
+
const scaleY = height / this.height;
|
|
14581
|
+
// Scale the line endpoints
|
|
14582
|
+
this.startX = this.startX * scaleX;
|
|
14583
|
+
this.startY = this.startY * scaleY;
|
|
14584
|
+
this.endX = this.endX * scaleX;
|
|
14585
|
+
this.endY = this.endY * scaleY;
|
|
14586
|
+
if (this.controlX !== undefined && this.controlY !== undefined) {
|
|
14587
|
+
this.controlX = this.controlX * scaleX;
|
|
14588
|
+
this.controlY = this.controlY * scaleY;
|
|
14589
|
+
}
|
|
14590
|
+
// Recalculate dimensions from scaled endpoints
|
|
14591
|
+
const { minX, minY, maxX, maxY } = this.calculateBoundingBox();
|
|
14592
|
+
this.width = maxX - minX;
|
|
14593
|
+
this.height = maxY - minY;
|
|
14594
|
+
this.x = minX;
|
|
14595
|
+
this.y = minY;
|
|
14596
|
+
// Set the new position
|
|
14597
|
+
this.translateX = x;
|
|
14598
|
+
this.translateY = y;
|
|
14599
|
+
this._adjustedPoints = null;
|
|
14600
|
+
this._core.store.state.objects.update(this);
|
|
14601
|
+
// Update anchors after the line is updated
|
|
14602
|
+
this._core.anchorManager.updateAnchorsForObject(this.id);
|
|
14603
|
+
if (this.startAnchor) {
|
|
14604
|
+
this._core.anchorManager.updateAnchorsForObject(this.startAnchor.objectId);
|
|
14605
|
+
}
|
|
14606
|
+
if (this.endAnchor) {
|
|
14607
|
+
this._core.anchorManager.updateAnchorsForObject(this.endAnchor.objectId);
|
|
14608
|
+
}
|
|
14609
|
+
}
|
|
14610
|
+
rotate(value) {
|
|
14611
|
+
this.rotation = value;
|
|
14612
|
+
this._adjustedPoints = null;
|
|
14613
|
+
this._core.store.state.objects.update(this);
|
|
14614
|
+
}
|
|
14615
|
+
move(startX, startY, endX, endY) {
|
|
14616
|
+
const deltaX = (startX - endX) / this._core.store.state.scale;
|
|
14617
|
+
const deltaY = (startY - endY) / this._core.store.state.scale;
|
|
14618
|
+
this.translateX += deltaX;
|
|
14619
|
+
this.translateY += deltaY;
|
|
14620
|
+
// If the line is anchored, we need to ensure the anchored endpoints stay at the target
|
|
14621
|
+
if (this.startAnchor) {
|
|
14622
|
+
this._core.anchorManager.updateAnchorsForObject(this.startAnchor.objectId);
|
|
14623
|
+
}
|
|
14624
|
+
if (this.endAnchor) {
|
|
14625
|
+
this._core.anchorManager.updateAnchorsForObject(this.endAnchor.objectId);
|
|
14626
|
+
}
|
|
14627
|
+
this._adjustedPoints = null;
|
|
14628
|
+
this._core.store.state.objects.update(this);
|
|
14629
|
+
}
|
|
14630
|
+
hitTest(x, y) {
|
|
14631
|
+
const halfStroke = this.strokeWidth / this.scale / 2;
|
|
14632
|
+
if (this._adjustedPoints === null) {
|
|
14633
|
+
this._adjustedPoints = this.computeAdjustedPoints();
|
|
14634
|
+
}
|
|
14635
|
+
// For curved lines, use distance to the Bezier curve
|
|
14636
|
+
if (this.controlX !== undefined && this.controlY !== undefined) {
|
|
14637
|
+
const distance = this.pointToBezierDistance(x, y);
|
|
14638
|
+
return distance <= halfStroke + 2;
|
|
14639
|
+
}
|
|
14640
|
+
// For straight lines, use distance to line segment
|
|
14641
|
+
const p1 = this._adjustedPoints[0];
|
|
14642
|
+
const p2 = this._adjustedPoints[1];
|
|
14643
|
+
const distance = this.pointToLineSegmentDistance(x, y, p1[0], p1[1], p2[0], p2[1]);
|
|
14644
|
+
return distance <= halfStroke + 2; // Add a small tolerance for easier selection
|
|
14645
|
+
}
|
|
14646
|
+
hitTestPolygon(polygon) {
|
|
14647
|
+
const halfStroke = this.strokeWidth / this.scale / 2;
|
|
14648
|
+
if (this._adjustedPoints === null) {
|
|
14649
|
+
this._adjustedPoints = this.computeAdjustedPoints();
|
|
14650
|
+
}
|
|
14651
|
+
const polyPoints = [
|
|
14652
|
+
{ x: polygon.bottomLeft.x, y: polygon.bottomLeft.y },
|
|
14653
|
+
{ x: polygon.bottomRight.x, y: polygon.bottomRight.y },
|
|
14654
|
+
{ x: polygon.topRight.x, y: polygon.topRight.y },
|
|
14655
|
+
{ x: polygon.topLeft.x, y: polygon.topLeft.y },
|
|
14656
|
+
];
|
|
14657
|
+
// Check if any endpoint is inside the polygon
|
|
14658
|
+
for (const [px, py] of this._adjustedPoints) {
|
|
14659
|
+
if (KritzelGeometryHelper.isPointInPolygon({ x: px, y: py }, polyPoints)) {
|
|
14660
|
+
return true;
|
|
14661
|
+
}
|
|
14662
|
+
}
|
|
14663
|
+
// Check if any polygon vertex is on the line/curve
|
|
14664
|
+
for (const pt of polyPoints) {
|
|
14665
|
+
if (this.hitTest(pt.x, pt.y)) {
|
|
14666
|
+
return true;
|
|
14667
|
+
}
|
|
14668
|
+
}
|
|
14669
|
+
// For curved lines, sample points along the curve and check if any are inside the polygon
|
|
14670
|
+
if (this.controlX !== undefined && this.controlY !== undefined) {
|
|
14671
|
+
const p0 = this._adjustedPoints[0];
|
|
14672
|
+
const p2 = this._adjustedPoints[1];
|
|
14673
|
+
const controlAdjusted = this.computeAdjustedControlPoint();
|
|
14674
|
+
const samples = 20;
|
|
14675
|
+
for (let i = 1; i < samples; i++) {
|
|
14676
|
+
const t = i / samples;
|
|
14677
|
+
const oneMinusT = 1 - t;
|
|
14678
|
+
const bx = oneMinusT * oneMinusT * p0[0] + 2 * oneMinusT * t * controlAdjusted[0] + t * t * p2[0];
|
|
14679
|
+
const by = oneMinusT * oneMinusT * p0[1] + 2 * oneMinusT * t * controlAdjusted[1] + t * t * p2[1];
|
|
14680
|
+
if (KritzelGeometryHelper.isPointInPolygon({ x: bx, y: by }, polyPoints)) {
|
|
14681
|
+
return true;
|
|
14682
|
+
}
|
|
14683
|
+
// Also check if curve point is within stroke distance of polygon edges
|
|
14684
|
+
for (let j = 0; j < polyPoints.length; j++) {
|
|
14685
|
+
const q1 = polyPoints[j];
|
|
14686
|
+
const q2 = polyPoints[(j + 1) % polyPoints.length];
|
|
14687
|
+
const d = this.pointToLineSegmentDistance(bx, by, q1.x, q1.y, q2.x, q2.y);
|
|
14688
|
+
if (d <= halfStroke) {
|
|
14689
|
+
return true;
|
|
14690
|
+
}
|
|
14691
|
+
}
|
|
14692
|
+
}
|
|
14693
|
+
return false;
|
|
14694
|
+
}
|
|
14695
|
+
// For straight lines, check if line intersects any polygon edge
|
|
14696
|
+
const p1 = { x: this._adjustedPoints[0][0], y: this._adjustedPoints[0][1] };
|
|
14697
|
+
const p2 = { x: this._adjustedPoints[1][0], y: this._adjustedPoints[1][1] };
|
|
14698
|
+
for (let j = 0; j < polyPoints.length; j++) {
|
|
14699
|
+
const q1 = polyPoints[j];
|
|
14700
|
+
const q2 = polyPoints[(j + 1) % polyPoints.length];
|
|
14701
|
+
if (KritzelGeometryHelper.intersectLines(p1, p2, q1, q2)) {
|
|
14702
|
+
return true;
|
|
14703
|
+
}
|
|
14704
|
+
// Check distance from polygon edges to line segment
|
|
14705
|
+
const d1 = this.pointToLineSegmentDistance(q1.x, q1.y, p1.x, p1.y, p2.x, p2.y);
|
|
14706
|
+
const d2 = this.pointToLineSegmentDistance(q2.x, q2.y, p1.x, p1.y, p2.x, p2.y);
|
|
14707
|
+
const d3 = this.pointToLineSegmentDistance(p1.x, p1.y, q1.x, q1.y, q2.x, q2.y);
|
|
14708
|
+
const d4 = this.pointToLineSegmentDistance(p2.x, p2.y, q1.x, q1.y, q2.x, q2.y);
|
|
14709
|
+
const minD = Math.min(d1, d2, d3, d4);
|
|
14710
|
+
if (minD <= halfStroke) {
|
|
14711
|
+
return true;
|
|
14712
|
+
}
|
|
14713
|
+
}
|
|
14714
|
+
return false;
|
|
14715
|
+
}
|
|
14716
|
+
updatePosition(x, y) {
|
|
14717
|
+
this.translateX = x;
|
|
14718
|
+
this.translateY = y;
|
|
14719
|
+
this._adjustedPoints = null;
|
|
14720
|
+
this._core.store.state.objects.update(this);
|
|
14721
|
+
}
|
|
14722
|
+
/**
|
|
14723
|
+
* Updates a specific endpoint of the line (start or end).
|
|
14724
|
+
* The coordinates are in viewBox-local space (relative to x, y origin).
|
|
14725
|
+
* This method recalculates the bounding box dimensions.
|
|
14726
|
+
*
|
|
14727
|
+
* @param handleType - Which endpoint to update: 'start' or 'end'
|
|
14728
|
+
* @param newX - New X coordinate in viewBox-local space
|
|
14729
|
+
* @param newY - New Y coordinate in viewBox-local space
|
|
14730
|
+
*/
|
|
14731
|
+
updateEndpoint(handleType, newX, newY) {
|
|
14732
|
+
// Update the appropriate endpoint
|
|
14733
|
+
if (handleType === 'start') {
|
|
14734
|
+
this.startX = newX;
|
|
14735
|
+
this.startY = newY;
|
|
14736
|
+
}
|
|
14737
|
+
else {
|
|
14738
|
+
this.endX = newX;
|
|
14739
|
+
this.endY = newY;
|
|
14740
|
+
}
|
|
14741
|
+
const oldWidth = this.width;
|
|
14742
|
+
const oldHeight = this.height;
|
|
14743
|
+
// Recalculate the bounding box (viewBox dimensions)
|
|
14744
|
+
const { minX, minY, maxX, maxY } = this.calculateBoundingBox();
|
|
14745
|
+
// Calculate the change in origin (in local coordinates)
|
|
14746
|
+
const deltaX = minX - this.x;
|
|
14747
|
+
const deltaY = minY - this.y;
|
|
14748
|
+
const newWidth = maxX - minX;
|
|
14749
|
+
const newHeight = maxY - minY;
|
|
14750
|
+
const deltaWidth = newWidth - oldWidth;
|
|
14751
|
+
const deltaHeight = newHeight - oldHeight;
|
|
14752
|
+
const deltaCx = deltaWidth / 2;
|
|
14753
|
+
const deltaCy = deltaHeight / 2;
|
|
14754
|
+
// Update viewBox dimensions
|
|
14755
|
+
this.x = minX;
|
|
14756
|
+
this.y = minY;
|
|
14757
|
+
this.width = newWidth;
|
|
14758
|
+
this.height = newHeight;
|
|
14759
|
+
// Rotate the delta to world coordinates before applying to translate
|
|
14760
|
+
const cos = Math.cos(this.rotation);
|
|
14761
|
+
const sin = Math.sin(this.rotation);
|
|
14762
|
+
// We need to compensate for:
|
|
14763
|
+
// 1. The shift of the top-left corner (deltaX, deltaY)
|
|
14764
|
+
// 2. The shift of the center point (deltaCx, deltaCy) which affects rotation pivot
|
|
14765
|
+
// Formula: DeltaT = R(DeltaPos + DeltaCenter) - DeltaCenter
|
|
14766
|
+
const vx = deltaX + deltaCx;
|
|
14767
|
+
const vy = deltaY + deltaCy;
|
|
14768
|
+
const rotatedVx = vx * cos - vy * sin;
|
|
14769
|
+
const rotatedVy = vx * sin + vy * cos;
|
|
14770
|
+
const correctionX = rotatedVx - deltaCx;
|
|
14771
|
+
const correctionY = rotatedVy - deltaCy;
|
|
14772
|
+
// Adjust translateX/Y to compensate for the origin shift
|
|
14773
|
+
// so the line stays visually in the same position
|
|
14774
|
+
this.translateX += correctionX / this.scale;
|
|
14775
|
+
this.translateY += correctionY / this.scale;
|
|
14776
|
+
// Clear cached adjusted points
|
|
14777
|
+
this._adjustedPoints = null;
|
|
14778
|
+
this._core.store.state.objects.update(this);
|
|
14779
|
+
}
|
|
14780
|
+
updateControlPoint(newX, newY) {
|
|
14781
|
+
this.controlX = newX;
|
|
14782
|
+
this.controlY = newY;
|
|
14783
|
+
const oldWidth = this.width;
|
|
14784
|
+
const oldHeight = this.height;
|
|
14785
|
+
// Recalculate the bounding box (viewBox dimensions)
|
|
14786
|
+
const { minX, minY, maxX, maxY } = this.calculateBoundingBox();
|
|
14787
|
+
// Calculate the change in origin (in local coordinates)
|
|
14788
|
+
const deltaX = minX - this.x;
|
|
14789
|
+
const deltaY = minY - this.y;
|
|
14790
|
+
const newWidth = maxX - minX;
|
|
14791
|
+
const newHeight = maxY - minY;
|
|
14792
|
+
const deltaWidth = newWidth - oldWidth;
|
|
14793
|
+
const deltaHeight = newHeight - oldHeight;
|
|
14794
|
+
const deltaCx = deltaWidth / 2;
|
|
14795
|
+
const deltaCy = deltaHeight / 2;
|
|
14796
|
+
// Update viewBox dimensions
|
|
14797
|
+
this.x = minX;
|
|
14798
|
+
this.y = minY;
|
|
14799
|
+
this.width = newWidth;
|
|
14800
|
+
this.height = newHeight;
|
|
14801
|
+
// Rotate the delta to world coordinates before applying to translate
|
|
14802
|
+
const cos = Math.cos(this.rotation);
|
|
14803
|
+
const sin = Math.sin(this.rotation);
|
|
14804
|
+
// We need to compensate for:
|
|
14805
|
+
// 1. The shift of the top-left corner (deltaX, deltaY)
|
|
14806
|
+
// 2. The shift of the center point (deltaCx, deltaCy) which affects rotation pivot
|
|
14807
|
+
// Formula: DeltaT = R(DeltaPos + DeltaCenter) - DeltaCenter
|
|
14808
|
+
const vx = deltaX + deltaCx;
|
|
14809
|
+
const vy = deltaY + deltaCy;
|
|
14810
|
+
const rotatedVx = vx * cos - vy * sin;
|
|
14811
|
+
const rotatedVy = vx * sin + vy * cos;
|
|
14812
|
+
const correctionX = rotatedVx - deltaCx;
|
|
14813
|
+
const correctionY = rotatedVy - deltaCy;
|
|
14814
|
+
// Adjust translateX/Y to compensate for the origin shift
|
|
14815
|
+
// so the line stays visually in the same position
|
|
14816
|
+
this.translateX += correctionX / this.scale;
|
|
14817
|
+
this.translateY += correctionY / this.scale;
|
|
14818
|
+
// Clear cached adjusted points
|
|
14819
|
+
this._adjustedPoints = null;
|
|
14820
|
+
this._core.store.state.objects.update(this);
|
|
14821
|
+
}
|
|
14822
|
+
computeAdjustedPoints() {
|
|
14823
|
+
const points = [
|
|
14824
|
+
[this.startX, this.startY],
|
|
14825
|
+
[this.endX, this.endY],
|
|
14826
|
+
];
|
|
14827
|
+
const angle = this.rotation;
|
|
14828
|
+
const cos = Math.cos(angle);
|
|
14829
|
+
const sin = Math.sin(angle);
|
|
14830
|
+
const xs = points.map(p => p[0]);
|
|
14831
|
+
const ys = points.map(p => p[1]);
|
|
14832
|
+
const pivot = {
|
|
14833
|
+
x: (Math.min(...xs) + Math.max(...xs)) / 2,
|
|
14834
|
+
y: (Math.min(...ys) + Math.max(...ys)) / 2,
|
|
14835
|
+
};
|
|
14836
|
+
const { x: cx, y: cy } = pivot;
|
|
14837
|
+
const rotatedPoints = points.map(([x, y]) => {
|
|
14838
|
+
const dx = x - cx;
|
|
14839
|
+
const dy = y - cy;
|
|
14840
|
+
return [cx + dx * cos - dy * sin, cy + dx * sin + dy * cos];
|
|
14841
|
+
});
|
|
14842
|
+
return rotatedPoints.map(([px, py]) => [(px - this.x) / this.scale + this.translateX, (py - this.y) / this.scale + this.translateY]);
|
|
14843
|
+
}
|
|
14844
|
+
pointToLineSegmentDistance(x, y, x1, y1, x2, y2) {
|
|
14845
|
+
const A = x - x1;
|
|
14846
|
+
const B = y - y1;
|
|
14847
|
+
const C = x2 - x1;
|
|
14848
|
+
const D = y2 - y1;
|
|
14849
|
+
const dot = A * C + B * D;
|
|
14850
|
+
const len_sq = C * C + D * D;
|
|
14851
|
+
let param = -1;
|
|
14852
|
+
if (len_sq !== 0) {
|
|
14853
|
+
param = dot / len_sq;
|
|
14854
|
+
}
|
|
14855
|
+
let xx, yy;
|
|
14856
|
+
if (param < 0) {
|
|
14857
|
+
xx = x1;
|
|
14858
|
+
yy = y1;
|
|
14859
|
+
}
|
|
14860
|
+
else if (param > 1) {
|
|
14861
|
+
xx = x2;
|
|
14862
|
+
yy = y2;
|
|
14863
|
+
}
|
|
14864
|
+
else {
|
|
14865
|
+
xx = x1 + param * C;
|
|
14866
|
+
yy = y1 + param * D;
|
|
14867
|
+
}
|
|
14868
|
+
const dx = x - xx;
|
|
14869
|
+
const dy = y - yy;
|
|
14870
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
14871
|
+
}
|
|
14872
|
+
/**
|
|
14873
|
+
* Calculates the minimum distance from a point to a quadratic Bezier curve.
|
|
14874
|
+
* Uses sampling along the curve to find the closest point.
|
|
14875
|
+
*/
|
|
14876
|
+
pointToBezierDistance(x, y) {
|
|
14877
|
+
if (this._adjustedPoints === null) {
|
|
14878
|
+
this._adjustedPoints = this.computeAdjustedPoints();
|
|
14879
|
+
}
|
|
14880
|
+
const p0 = this._adjustedPoints[0];
|
|
14881
|
+
const p2 = this._adjustedPoints[1];
|
|
14882
|
+
// Calculate the adjusted control point
|
|
14883
|
+
const controlAdjusted = this.computeAdjustedControlPoint();
|
|
14884
|
+
let minDistance = Infinity;
|
|
14885
|
+
const samples = 20; // Number of samples along the curve
|
|
14886
|
+
for (let i = 0; i <= samples; i++) {
|
|
14887
|
+
const t = i / samples;
|
|
14888
|
+
const oneMinusT = 1 - t;
|
|
14889
|
+
// Quadratic Bezier: B(t) = (1-t)²P₀ + 2(1-t)tP₁ + t²P₂
|
|
14890
|
+
const bx = oneMinusT * oneMinusT * p0[0] + 2 * oneMinusT * t * controlAdjusted[0] + t * t * p2[0];
|
|
14891
|
+
const by = oneMinusT * oneMinusT * p0[1] + 2 * oneMinusT * t * controlAdjusted[1] + t * t * p2[1];
|
|
14892
|
+
const dx = x - bx;
|
|
14893
|
+
const dy = y - by;
|
|
14894
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
14895
|
+
if (distance < minDistance) {
|
|
14896
|
+
minDistance = distance;
|
|
14897
|
+
}
|
|
14898
|
+
}
|
|
14899
|
+
return minDistance;
|
|
14900
|
+
}
|
|
14901
|
+
/**
|
|
14902
|
+
* Computes the adjusted control point in world coordinates, accounting for rotation and translation.
|
|
14903
|
+
*/
|
|
14904
|
+
computeAdjustedControlPoint() {
|
|
14905
|
+
if (this.controlX === undefined || this.controlY === undefined) {
|
|
14906
|
+
// Return midpoint if no control point
|
|
14907
|
+
return [(this.startX + this.endX) / 2, (this.startY + this.endY) / 2];
|
|
14908
|
+
}
|
|
14909
|
+
const angle = this.rotation;
|
|
14910
|
+
const cos = Math.cos(angle);
|
|
14911
|
+
const sin = Math.sin(angle);
|
|
14912
|
+
// Calculate pivot (center of bounding box for endpoints)
|
|
14913
|
+
const xs = [this.startX, this.endX];
|
|
14914
|
+
const ys = [this.startY, this.endY];
|
|
14915
|
+
const pivot = {
|
|
14916
|
+
x: (Math.min(...xs) + Math.max(...xs)) / 2,
|
|
14917
|
+
y: (Math.min(...ys) + Math.max(...ys)) / 2,
|
|
14918
|
+
};
|
|
14919
|
+
const { x: cx, y: cy } = pivot;
|
|
14920
|
+
// Rotate control point around pivot
|
|
14921
|
+
const dx = this.controlX - cx;
|
|
14922
|
+
const dy = this.controlY - cy;
|
|
14923
|
+
const rotatedX = cx + dx * cos - dy * sin;
|
|
14924
|
+
const rotatedY = cy + dx * sin + dy * cos;
|
|
14925
|
+
// Transform to world coordinates
|
|
14926
|
+
return [(rotatedX - this.x) / this.scale + this.translateX, (rotatedY - this.y) / this.scale + this.translateY];
|
|
14927
|
+
}
|
|
14928
|
+
get rotatedPolygon() {
|
|
14929
|
+
const padding = this.padding;
|
|
14930
|
+
// Use the bounding box which accounts for curve extrema
|
|
14931
|
+
const { minX, minY, maxX, maxY } = KritzelLine.calculateBoundingBox(this.startX, this.startY, this.endX, this.endY, this.controlX, this.controlY, this.strokeWidth);
|
|
14932
|
+
// Convert to local coordinates (relative to this.x, this.y)
|
|
14933
|
+
const localMinX = minX - this.x + padding;
|
|
14934
|
+
const localMinY = minY - this.y + padding;
|
|
14935
|
+
const localMaxX = maxX - this.x + padding;
|
|
14936
|
+
const localMaxY = maxY - this.y + padding;
|
|
14937
|
+
const c1 = { x: localMinX, y: localMinY }; // top-left
|
|
14938
|
+
const c2 = { x: localMaxX, y: localMinY }; // top-right
|
|
14939
|
+
const c3 = { x: localMaxX, y: localMaxY }; // bottom-right
|
|
14940
|
+
const c4 = { x: localMinX, y: localMaxY }; // bottom-left
|
|
14941
|
+
const cx = this.totalWidth / 2;
|
|
14942
|
+
const cy = this.totalHeight / 2;
|
|
14943
|
+
const angle = this.rotation;
|
|
14944
|
+
const cos = Math.cos(angle);
|
|
14945
|
+
const sin = Math.sin(angle);
|
|
14946
|
+
const transform = (p) => {
|
|
14947
|
+
const rx = (p.x - cx) * cos - (p.y - cy) * sin + cx;
|
|
14948
|
+
const ry = (p.x - cx) * sin + (p.y - cy) * cos + cy;
|
|
14949
|
+
return {
|
|
14950
|
+
x: rx / this.scale + this.translateX,
|
|
14951
|
+
y: ry / this.scale + this.translateY,
|
|
14952
|
+
};
|
|
14953
|
+
};
|
|
14954
|
+
return {
|
|
14955
|
+
topLeft: transform(c1),
|
|
14956
|
+
topRight: transform(c2),
|
|
14957
|
+
bottomRight: transform(c3),
|
|
14958
|
+
bottomLeft: transform(c4),
|
|
14959
|
+
};
|
|
14960
|
+
}
|
|
14961
|
+
/**
|
|
14962
|
+
* Calculates the tight bounding box for a quadratic Bezier curve or straight line.
|
|
14963
|
+
* For curves, it finds the extrema points where the derivative equals zero.
|
|
14964
|
+
*/
|
|
14965
|
+
static calculateBoundingBox(startX, startY, endX, endY, controlX, controlY, strokeWidth) {
|
|
14966
|
+
let minX = Math.min(startX, endX);
|
|
14967
|
+
let minY = Math.min(startY, endY);
|
|
14968
|
+
let maxX = Math.max(startX, endX);
|
|
14969
|
+
let maxY = Math.max(startY, endY);
|
|
14970
|
+
if (controlX !== undefined && controlY !== undefined) {
|
|
14971
|
+
// For a quadratic Bezier: B(t) = (1-t)²P₀ + 2(1-t)tP₁ + t²P₂
|
|
14972
|
+
// Derivative: B'(t) = 2(1-t)(P₁-P₀) + 2t(P₂-P₁) = 0
|
|
14973
|
+
// Solving for t: t = (P₀ - P₁) / (P₀ - 2P₁ + P₂)
|
|
14974
|
+
// Find extrema for X
|
|
14975
|
+
const aX = startX - 2 * controlX + endX;
|
|
14976
|
+
if (aX !== 0) {
|
|
14977
|
+
const tX = (startX - controlX) / aX;
|
|
14978
|
+
if (tX > 0 && tX < 1) {
|
|
14979
|
+
const extremaX = (1 - tX) * (1 - tX) * startX + 2 * (1 - tX) * tX * controlX + tX * tX * endX;
|
|
14980
|
+
minX = Math.min(minX, extremaX);
|
|
14981
|
+
maxX = Math.max(maxX, extremaX);
|
|
14982
|
+
}
|
|
14983
|
+
}
|
|
14984
|
+
// Find extrema for Y
|
|
14985
|
+
const aY = startY - 2 * controlY + endY;
|
|
14986
|
+
if (aY !== 0) {
|
|
14987
|
+
const tY = (startY - controlY) / aY;
|
|
14988
|
+
if (tY > 0 && tY < 1) {
|
|
14989
|
+
const extremaY = (1 - tY) * (1 - tY) * startY + 2 * (1 - tY) * tY * controlY + tY * tY * endY;
|
|
14990
|
+
minY = Math.min(minY, extremaY);
|
|
14991
|
+
maxY = Math.max(maxY, extremaY);
|
|
14992
|
+
}
|
|
14993
|
+
}
|
|
14994
|
+
}
|
|
14995
|
+
const halfStroke = strokeWidth / 2;
|
|
14996
|
+
return {
|
|
14997
|
+
minX: minX - halfStroke,
|
|
14998
|
+
minY: minY - halfStroke,
|
|
14999
|
+
maxX: maxX + halfStroke,
|
|
15000
|
+
maxY: maxY + halfStroke,
|
|
15001
|
+
};
|
|
15002
|
+
}
|
|
15003
|
+
calculateBoundingBox() {
|
|
15004
|
+
return KritzelLine.calculateBoundingBox(this.startX, this.startY, this.endX, this.endY, this.controlX, this.controlY, this.strokeWidth);
|
|
15005
|
+
}
|
|
15006
|
+
updateDimensions() {
|
|
15007
|
+
const { minX, minY, maxX, maxY } = this.calculateBoundingBox();
|
|
15008
|
+
this.width = maxX - minX;
|
|
15009
|
+
this.height = maxY - minY;
|
|
15010
|
+
this.x = minX;
|
|
15011
|
+
this.y = minY;
|
|
15012
|
+
this.translateX = (this.x + this.translateX) / this.scale;
|
|
15013
|
+
this.translateY = (this.y + this.translateY) / this.scale;
|
|
15014
|
+
}
|
|
15015
|
+
/** Get unique marker ID for SVG defs at the start of the line */
|
|
15016
|
+
get startMarkerId() {
|
|
15017
|
+
return `arrow-start-${this.id}`;
|
|
15018
|
+
}
|
|
15019
|
+
/** Get unique marker ID for SVG defs at the end of the line */
|
|
15020
|
+
get endMarkerId() {
|
|
15021
|
+
return `arrow-end-${this.id}`;
|
|
15022
|
+
}
|
|
15023
|
+
/** Get the arrow size for start or end, defaulting to strokeWidth * 3 */
|
|
15024
|
+
getArrowSize(end) {
|
|
15025
|
+
const config = end === 'start' ? this.arrows?.start : this.arrows?.end;
|
|
15026
|
+
return config?.size ?? this.strokeWidth * 3;
|
|
15027
|
+
}
|
|
15028
|
+
/** Get the arrow fill color for start or end, defaulting to stroke color */
|
|
15029
|
+
getArrowFill(end) {
|
|
15030
|
+
const config = end === 'start' ? this.arrows?.start : this.arrows?.end;
|
|
15031
|
+
return config?.fill ?? this.stroke;
|
|
15032
|
+
}
|
|
15033
|
+
/**
|
|
15034
|
+
* Generate SVG path data for an arrow head based on the given style.
|
|
15035
|
+
* The path is designed to fit in a 10x10 viewBox with the tip at (10, 5).
|
|
15036
|
+
* @param style The arrow head style
|
|
15037
|
+
* @returns SVG path data string
|
|
15038
|
+
*/
|
|
15039
|
+
getArrowPath(style = 'triangle') {
|
|
15040
|
+
switch (style) {
|
|
15041
|
+
case 'triangle':
|
|
15042
|
+
return 'M 0 0 L 10 5 L 0 10 z';
|
|
15043
|
+
case 'open':
|
|
15044
|
+
return 'M 0 0 L 10 5 L 0 10';
|
|
15045
|
+
case 'diamond':
|
|
15046
|
+
return 'M 0 5 L 5 0 L 10 5 L 5 10 z';
|
|
15047
|
+
case 'circle':
|
|
15048
|
+
return 'M 5,0 A 5,5 0 1,1 5,10 A 5,5 0 1,1 5,0';
|
|
15049
|
+
default:
|
|
15050
|
+
return 'M 0 0 L 10 5 L 0 10 z';
|
|
15051
|
+
}
|
|
15052
|
+
}
|
|
15053
|
+
/** Check if start arrow is enabled */
|
|
15054
|
+
get hasStartArrow() {
|
|
15055
|
+
return this.arrows?.start?.enabled === true;
|
|
15056
|
+
}
|
|
15057
|
+
/** Check if end arrow is enabled */
|
|
15058
|
+
get hasEndArrow() {
|
|
15059
|
+
return this.arrows?.end?.enabled === true;
|
|
15060
|
+
}
|
|
15061
|
+
}
|
|
15062
|
+
|
|
14457
15063
|
exports.KritzelMouseButton = void 0;
|
|
14458
15064
|
(function (KritzelMouseButton) {
|
|
14459
15065
|
KritzelMouseButton[KritzelMouseButton["Left"] = 0] = "Left";
|
|
@@ -14678,198 +15284,318 @@ class KritzelBrushTool extends KritzelBaseTool {
|
|
|
14678
15284
|
}
|
|
14679
15285
|
}
|
|
14680
15286
|
|
|
14681
|
-
class
|
|
14682
|
-
|
|
14683
|
-
|
|
14684
|
-
|
|
14685
|
-
|
|
14686
|
-
|
|
14687
|
-
|
|
14688
|
-
|
|
14689
|
-
|
|
14690
|
-
|
|
14691
|
-
|
|
14692
|
-
|
|
14693
|
-
|
|
14694
|
-
|
|
14695
|
-
|
|
14696
|
-
|
|
14697
|
-
|
|
14698
|
-
}
|
|
15287
|
+
class KritzelSelectionGroup extends KritzelBaseObject {
|
|
15288
|
+
__class__ = 'KritzelSelectionGroup';
|
|
15289
|
+
// Store only object IDs instead of full objects
|
|
15290
|
+
objectIds = [];
|
|
15291
|
+
// Store snapshots of object state for transformations (rotation, resize)
|
|
15292
|
+
unchangedObjectSnapshots = new Map();
|
|
15293
|
+
snapshotRotation = 0;
|
|
15294
|
+
minX;
|
|
15295
|
+
maxX;
|
|
15296
|
+
minY;
|
|
15297
|
+
maxY;
|
|
15298
|
+
// Getter to retrieve actual objects from the store by their IDs
|
|
15299
|
+
get objects() {
|
|
15300
|
+
return this.objectIds
|
|
15301
|
+
.map(id => {
|
|
15302
|
+
const found = this._core.store.state.objects.filter(obj => obj.id === id);
|
|
15303
|
+
return found.length > 0 ? found[0] : null;
|
|
15304
|
+
})
|
|
15305
|
+
.filter(obj => obj !== null);
|
|
14699
15306
|
}
|
|
14700
|
-
|
|
14701
|
-
|
|
14702
|
-
if (this._core.store.state.isErasing) {
|
|
14703
|
-
const selectedObjects = this._core.getObjectsFromPointerEvent(event, '.object');
|
|
14704
|
-
if (selectedObjects.length === 0)
|
|
14705
|
-
return;
|
|
14706
|
-
const x = this._core.store.state.pointerX;
|
|
14707
|
-
const y = this._core.store.state.pointerY;
|
|
14708
|
-
selectedObjects.forEach(selectedObject => {
|
|
14709
|
-
selectedObject.markedForRemoval = selectedObject.hitTest(x, y);
|
|
14710
|
-
});
|
|
14711
|
-
this._core.rerender();
|
|
14712
|
-
}
|
|
14713
|
-
}
|
|
14714
|
-
if (event.pointerType === 'touch') {
|
|
14715
|
-
if (this._core.store.state.pointers.size === 1 && this._core.store.state.isErasing) {
|
|
14716
|
-
const shadowRoot = this._core.store.state.host?.shadowRoot;
|
|
14717
|
-
if (!shadowRoot)
|
|
14718
|
-
return;
|
|
14719
|
-
const selectedObjects = this._core.getObjectsFromPointerEvent(event, '.object');
|
|
14720
|
-
if (selectedObjects.length === 0)
|
|
14721
|
-
return;
|
|
14722
|
-
const x = this._core.store.state.pointerX;
|
|
14723
|
-
const y = this._core.store.state.pointerY;
|
|
14724
|
-
selectedObjects.forEach(selectedObject => {
|
|
14725
|
-
selectedObject.markedForRemoval = selectedObject.hitTest(x, y);
|
|
14726
|
-
});
|
|
14727
|
-
this._core.rerender();
|
|
14728
|
-
}
|
|
14729
|
-
}
|
|
15307
|
+
get length() {
|
|
15308
|
+
return this.objectIds.length;
|
|
14730
15309
|
}
|
|
14731
|
-
|
|
14732
|
-
|
|
14733
|
-
|
|
14734
|
-
|
|
14735
|
-
|
|
14736
|
-
|
|
14737
|
-
|
|
14738
|
-
|
|
14739
|
-
|
|
14740
|
-
|
|
14741
|
-
|
|
14742
|
-
|
|
14743
|
-
|
|
14744
|
-
}
|
|
15310
|
+
static create(core) {
|
|
15311
|
+
const object = new KritzelSelectionGroup();
|
|
15312
|
+
object._core = core;
|
|
15313
|
+
object.id = object.generateId();
|
|
15314
|
+
object.workspaceId = core.store.state.activeWorkspace.id;
|
|
15315
|
+
object.scale = core.store.state.scale;
|
|
15316
|
+
object.zIndex = 99999;
|
|
15317
|
+
return object;
|
|
15318
|
+
}
|
|
15319
|
+
addOrRemove(object) {
|
|
15320
|
+
const index = this.objectIds.findIndex(id => id === object.id);
|
|
15321
|
+
if (index === -1) {
|
|
15322
|
+
this.objectIds.push(object.id);
|
|
14745
15323
|
}
|
|
14746
|
-
|
|
14747
|
-
|
|
14748
|
-
if (this._core.store.state.isErasing) {
|
|
14749
|
-
const objectsToRemove = this._core.store.allObjects.filter(object => object.markedForRemoval);
|
|
14750
|
-
objectsToRemove.forEach(object => {
|
|
14751
|
-
object.markedForRemoval = false;
|
|
14752
|
-
this._core.removeObject(object);
|
|
14753
|
-
});
|
|
14754
|
-
if (objectsToRemove.length > 0) {
|
|
14755
|
-
this._core.rerender();
|
|
14756
|
-
}
|
|
14757
|
-
this._core.store.state.isErasing = false;
|
|
14758
|
-
this._core.engine.emitObjectsChange();
|
|
14759
|
-
}
|
|
15324
|
+
else {
|
|
15325
|
+
this.objectIds.splice(index, 1);
|
|
14760
15326
|
}
|
|
15327
|
+
this.captureUnchangedSnapshots();
|
|
15328
|
+
this.refreshObjectDimensions();
|
|
14761
15329
|
}
|
|
14762
|
-
|
|
14763
|
-
|
|
14764
|
-
/**
|
|
14765
|
-
* Browser Image Compression
|
|
14766
|
-
* v2.0.2
|
|
14767
|
-
* by Donald <donaldcwl@gmail.com>
|
|
14768
|
-
* https://github.com/Donaldcwl/browser-image-compression
|
|
14769
|
-
*/
|
|
14770
|
-
|
|
14771
|
-
function _mergeNamespaces$1(e,t){return t.forEach((function(t){t&&"string"!=typeof t&&!Array.isArray(t)&&Object.keys(t).forEach((function(r){if("default"!==r&&!(r in e)){var i=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(e,r,i.get?i:{enumerable:true,get:function(){return t[r]}});}}));})),Object.freeze(e)}function copyExifWithoutOrientation(e,t){return new Promise((function(r,i){let o;return getApp1Segment(e).then((function(e){try{return o=e,r(new Blob([t.slice(0,2),o,t.slice(2)],{type:"image/jpeg"}))}catch(e){return i(e)}}),i)}))}const getApp1Segment=e=>new Promise(((t,r)=>{const i=new FileReader;i.addEventListener("load",(({target:{result:e}})=>{const i=new DataView(e);let o=0;if(65496!==i.getUint16(o))return r("not a valid JPEG");for(o+=2;;){const a=i.getUint16(o);if(65498===a)break;const s=i.getUint16(o+2);if(65505===a&&1165519206===i.getUint32(o+4)){const a=o+10;let f;switch(i.getUint16(a)){case 18761:f=true;break;case 19789:f=false;break;default:return r("TIFF header contains invalid endian")}if(42!==i.getUint16(a+2,f))return r("TIFF header contains invalid version");const l=i.getUint32(a+4,f),c=a+l+2+12*i.getUint16(a+l,f);for(let e=a+l+2;e<c;e+=12){if(274==i.getUint16(e,f)){if(3!==i.getUint16(e+2,f))return r("Orientation data type is invalid");if(1!==i.getUint32(e+4,f))return r("Orientation data count is invalid");i.setUint16(e+8,1,f);break}}return t(e.slice(o,o+2+s))}o+=2+s;}return t(new Blob)})),i.readAsArrayBuffer(e);}));var e={},t={get exports(){return e},set exports(t){e=t;}};!function(e){var r,i,UZIP={};t.exports=UZIP,UZIP.parse=function(e,t){for(var r=UZIP.bin.readUshort,i=UZIP.bin.readUint,o=0,a={},s=new Uint8Array(e),f=s.length-4;101010256!=i(s,f);)f--;o=f;o+=4;var l=r(s,o+=4);r(s,o+=2);var c=i(s,o+=2),u=i(s,o+=4);o+=4,o=u;for(var h=0;h<l;h++){i(s,o),o+=4,o+=4,o+=4,i(s,o+=4);c=i(s,o+=4);var d=i(s,o+=4),A=r(s,o+=4),g=r(s,o+2),p=r(s,o+4);o+=6;var m=i(s,o+=8);o+=4,o+=A+g+p,UZIP._readLocal(s,m,a,c,d,t);}return a},UZIP._readLocal=function(e,t,r,i,o,a){var s=UZIP.bin.readUshort,f=UZIP.bin.readUint;f(e,t),s(e,t+=4),s(e,t+=2);var l=s(e,t+=2);f(e,t+=2),f(e,t+=4),t+=4;var c=s(e,t+=8),u=s(e,t+=2);t+=2;var h=UZIP.bin.readUTF8(e,t,c);if(t+=c,t+=u,a)r[h]={size:o,csize:i};else {var d=new Uint8Array(e.buffer,t);if(0==l)r[h]=new Uint8Array(d.buffer.slice(t,t+i));else {if(8!=l)throw "unknown compression method: "+l;var A=new Uint8Array(o);UZIP.inflateRaw(d,A),r[h]=A;}}},UZIP.inflateRaw=function(e,t){return UZIP.F.inflate(e,t)},UZIP.inflate=function(e,t){return UZIP.inflateRaw(new Uint8Array(e.buffer,e.byteOffset+2,e.length-6),t)},UZIP.deflate=function(e,t){null==t&&(t={level:6});var r=0,i=new Uint8Array(50+Math.floor(1.1*e.length));i[r]=120,i[r+1]=156,r+=2,r=UZIP.F.deflateRaw(e,i,r,t.level);var o=UZIP.adler(e,0,e.length);return i[r+0]=o>>>24&255,i[r+1]=o>>>16&255,i[r+2]=o>>>8&255,i[r+3]=o>>>0&255,new Uint8Array(i.buffer,0,r+4)},UZIP.deflateRaw=function(e,t){null==t&&(t={level:6});var r=new Uint8Array(50+Math.floor(1.1*e.length)),i=UZIP.F.deflateRaw(e,r,i,t.level);return new Uint8Array(r.buffer,0,i)},UZIP.encode=function(e,t){null==t&&(t=false);var r=0,i=UZIP.bin.writeUint,o=UZIP.bin.writeUshort,a={};for(var s in e){var f=!UZIP._noNeed(s)&&!t,l=e[s],c=UZIP.crc.crc(l,0,l.length);a[s]={cpr:f,usize:l.length,crc:c,file:f?UZIP.deflateRaw(l):l};}for(var s in a)r+=a[s].file.length+30+46+2*UZIP.bin.sizeUTF8(s);r+=22;var u=new Uint8Array(r),h=0,d=[];for(var s in a){var A=a[s];d.push(h),h=UZIP._writeHeader(u,h,s,A,0);}var g=0,p=h;for(var s in a){A=a[s];d.push(h),h=UZIP._writeHeader(u,h,s,A,1,d[g++]);}var m=h-p;return i(u,h,101010256),h+=4,o(u,h+=4,g),o(u,h+=2,g),i(u,h+=2,m),i(u,h+=4,p),h+=4,h+=2,u.buffer},UZIP._noNeed=function(e){var t=e.split(".").pop().toLowerCase();return -1!="png,jpg,jpeg,zip".indexOf(t)},UZIP._writeHeader=function(e,t,r,i,o,a){var s=UZIP.bin.writeUint,f=UZIP.bin.writeUshort,l=i.file;return s(e,t,0==o?67324752:33639248),t+=4,1==o&&(t+=2),f(e,t,20),f(e,t+=2,0),f(e,t+=2,i.cpr?8:0),s(e,t+=2,0),s(e,t+=4,i.crc),s(e,t+=4,l.length),s(e,t+=4,i.usize),f(e,t+=4,UZIP.bin.sizeUTF8(r)),f(e,t+=2,0),t+=2,1==o&&(t+=2,t+=2,s(e,t+=6,a),t+=4),t+=UZIP.bin.writeUTF8(e,t,r),0==o&&(e.set(l,t),t+=l.length),t},UZIP.crc={table:function(){for(var e=new Uint32Array(256),t=0;t<256;t++){for(var r=t,i=0;i<8;i++)1&r?r=3988292384^r>>>1:r>>>=1;e[t]=r;}return e}(),update:function(e,t,r,i){for(var o=0;o<i;o++)e=UZIP.crc.table[255&(e^t[r+o])]^e>>>8;return e},crc:function(e,t,r){return 4294967295^UZIP.crc.update(4294967295,e,t,r)}},UZIP.adler=function(e,t,r){for(var i=1,o=0,a=t,s=t+r;a<s;){for(var f=Math.min(a+5552,s);a<f;)o+=i+=e[a++];i%=65521,o%=65521;}return o<<16|i},UZIP.bin={readUshort:function(e,t){return e[t]|e[t+1]<<8},writeUshort:function(e,t,r){e[t]=255&r,e[t+1]=r>>8&255;},readUint:function(e,t){return 16777216*e[t+3]+(e[t+2]<<16|e[t+1]<<8|e[t])},writeUint:function(e,t,r){e[t]=255&r,e[t+1]=r>>8&255,e[t+2]=r>>16&255,e[t+3]=r>>24&255;},readASCII:function(e,t,r){for(var i="",o=0;o<r;o++)i+=String.fromCharCode(e[t+o]);return i},writeASCII:function(e,t,r){for(var i=0;i<r.length;i++)e[t+i]=r.charCodeAt(i);},pad:function(e){return e.length<2?"0"+e:e},readUTF8:function(e,t,r){for(var i,o="",a=0;a<r;a++)o+="%"+UZIP.bin.pad(e[t+a].toString(16));try{i=decodeURIComponent(o);}catch(i){return UZIP.bin.readASCII(e,t,r)}return i},writeUTF8:function(e,t,r){for(var i=r.length,o=0,a=0;a<i;a++){var s=r.charCodeAt(a);if(0==(4294967168&s))e[t+o]=s,o++;else if(0==(4294965248&s))e[t+o]=192|s>>6,e[t+o+1]=128|s>>0&63,o+=2;else if(0==(4294901760&s))e[t+o]=224|s>>12,e[t+o+1]=128|s>>6&63,e[t+o+2]=128|s>>0&63,o+=3;else {if(0!=(4292870144&s))throw "e";e[t+o]=240|s>>18,e[t+o+1]=128|s>>12&63,e[t+o+2]=128|s>>6&63,e[t+o+3]=128|s>>0&63,o+=4;}}return o},sizeUTF8:function(e){for(var t=e.length,r=0,i=0;i<t;i++){var o=e.charCodeAt(i);if(0==(4294967168&o))r++;else if(0==(4294965248&o))r+=2;else if(0==(4294901760&o))r+=3;else {if(0!=(4292870144&o))throw "e";r+=4;}}return r}},UZIP.F={},UZIP.F.deflateRaw=function(e,t,r,i){var o=[[0,0,0,0,0],[4,4,8,4,0],[4,5,16,8,0],[4,6,16,16,0],[4,10,16,32,0],[8,16,32,32,0],[8,16,128,128,0],[8,32,128,256,0],[32,128,258,1024,1],[32,258,258,4096,1]][i],a=UZIP.F.U,s=UZIP.F._goodIndex;var f=UZIP.F._putsE,l=0,c=r<<3,u=0,h=e.length;if(0==i){for(;l<h;){f(t,c,l+(_=Math.min(65535,h-l))==h?1:0),c=UZIP.F._copyExact(e,l,_,t,c+8),l+=_;}return c>>>3}var d=a.lits,A=a.strt,g=a.prev,p=0,m=0,w=0,v=0,b=0,y=0;for(h>2&&(A[y=UZIP.F._hash(e,0)]=0),l=0;l<h;l++){if(b=y,l+1<h-2){y=UZIP.F._hash(e,l+1);var E=l+1&32767;g[E]=A[y],A[y]=E;}if(u<=l){(p>14e3||m>26697)&&h-l>100&&(u<l&&(d[p]=l-u,p+=2,u=l),c=UZIP.F._writeBlock(l==h-1||u==h?1:0,d,p,v,e,w,l-w,t,c),p=m=v=0,w=l);var F=0;l<h-2&&(F=UZIP.F._bestMatch(e,l,g,b,Math.min(o[2],h-l),o[3]));var _=F>>>16,B=65535&F;if(0!=F){B=65535&F;var U=s(_=F>>>16,a.of0);a.lhst[257+U]++;var C=s(B,a.df0);a.dhst[C]++,v+=a.exb[U]+a.dxb[C],d[p]=_<<23|l-u,d[p+1]=B<<16|U<<8|C,p+=2,u=l+_;}else a.lhst[e[l]]++;m++;}}for(w==l&&0!=e.length||(u<l&&(d[p]=l-u,p+=2,u=l),c=UZIP.F._writeBlock(1,d,p,v,e,w,l-w,t,c),p=0,m=0,p=m=v=0,w=l);0!=(7&c);)c++;return c>>>3},UZIP.F._bestMatch=function(e,t,r,i,o,a){var s=32767&t,f=r[s],l=s-f+32768&32767;if(f==s||i!=UZIP.F._hash(e,t-l))return 0;for(var c=0,u=0,h=Math.min(32767,t);l<=h&&0!=--a&&f!=s;){if(0==c||e[t+c]==e[t+c-l]){var d=UZIP.F._howLong(e,t,l);if(d>c){if(u=l,(c=d)>=o)break;l+2<d&&(d=l+2);for(var A=0,g=0;g<d-2;g++){var p=t-l+g+32768&32767,m=p-r[p]+32768&32767;m>A&&(A=m,f=p);}}}l+=(s=f)-(f=r[s])+32768&32767;}return c<<16|u},UZIP.F._howLong=function(e,t,r){if(e[t]!=e[t-r]||e[t+1]!=e[t+1-r]||e[t+2]!=e[t+2-r])return 0;var i=t,o=Math.min(e.length,t+258);for(t+=3;t<o&&e[t]==e[t-r];)t++;return t-i},UZIP.F._hash=function(e,t){return (e[t]<<8|e[t+1])+(e[t+2]<<4)&65535},UZIP.saved=0,UZIP.F._writeBlock=function(e,t,r,i,o,a,s,f,l){var c,u,h,d,A,g,p,m,w,v=UZIP.F.U,b=UZIP.F._putsF,y=UZIP.F._putsE;v.lhst[256]++,u=(c=UZIP.F.getTrees())[0],h=c[1],d=c[2],A=c[3],g=c[4],p=c[5],m=c[6],w=c[7];var E=32+(0==(l+3&7)?0:8-(l+3&7))+(s<<3),F=i+UZIP.F.contSize(v.fltree,v.lhst)+UZIP.F.contSize(v.fdtree,v.dhst),_=i+UZIP.F.contSize(v.ltree,v.lhst)+UZIP.F.contSize(v.dtree,v.dhst);_+=14+3*p+UZIP.F.contSize(v.itree,v.ihst)+(2*v.ihst[16]+3*v.ihst[17]+7*v.ihst[18]);for(var B=0;B<286;B++)v.lhst[B]=0;for(B=0;B<30;B++)v.dhst[B]=0;for(B=0;B<19;B++)v.ihst[B]=0;var U=E<F&&E<_?0:F<_?1:2;if(b(f,l,e),b(f,l+1,U),l+=3,0==U){for(;0!=(7&l);)l++;l=UZIP.F._copyExact(o,a,s,f,l);}else {var C,I;if(1==U&&(C=v.fltree,I=v.fdtree),2==U){UZIP.F.makeCodes(v.ltree,u),UZIP.F.revCodes(v.ltree,u),UZIP.F.makeCodes(v.dtree,h),UZIP.F.revCodes(v.dtree,h),UZIP.F.makeCodes(v.itree,d),UZIP.F.revCodes(v.itree,d),C=v.ltree,I=v.dtree,y(f,l,A-257),y(f,l+=5,g-1),y(f,l+=5,p-4),l+=4;for(var Q=0;Q<p;Q++)y(f,l+3*Q,v.itree[1+(v.ordr[Q]<<1)]);l+=3*p,l=UZIP.F._codeTiny(m,v.itree,f,l),l=UZIP.F._codeTiny(w,v.itree,f,l);}for(var M=a,x=0;x<r;x+=2){for(var S=t[x],R=S>>>23,T=M+(8388607&S);M<T;)l=UZIP.F._writeLit(o[M++],C,f,l);if(0!=R){var O=t[x+1],P=O>>16,H=O>>8&255,L=255&O;y(f,l=UZIP.F._writeLit(257+H,C,f,l),R-v.of0[H]),l+=v.exb[H],b(f,l=UZIP.F._writeLit(L,I,f,l),P-v.df0[L]),l+=v.dxb[L],M+=R;}}l=UZIP.F._writeLit(256,C,f,l);}return l},UZIP.F._copyExact=function(e,t,r,i,o){var a=o>>>3;return i[a]=r,i[a+1]=r>>>8,i[a+2]=255-i[a],i[a+3]=255-i[a+1],a+=4,i.set(new Uint8Array(e.buffer,t,r),a),o+(r+4<<3)},UZIP.F.getTrees=function(){for(var e=UZIP.F.U,t=UZIP.F._hufTree(e.lhst,e.ltree,15),r=UZIP.F._hufTree(e.dhst,e.dtree,15),i=[],o=UZIP.F._lenCodes(e.ltree,i),a=[],s=UZIP.F._lenCodes(e.dtree,a),f=0;f<i.length;f+=2)e.ihst[i[f]]++;for(f=0;f<a.length;f+=2)e.ihst[a[f]]++;for(var l=UZIP.F._hufTree(e.ihst,e.itree,7),c=19;c>4&&0==e.itree[1+(e.ordr[c-1]<<1)];)c--;return [t,r,l,o,s,c,i,a]},UZIP.F.getSecond=function(e){for(var t=[],r=0;r<e.length;r+=2)t.push(e[r+1]);return t},UZIP.F.nonZero=function(e){for(var t="",r=0;r<e.length;r+=2)0!=e[r+1]&&(t+=(r>>1)+",");return t},UZIP.F.contSize=function(e,t){for(var r=0,i=0;i<t.length;i++)r+=t[i]*e[1+(i<<1)];return r},UZIP.F._codeTiny=function(e,t,r,i){for(var o=0;o<e.length;o+=2){var a=e[o],s=e[o+1];i=UZIP.F._writeLit(a,t,r,i);var f=16==a?2:17==a?3:7;a>15&&(UZIP.F._putsE(r,i,s,f),i+=f);}return i},UZIP.F._lenCodes=function(e,t){for(var r=e.length;2!=r&&0==e[r-1];)r-=2;for(var i=0;i<r;i+=2){var o=e[i+1],a=i+3<r?e[i+3]:-1,s=i+5<r?e[i+5]:-1,f=0==i?-1:e[i-1];if(0==o&&a==o&&s==o){for(var l=i+5;l+2<r&&e[l+2]==o;)l+=2;(c=Math.min(l+1-i>>>1,138))<11?t.push(17,c-3):t.push(18,c-11),i+=2*c-2;}else if(o==f&&a==o&&s==o){for(l=i+5;l+2<r&&e[l+2]==o;)l+=2;var c=Math.min(l+1-i>>>1,6);t.push(16,c-3),i+=2*c-2;}else t.push(o,0);}return r>>>1},UZIP.F._hufTree=function(e,t,r){var i=[],o=e.length,a=t.length,s=0;for(s=0;s<a;s+=2)t[s]=0,t[s+1]=0;for(s=0;s<o;s++)0!=e[s]&&i.push({lit:s,f:e[s]});var f=i.length,l=i.slice(0);if(0==f)return 0;if(1==f){var c=i[0].lit;l=0==c?1:0;return t[1+(c<<1)]=1,t[1+(l<<1)]=1,1}i.sort((function(e,t){return e.f-t.f}));var u=i[0],h=i[1],d=0,A=1,g=2;for(i[0]={lit:-1,f:u.f+h.f,l:u,r:h,d:0};A!=f-1;)u=d!=A&&(g==f||i[d].f<i[g].f)?i[d++]:i[g++],h=d!=A&&(g==f||i[d].f<i[g].f)?i[d++]:i[g++],i[A++]={lit:-1,f:u.f+h.f,l:u,r:h};var p=UZIP.F.setDepth(i[A-1],0);for(p>r&&(UZIP.F.restrictDepth(l,r,p),p=r),s=0;s<f;s++)t[1+(l[s].lit<<1)]=l[s].d;return p},UZIP.F.setDepth=function(e,t){return -1!=e.lit?(e.d=t,t):Math.max(UZIP.F.setDepth(e.l,t+1),UZIP.F.setDepth(e.r,t+1))},UZIP.F.restrictDepth=function(e,t,r){var i=0,o=1<<r-t,a=0;for(e.sort((function(e,t){return t.d==e.d?e.f-t.f:t.d-e.d})),i=0;i<e.length&&e[i].d>t;i++){var s=e[i].d;e[i].d=t,a+=o-(1<<r-s);}for(a>>>=r-t;a>0;){(s=e[i].d)<t?(e[i].d++,a-=1<<t-s-1):i++;}for(;i>=0;i--)e[i].d==t&&a<0&&(e[i].d--,a++);0!=a&&console.log("debt left");},UZIP.F._goodIndex=function(e,t){var r=0;return t[16|r]<=e&&(r|=16),t[8|r]<=e&&(r|=8),t[4|r]<=e&&(r|=4),t[2|r]<=e&&(r|=2),t[1|r]<=e&&(r|=1),r},UZIP.F._writeLit=function(e,t,r,i){return UZIP.F._putsF(r,i,t[e<<1]),i+t[1+(e<<1)]},UZIP.F.inflate=function(e,t){var r=Uint8Array;if(3==e[0]&&0==e[1])return t||new r(0);var i=UZIP.F,o=i._bitsF,a=i._bitsE,s=i._decodeTiny,f=i.makeCodes,l=i.codes2map,c=i._get17,u=i.U,h=null==t;h&&(t=new r(e.length>>>2<<3));for(var d,A,g=0,p=0,m=0,w=0,v=0,b=0,y=0,E=0,F=0;0==g;)if(g=o(e,F,1),p=o(e,F+1,2),F+=3,0!=p){if(h&&(t=UZIP.F._check(t,E+(1<<17))),1==p&&(d=u.flmap,A=u.fdmap,b=511,y=31),2==p){m=a(e,F,5)+257,w=a(e,F+5,5)+1,v=a(e,F+10,4)+4,F+=14;for(var _=0;_<38;_+=2)u.itree[_]=0,u.itree[_+1]=0;var B=1;for(_=0;_<v;_++){var U=a(e,F+3*_,3);u.itree[1+(u.ordr[_]<<1)]=U,U>B&&(B=U);}F+=3*v,f(u.itree,B),l(u.itree,B,u.imap),d=u.lmap,A=u.dmap,F=s(u.imap,(1<<B)-1,m+w,e,F,u.ttree);var C=i._copyOut(u.ttree,0,m,u.ltree);b=(1<<C)-1;var I=i._copyOut(u.ttree,m,w,u.dtree);y=(1<<I)-1,f(u.ltree,C),l(u.ltree,C,d),f(u.dtree,I),l(u.dtree,I,A);}for(;;){var Q=d[c(e,F)&b];F+=15&Q;var M=Q>>>4;if(M>>>8==0)t[E++]=M;else {if(256==M)break;var x=E+M-254;if(M>264){var S=u.ldef[M-257];x=E+(S>>>3)+a(e,F,7&S),F+=7&S;}var R=A[c(e,F)&y];F+=15&R;var T=R>>>4,O=u.ddef[T],P=(O>>>4)+o(e,F,15&O);for(F+=15&O,h&&(t=UZIP.F._check(t,E+(1<<17)));E<x;)t[E]=t[E++-P],t[E]=t[E++-P],t[E]=t[E++-P],t[E]=t[E++-P];E=x;}}}else {0!=(7&F)&&(F+=8-(7&F));var H=4+(F>>>3),L=e[H-4]|e[H-3]<<8;h&&(t=UZIP.F._check(t,E+L)),t.set(new r(e.buffer,e.byteOffset+H,L),E),F=H+L<<3,E+=L;}return t.length==E?t:t.slice(0,E)},UZIP.F._check=function(e,t){var r=e.length;if(t<=r)return e;var i=new Uint8Array(Math.max(r<<1,t));return i.set(e,0),i},UZIP.F._decodeTiny=function(e,t,r,i,o,a){for(var s=UZIP.F._bitsE,f=UZIP.F._get17,l=0;l<r;){var c=e[f(i,o)&t];o+=15&c;var u=c>>>4;if(u<=15)a[l]=u,l++;else {var h=0,d=0;16==u?(d=3+s(i,o,2),o+=2,h=a[l-1]):17==u?(d=3+s(i,o,3),o+=3):18==u&&(d=11+s(i,o,7),o+=7);for(var A=l+d;l<A;)a[l]=h,l++;}}return o},UZIP.F._copyOut=function(e,t,r,i){for(var o=0,a=0,s=i.length>>>1;a<r;){var f=e[a+t];i[a<<1]=0,i[1+(a<<1)]=f,f>o&&(o=f),a++;}for(;a<s;)i[a<<1]=0,i[1+(a<<1)]=0,a++;return o},UZIP.F.makeCodes=function(e,t){for(var r,i,o,a,s=UZIP.F.U,f=e.length,l=s.bl_count,c=0;c<=t;c++)l[c]=0;for(c=1;c<f;c+=2)l[e[c]]++;var u=s.next_code;for(r=0,l[0]=0,i=1;i<=t;i++)r=r+l[i-1]<<1,u[i]=r;for(o=0;o<f;o+=2)0!=(a=e[o+1])&&(e[o]=u[a],u[a]++);},UZIP.F.codes2map=function(e,t,r){for(var i=e.length,o=UZIP.F.U.rev15,a=0;a<i;a+=2)if(0!=e[a+1])for(var s=a>>1,f=e[a+1],l=s<<4|f,c=t-f,u=e[a]<<c,h=u+(1<<c);u!=h;){r[o[u]>>>15-t]=l,u++;}},UZIP.F.revCodes=function(e,t){for(var r=UZIP.F.U.rev15,i=15-t,o=0;o<e.length;o+=2){var a=e[o]<<t-e[o+1];e[o]=r[a]>>>i;}},UZIP.F._putsE=function(e,t,r){r<<=7&t;var i=t>>>3;e[i]|=r,e[i+1]|=r>>>8;},UZIP.F._putsF=function(e,t,r){r<<=7&t;var i=t>>>3;e[i]|=r,e[i+1]|=r>>>8,e[i+2]|=r>>>16;},UZIP.F._bitsE=function(e,t,r){return (e[t>>>3]|e[1+(t>>>3)]<<8)>>>(7&t)&(1<<r)-1},UZIP.F._bitsF=function(e,t,r){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16)>>>(7&t)&(1<<r)-1},UZIP.F._get17=function(e,t){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16)>>>(7&t)},UZIP.F._get25=function(e,t){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16|e[3+(t>>>3)]<<24)>>>(7&t)},UZIP.F.U=(r=Uint16Array,i=Uint32Array,{next_code:new r(16),bl_count:new r(16),ordr:[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],of0:[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,999,999,999],exb:[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0],ldef:new r(32),df0:[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,65535,65535],dxb:[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0],ddef:new i(32),flmap:new r(512),fltree:[],fdmap:new r(32),fdtree:[],lmap:new r(32768),ltree:[],ttree:[],dmap:new r(32768),dtree:[],imap:new r(512),itree:[],rev15:new r(32768),lhst:new i(286),dhst:new i(30),ihst:new i(19),lits:new i(15e3),strt:new r(65536),prev:new r(32768)}),function(){for(var e=UZIP.F.U,t=0;t<32768;t++){var r=t;r=(4278255360&(r=(4042322160&(r=(3435973836&(r=(2863311530&r)>>>1|(1431655765&r)<<1))>>>2|(858993459&r)<<2))>>>4|(252645135&r)<<4))>>>8|(16711935&r)<<8,e.rev15[t]=(r>>>16|r<<16)>>>17;}function pushV(e,t,r){for(;0!=t--;)e.push(0,r);}for(t=0;t<32;t++)e.ldef[t]=e.of0[t]<<3|e.exb[t],e.ddef[t]=e.df0[t]<<4|e.dxb[t];pushV(e.fltree,144,8),pushV(e.fltree,112,9),pushV(e.fltree,24,7),pushV(e.fltree,8,8),UZIP.F.makeCodes(e.fltree,9),UZIP.F.codes2map(e.fltree,9,e.flmap),UZIP.F.revCodes(e.fltree,9),pushV(e.fdtree,32,5),UZIP.F.makeCodes(e.fdtree,5),UZIP.F.codes2map(e.fdtree,5,e.fdmap),UZIP.F.revCodes(e.fdtree,5),pushV(e.itree,19,0),pushV(e.ltree,286,0),pushV(e.dtree,30,0),pushV(e.ttree,320,0);}();}();var UZIP=_mergeNamespaces$1({__proto__:null,default:e},[e]);const UPNG=function(){var e={nextZero(e,t){for(;0!=e[t];)t++;return t},readUshort:(e,t)=>e[t]<<8|e[t+1],writeUshort(e,t,r){e[t]=r>>8&255,e[t+1]=255&r;},readUint:(e,t)=>16777216*e[t]+(e[t+1]<<16|e[t+2]<<8|e[t+3]),writeUint(e,t,r){e[t]=r>>24&255,e[t+1]=r>>16&255,e[t+2]=r>>8&255,e[t+3]=255&r;},readASCII(e,t,r){let i="";for(let o=0;o<r;o++)i+=String.fromCharCode(e[t+o]);return i},writeASCII(e,t,r){for(let i=0;i<r.length;i++)e[t+i]=r.charCodeAt(i);},readBytes(e,t,r){const i=[];for(let o=0;o<r;o++)i.push(e[t+o]);return i},pad:e=>e.length<2?`0${e}`:e,readUTF8(t,r,i){let o,a="";for(let o=0;o<i;o++)a+=`%${e.pad(t[r+o].toString(16))}`;try{o=decodeURIComponent(a);}catch(o){return e.readASCII(t,r,i)}return o}};function decodeImage(t,r,i,o){const a=r*i,s=_getBPP(o),f=Math.ceil(r*s/8),l=new Uint8Array(4*a),c=new Uint32Array(l.buffer),{ctype:u}=o,{depth:h}=o,d=e.readUshort;if(6==u){const e=a<<2;if(8==h)for(var A=0;A<e;A+=4)l[A]=t[A],l[A+1]=t[A+1],l[A+2]=t[A+2],l[A+3]=t[A+3];if(16==h)for(A=0;A<e;A++)l[A]=t[A<<1];}else if(2==u){const e=o.tabs.tRNS;if(null==e){if(8==h)for(A=0;A<a;A++){var g=3*A;c[A]=255<<24|t[g+2]<<16|t[g+1]<<8|t[g];}if(16==h)for(A=0;A<a;A++){g=6*A;c[A]=255<<24|t[g+4]<<16|t[g+2]<<8|t[g];}}else {var p=e[0];const r=e[1],i=e[2];if(8==h)for(A=0;A<a;A++){var m=A<<2;g=3*A;c[A]=255<<24|t[g+2]<<16|t[g+1]<<8|t[g],t[g]==p&&t[g+1]==r&&t[g+2]==i&&(l[m+3]=0);}if(16==h)for(A=0;A<a;A++){m=A<<2,g=6*A;c[A]=255<<24|t[g+4]<<16|t[g+2]<<8|t[g],d(t,g)==p&&d(t,g+2)==r&&d(t,g+4)==i&&(l[m+3]=0);}}}else if(3==u){const e=o.tabs.PLTE,s=o.tabs.tRNS,c=s?s.length:0;if(1==h)for(var w=0;w<i;w++){var v=w*f,b=w*r;for(A=0;A<r;A++){m=b+A<<2;var y=3*(E=t[v+(A>>3)]>>7-((7&A)<<0)&1);l[m]=e[y],l[m+1]=e[y+1],l[m+2]=e[y+2],l[m+3]=E<c?s[E]:255;}}if(2==h)for(w=0;w<i;w++)for(v=w*f,b=w*r,A=0;A<r;A++){m=b+A<<2,y=3*(E=t[v+(A>>2)]>>6-((3&A)<<1)&3);l[m]=e[y],l[m+1]=e[y+1],l[m+2]=e[y+2],l[m+3]=E<c?s[E]:255;}if(4==h)for(w=0;w<i;w++)for(v=w*f,b=w*r,A=0;A<r;A++){m=b+A<<2,y=3*(E=t[v+(A>>1)]>>4-((1&A)<<2)&15);l[m]=e[y],l[m+1]=e[y+1],l[m+2]=e[y+2],l[m+3]=E<c?s[E]:255;}if(8==h)for(A=0;A<a;A++){var E;m=A<<2,y=3*(E=t[A]);l[m]=e[y],l[m+1]=e[y+1],l[m+2]=e[y+2],l[m+3]=E<c?s[E]:255;}}else if(4==u){if(8==h)for(A=0;A<a;A++){m=A<<2;var F=t[_=A<<1];l[m]=F,l[m+1]=F,l[m+2]=F,l[m+3]=t[_+1];}if(16==h)for(A=0;A<a;A++){var _;m=A<<2,F=t[_=A<<2];l[m]=F,l[m+1]=F,l[m+2]=F,l[m+3]=t[_+2];}}else if(0==u)for(p=o.tabs.tRNS?o.tabs.tRNS:-1,w=0;w<i;w++){const e=w*f,i=w*r;if(1==h)for(var B=0;B<r;B++){var U=(F=255*(t[e+(B>>>3)]>>>7-(7&B)&1))==255*p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}else if(2==h)for(B=0;B<r;B++){U=(F=85*(t[e+(B>>>2)]>>>6-((3&B)<<1)&3))==85*p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}else if(4==h)for(B=0;B<r;B++){U=(F=17*(t[e+(B>>>1)]>>>4-((1&B)<<2)&15))==17*p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}else if(8==h)for(B=0;B<r;B++){U=(F=t[e+B])==p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}else if(16==h)for(B=0;B<r;B++){F=t[e+(B<<1)],U=d(t,e+(B<<1))==p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}}return l}function _decompress(e,r,i,o){const a=_getBPP(e),s=Math.ceil(i*a/8),f=new Uint8Array((s+1+e.interlace)*o);return r=e.tabs.CgBI?t(r,f):_inflate(r,f),0==e.interlace?r=_filterZero(r,e,0,i,o):1==e.interlace&&(r=function _readInterlace(e,t){const r=t.width,i=t.height,o=_getBPP(t),a=o>>3,s=Math.ceil(r*o/8),f=new Uint8Array(i*s);let l=0;const c=[0,0,4,0,2,0,1],u=[0,4,0,2,0,1,0],h=[8,8,8,4,4,2,2],d=[8,8,4,4,2,2,1];let A=0;for(;A<7;){const p=h[A],m=d[A];let w=0,v=0,b=c[A];for(;b<i;)b+=p,v++;let y=u[A];for(;y<r;)y+=m,w++;const E=Math.ceil(w*o/8);_filterZero(e,t,l,w,v);let F=0,_=c[A];for(;_<i;){let t=u[A],i=l+F*E<<3;for(;t<r;){var g;if(1==o)g=(g=e[i>>3])>>7-(7&i)&1,f[_*s+(t>>3)]|=g<<7-((7&t)<<0);if(2==o)g=(g=e[i>>3])>>6-(7&i)&3,f[_*s+(t>>2)]|=g<<6-((3&t)<<1);if(4==o)g=(g=e[i>>3])>>4-(7&i)&15,f[_*s+(t>>1)]|=g<<4-((1&t)<<2);if(o>=8){const r=_*s+t*a;for(let t=0;t<a;t++)f[r+t]=e[(i>>3)+t];}i+=o,t+=m;}F++,_+=p;}w*v!=0&&(l+=v*(1+E)),A+=1;}return f}(r,e)),r}function _inflate(e,r){return t(new Uint8Array(e.buffer,2,e.length-6),r)}var t=function(){const e={H:{}};return e.H.N=function(t,r){const i=Uint8Array;let o,a,s=0,f=0,l=0,c=0,u=0,h=0,d=0,A=0,g=0;if(3==t[0]&&0==t[1])return r||new i(0);const p=e.H,m=p.b,w=p.e,v=p.R,b=p.n,y=p.A,E=p.Z,F=p.m,_=null==r;for(_&&(r=new i(t.length>>>2<<5));0==s;)if(s=m(t,g,1),f=m(t,g+1,2),g+=3,0!=f){if(_&&(r=e.H.W(r,A+(1<<17))),1==f&&(o=F.J,a=F.h,h=511,d=31),2==f){l=w(t,g,5)+257,c=w(t,g+5,5)+1,u=w(t,g+10,4)+4,g+=14;let e=1;for(var B=0;B<38;B+=2)F.Q[B]=0,F.Q[B+1]=0;for(B=0;B<u;B++){const r=w(t,g+3*B,3);F.Q[1+(F.X[B]<<1)]=r,r>e&&(e=r);}g+=3*u,b(F.Q,e),y(F.Q,e,F.u),o=F.w,a=F.d,g=v(F.u,(1<<e)-1,l+c,t,g,F.v);const r=p.V(F.v,0,l,F.C);h=(1<<r)-1;const i=p.V(F.v,l,c,F.D);d=(1<<i)-1,b(F.C,r),y(F.C,r,o),b(F.D,i),y(F.D,i,a);}for(;;){const e=o[E(t,g)&h];g+=15&e;const i=e>>>4;if(i>>>8==0)r[A++]=i;else {if(256==i)break;{let e=A+i-254;if(i>264){const r=F.q[i-257];e=A+(r>>>3)+w(t,g,7&r),g+=7&r;}const o=a[E(t,g)&d];g+=15&o;const s=o>>>4,f=F.c[s],l=(f>>>4)+m(t,g,15&f);for(g+=15&f;A<e;)r[A]=r[A++-l],r[A]=r[A++-l],r[A]=r[A++-l],r[A]=r[A++-l];A=e;}}}}else {0!=(7&g)&&(g+=8-(7&g));const o=4+(g>>>3),a=t[o-4]|t[o-3]<<8;_&&(r=e.H.W(r,A+a)),r.set(new i(t.buffer,t.byteOffset+o,a),A),g=o+a<<3,A+=a;}return r.length==A?r:r.slice(0,A)},e.H.W=function(e,t){const r=e.length;if(t<=r)return e;const i=new Uint8Array(r<<1);return i.set(e,0),i},e.H.R=function(t,r,i,o,a,s){const f=e.H.e,l=e.H.Z;let c=0;for(;c<i;){const e=t[l(o,a)&r];a+=15&e;const i=e>>>4;if(i<=15)s[c]=i,c++;else {let e=0,t=0;16==i?(t=3+f(o,a,2),a+=2,e=s[c-1]):17==i?(t=3+f(o,a,3),a+=3):18==i&&(t=11+f(o,a,7),a+=7);const r=c+t;for(;c<r;)s[c]=e,c++;}}return a},e.H.V=function(e,t,r,i){let o=0,a=0;const s=i.length>>>1;for(;a<r;){const r=e[a+t];i[a<<1]=0,i[1+(a<<1)]=r,r>o&&(o=r),a++;}for(;a<s;)i[a<<1]=0,i[1+(a<<1)]=0,a++;return o},e.H.n=function(t,r){const i=e.H.m,o=t.length;let a,s,f;let l;const c=i.j;for(var u=0;u<=r;u++)c[u]=0;for(u=1;u<o;u+=2)c[t[u]]++;const h=i.K;for(a=0,c[0]=0,s=1;s<=r;s++)a=a+c[s-1]<<1,h[s]=a;for(f=0;f<o;f+=2)l=t[f+1],0!=l&&(t[f]=h[l],h[l]++);},e.H.A=function(t,r,i){const o=t.length,a=e.H.m.r;for(let e=0;e<o;e+=2)if(0!=t[e+1]){const o=e>>1,s=t[e+1],f=o<<4|s,l=r-s;let c=t[e]<<l;const u=c+(1<<l);for(;c!=u;){i[a[c]>>>15-r]=f,c++;}}},e.H.l=function(t,r){const i=e.H.m.r,o=15-r;for(let e=0;e<t.length;e+=2){const a=t[e]<<r-t[e+1];t[e]=i[a]>>>o;}},e.H.M=function(e,t,r){r<<=7&t;const i=t>>>3;e[i]|=r,e[i+1]|=r>>>8;},e.H.I=function(e,t,r){r<<=7&t;const i=t>>>3;e[i]|=r,e[i+1]|=r>>>8,e[i+2]|=r>>>16;},e.H.e=function(e,t,r){return (e[t>>>3]|e[1+(t>>>3)]<<8)>>>(7&t)&(1<<r)-1},e.H.b=function(e,t,r){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16)>>>(7&t)&(1<<r)-1},e.H.Z=function(e,t){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16)>>>(7&t)},e.H.i=function(e,t){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16|e[3+(t>>>3)]<<24)>>>(7&t)},e.H.m=function(){const e=Uint16Array,t=Uint32Array;return {K:new e(16),j:new e(16),X:[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],S:[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,999,999,999],T:[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0],q:new e(32),p:[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,65535,65535],z:[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0],c:new t(32),J:new e(512),_:[],h:new e(32),$:[],w:new e(32768),C:[],v:[],d:new e(32768),D:[],u:new e(512),Q:[],r:new e(32768),s:new t(286),Y:new t(30),a:new t(19),t:new t(15e3),k:new e(65536),g:new e(32768)}}(),function(){const t=e.H.m;for(var r=0;r<32768;r++){let e=r;e=(2863311530&e)>>>1|(1431655765&e)<<1,e=(3435973836&e)>>>2|(858993459&e)<<2,e=(4042322160&e)>>>4|(252645135&e)<<4,e=(4278255360&e)>>>8|(16711935&e)<<8,t.r[r]=(e>>>16|e<<16)>>>17;}function n(e,t,r){for(;0!=t--;)e.push(0,r);}for(r=0;r<32;r++)t.q[r]=t.S[r]<<3|t.T[r],t.c[r]=t.p[r]<<4|t.z[r];n(t._,144,8),n(t._,112,9),n(t._,24,7),n(t._,8,8),e.H.n(t._,9),e.H.A(t._,9,t.J),e.H.l(t._,9),n(t.$,32,5),e.H.n(t.$,5),e.H.A(t.$,5,t.h),e.H.l(t.$,5),n(t.Q,19,0),n(t.C,286,0),n(t.D,30,0),n(t.v,320,0);}(),e.H.N}();function _getBPP(e){return [1,null,3,1,2,null,4][e.ctype]*e.depth}function _filterZero(e,t,r,i,o){let a=_getBPP(t);const s=Math.ceil(i*a/8);let f,l;a=Math.ceil(a/8);let c=e[r],u=0;if(c>1&&(e[r]=[0,0,1][c-2]),3==c)for(u=a;u<s;u++)e[u+1]=e[u+1]+(e[u+1-a]>>>1)&255;for(let t=0;t<o;t++)if(f=r+t*s,l=f+t+1,c=e[l-1],u=0,0==c)for(;u<s;u++)e[f+u]=e[l+u];else if(1==c){for(;u<a;u++)e[f+u]=e[l+u];for(;u<s;u++)e[f+u]=e[l+u]+e[f+u-a];}else if(2==c)for(;u<s;u++)e[f+u]=e[l+u]+e[f+u-s];else if(3==c){for(;u<a;u++)e[f+u]=e[l+u]+(e[f+u-s]>>>1);for(;u<s;u++)e[f+u]=e[l+u]+(e[f+u-s]+e[f+u-a]>>>1);}else {for(;u<a;u++)e[f+u]=e[l+u]+_paeth(0,e[f+u-s],0);for(;u<s;u++)e[f+u]=e[l+u]+_paeth(e[f+u-a],e[f+u-s],e[f+u-a-s]);}return e}function _paeth(e,t,r){const i=e+t-r,o=i-e,a=i-t,s=i-r;return o*o<=a*a&&o*o<=s*s?e:a*a<=s*s?t:r}function _IHDR(t,r,i){i.width=e.readUint(t,r),r+=4,i.height=e.readUint(t,r),r+=4,i.depth=t[r],r++,i.ctype=t[r],r++,i.compress=t[r],r++,i.filter=t[r],r++,i.interlace=t[r],r++;}function _copyTile(e,t,r,i,o,a,s,f,l){const c=Math.min(t,o),u=Math.min(r,a);let h=0,d=0;for(let r=0;r<u;r++)for(let a=0;a<c;a++)if(s>=0&&f>=0?(h=r*t+a<<2,d=(f+r)*o+s+a<<2):(h=(-f+r)*t-s+a<<2,d=r*o+a<<2),0==l)i[d]=e[h],i[d+1]=e[h+1],i[d+2]=e[h+2],i[d+3]=e[h+3];else if(1==l){var A=e[h+3]*(1/255),g=e[h]*A,p=e[h+1]*A,m=e[h+2]*A,w=i[d+3]*(1/255),v=i[d]*w,b=i[d+1]*w,y=i[d+2]*w;const t=1-A,r=A+w*t,o=0==r?0:1/r;i[d+3]=255*r,i[d+0]=(g+v*t)*o,i[d+1]=(p+b*t)*o,i[d+2]=(m+y*t)*o;}else if(2==l){A=e[h+3],g=e[h],p=e[h+1],m=e[h+2],w=i[d+3],v=i[d],b=i[d+1],y=i[d+2];A==w&&g==v&&p==b&&m==y?(i[d]=0,i[d+1]=0,i[d+2]=0,i[d+3]=0):(i[d]=g,i[d+1]=p,i[d+2]=m,i[d+3]=A);}else if(3==l){A=e[h+3],g=e[h],p=e[h+1],m=e[h+2],w=i[d+3],v=i[d],b=i[d+1],y=i[d+2];if(A==w&&g==v&&p==b&&m==y)continue;if(A<220&&w>20)return false}return true}return {decode:function decode(r){const i=new Uint8Array(r);let o=8;const a=e,s=a.readUshort,f=a.readUint,l={tabs:{},frames:[]},c=new Uint8Array(i.length);let u,h=0,d=0;const A=[137,80,78,71,13,10,26,10];for(var g=0;g<8;g++)if(i[g]!=A[g])throw "The input is not a PNG file!";for(;o<i.length;){const e=a.readUint(i,o);o+=4;const r=a.readASCII(i,o,4);if(o+=4,"IHDR"==r)_IHDR(i,o,l);else if("iCCP"==r){for(var p=o;0!=i[p];)p++;a.readASCII(i,o,p-o);const s=i.slice(p+2,o+e);let f=null;try{f=_inflate(s);}catch(e){f=t(s);}l.tabs[r]=f;}else if("CgBI"==r)l.tabs[r]=i.slice(o,o+4);else if("IDAT"==r){for(g=0;g<e;g++)c[h+g]=i[o+g];h+=e;}else if("acTL"==r)l.tabs[r]={num_frames:f(i,o),num_plays:f(i,o+4)},u=new Uint8Array(i.length);else if("fcTL"==r){if(0!=d)(E=l.frames[l.frames.length-1]).data=_decompress(l,u.slice(0,d),E.rect.width,E.rect.height),d=0;const e={x:f(i,o+12),y:f(i,o+16),width:f(i,o+4),height:f(i,o+8)};let t=s(i,o+22);t=s(i,o+20)/(0==t?100:t);const r={rect:e,delay:Math.round(1e3*t),dispose:i[o+24],blend:i[o+25]};l.frames.push(r);}else if("fdAT"==r){for(g=0;g<e-4;g++)u[d+g]=i[o+g+4];d+=e-4;}else if("pHYs"==r)l.tabs[r]=[a.readUint(i,o),a.readUint(i,o+4),i[o+8]];else if("cHRM"==r){l.tabs[r]=[];for(g=0;g<8;g++)l.tabs[r].push(a.readUint(i,o+4*g));}else if("tEXt"==r||"zTXt"==r){null==l.tabs[r]&&(l.tabs[r]={});var m=a.nextZero(i,o),w=a.readASCII(i,o,m-o),v=o+e-m-1;if("tEXt"==r)y=a.readASCII(i,m+1,v);else {var b=_inflate(i.slice(m+2,m+2+v));y=a.readUTF8(b,0,b.length);}l.tabs[r][w]=y;}else if("iTXt"==r){null==l.tabs[r]&&(l.tabs[r]={});m=0,p=o;m=a.nextZero(i,p);w=a.readASCII(i,p,m-p);const t=i[p=m+1];var y;p+=2,m=a.nextZero(i,p),a.readASCII(i,p,m-p),p=m+1,m=a.nextZero(i,p),a.readUTF8(i,p,m-p);v=e-((p=m+1)-o);if(0==t)y=a.readUTF8(i,p,v);else {b=_inflate(i.slice(p,p+v));y=a.readUTF8(b,0,b.length);}l.tabs[r][w]=y;}else if("PLTE"==r)l.tabs[r]=a.readBytes(i,o,e);else if("hIST"==r){const e=l.tabs.PLTE.length/3;l.tabs[r]=[];for(g=0;g<e;g++)l.tabs[r].push(s(i,o+2*g));}else if("tRNS"==r)3==l.ctype?l.tabs[r]=a.readBytes(i,o,e):0==l.ctype?l.tabs[r]=s(i,o):2==l.ctype&&(l.tabs[r]=[s(i,o),s(i,o+2),s(i,o+4)]);else if("gAMA"==r)l.tabs[r]=a.readUint(i,o)/1e5;else if("sRGB"==r)l.tabs[r]=i[o];else if("bKGD"==r)0==l.ctype||4==l.ctype?l.tabs[r]=[s(i,o)]:2==l.ctype||6==l.ctype?l.tabs[r]=[s(i,o),s(i,o+2),s(i,o+4)]:3==l.ctype&&(l.tabs[r]=i[o]);else if("IEND"==r)break;o+=e,a.readUint(i,o),o+=4;}var E;return 0!=d&&((E=l.frames[l.frames.length-1]).data=_decompress(l,u.slice(0,d),E.rect.width,E.rect.height)),l.data=_decompress(l,c,l.width,l.height),delete l.compress,delete l.interlace,delete l.filter,l},toRGBA8:function toRGBA8(e){const t=e.width,r=e.height;if(null==e.tabs.acTL)return [decodeImage(e.data,t,r,e).buffer];const i=[];null==e.frames[0].data&&(e.frames[0].data=e.data);const o=t*r*4,a=new Uint8Array(o),s=new Uint8Array(o),f=new Uint8Array(o);for(let c=0;c<e.frames.length;c++){const u=e.frames[c],h=u.rect.x,d=u.rect.y,A=u.rect.width,g=u.rect.height,p=decodeImage(u.data,A,g,e);if(0!=c)for(var l=0;l<o;l++)f[l]=a[l];if(0==u.blend?_copyTile(p,A,g,a,t,r,h,d,0):1==u.blend&&_copyTile(p,A,g,a,t,r,h,d,1),i.push(a.buffer.slice(0)),0==u.dispose);else if(1==u.dispose)_copyTile(s,A,g,a,t,r,h,d,0);else if(2==u.dispose)for(l=0;l<o;l++)a[l]=f[l];}return i},_paeth:_paeth,_copyTile:_copyTile,_bin:e}}();!function(){const{_copyTile:e}=UPNG,{_bin:t}=UPNG,r=UPNG._paeth;var i={table:function(){const e=new Uint32Array(256);for(let t=0;t<256;t++){let r=t;for(let e=0;e<8;e++)1&r?r=3988292384^r>>>1:r>>>=1;e[t]=r;}return e}(),update(e,t,r,o){for(let a=0;a<o;a++)e=i.table[255&(e^t[r+a])]^e>>>8;return e},crc:(e,t,r)=>4294967295^i.update(4294967295,e,t,r)};function addErr(e,t,r,i){t[r]+=e[0]*i>>4,t[r+1]+=e[1]*i>>4,t[r+2]+=e[2]*i>>4,t[r+3]+=e[3]*i>>4;}function N(e){return Math.max(0,Math.min(255,e))}function D(e,t){const r=e[0]-t[0],i=e[1]-t[1],o=e[2]-t[2],a=e[3]-t[3];return r*r+i*i+o*o+a*a}function dither(e,t,r,i,o,a,s){null==s&&(s=1);const f=i.length,l=[];for(var c=0;c<f;c++){const e=i[c];l.push([e>>>0&255,e>>>8&255,e>>>16&255,e>>>24&255]);}for(c=0;c<f;c++){let e=4294967295;for(var u=0,h=0;h<f;h++){var d=D(l[c],l[h]);h!=c&&d<e&&(e=d,u=h);}}const A=new Uint32Array(o.buffer),g=new Int16Array(t*r*4),p=[0,8,2,10,12,4,14,6,3,11,1,9,15,7,13,5];for(c=0;c<p.length;c++)p[c]=255*((p[c]+.5)/16-.5);for(let o=0;o<r;o++)for(let w=0;w<t;w++){var m;c=4*(o*t+w);if(2!=s)m=[N(e[c]+g[c]),N(e[c+1]+g[c+1]),N(e[c+2]+g[c+2]),N(e[c+3]+g[c+3])];else {d=p[4*(3&o)+(3&w)];m=[N(e[c]+d),N(e[c+1]+d),N(e[c+2]+d),N(e[c+3]+d)];}u=0;let v=16777215;for(h=0;h<f;h++){const e=D(m,l[h]);e<v&&(v=e,u=h);}const b=l[u],y=[m[0]-b[0],m[1]-b[1],m[2]-b[2],m[3]-b[3]];1==s&&(w!=t-1&&addErr(y,g,c+4,7),o!=r-1&&(0!=w&&addErr(y,g,c+4*t-4,3),addErr(y,g,c+4*t,5),w!=t-1&&addErr(y,g,c+4*t+4,1))),a[c>>2]=u,A[c>>2]=i[u];}}function _main(e,r,o,a,s){null==s&&(s={});const{crc:f}=i,l=t.writeUint,c=t.writeUshort,u=t.writeASCII;let h=8;const d=e.frames.length>1;let A,g=false,p=33+(d?20:0);if(null!=s.sRGB&&(p+=13),null!=s.pHYs&&(p+=21),null!=s.iCCP&&(A=pako.deflate(s.iCCP),p+=21+A.length+4),3==e.ctype){for(var m=e.plte.length,w=0;w<m;w++)e.plte[w]>>>24!=255&&(g=true);p+=8+3*m+4+(g?8+1*m+4:0);}for(var v=0;v<e.frames.length;v++){d&&(p+=38),p+=(F=e.frames[v]).cimg.length+12,0!=v&&(p+=4);}p+=12;const b=new Uint8Array(p),y=[137,80,78,71,13,10,26,10];for(w=0;w<8;w++)b[w]=y[w];if(l(b,h,13),h+=4,u(b,h,"IHDR"),h+=4,l(b,h,r),h+=4,l(b,h,o),h+=4,b[h]=e.depth,h++,b[h]=e.ctype,h++,b[h]=0,h++,b[h]=0,h++,b[h]=0,h++,l(b,h,f(b,h-17,17)),h+=4,null!=s.sRGB&&(l(b,h,1),h+=4,u(b,h,"sRGB"),h+=4,b[h]=s.sRGB,h++,l(b,h,f(b,h-5,5)),h+=4),null!=s.iCCP){const e=13+A.length;l(b,h,e),h+=4,u(b,h,"iCCP"),h+=4,u(b,h,"ICC profile"),h+=11,h+=2,b.set(A,h),h+=A.length,l(b,h,f(b,h-(e+4),e+4)),h+=4;}if(null!=s.pHYs&&(l(b,h,9),h+=4,u(b,h,"pHYs"),h+=4,l(b,h,s.pHYs[0]),h+=4,l(b,h,s.pHYs[1]),h+=4,b[h]=s.pHYs[2],h++,l(b,h,f(b,h-13,13)),h+=4),d&&(l(b,h,8),h+=4,u(b,h,"acTL"),h+=4,l(b,h,e.frames.length),h+=4,l(b,h,null!=s.loop?s.loop:0),h+=4,l(b,h,f(b,h-12,12)),h+=4),3==e.ctype){l(b,h,3*(m=e.plte.length)),h+=4,u(b,h,"PLTE"),h+=4;for(w=0;w<m;w++){const t=3*w,r=e.plte[w],i=255&r,o=r>>>8&255,a=r>>>16&255;b[h+t+0]=i,b[h+t+1]=o,b[h+t+2]=a;}if(h+=3*m,l(b,h,f(b,h-3*m-4,3*m+4)),h+=4,g){l(b,h,m),h+=4,u(b,h,"tRNS"),h+=4;for(w=0;w<m;w++)b[h+w]=e.plte[w]>>>24&255;h+=m,l(b,h,f(b,h-m-4,m+4)),h+=4;}}let E=0;for(v=0;v<e.frames.length;v++){var F=e.frames[v];d&&(l(b,h,26),h+=4,u(b,h,"fcTL"),h+=4,l(b,h,E++),h+=4,l(b,h,F.rect.width),h+=4,l(b,h,F.rect.height),h+=4,l(b,h,F.rect.x),h+=4,l(b,h,F.rect.y),h+=4,c(b,h,a[v]),h+=2,c(b,h,1e3),h+=2,b[h]=F.dispose,h++,b[h]=F.blend,h++,l(b,h,f(b,h-30,30)),h+=4);const t=F.cimg;l(b,h,(m=t.length)+(0==v?0:4)),h+=4;const r=h;u(b,h,0==v?"IDAT":"fdAT"),h+=4,0!=v&&(l(b,h,E++),h+=4),b.set(t,h),h+=m,l(b,h,f(b,r,h-r)),h+=4;}return l(b,h,0),h+=4,u(b,h,"IEND"),h+=4,l(b,h,f(b,h-4,4)),h+=4,b.buffer}function compressPNG(e,t,r){for(let i=0;i<e.frames.length;i++){const o=e.frames[i];const a=o.rect.height,s=new Uint8Array(a*o.bpl+a);o.cimg=_filterZero(o.img,a,o.bpp,o.bpl,s,t,r);}}function compress(t,r,i,o,a){const s=a[0],f=a[1],l=a[2],c=a[3],u=a[4],h=a[5];let d=6,A=8,g=255;for(var p=0;p<t.length;p++){const e=new Uint8Array(t[p]);for(var m=e.length,w=0;w<m;w+=4)g&=e[w+3];}const v=255!=g,b=function framize(t,r,i,o,a,s){const f=[];for(var l=0;l<t.length;l++){const h=new Uint8Array(t[l]),A=new Uint32Array(h.buffer);var c;let g=0,p=0,m=r,w=i,v=o?1:0;if(0!=l){const b=s||o||1==l||0!=f[l-2].dispose?1:2;let y=0,E=1e9;for(let e=0;e<b;e++){var u=new Uint8Array(t[l-1-e]);const o=new Uint32Array(t[l-1-e]);let s=r,f=i,c=-1,h=-1;for(let e=0;e<i;e++)for(let t=0;t<r;t++){A[d=e*r+t]!=o[d]&&(t<s&&(s=t),t>c&&(c=t),e<f&&(f=e),e>h&&(h=e));} -1==c&&(s=f=c=h=0),a&&(1==(1&s)&&s--,1==(1&f)&&f--);const v=(c-s+1)*(h-f+1);v<E&&(E=v,y=e,g=s,p=f,m=c-s+1,w=h-f+1);}u=new Uint8Array(t[l-1-y]);1==y&&(f[l-1].dispose=2),c=new Uint8Array(m*w*4),e(u,r,i,c,m,w,-g,-p,0),v=e(h,r,i,c,m,w,-g,-p,3)?1:0,1==v?_prepareDiff(h,r,i,c,{x:g,y:p,width:m,height:w}):e(h,r,i,c,m,w,-g,-p,0);}else c=h.slice(0);f.push({rect:{x:g,y:p,width:m,height:w},img:c,blend:v,dispose:0});}if(o)for(l=0;l<f.length;l++){if(1==(A=f[l]).blend)continue;const e=A.rect,o=f[l-1].rect,s=Math.min(e.x,o.x),c=Math.min(e.y,o.y),u={x:s,y:c,width:Math.max(e.x+e.width,o.x+o.width)-s,height:Math.max(e.y+e.height,o.y+o.height)-c};f[l-1].dispose=1,l-1!=0&&_updateFrame(t,r,i,f,l-1,u,a),_updateFrame(t,r,i,f,l,u,a);}let h=0;if(1!=t.length)for(var d=0;d<f.length;d++){var A;h+=(A=f[d]).rect.width*A.rect.height;}return f}(t,r,i,s,f,l),y={},E=[],F=[];if(0!=o){const e=[];for(w=0;w<b.length;w++)e.push(b[w].img.buffer);const t=function concatRGBA(e){let t=0;for(var r=0;r<e.length;r++)t+=e[r].byteLength;const i=new Uint8Array(t);let o=0;for(r=0;r<e.length;r++){const t=new Uint8Array(e[r]),a=t.length;for(let e=0;e<a;e+=4){let r=t[e],a=t[e+1],s=t[e+2];const f=t[e+3];0==f&&(r=a=s=0),i[o+e]=r,i[o+e+1]=a,i[o+e+2]=s,i[o+e+3]=f;}o+=a;}return i.buffer}(e),r=quantize(t,o);for(w=0;w<r.plte.length;w++)E.push(r.plte[w].est.rgba);let i=0;for(w=0;w<b.length;w++){const e=(B=b[w]).img.length;var _=new Uint8Array(r.inds.buffer,i>>2,e>>2);F.push(_);const t=new Uint8Array(r.abuf,i,e);h&&dither(B.img,B.rect.width,B.rect.height,E,t,_),B.img.set(t),i+=e;}}else for(p=0;p<b.length;p++){var B=b[p];const e=new Uint32Array(B.img.buffer);var U=B.rect.width;m=e.length,_=new Uint8Array(m);F.push(_);for(w=0;w<m;w++){const t=e[w];if(0!=w&&t==e[w-1])_[w]=_[w-1];else if(w>U&&t==e[w-U])_[w]=_[w-U];else {let e=y[t];if(null==e&&(y[t]=e=E.length,E.push(t),E.length>=300))break;_[w]=e;}}}const C=E.length;C<=256&&0==u&&(A=C<=2?1:C<=4?2:C<=16?4:8,A=Math.max(A,c));for(p=0;p<b.length;p++){(B=b[p]).rect.x;U=B.rect.width;const e=B.rect.height;let t=B.img;let r=4*U,i=4;if(C<=256&&0==u){r=Math.ceil(A*U/8);var I=new Uint8Array(r*e);const o=F[p];for(let t=0;t<e;t++){w=t*r;const e=t*U;if(8==A)for(var Q=0;Q<U;Q++)I[w+Q]=o[e+Q];else if(4==A)for(Q=0;Q<U;Q++)I[w+(Q>>1)]|=o[e+Q]<<4-4*(1&Q);else if(2==A)for(Q=0;Q<U;Q++)I[w+(Q>>2)]|=o[e+Q]<<6-2*(3&Q);else if(1==A)for(Q=0;Q<U;Q++)I[w+(Q>>3)]|=o[e+Q]<<7-1*(7&Q);}t=I,d=3,i=1;}else if(0==v&&1==b.length){I=new Uint8Array(U*e*3);const o=U*e;for(w=0;w<o;w++){const e=3*w,r=4*w;I[e]=t[r],I[e+1]=t[r+1],I[e+2]=t[r+2];}t=I,d=2,i=3,r=3*U;}B.img=t,B.bpl=r,B.bpp=i;}return {ctype:d,depth:A,plte:E,frames:b}}function _updateFrame(t,r,i,o,a,s,f){const l=Uint8Array,c=Uint32Array,u=new l(t[a-1]),h=new c(t[a-1]),d=a+1<t.length?new l(t[a+1]):null,A=new l(t[a]),g=new c(A.buffer);let p=r,m=i,w=-1,v=-1;for(let e=0;e<s.height;e++)for(let t=0;t<s.width;t++){const i=s.x+t,f=s.y+e,l=f*r+i,c=g[l];0==c||0==o[a-1].dispose&&h[l]==c&&(null==d||0!=d[4*l+3])||(i<p&&(p=i),i>w&&(w=i),f<m&&(m=f),f>v&&(v=f));} -1==w&&(p=m=w=v=0),f&&(1==(1&p)&&p--,1==(1&m)&&m--),s={x:p,y:m,width:w-p+1,height:v-m+1};const b=o[a];b.rect=s,b.blend=1,b.img=new Uint8Array(s.width*s.height*4),0==o[a-1].dispose?(e(u,r,i,b.img,s.width,s.height,-s.x,-s.y,0),_prepareDiff(A,r,i,b.img,s)):e(A,r,i,b.img,s.width,s.height,-s.x,-s.y,0);}function _prepareDiff(t,r,i,o,a){e(t,r,i,o,a.width,a.height,-a.x,-a.y,2);}function _filterZero(e,t,r,i,o,a,s){const f=[];let l,c=[0,1,2,3,4];-1!=a?c=[a]:(t*i>5e5||1==r)&&(c=[0]),s&&(l={level:0});const u=UZIP;for(var h=0;h<c.length;h++){for(let a=0;a<t;a++)_filterLine(o,e,a,i,r,c[h]);f.push(u.deflate(o,l));}let d,A=1e9;for(h=0;h<f.length;h++)f[h].length<A&&(d=h,A=f[h].length);return f[d]}function _filterLine(e,t,i,o,a,s){const f=i*o;let l=f+i;if(e[l]=s,l++,0==s)if(o<500)for(var c=0;c<o;c++)e[l+c]=t[f+c];else e.set(new Uint8Array(t.buffer,f,o),l);else if(1==s){for(c=0;c<a;c++)e[l+c]=t[f+c];for(c=a;c<o;c++)e[l+c]=t[f+c]-t[f+c-a]+256&255;}else if(0==i){for(c=0;c<a;c++)e[l+c]=t[f+c];if(2==s)for(c=a;c<o;c++)e[l+c]=t[f+c];if(3==s)for(c=a;c<o;c++)e[l+c]=t[f+c]-(t[f+c-a]>>1)+256&255;if(4==s)for(c=a;c<o;c++)e[l+c]=t[f+c]-r(t[f+c-a],0,0)+256&255;}else {if(2==s)for(c=0;c<o;c++)e[l+c]=t[f+c]+256-t[f+c-o]&255;if(3==s){for(c=0;c<a;c++)e[l+c]=t[f+c]+256-(t[f+c-o]>>1)&255;for(c=a;c<o;c++)e[l+c]=t[f+c]+256-(t[f+c-o]+t[f+c-a]>>1)&255;}if(4==s){for(c=0;c<a;c++)e[l+c]=t[f+c]+256-r(0,t[f+c-o],0)&255;for(c=a;c<o;c++)e[l+c]=t[f+c]+256-r(t[f+c-a],t[f+c-o],t[f+c-a-o])&255;}}}function quantize(e,t){const r=new Uint8Array(e),i=r.slice(0),o=new Uint32Array(i.buffer),a=getKDtree(i,t),s=a[0],f=a[1],l=r.length,c=new Uint8Array(l>>2);let u;if(r.length<2e7)for(var h=0;h<l;h+=4){u=getNearest(s,d=r[h]*(1/255),A=r[h+1]*(1/255),g=r[h+2]*(1/255),p=r[h+3]*(1/255)),c[h>>2]=u.ind,o[h>>2]=u.est.rgba;}else for(h=0;h<l;h+=4){var d=r[h]*(1/255),A=r[h+1]*(1/255),g=r[h+2]*(1/255),p=r[h+3]*(1/255);for(u=s;u.left;)u=planeDst(u.est,d,A,g,p)<=0?u.left:u.right;c[h>>2]=u.ind,o[h>>2]=u.est.rgba;}return {abuf:i.buffer,inds:c,plte:f}}function getKDtree(e,t,r){null==r&&(r=1e-4);const i=new Uint32Array(e.buffer),o={i0:0,i1:e.length,bst:null,est:null,tdst:0,left:null,right:null};o.bst=stats(e,o.i0,o.i1),o.est=estats(o.bst);const a=[o];for(;a.length<t;){let t=0,o=0;for(var s=0;s<a.length;s++)a[s].est.L>t&&(t=a[s].est.L,o=s);if(t<r)break;const f=a[o],l=splitPixels(e,i,f.i0,f.i1,f.est.e,f.est.eMq255);if(f.i0>=l||f.i1<=l){f.est.L=0;continue}const c={i0:f.i0,i1:l,bst:null,est:null,tdst:0,left:null,right:null};c.bst=stats(e,c.i0,c.i1),c.est=estats(c.bst);const u={i0:l,i1:f.i1,bst:null,est:null,tdst:0,left:null,right:null};u.bst={R:[],m:[],N:f.bst.N-c.bst.N};for(s=0;s<16;s++)u.bst.R[s]=f.bst.R[s]-c.bst.R[s];for(s=0;s<4;s++)u.bst.m[s]=f.bst.m[s]-c.bst.m[s];u.est=estats(u.bst),f.left=c,f.right=u,a[o]=c,a.push(u);}a.sort(((e,t)=>t.bst.N-e.bst.N));for(s=0;s<a.length;s++)a[s].ind=s;return [o,a]}function getNearest(e,t,r,i,o){if(null==e.left)return e.tdst=function dist(e,t,r,i,o){const a=t-e[0],s=r-e[1],f=i-e[2],l=o-e[3];return a*a+s*s+f*f+l*l}(e.est.q,t,r,i,o),e;const a=planeDst(e.est,t,r,i,o);let s=e.left,f=e.right;a>0&&(s=e.right,f=e.left);const l=getNearest(s,t,r,i,o);if(l.tdst<=a*a)return l;const c=getNearest(f,t,r,i,o);return c.tdst<l.tdst?c:l}function planeDst(e,t,r,i,o){const{e:a}=e;return a[0]*t+a[1]*r+a[2]*i+a[3]*o-e.eMq}function splitPixels(e,t,r,i,o,a){for(i-=4;r<i;){for(;vecDot(e,r,o)<=a;)r+=4;for(;vecDot(e,i,o)>a;)i-=4;if(r>=i)break;const s=t[r>>2];t[r>>2]=t[i>>2],t[i>>2]=s,r+=4,i-=4;}for(;vecDot(e,r,o)>a;)r-=4;return r+4}function vecDot(e,t,r){return e[t]*r[0]+e[t+1]*r[1]+e[t+2]*r[2]+e[t+3]*r[3]}function stats(e,t,r){const i=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],o=[0,0,0,0],a=r-t>>2;for(let a=t;a<r;a+=4){const t=e[a]*(1/255),r=e[a+1]*(1/255),s=e[a+2]*(1/255),f=e[a+3]*(1/255);o[0]+=t,o[1]+=r,o[2]+=s,o[3]+=f,i[0]+=t*t,i[1]+=t*r,i[2]+=t*s,i[3]+=t*f,i[5]+=r*r,i[6]+=r*s,i[7]+=r*f,i[10]+=s*s,i[11]+=s*f,i[15]+=f*f;}return i[4]=i[1],i[8]=i[2],i[9]=i[6],i[12]=i[3],i[13]=i[7],i[14]=i[11],{R:i,m:o,N:a}}function estats(e){const{R:t}=e,{m:r}=e,{N:i}=e,a=r[0],s=r[1],f=r[2],l=r[3],c=0==i?0:1/i,u=[t[0]-a*a*c,t[1]-a*s*c,t[2]-a*f*c,t[3]-a*l*c,t[4]-s*a*c,t[5]-s*s*c,t[6]-s*f*c,t[7]-s*l*c,t[8]-f*a*c,t[9]-f*s*c,t[10]-f*f*c,t[11]-f*l*c,t[12]-l*a*c,t[13]-l*s*c,t[14]-l*f*c,t[15]-l*l*c],h=u,d=o;let A=[Math.random(),Math.random(),Math.random(),Math.random()],g=0,p=0;if(0!=i)for(let e=0;e<16&&(A=d.multVec(h,A),p=Math.sqrt(d.dot(A,A)),A=d.sml(1/p,A),!(0!=e&&Math.abs(p-g)<1e-9));e++)g=p;const m=[a*c,s*c,f*c,l*c];return {Cov:u,q:m,e:A,L:g,eMq255:d.dot(d.sml(255,m),A),eMq:d.dot(A,m),rgba:(Math.round(255*m[3])<<24|Math.round(255*m[2])<<16|Math.round(255*m[1])<<8|Math.round(255*m[0])<<0)>>>0}}var o={multVec:(e,t)=>[e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3]*t[3],e[4]*t[0]+e[5]*t[1]+e[6]*t[2]+e[7]*t[3],e[8]*t[0]+e[9]*t[1]+e[10]*t[2]+e[11]*t[3],e[12]*t[0]+e[13]*t[1]+e[14]*t[2]+e[15]*t[3]],dot:(e,t)=>e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3]*t[3],sml:(e,t)=>[e*t[0],e*t[1],e*t[2],e*t[3]]};UPNG.encode=function encode(e,t,r,i,o,a,s){null==i&&(i=0),null==s&&(s=false);const f=compress(e,t,r,i,[false,false,false,0,s,false]);return compressPNG(f,-1),_main(f,t,r,o,a)},UPNG.encodeLL=function encodeLL(e,t,r,i,o,a,s,f){const l={ctype:0+(1==i?0:2)+(0==o?0:4),depth:a,frames:[]},c=(i+o)*a,u=c*t;for(let i=0;i<e.length;i++)l.frames.push({rect:{x:0,y:0,width:t,height:r},img:new Uint8Array(e[i]),blend:0,dispose:1,bpp:Math.ceil(c/8),bpl:Math.ceil(u/8)});return compressPNG(l,0,true),_main(l,t,r,s,f)},UPNG.encode.compress=compress,UPNG.encode.dither=dither,UPNG.quantize=quantize,UPNG.quantize.getKDtree=getKDtree,UPNG.quantize.getNearest=getNearest;}();const r={toArrayBuffer(e,t){const i=e.width,o=e.height,a=i<<2,s=e.getContext("2d").getImageData(0,0,i,o),f=new Uint32Array(s.data.buffer),l=(32*i+31)/32<<2,c=l*o,u=122+c,h=new ArrayBuffer(u),d=new DataView(h),A=1<<20;let g,p,m,w,v=A,b=0,y=0,E=0;function set16(e){d.setUint16(y,e,true),y+=2;}function set32(e){d.setUint32(y,e,true),y+=4;}function seek(e){y+=e;}set16(19778),set32(u),seek(4),set32(122),set32(108),set32(i),set32(-o>>>0),set16(1),set16(32),set32(3),set32(c),set32(2835),set32(2835),seek(8),set32(16711680),set32(65280),set32(255),set32(4278190080),set32(1466527264),function convert(){for(;b<o&&v>0;){for(w=122+b*l,g=0;g<a;)v--,p=f[E++],m=p>>>24,d.setUint32(w+g,p<<8|m),g+=4;b++;}E<f.length?(v=A,setTimeout(convert,r._dly)):t(h);}();},toBlob(e,t){this.toArrayBuffer(e,(e=>{t(new Blob([e],{type:"image/bmp"}));}));},_dly:9};var i={CHROME:"CHROME",FIREFOX:"FIREFOX",DESKTOP_SAFARI:"DESKTOP_SAFARI",IE:"IE",IOS:"IOS",ETC:"ETC"},o={[i.CHROME]:16384,[i.FIREFOX]:11180,[i.DESKTOP_SAFARI]:16384,[i.IE]:8192,[i.IOS]:4096,[i.ETC]:8192};const a="undefined"!=typeof window,s="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,f=a&&window.cordova&&window.cordova.require&&window.cordova.require("cordova/modulemapper"),CustomFile=(a||s)&&(f&&f.getOriginalSymbol(window,"File")||"undefined"!=typeof File&&File),CustomFileReader=(a||s)&&(f&&f.getOriginalSymbol(window,"FileReader")||"undefined"!=typeof FileReader&&FileReader);function getFilefromDataUrl(e,t,r=Date.now()){return new Promise((i=>{const o=e.split(","),a=o[0].match(/:(.*?);/)[1],s=globalThis.atob(o[1]);let f=s.length;const l=new Uint8Array(f);for(;f--;)l[f]=s.charCodeAt(f);const c=new Blob([l],{type:a});c.name=t,c.lastModified=r,i(c);}))}function getDataUrlFromFile(e){return new Promise(((t,r)=>{const i=new CustomFileReader;i.onload=()=>t(i.result),i.onerror=e=>r(e),i.readAsDataURL(e);}))}function loadImage(e){return new Promise(((t,r)=>{const i=new Image;i.onload=()=>t(i),i.onerror=e=>r(e),i.src=e;}))}function getBrowserName(){if(void 0!==getBrowserName.cachedResult)return getBrowserName.cachedResult;let e=i.ETC;const{userAgent:t}=navigator;return /Chrom(e|ium)/i.test(t)?e=i.CHROME:/iP(ad|od|hone)/i.test(t)&&/WebKit/i.test(t)?e=i.IOS:/Safari/i.test(t)?e=i.DESKTOP_SAFARI:/Firefox/i.test(t)?e=i.FIREFOX:(/MSIE/i.test(t)||true==!!document.documentMode)&&(e=i.IE),getBrowserName.cachedResult=e,getBrowserName.cachedResult}function approximateBelowMaximumCanvasSizeOfBrowser(e,t){const r=getBrowserName(),i=o[r];let a=e,s=t,f=a*s;const l=a>s?s/a:a/s;for(;f>i*i;){const e=(i+a)/2,t=(i+s)/2;e<t?(s=t,a=t*l):(s=e*l,a=e),f=a*s;}return {width:a,height:s}}function getNewCanvasAndCtx(e,t){let r,i;try{if(r=new OffscreenCanvas(e,t),i=r.getContext("2d"),null===i)throw new Error("getContext of OffscreenCanvas returns null")}catch(e){r=document.createElement("canvas"),i=r.getContext("2d");}return r.width=e,r.height=t,[r,i]}function drawImageInCanvas(e,t){const{width:r,height:i}=approximateBelowMaximumCanvasSizeOfBrowser(e.width,e.height),[o,a]=getNewCanvasAndCtx(r,i);return t&&/jpe?g/.test(t)&&(a.fillStyle="white",a.fillRect(0,0,o.width,o.height)),a.drawImage(e,0,0,o.width,o.height),o}function isIOS(){return void 0!==isIOS.cachedResult||(isIOS.cachedResult=["iPad Simulator","iPhone Simulator","iPod Simulator","iPad","iPhone","iPod"].includes(navigator.platform)||navigator.userAgent.includes("Mac")&&"undefined"!=typeof document&&"ontouchend"in document),isIOS.cachedResult}function drawFileInCanvas(e,t={}){return new Promise((function(r,o){let a,s;var $Try_2_Post=function(){try{return s=drawImageInCanvas(a,t.fileType||e.type),r([a,s])}catch(e){return o(e)}},$Try_2_Catch=function(t){try{var $Try_3_Catch=function(e){try{throw e}catch(e){return o(e)}};try{let t;return getDataUrlFromFile(e).then((function(e){try{return t=e,loadImage(t).then((function(e){try{return a=e,function(){try{return $Try_2_Post()}catch(e){return o(e)}}()}catch(e){return $Try_3_Catch(e)}}),$Try_3_Catch)}catch(e){return $Try_3_Catch(e)}}),$Try_3_Catch)}catch(e){$Try_3_Catch(e);}}catch(e){return o(e)}};try{if(isIOS()||[i.DESKTOP_SAFARI,i.MOBILE_SAFARI].includes(getBrowserName()))throw new Error("Skip createImageBitmap on IOS and Safari");return createImageBitmap(e).then((function(e){try{return a=e,$Try_2_Post()}catch(e){return $Try_2_Catch()}}),$Try_2_Catch)}catch(e){$Try_2_Catch();}}))}function canvasToFile(e,t,i,o,a=1){return new Promise((function(s,f){let l;if("image/png"===t){let c,u,h;return c=e.getContext("2d"),({data:u}=c.getImageData(0,0,e.width,e.height)),h=UPNG.encode([u.buffer],e.width,e.height,4096*a),l=new Blob([h],{type:t}),l.name=i,l.lastModified=o,$If_4.call(this)}{if("image/bmp"===t)return new Promise((t=>r.toBlob(e,t))).then(function(e){try{return l=e,l.name=i,l.lastModified=o,$If_5.call(this)}catch(e){return f(e)}}.bind(this),f);{if("function"==typeof OffscreenCanvas&&e instanceof OffscreenCanvas)return e.convertToBlob({type:t,quality:a}).then(function(e){try{return l=e,l.name=i,l.lastModified=o,$If_6.call(this)}catch(e){return f(e)}}.bind(this),f);{let d;return d=e.toDataURL(t,a),getFilefromDataUrl(d,i,o).then(function(e){try{return l=e,$If_6.call(this)}catch(e){return f(e)}}.bind(this),f)}function $If_6(){return $If_5.call(this)}}function $If_5(){return $If_4.call(this)}}function $If_4(){return s(l)}}))}function cleanupCanvasMemory(e){e.width=0,e.height=0;}function isAutoOrientationInBrowser(){return new Promise((function(e,t){let i,o,a,s;return void 0!==isAutoOrientationInBrowser.cachedResult?e(isAutoOrientationInBrowser.cachedResult):(getFilefromDataUrl("data:image/jpeg;base64,/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAAAAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/xABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q==","test.jpg",Date.now()).then((function(r){try{return i=r,drawFileInCanvas(i).then((function(r){try{return o=r[1],canvasToFile(o,i.type,i.name,i.lastModified).then((function(r){try{return a=r,cleanupCanvasMemory(o),drawFileInCanvas(a).then((function(r){try{return s=r[0],isAutoOrientationInBrowser.cachedResult=1===s.width&&2===s.height,e(isAutoOrientationInBrowser.cachedResult)}catch(e){return t(e)}}),t)}catch(e){return t(e)}}),t)}catch(e){return t(e)}}),t)}catch(e){return t(e)}}),t))}))}function getExifOrientation(e){return new Promise(((t,r)=>{const i=new CustomFileReader;i.onload=e=>{const r=new DataView(e.target.result);if(65496!=r.getUint16(0,false))return t(-2);const i=r.byteLength;let o=2;for(;o<i;){if(r.getUint16(o+2,false)<=8)return t(-1);const e=r.getUint16(o,false);if(o+=2,65505==e){if(1165519206!=r.getUint32(o+=2,false))return t(-1);const e=18761==r.getUint16(o+=6,false);o+=r.getUint32(o+4,e);const i=r.getUint16(o,e);o+=2;for(let a=0;a<i;a++)if(274==r.getUint16(o+12*a,e))return t(r.getUint16(o+12*a+8,e))}else {if(65280!=(65280&e))break;o+=r.getUint16(o,false);}}return t(-1)},i.onerror=e=>r(e),i.readAsArrayBuffer(e);}))}function handleMaxWidthOrHeight(e,t){const{width:r}=e,{height:i}=e,{maxWidthOrHeight:o}=t;let a,s=e;return isFinite(o)&&(r>o||i>o)&&([s,a]=getNewCanvasAndCtx(r,i),r>i?(s.width=o,s.height=i/r*o):(s.width=r/i*o,s.height=o),a.drawImage(e,0,0,s.width,s.height),cleanupCanvasMemory(e)),s}function followExifOrientation(e,t){const{width:r}=e,{height:i}=e,[o,a]=getNewCanvasAndCtx(r,i);switch(t>4&&t<9?(o.width=i,o.height=r):(o.width=r,o.height=i),t){case 2:a.transform(-1,0,0,1,r,0);break;case 3:a.transform(-1,0,0,-1,r,i);break;case 4:a.transform(1,0,0,-1,0,i);break;case 5:a.transform(0,1,1,0,0,0);break;case 6:a.transform(0,1,-1,0,i,0);break;case 7:a.transform(0,-1,-1,0,i,r);break;case 8:a.transform(0,-1,1,0,0,r);}return a.drawImage(e,0,0,r,i),cleanupCanvasMemory(e),o}function compress(e,t,r=0){return new Promise((function(i,o){let a,s,f,l,c,u,h,d,A,g,p,m,w,v,b,y,E,F,_,B;function incProgress(e=5){if(t.signal&&t.signal.aborted)throw t.signal.reason;a+=e,t.onProgress(Math.min(a,100));}function setProgress(e){if(t.signal&&t.signal.aborted)throw t.signal.reason;a=Math.min(Math.max(e,a),100),t.onProgress(a);}return a=r,s=t.maxIteration||10,f=1024*t.maxSizeMB*1024,incProgress(),drawFileInCanvas(e,t).then(function(r){try{return [,l]=r,incProgress(),c=handleMaxWidthOrHeight(l,t),incProgress(),new Promise((function(r,i){var o;if(!(o=t.exifOrientation))return getExifOrientation(e).then(function(e){try{return o=e,$If_2.call(this)}catch(e){return i(e)}}.bind(this),i);function $If_2(){return r(o)}return $If_2.call(this)})).then(function(r){try{return u=r,incProgress(),isAutoOrientationInBrowser().then(function(r){try{return h=r?c:followExifOrientation(c,u),incProgress(),d=t.initialQuality||1,A=t.fileType||e.type,canvasToFile(h,A,e.name,e.lastModified,d).then(function(r){try{{if(g=r,incProgress(),p=g.size>f,m=g.size>e.size,!p&&!m)return setProgress(100),i(g);var a;function $Loop_3(){if(s--&&(b>f||b>w)){let t,r;return t=B?.95*_.width:_.width,r=B?.95*_.height:_.height,[E,F]=getNewCanvasAndCtx(t,r),F.drawImage(_,0,0,t,r),d*="image/png"===A?.85:.95,canvasToFile(E,A,e.name,e.lastModified,d).then((function(e){try{return y=e,cleanupCanvasMemory(_),_=E,b=y.size,setProgress(Math.min(99,Math.floor((v-b)/(v-f)*100))),$Loop_3}catch(e){return o(e)}}),o)}return [1]}return w=e.size,v=g.size,b=v,_=h,B=!t.alwaysKeepResolution&&p,(a=function(e){for(;e;){if(e.then)return void e.then(a,o);try{if(e.pop){if(e.length)return e.pop()?$Loop_3_exit.call(this):e;e=$Loop_3;}else e=e.call(this);}catch(e){return o(e)}}}.bind(this))($Loop_3);function $Loop_3_exit(){return cleanupCanvasMemory(_),cleanupCanvasMemory(E),cleanupCanvasMemory(c),cleanupCanvasMemory(h),cleanupCanvasMemory(l),setProgress(100),i(y)}}}catch(u){return o(u)}}.bind(this),o)}catch(e){return o(e)}}.bind(this),o)}catch(e){return o(e)}}.bind(this),o)}catch(e){return o(e)}}.bind(this),o)}))}const l="\nlet scriptImported = false\nself.addEventListener('message', async (e) => {\n const { file, id, imageCompressionLibUrl, options } = e.data\n options.onProgress = (progress) => self.postMessage({ progress, id })\n try {\n if (!scriptImported) {\n // console.log('[worker] importScripts', imageCompressionLibUrl)\n self.importScripts(imageCompressionLibUrl)\n scriptImported = true\n }\n // console.log('[worker] self', self)\n const compressedFile = await imageCompression(file, options)\n self.postMessage({ file: compressedFile, id })\n } catch (e) {\n // console.error('[worker] error', e)\n self.postMessage({ error: e.message + '\\n' + e.stack, id })\n }\n})\n";let c;function compressOnWebWorker(e,t){return new Promise(((r,i)=>{c||(c=function createWorkerScriptURL(e){const t=[];return t.push(e),URL.createObjectURL(new Blob(t))}(l));const o=new Worker(c);o.addEventListener("message",(function handler(e){if(t.signal&&t.signal.aborted)o.terminate();else if(void 0===e.data.progress){if(e.data.error)return i(new Error(e.data.error)),void o.terminate();r(e.data.file),o.terminate();}else t.onProgress(e.data.progress);})),o.addEventListener("error",i),t.signal&&t.signal.addEventListener("abort",(()=>{i(t.signal.reason),o.terminate();})),o.postMessage({file:e,imageCompressionLibUrl:t.libURL,options:{...t,onProgress:void 0,signal:void 0}});}))}function imageCompression(e,t){return new Promise((function(r,i){let o,a,s,f,l,c;if(o={...t},s=0,({onProgress:f}=o),o.maxSizeMB=o.maxSizeMB||Number.POSITIVE_INFINITY,l="boolean"!=typeof o.useWebWorker||o.useWebWorker,delete o.useWebWorker,o.onProgress=e=>{s=e,"function"==typeof f&&f(s);},!(e instanceof Blob||e instanceof CustomFile))return i(new Error("The file given is not an instance of Blob or File"));if(!/^image/.test(e.type))return i(new Error("The file given is not an image"));if(c="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,!l||"function"!=typeof Worker||c)return compress(e,o).then(function(e){try{return a=e,$If_4.call(this)}catch(e){return i(e)}}.bind(this),i);var u=function(){try{return $If_4.call(this)}catch(e){return i(e)}}.bind(this),$Try_1_Catch=function(t){try{return compress(e,o).then((function(e){try{return a=e,u()}catch(e){return i(e)}}),i)}catch(e){return i(e)}};try{return o.libURL=o.libURL||"https://cdn.jsdelivr.net/npm/browser-image-compression@2.0.2/dist/browser-image-compression.js",compressOnWebWorker(e,o).then((function(e){try{return a=e,u()}catch(e){return $Try_1_Catch()}}),$Try_1_Catch)}catch(e){$Try_1_Catch();}function $If_4(){try{a.name=e.name,a.lastModified=e.lastModified;}catch(e){}try{o.preserveExif&&"image/jpeg"===e.type&&(!o.fileType||o.fileType&&o.fileType===e.type)&&(a=copyExifWithoutOrientation(e,a));}catch(e){}return r(a)}}))}imageCompression.getDataUrlFromFile=getDataUrlFromFile,imageCompression.getFilefromDataUrl=getFilefromDataUrl,imageCompression.loadImage=loadImage,imageCompression.drawImageInCanvas=drawImageInCanvas,imageCompression.drawFileInCanvas=drawFileInCanvas,imageCompression.canvasToFile=canvasToFile,imageCompression.getExifOrientation=getExifOrientation,imageCompression.handleMaxWidthOrHeight=handleMaxWidthOrHeight,imageCompression.followExifOrientation=followExifOrientation,imageCompression.cleanupCanvasMemory=cleanupCanvasMemory,imageCompression.isAutoOrientationInBrowser=isAutoOrientationInBrowser,imageCompression.approximateBelowMaximumCanvasSizeOfBrowser=approximateBelowMaximumCanvasSizeOfBrowser,imageCompression.copyExifWithoutOrientation=copyExifWithoutOrientation,imageCompression.getBrowserName=getBrowserName,imageCompression.version="2.0.2";
|
|
14772
|
-
|
|
14773
|
-
class KritzelImageTool extends KritzelBaseTool {
|
|
14774
|
-
fileInput = null;
|
|
14775
|
-
maxCompressionSize = 300;
|
|
14776
|
-
constructor(core) {
|
|
14777
|
-
super(core);
|
|
14778
|
-
this.setupFileInput();
|
|
15330
|
+
deselectAllChildren() {
|
|
15331
|
+
this.objects.forEach(obj => (obj.isSelected = false));
|
|
14779
15332
|
}
|
|
14780
|
-
|
|
14781
|
-
this.
|
|
15333
|
+
updateWorkspaceId(workspaceId) {
|
|
15334
|
+
this.workspaceId = workspaceId;
|
|
15335
|
+
this.objects.forEach(obj => (obj.workspaceId = workspaceId));
|
|
14782
15336
|
}
|
|
14783
|
-
|
|
14784
|
-
|
|
14785
|
-
|
|
14786
|
-
}
|
|
14787
|
-
this.fileInput.click();
|
|
15337
|
+
updateZIndices(startZIndex) {
|
|
15338
|
+
this.objects.forEach((obj, i) => {
|
|
15339
|
+
obj.zIndex = startZIndex + i;
|
|
15340
|
+
});
|
|
14788
15341
|
}
|
|
14789
|
-
|
|
14790
|
-
this.
|
|
14791
|
-
|
|
14792
|
-
|
|
14793
|
-
|
|
14794
|
-
|
|
14795
|
-
|
|
14796
|
-
|
|
15342
|
+
updatePosition(x, y) {
|
|
15343
|
+
this.objects.forEach(obj => {
|
|
15344
|
+
const deltaX = obj.translateX - this.translateX;
|
|
15345
|
+
const deltaY = obj.translateY - this.translateY;
|
|
15346
|
+
obj.updatePosition(x + deltaX, y + deltaY);
|
|
15347
|
+
});
|
|
15348
|
+
// Update snapshots
|
|
15349
|
+
this.unchangedObjectSnapshots.forEach(snapshot => {
|
|
15350
|
+
const deltaX = snapshot.translateX - this.translateX;
|
|
15351
|
+
const deltaY = snapshot.translateY - this.translateY;
|
|
15352
|
+
snapshot.translateX = x + deltaX;
|
|
15353
|
+
snapshot.translateY = x + deltaY;
|
|
15354
|
+
});
|
|
15355
|
+
this.translateX = x;
|
|
15356
|
+
this.translateY = y;
|
|
15357
|
+
this._core.store.state.objects.update(this);
|
|
14797
15358
|
}
|
|
14798
|
-
|
|
14799
|
-
|
|
14800
|
-
|
|
14801
|
-
|
|
14802
|
-
|
|
14803
|
-
|
|
14804
|
-
|
|
14805
|
-
|
|
14806
|
-
|
|
14807
|
-
|
|
14808
|
-
.
|
|
14809
|
-
|
|
14810
|
-
|
|
15359
|
+
/**
|
|
15360
|
+
* Capture snapshots of current object states for undo/redo operations
|
|
15361
|
+
*/
|
|
15362
|
+
captureUnchangedSnapshots() {
|
|
15363
|
+
this.unchangedObjectSnapshots.clear();
|
|
15364
|
+
this.snapshotRotation = this.rotation;
|
|
15365
|
+
this.objects.forEach(obj => {
|
|
15366
|
+
this.unchangedObjectSnapshots.set(obj.id, {
|
|
15367
|
+
id: obj.id,
|
|
15368
|
+
translateX: obj.translateX,
|
|
15369
|
+
translateY: obj.translateY,
|
|
15370
|
+
rotation: obj.rotation,
|
|
15371
|
+
width: obj.width,
|
|
15372
|
+
height: obj.height,
|
|
15373
|
+
totalWidth: obj.totalWidth,
|
|
15374
|
+
totalHeight: obj.totalHeight,
|
|
15375
|
+
scale: obj.scale,
|
|
14811
15376
|
});
|
|
15377
|
+
});
|
|
15378
|
+
}
|
|
15379
|
+
serialize() {
|
|
15380
|
+
const { _core, _elementRef, element, totalWidth, totalHeight, unchangedObjectSnapshots, ...remainingProps } = this;
|
|
15381
|
+
const clonedProps = structuredClone(remainingProps);
|
|
15382
|
+
if (element && typeof element === 'object' && 'nodeType' in element && element.nodeType === 1) {
|
|
15383
|
+
clonedProps.element = element.cloneNode(true);
|
|
14812
15384
|
}
|
|
14813
|
-
|
|
14814
|
-
|
|
14815
|
-
|
|
14816
|
-
|
|
14817
|
-
|
|
14818
|
-
|
|
15385
|
+
// Convert Map to plain object for serialization
|
|
15386
|
+
clonedProps.unchangedObjectSnapshots = Object.fromEntries(this.unchangedObjectSnapshots);
|
|
15387
|
+
return clonedProps;
|
|
15388
|
+
}
|
|
15389
|
+
deserialize(object) {
|
|
15390
|
+
// First, deserialize all base properties using parent's deserialize
|
|
15391
|
+
super.deserialize(object);
|
|
15392
|
+
// Restore the Map from serialized object
|
|
15393
|
+
if (object.unchangedObjectSnapshots) {
|
|
15394
|
+
this.unchangedObjectSnapshots = new Map(Object.entries(object.unchangedObjectSnapshots));
|
|
14819
15395
|
}
|
|
15396
|
+
return this;
|
|
14820
15397
|
}
|
|
14821
|
-
|
|
14822
|
-
|
|
14823
|
-
|
|
14824
|
-
|
|
14825
|
-
|
|
14826
|
-
img.onload = () => this.createKritzelImage(img);
|
|
14827
|
-
};
|
|
14828
|
-
reader.readAsDataURL(file);
|
|
15398
|
+
update() {
|
|
15399
|
+
// Only update the selection group itself
|
|
15400
|
+
// Child objects are already updated during move/resize/rotate operations
|
|
15401
|
+
// Updating them again here would create redundant y.js updates
|
|
15402
|
+
this._core.store.state.objects.update(this);
|
|
14829
15403
|
}
|
|
14830
|
-
|
|
14831
|
-
const
|
|
14832
|
-
const
|
|
14833
|
-
|
|
14834
|
-
|
|
14835
|
-
|
|
14836
|
-
|
|
14837
|
-
|
|
14838
|
-
|
|
14839
|
-
|
|
15404
|
+
move(startX, startY, endX, endY) {
|
|
15405
|
+
const deltaX = (startX - endX) / this._core.store.state.scale;
|
|
15406
|
+
const deltaY = (startY - endY) / this._core.store.state.scale;
|
|
15407
|
+
this.translateX += deltaX;
|
|
15408
|
+
this.translateY += deltaY;
|
|
15409
|
+
this._core.store.state.objects.transaction(() => {
|
|
15410
|
+
this._core.store.state.objects.update(this);
|
|
15411
|
+
this.objects.forEach(obj => {
|
|
15412
|
+
obj.move(startX, startY, endX, endY);
|
|
15413
|
+
// Update any lines that are anchored to this object
|
|
15414
|
+
this._core.anchorManager.updateAnchorsForObject(obj.id);
|
|
15415
|
+
});
|
|
15416
|
+
});
|
|
15417
|
+
// Update snapshots
|
|
15418
|
+
this.unchangedObjectSnapshots.forEach(snapshot => {
|
|
15419
|
+
snapshot.translateX += deltaX;
|
|
15420
|
+
snapshot.translateY += deltaY;
|
|
15421
|
+
});
|
|
14840
15422
|
}
|
|
14841
|
-
|
|
14842
|
-
this.
|
|
14843
|
-
this.
|
|
14844
|
-
|
|
14845
|
-
this.
|
|
15423
|
+
resize(x, y, width, height) {
|
|
15424
|
+
const widthScaleFactor = width / this.width;
|
|
15425
|
+
const heightScaleFactor = height / this.height;
|
|
15426
|
+
// Calculate old center
|
|
15427
|
+
const oldCenterX = this.translateX + this.totalWidth / 2 / this.scale;
|
|
15428
|
+
const oldCenterY = this.translateY + this.totalHeight / 2 / this.scale;
|
|
15429
|
+
// Calculate new center
|
|
15430
|
+
const newTotalWidth = width + this.padding * 2;
|
|
15431
|
+
const newTotalHeight = height + this.padding * 2;
|
|
15432
|
+
const newCenterX = x + newTotalWidth / 2 / this.scale;
|
|
15433
|
+
const newCenterY = y + newTotalHeight / 2 / this.scale;
|
|
15434
|
+
const rotation = this.rotation;
|
|
15435
|
+
const cos = Math.cos(-rotation);
|
|
15436
|
+
const sin = Math.sin(-rotation);
|
|
15437
|
+
const cosR = Math.cos(rotation);
|
|
15438
|
+
const sinR = Math.sin(rotation);
|
|
15439
|
+
this._core.store.state.objects.transaction(() => {
|
|
15440
|
+
this.objects.forEach(child => {
|
|
15441
|
+
// Calculate child center
|
|
15442
|
+
const childCenterX = child.translateX + child.totalWidth / 2 / child.scale;
|
|
15443
|
+
const childCenterY = child.translateY + child.totalHeight / 2 / child.scale;
|
|
15444
|
+
// Vector from old group center to child center
|
|
15445
|
+
const dx = childCenterX - oldCenterX;
|
|
15446
|
+
const dy = childCenterY - oldCenterY;
|
|
15447
|
+
// Rotate to local space (align with group axes)
|
|
15448
|
+
const localX = dx * cos - dy * sin;
|
|
15449
|
+
const localY = dx * sin + dy * cos;
|
|
15450
|
+
// Scale in local space
|
|
15451
|
+
const scaledLocalX = localX * widthScaleFactor;
|
|
15452
|
+
const scaledLocalY = localY * heightScaleFactor;
|
|
15453
|
+
// Rotate back to world space
|
|
15454
|
+
const rotatedX = scaledLocalX * cosR - scaledLocalY * sinR;
|
|
15455
|
+
const rotatedY = scaledLocalX * sinR + scaledLocalY * cosR;
|
|
15456
|
+
// New child center
|
|
15457
|
+
const newChildCenterX = newCenterX + rotatedX;
|
|
15458
|
+
const newChildCenterY = newCenterY + rotatedY;
|
|
15459
|
+
// New child top-left
|
|
15460
|
+
// Calculate relative rotation
|
|
15461
|
+
const relativeRotation = child.rotation - rotation;
|
|
15462
|
+
const cosRel = Math.cos(relativeRotation);
|
|
15463
|
+
const sinRel = Math.sin(relativeRotation);
|
|
15464
|
+
// Project the group's scale factors onto the child's local axes
|
|
15465
|
+
// We use absolute values because scaling is magnitude-based
|
|
15466
|
+
// If the child is aligned (0 deg), cos=1, sin=0 -> scales match
|
|
15467
|
+
// If the child is 90 deg, cos=0, sin=1 -> scales swap
|
|
15468
|
+
const newChildWidthScale = Math.sqrt(Math.pow(widthScaleFactor * cosRel, 2) + Math.pow(heightScaleFactor * sinRel, 2));
|
|
15469
|
+
const newChildHeightScale = Math.sqrt(Math.pow(widthScaleFactor * sinRel, 2) + Math.pow(heightScaleFactor * cosRel, 2));
|
|
15470
|
+
const updatedWidth = child.width * newChildWidthScale;
|
|
15471
|
+
const updatedHeight = child.height * newChildHeightScale;
|
|
15472
|
+
const updatedTotalWidth = updatedWidth + child.padding * 2;
|
|
15473
|
+
const updatedTotalHeight = updatedHeight + child.padding * 2;
|
|
15474
|
+
const updatedX = newChildCenterX - updatedTotalWidth / 2 / child.scale;
|
|
15475
|
+
const updatedY = newChildCenterY - updatedTotalHeight / 2 / child.scale;
|
|
15476
|
+
child.resize(updatedX, updatedY, updatedWidth, updatedHeight);
|
|
15477
|
+
// Update any lines that are anchored to this child object
|
|
15478
|
+
this._core.anchorManager.updateAnchorsForObject(child.id);
|
|
15479
|
+
});
|
|
15480
|
+
// Refresh dimensions and update the SelectionGroup to propagate changes to other tabs
|
|
15481
|
+
this.refreshObjectDimensions();
|
|
15482
|
+
this.captureUnchangedSnapshots();
|
|
15483
|
+
this._core.store.state.objects.update(this);
|
|
15484
|
+
});
|
|
14846
15485
|
}
|
|
14847
|
-
|
|
14848
|
-
this.
|
|
15486
|
+
rotate(value) {
|
|
15487
|
+
this.rotation = value;
|
|
15488
|
+
const centerX = this.translateX + this.totalWidth / 2 / this.scale;
|
|
15489
|
+
const centerY = this.translateY + this.totalHeight / 2 / this.scale;
|
|
15490
|
+
const angle = value - this.snapshotRotation;
|
|
15491
|
+
const cos = Math.cos(angle);
|
|
15492
|
+
const sin = Math.sin(angle);
|
|
15493
|
+
this._core.store.state.objects.transaction(() => {
|
|
15494
|
+
// Update the SelectionGroup itself to propagate rotation to other tabs
|
|
15495
|
+
this._core.store.state.objects.update(this);
|
|
15496
|
+
this.objects.forEach(child => {
|
|
15497
|
+
const unchangedSnapshot = this.unchangedObjectSnapshots.get(child.id);
|
|
15498
|
+
if (!unchangedSnapshot)
|
|
15499
|
+
return;
|
|
15500
|
+
const offsetX = this.getOffsetXToCenterFromSnapshot(unchangedSnapshot);
|
|
15501
|
+
const offsetY = this.getOffsetYToCenterFromSnapshot(unchangedSnapshot);
|
|
15502
|
+
const rotatedX = cos * offsetX - sin * offsetY;
|
|
15503
|
+
const rotatedY = sin * offsetX + cos * offsetY;
|
|
15504
|
+
child.translateX = centerX + rotatedX - child.totalWidth / 2 / child.scale;
|
|
15505
|
+
child.translateY = centerY + rotatedY - child.totalHeight / 2 / child.scale;
|
|
15506
|
+
child.rotate(this.objects.length === 1 ? value : unchangedSnapshot.rotation + angle);
|
|
15507
|
+
});
|
|
15508
|
+
});
|
|
15509
|
+
}
|
|
15510
|
+
copy() {
|
|
15511
|
+
const selectionGroup = KritzelSelectionGroup.create(this._core);
|
|
15512
|
+
this.objects
|
|
15513
|
+
.sort((a, b) => a.zIndex - b.zIndex)
|
|
15514
|
+
.forEach(obj => {
|
|
15515
|
+
const copiedObject = obj.copy();
|
|
15516
|
+
selectionGroup.addOrRemove(copiedObject);
|
|
15517
|
+
});
|
|
15518
|
+
selectionGroup.captureUnchangedSnapshots();
|
|
15519
|
+
if (this.objects.length === 1) {
|
|
15520
|
+
selectionGroup.rotation = this.objects[0].rotation;
|
|
15521
|
+
}
|
|
15522
|
+
return selectionGroup;
|
|
15523
|
+
}
|
|
15524
|
+
refreshObjectDimensions() {
|
|
15525
|
+
if (this.objects.length === 1) {
|
|
15526
|
+
const obj = this.objects[0];
|
|
15527
|
+
this.minX = obj.boundingBox.x / this.scale;
|
|
15528
|
+
this.maxX = obj.boundingBox.x / this.scale + obj.boundingBox.width;
|
|
15529
|
+
this.minY = obj.boundingBox.y / this.scale;
|
|
15530
|
+
this.maxY = obj.boundingBox.y / this.scale + obj.boundingBox.height;
|
|
15531
|
+
this.translateX = (this.minX - this.padding) * this.scale;
|
|
15532
|
+
this.translateY = (this.minY - this.padding) * this.scale;
|
|
15533
|
+
this.width = (this.maxX - this.minX - this.padding) * this.scale;
|
|
15534
|
+
this.height = (this.maxY - this.minY - this.padding) * this.scale;
|
|
15535
|
+
}
|
|
15536
|
+
else {
|
|
15537
|
+
const rotation = this.rotation;
|
|
15538
|
+
const cos = Math.cos(-rotation);
|
|
15539
|
+
const sin = Math.sin(-rotation);
|
|
15540
|
+
let minX = Infinity;
|
|
15541
|
+
let maxX = -Infinity;
|
|
15542
|
+
let minY = Infinity;
|
|
15543
|
+
let maxY = -Infinity;
|
|
15544
|
+
this.objects.forEach(obj => {
|
|
15545
|
+
const polygon = obj.rotatedPolygon;
|
|
15546
|
+
const corners = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
|
|
15547
|
+
corners.forEach(corner => {
|
|
15548
|
+
// Rotate corner into local space (aligned with group rotation)
|
|
15549
|
+
const rx = corner.x * cos - corner.y * sin;
|
|
15550
|
+
const ry = corner.x * sin + corner.y * cos;
|
|
15551
|
+
if (rx < minX)
|
|
15552
|
+
minX = rx;
|
|
15553
|
+
if (rx > maxX)
|
|
15554
|
+
maxX = rx;
|
|
15555
|
+
if (ry < minY)
|
|
15556
|
+
minY = ry;
|
|
15557
|
+
if (ry > maxY)
|
|
15558
|
+
maxY = ry;
|
|
15559
|
+
});
|
|
15560
|
+
});
|
|
15561
|
+
// Dimensions in world units (unrotated)
|
|
15562
|
+
const worldWidth = maxX - minX;
|
|
15563
|
+
const worldHeight = maxY - minY;
|
|
15564
|
+
this.width = (worldWidth - this.padding) * this.scale;
|
|
15565
|
+
this.height = (worldHeight - this.padding) * this.scale;
|
|
15566
|
+
// Center of the box in rotated space
|
|
15567
|
+
const cRx = (minX + maxX) / 2;
|
|
15568
|
+
const cRy = (minY + maxY) / 2;
|
|
15569
|
+
// Rotate center back to world space
|
|
15570
|
+
const cosR = Math.cos(rotation);
|
|
15571
|
+
const sinR = Math.sin(rotation);
|
|
15572
|
+
const cx = cRx * cosR - cRy * sinR;
|
|
15573
|
+
const cy = cRx * sinR + cRy * cosR;
|
|
15574
|
+
this.translateX = cx - (this.width / this.scale + 2 * this.padding) / 2;
|
|
15575
|
+
this.translateY = cy - (this.height / this.scale + 2 * this.padding) / 2;
|
|
15576
|
+
}
|
|
15577
|
+
this._core.store.state.objects.update(this);
|
|
15578
|
+
}
|
|
15579
|
+
getOffsetXToCenterFromSnapshot(snapshot) {
|
|
15580
|
+
const objCenterX = snapshot.translateX + snapshot.totalWidth / snapshot.scale / 2;
|
|
15581
|
+
const groupCenterX = this.translateX + this.totalWidth / this.scale / 2;
|
|
15582
|
+
return objCenterX - groupCenterX;
|
|
15583
|
+
}
|
|
15584
|
+
getOffsetYToCenterFromSnapshot(snapshot) {
|
|
15585
|
+
const objCenterY = snapshot.translateY + snapshot.totalHeight / snapshot.scale / 2;
|
|
15586
|
+
const groupCenterY = this.translateY + this.totalHeight / this.scale / 2;
|
|
15587
|
+
return objCenterY - groupCenterY;
|
|
14849
15588
|
}
|
|
14850
15589
|
}
|
|
14851
15590
|
|
|
14852
|
-
class
|
|
14853
|
-
|
|
14854
|
-
|
|
14855
|
-
|
|
14856
|
-
|
|
14857
|
-
|
|
14858
|
-
|
|
14859
|
-
|
|
14860
|
-
'#00FF00',
|
|
14861
|
-
'#0000FF',
|
|
14862
|
-
'#FFFF00',
|
|
14863
|
-
'#FF00FF',
|
|
14864
|
-
'#00FFFF',
|
|
14865
|
-
'#808080',
|
|
14866
|
-
'#C0C0C0',
|
|
14867
|
-
'#800000',
|
|
14868
|
-
'#008000',
|
|
14869
|
-
'#000080',
|
|
14870
|
-
'#808000',
|
|
14871
|
-
'#800080',
|
|
14872
|
-
];
|
|
15591
|
+
class KritzelLineTool extends KritzelBaseTool {
|
|
15592
|
+
color = '#000000';
|
|
15593
|
+
size = 4;
|
|
15594
|
+
palettes = ['#000000', '#FFFFFF', '#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF', '#808080', '#C0C0C0', '#800000', '#008000', '#000080', '#808000', '#800080'];
|
|
15595
|
+
/** Arrow head configuration for lines created with this tool */
|
|
15596
|
+
arrows;
|
|
15597
|
+
_startX = 0;
|
|
15598
|
+
_startY = 0;
|
|
14873
15599
|
constructor(core) {
|
|
14874
15600
|
super(core);
|
|
14875
15601
|
}
|
|
@@ -14878,867 +15604,1040 @@ class KritzelTextTool extends KritzelBaseTool {
|
|
|
14878
15604
|
event.preventDefault();
|
|
14879
15605
|
}
|
|
14880
15606
|
if (event.pointerType === 'mouse') {
|
|
14881
|
-
|
|
14882
|
-
|
|
14883
|
-
|
|
14884
|
-
|
|
14885
|
-
|
|
14886
|
-
|
|
14887
|
-
|
|
14888
|
-
|
|
14889
|
-
|
|
14890
|
-
|
|
14891
|
-
|
|
14892
|
-
|
|
15607
|
+
if (KritzelEventHelper.isLeftClick(event)) {
|
|
15608
|
+
this._core.store.state.isDrawing = true;
|
|
15609
|
+
const x = event.clientX - this._core.store.offsetX;
|
|
15610
|
+
const y = event.clientY - this._core.store.offsetY;
|
|
15611
|
+
this._startX = x;
|
|
15612
|
+
this._startY = y;
|
|
15613
|
+
const line = KritzelLine.create(this._core, {
|
|
15614
|
+
startX: x,
|
|
15615
|
+
startY: y,
|
|
15616
|
+
endX: x,
|
|
15617
|
+
endY: y,
|
|
15618
|
+
translateX: -this._core.store.state.translateX,
|
|
15619
|
+
translateY: -this._core.store.state.translateY,
|
|
15620
|
+
scale: this._core.store.state.scale,
|
|
15621
|
+
stroke: this.color,
|
|
15622
|
+
strokeWidth: this.size,
|
|
15623
|
+
arrows: this.arrows,
|
|
15624
|
+
});
|
|
15625
|
+
line.isCompleted = false;
|
|
15626
|
+
this._core.store.state.objects.insert(line);
|
|
14893
15627
|
}
|
|
14894
|
-
|
|
14895
|
-
|
|
14896
|
-
|
|
14897
|
-
|
|
15628
|
+
}
|
|
15629
|
+
if (event.pointerType === 'touch') {
|
|
15630
|
+
const activePointers = Array.from(this._core.store.state.pointers.values());
|
|
15631
|
+
if (activePointers.length === 1) {
|
|
15632
|
+
const x = Math.round(activePointers[0].clientX - this._core.store.offsetX);
|
|
15633
|
+
const y = Math.round(activePointers[0].clientY - this._core.store.offsetY);
|
|
15634
|
+
this._core.store.state.isDrawing = true;
|
|
15635
|
+
this._startX = x;
|
|
15636
|
+
this._startY = y;
|
|
15637
|
+
const line = KritzelLine.create(this._core, {
|
|
15638
|
+
startX: x,
|
|
15639
|
+
startY: y,
|
|
15640
|
+
endX: x,
|
|
15641
|
+
endY: y,
|
|
15642
|
+
translateX: -this._core.store.state.translateX,
|
|
15643
|
+
translateY: -this._core.store.state.translateY,
|
|
15644
|
+
scale: this._core.store.state.scale,
|
|
15645
|
+
stroke: this.color,
|
|
15646
|
+
strokeWidth: this.size,
|
|
15647
|
+
arrows: this.arrows,
|
|
15648
|
+
});
|
|
15649
|
+
line.isCompleted = false;
|
|
15650
|
+
this._core.store.state.objects.insert(line);
|
|
14898
15651
|
}
|
|
14899
|
-
|
|
14900
|
-
|
|
15652
|
+
}
|
|
15653
|
+
}
|
|
15654
|
+
handlePointerMove(event) {
|
|
15655
|
+
if (event.cancelable) {
|
|
15656
|
+
event.preventDefault();
|
|
15657
|
+
}
|
|
15658
|
+
if (event.pointerType === 'mouse') {
|
|
15659
|
+
if (this._core.store.state.isDrawing) {
|
|
15660
|
+
const currentLine = this._core.store.currentLine;
|
|
15661
|
+
if (currentLine) {
|
|
15662
|
+
const x = event.clientX - this._core.store.offsetX;
|
|
15663
|
+
const y = event.clientY - this._core.store.offsetY;
|
|
15664
|
+
const updatedLine = KritzelLine.create(this._core, {
|
|
15665
|
+
startX: this._startX,
|
|
15666
|
+
startY: this._startY,
|
|
15667
|
+
endX: x,
|
|
15668
|
+
endY: y,
|
|
15669
|
+
translateX: -this._core.store.state.translateX,
|
|
15670
|
+
translateY: -this._core.store.state.translateY,
|
|
15671
|
+
scale: this._core.store.state.scale,
|
|
15672
|
+
stroke: this.color,
|
|
15673
|
+
strokeWidth: this.size,
|
|
15674
|
+
arrows: this.arrows,
|
|
15675
|
+
});
|
|
15676
|
+
updatedLine.id = currentLine.id;
|
|
15677
|
+
updatedLine.workspaceId = currentLine.workspaceId;
|
|
15678
|
+
updatedLine.isCompleted = false;
|
|
15679
|
+
this._core.store.state.objects.update(updatedLine);
|
|
15680
|
+
}
|
|
14901
15681
|
}
|
|
14902
|
-
const clientX = event.clientX - this._core.store.offsetX;
|
|
14903
|
-
const clientY = event.clientY - this._core.store.offsetY;
|
|
14904
|
-
const text = KritzelText.create(this._core, this.fontSize, this.fontFamily);
|
|
14905
|
-
text.fontColor = this.fontColor;
|
|
14906
|
-
text.translateX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
14907
|
-
text.translateY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
14908
|
-
text.zIndex = this._core.store.currentZIndex;
|
|
14909
|
-
this._core.store.state.objects.insert(text);
|
|
14910
|
-
this._core.rerender();
|
|
14911
|
-
text.edit(event);
|
|
14912
15682
|
}
|
|
14913
15683
|
if (event.pointerType === 'touch') {
|
|
14914
15684
|
const activePointers = Array.from(this._core.store.state.pointers.values());
|
|
14915
|
-
|
|
14916
|
-
|
|
14917
|
-
|
|
14918
|
-
|
|
14919
|
-
|
|
14920
|
-
|
|
14921
|
-
|
|
14922
|
-
|
|
14923
|
-
|
|
14924
|
-
|
|
14925
|
-
|
|
14926
|
-
|
|
14927
|
-
|
|
14928
|
-
|
|
14929
|
-
|
|
14930
|
-
|
|
14931
|
-
|
|
14932
|
-
|
|
14933
|
-
|
|
14934
|
-
|
|
15685
|
+
if (activePointers.length === 1) {
|
|
15686
|
+
const currentLine = this._core.store.currentLine;
|
|
15687
|
+
if (currentLine) {
|
|
15688
|
+
const x = Math.round(activePointers[0].clientX - this._core.store.offsetX);
|
|
15689
|
+
const y = Math.round(activePointers[0].clientY - this._core.store.offsetY);
|
|
15690
|
+
const updatedLine = KritzelLine.create(this._core, {
|
|
15691
|
+
startX: this._startX,
|
|
15692
|
+
startY: this._startY,
|
|
15693
|
+
endX: x,
|
|
15694
|
+
endY: y,
|
|
15695
|
+
translateX: -this._core.store.state.translateX,
|
|
15696
|
+
translateY: -this._core.store.state.translateY,
|
|
15697
|
+
scale: this._core.store.state.scale,
|
|
15698
|
+
stroke: this.color,
|
|
15699
|
+
strokeWidth: this.size,
|
|
15700
|
+
arrows: this.arrows,
|
|
15701
|
+
});
|
|
15702
|
+
updatedLine.id = currentLine.id;
|
|
15703
|
+
updatedLine.workspaceId = currentLine.workspaceId;
|
|
15704
|
+
updatedLine.isCompleted = false;
|
|
15705
|
+
this._core.store.state.objects.update(updatedLine);
|
|
15706
|
+
}
|
|
14935
15707
|
}
|
|
14936
|
-
const clientX = Math.round(activePointers[0].clientX - this._core.store.offsetX);
|
|
14937
|
-
const clientY = Math.round(activePointers[0].clientY - this._core.store.offsetY);
|
|
14938
|
-
const text = KritzelText.create(this._core, this.fontSize, this.fontFamily);
|
|
14939
|
-
text.fontColor = this.fontColor;
|
|
14940
|
-
text.translateX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
14941
|
-
text.translateY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
14942
|
-
text.zIndex = this._core.store.currentZIndex;
|
|
14943
|
-
this._core.store.state.objects.insert(text);
|
|
14944
|
-
this._core.rerender();
|
|
14945
|
-
text.edit(event);
|
|
14946
15708
|
}
|
|
14947
15709
|
}
|
|
14948
15710
|
handlePointerUp(event) {
|
|
14949
15711
|
if (event.cancelable) {
|
|
14950
15712
|
event.preventDefault();
|
|
14951
15713
|
}
|
|
14952
|
-
|
|
15714
|
+
if (event.pointerType === 'mouse') {
|
|
15715
|
+
if (this._core.store.state.isDrawing) {
|
|
15716
|
+
this._core.store.state.isDrawing = false;
|
|
15717
|
+
const currentLine = this._core.store.currentLine;
|
|
15718
|
+
if (currentLine) {
|
|
15719
|
+
currentLine.isCompleted = true;
|
|
15720
|
+
currentLine.zIndex = this._core.store.currentZIndex;
|
|
15721
|
+
this._core.store.state.objects.update(currentLine);
|
|
15722
|
+
this._core.engine.emitObjectsChange();
|
|
15723
|
+
// Switch to selection tool and select the drawn line
|
|
15724
|
+
this.selectLineAndSwitchTool(currentLine);
|
|
15725
|
+
}
|
|
15726
|
+
}
|
|
15727
|
+
}
|
|
15728
|
+
if (event.pointerType === 'touch') {
|
|
15729
|
+
if (this._core.store.state.isDrawing) {
|
|
15730
|
+
this._core.store.state.isDrawing = false;
|
|
15731
|
+
const currentLine = this._core.store.currentLine;
|
|
15732
|
+
if (currentLine) {
|
|
15733
|
+
currentLine.isCompleted = true;
|
|
15734
|
+
currentLine.zIndex = this._core.store.currentZIndex;
|
|
15735
|
+
this._core.store.state.objects.update(currentLine);
|
|
15736
|
+
this._core.engine.emitObjectsChange();
|
|
15737
|
+
// Switch to selection tool and select the drawn line
|
|
15738
|
+
this.selectLineAndSwitchTool(currentLine);
|
|
15739
|
+
}
|
|
15740
|
+
}
|
|
15741
|
+
}
|
|
14953
15742
|
}
|
|
14954
|
-
|
|
14955
|
-
|
|
14956
|
-
|
|
14957
|
-
|
|
14958
|
-
|
|
14959
|
-
|
|
15743
|
+
selectLineAndSwitchTool(line) {
|
|
15744
|
+
// Create a selection group with the drawn line
|
|
15745
|
+
const selectionGroup = KritzelSelectionGroup.create(this._core);
|
|
15746
|
+
selectionGroup.addOrRemove(line);
|
|
15747
|
+
selectionGroup.isSelected = true;
|
|
15748
|
+
selectionGroup.rotation = line.rotation;
|
|
15749
|
+
// Add the selection group
|
|
15750
|
+
this._core.addSelectionGroup(selectionGroup);
|
|
15751
|
+
// Switch to selection tool
|
|
15752
|
+
this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
|
|
15753
|
+
// Rerender
|
|
15754
|
+
this._core.rerender();
|
|
14960
15755
|
}
|
|
14961
15756
|
}
|
|
14962
15757
|
|
|
14963
|
-
class
|
|
14964
|
-
|
|
14965
|
-
dragStartY;
|
|
14966
|
-
startX;
|
|
14967
|
-
startY;
|
|
14968
|
-
endX;
|
|
14969
|
-
endY;
|
|
14970
|
-
hasMoved = false;
|
|
14971
|
-
trackedPointerId = null;
|
|
15758
|
+
class KritzelEraserTool extends KritzelBaseTool {
|
|
15759
|
+
touchStartTimeout = null;
|
|
14972
15760
|
constructor(core) {
|
|
14973
15761
|
super(core);
|
|
14974
15762
|
}
|
|
14975
|
-
reset() {
|
|
14976
|
-
this.dragStartX = 0;
|
|
14977
|
-
this.dragStartY = 0;
|
|
14978
|
-
this.startX = 0;
|
|
14979
|
-
this.startY = 0;
|
|
14980
|
-
this.endX = 0;
|
|
14981
|
-
this.endY = 0;
|
|
14982
|
-
this.hasMoved = false;
|
|
14983
|
-
this.trackedPointerId = null;
|
|
14984
|
-
}
|
|
14985
|
-
cancelPendingDrag() {
|
|
14986
|
-
this._core.store.state.isDragging = false;
|
|
14987
|
-
this.reset();
|
|
14988
|
-
}
|
|
14989
15763
|
handlePointerDown(event) {
|
|
14990
15764
|
if (event.pointerType === 'mouse') {
|
|
14991
15765
|
if (KritzelEventHelper.isLeftClick(event)) {
|
|
14992
|
-
|
|
14993
|
-
if (selectionGroup?.isSelected && !this._core.store.state.isResizeHandleSelected && !this._core.store.state.isRotationHandleSelected) {
|
|
14994
|
-
const clientX = event.clientX - this._core.store.offsetX;
|
|
14995
|
-
const clientY = event.clientY - this._core.store.offsetY;
|
|
14996
|
-
this._core.store.state.isDragging = true;
|
|
14997
|
-
this.dragStartX = clientX;
|
|
14998
|
-
this.dragStartY = clientY;
|
|
14999
|
-
this.startX = this.dragStartX;
|
|
15000
|
-
this.startY = this.dragStartY;
|
|
15001
|
-
this.trackedPointerId = event.pointerId;
|
|
15002
|
-
}
|
|
15003
|
-
else {
|
|
15004
|
-
this.trackedPointerId = null;
|
|
15005
|
-
}
|
|
15006
|
-
}
|
|
15007
|
-
else {
|
|
15008
|
-
this.trackedPointerId = null;
|
|
15766
|
+
this._core.store.state.isErasing = true;
|
|
15009
15767
|
}
|
|
15010
15768
|
}
|
|
15011
15769
|
if (event.pointerType === 'touch') {
|
|
15012
|
-
|
|
15013
|
-
|
|
15014
|
-
|
|
15015
|
-
this.trackedPointerId = null;
|
|
15016
|
-
return;
|
|
15017
|
-
}
|
|
15018
|
-
const selectionGroup = this._core.store.selectionGroup;
|
|
15019
|
-
if (selectionGroup?.isSelected && !this._core.store.state.isResizeHandleSelected && !this._core.store.state.isRotationHandleSelected) {
|
|
15020
|
-
const x = Math.round(activePointers[0].clientX - this._core.store.offsetX);
|
|
15021
|
-
const y = Math.round(activePointers[0].clientY - this._core.store.offsetY);
|
|
15022
|
-
this.dragStartX = x;
|
|
15023
|
-
this.dragStartY = y;
|
|
15024
|
-
this.startX = x;
|
|
15025
|
-
this.startY = y;
|
|
15026
|
-
this.trackedPointerId = event.pointerId;
|
|
15027
|
-
}
|
|
15028
|
-
else {
|
|
15029
|
-
this.trackedPointerId = null;
|
|
15770
|
+
this.touchStartTimeout = setTimeout(() => {
|
|
15771
|
+
if (this._core.store.state.pointers.size === 1 && !this._core.store.state.isScaling) {
|
|
15772
|
+
this._core.store.state.isErasing = true;
|
|
15030
15773
|
}
|
|
15031
|
-
}
|
|
15774
|
+
}, 80);
|
|
15032
15775
|
}
|
|
15033
15776
|
}
|
|
15034
15777
|
handlePointerMove(event) {
|
|
15035
|
-
if (this.trackedPointerId === null || this.trackedPointerId !== event.pointerId) {
|
|
15036
|
-
return;
|
|
15037
|
-
}
|
|
15038
15778
|
if (event.pointerType === 'mouse') {
|
|
15039
|
-
|
|
15040
|
-
|
|
15041
|
-
|
|
15042
|
-
|
|
15043
|
-
|
|
15044
|
-
|
|
15045
|
-
|
|
15046
|
-
|
|
15047
|
-
|
|
15048
|
-
|
|
15049
|
-
selectionGroup.move(clientX, clientY, this.dragStartX, this.dragStartY);
|
|
15050
|
-
this.dragStartX = clientX;
|
|
15051
|
-
this.dragStartY = clientY;
|
|
15052
|
-
this.hasMoved = true;
|
|
15053
|
-
}
|
|
15779
|
+
if (this._core.store.state.isErasing) {
|
|
15780
|
+
const selectedObjects = this._core.getObjectsFromPointerEvent(event, '.object');
|
|
15781
|
+
if (selectedObjects.length === 0)
|
|
15782
|
+
return;
|
|
15783
|
+
const x = this._core.store.state.pointerX;
|
|
15784
|
+
const y = this._core.store.state.pointerY;
|
|
15785
|
+
selectedObjects.forEach(selectedObject => {
|
|
15786
|
+
selectedObject.markedForRemoval = selectedObject.hitTest(x, y);
|
|
15787
|
+
});
|
|
15788
|
+
this._core.rerender();
|
|
15054
15789
|
}
|
|
15055
15790
|
}
|
|
15056
15791
|
if (event.pointerType === 'touch') {
|
|
15057
|
-
|
|
15058
|
-
|
|
15059
|
-
|
|
15060
|
-
|
|
15061
|
-
|
|
15062
|
-
|
|
15063
|
-
|
|
15064
|
-
const x =
|
|
15065
|
-
const y =
|
|
15066
|
-
|
|
15067
|
-
|
|
15068
|
-
|
|
15069
|
-
|
|
15070
|
-
const moveDeltaY = Math.abs(y - this.startY);
|
|
15071
|
-
const moveThreshold = 5;
|
|
15072
|
-
if (moveDeltaX > moveThreshold || moveDeltaY > moveThreshold) {
|
|
15073
|
-
clearTimeout(this._core.store.state.longTouchTimeout);
|
|
15074
|
-
selectionGroup.move(x, y, this.dragStartX, this.dragStartY);
|
|
15075
|
-
this.dragStartX = x;
|
|
15076
|
-
this.dragStartY = y;
|
|
15077
|
-
this.hasMoved = true;
|
|
15078
|
-
}
|
|
15792
|
+
if (this._core.store.state.pointers.size === 1 && this._core.store.state.isErasing) {
|
|
15793
|
+
const shadowRoot = this._core.store.state.host?.shadowRoot;
|
|
15794
|
+
if (!shadowRoot)
|
|
15795
|
+
return;
|
|
15796
|
+
const selectedObjects = this._core.getObjectsFromPointerEvent(event, '.object');
|
|
15797
|
+
if (selectedObjects.length === 0)
|
|
15798
|
+
return;
|
|
15799
|
+
const x = this._core.store.state.pointerX;
|
|
15800
|
+
const y = this._core.store.state.pointerY;
|
|
15801
|
+
selectedObjects.forEach(selectedObject => {
|
|
15802
|
+
selectedObject.markedForRemoval = selectedObject.hitTest(x, y);
|
|
15803
|
+
});
|
|
15804
|
+
this._core.rerender();
|
|
15079
15805
|
}
|
|
15080
15806
|
}
|
|
15081
15807
|
}
|
|
15082
15808
|
handlePointerUp(event) {
|
|
15083
|
-
if (this.trackedPointerId === null || this.trackedPointerId !== event.pointerId) {
|
|
15084
|
-
return;
|
|
15085
|
-
}
|
|
15086
15809
|
if (event.pointerType === 'mouse') {
|
|
15087
|
-
if (this._core.store.state.
|
|
15088
|
-
this._core.store.
|
|
15089
|
-
|
|
15090
|
-
|
|
15091
|
-
this._core.
|
|
15092
|
-
|
|
15810
|
+
if (this._core.store.state.isErasing) {
|
|
15811
|
+
const objectsToRemove = this._core.store.allObjects.filter(object => object.markedForRemoval);
|
|
15812
|
+
objectsToRemove.forEach(object => {
|
|
15813
|
+
object.markedForRemoval = false;
|
|
15814
|
+
this._core.removeObject(object);
|
|
15815
|
+
});
|
|
15816
|
+
if (objectsToRemove.length > 0) {
|
|
15817
|
+
this._core.rerender();
|
|
15093
15818
|
}
|
|
15819
|
+
this._core.store.state.isErasing = false;
|
|
15820
|
+
this._core.engine.emitObjectsChange();
|
|
15094
15821
|
}
|
|
15095
15822
|
}
|
|
15096
15823
|
if (event.pointerType === 'touch') {
|
|
15097
|
-
|
|
15098
|
-
|
|
15099
|
-
|
|
15100
|
-
|
|
15101
|
-
|
|
15102
|
-
this._core.
|
|
15824
|
+
clearTimeout(this.touchStartTimeout);
|
|
15825
|
+
if (this._core.store.state.isErasing) {
|
|
15826
|
+
const objectsToRemove = this._core.store.allObjects.filter(object => object.markedForRemoval);
|
|
15827
|
+
objectsToRemove.forEach(object => {
|
|
15828
|
+
object.markedForRemoval = false;
|
|
15829
|
+
this._core.removeObject(object);
|
|
15830
|
+
});
|
|
15831
|
+
if (objectsToRemove.length > 0) {
|
|
15832
|
+
this._core.rerender();
|
|
15103
15833
|
}
|
|
15834
|
+
this._core.store.state.isErasing = false;
|
|
15835
|
+
this._core.engine.emitObjectsChange();
|
|
15104
15836
|
}
|
|
15105
15837
|
}
|
|
15106
|
-
this.reset();
|
|
15107
15838
|
}
|
|
15108
15839
|
}
|
|
15109
15840
|
|
|
15110
|
-
|
|
15111
|
-
|
|
15112
|
-
|
|
15113
|
-
|
|
15114
|
-
|
|
15115
|
-
|
|
15116
|
-
})(KritzelHandleType || (KritzelHandleType = {}));
|
|
15841
|
+
/**
|
|
15842
|
+
* Browser Image Compression
|
|
15843
|
+
* v2.0.2
|
|
15844
|
+
* by Donald <donaldcwl@gmail.com>
|
|
15845
|
+
* https://github.com/Donaldcwl/browser-image-compression
|
|
15846
|
+
*/
|
|
15117
15847
|
|
|
15118
|
-
class KritzelResizeHandler extends KritzelBaseHandler {
|
|
15119
|
-
|
|
15120
|
-
|
|
15121
|
-
|
|
15122
|
-
|
|
15123
|
-
hasResized = false;
|
|
15848
|
+
function _mergeNamespaces$1(e,t){return t.forEach((function(t){t&&"string"!=typeof t&&!Array.isArray(t)&&Object.keys(t).forEach((function(r){if("default"!==r&&!(r in e)){var i=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(e,r,i.get?i:{enumerable:true,get:function(){return t[r]}});}}));})),Object.freeze(e)}function copyExifWithoutOrientation(e,t){return new Promise((function(r,i){let o;return getApp1Segment(e).then((function(e){try{return o=e,r(new Blob([t.slice(0,2),o,t.slice(2)],{type:"image/jpeg"}))}catch(e){return i(e)}}),i)}))}const getApp1Segment=e=>new Promise(((t,r)=>{const i=new FileReader;i.addEventListener("load",(({target:{result:e}})=>{const i=new DataView(e);let o=0;if(65496!==i.getUint16(o))return r("not a valid JPEG");for(o+=2;;){const a=i.getUint16(o);if(65498===a)break;const s=i.getUint16(o+2);if(65505===a&&1165519206===i.getUint32(o+4)){const a=o+10;let f;switch(i.getUint16(a)){case 18761:f=true;break;case 19789:f=false;break;default:return r("TIFF header contains invalid endian")}if(42!==i.getUint16(a+2,f))return r("TIFF header contains invalid version");const l=i.getUint32(a+4,f),c=a+l+2+12*i.getUint16(a+l,f);for(let e=a+l+2;e<c;e+=12){if(274==i.getUint16(e,f)){if(3!==i.getUint16(e+2,f))return r("Orientation data type is invalid");if(1!==i.getUint32(e+4,f))return r("Orientation data count is invalid");i.setUint16(e+8,1,f);break}}return t(e.slice(o,o+2+s))}o+=2+s;}return t(new Blob)})),i.readAsArrayBuffer(e);}));var e={},t={get exports(){return e},set exports(t){e=t;}};!function(e){var r,i,UZIP={};t.exports=UZIP,UZIP.parse=function(e,t){for(var r=UZIP.bin.readUshort,i=UZIP.bin.readUint,o=0,a={},s=new Uint8Array(e),f=s.length-4;101010256!=i(s,f);)f--;o=f;o+=4;var l=r(s,o+=4);r(s,o+=2);var c=i(s,o+=2),u=i(s,o+=4);o+=4,o=u;for(var h=0;h<l;h++){i(s,o),o+=4,o+=4,o+=4,i(s,o+=4);c=i(s,o+=4);var d=i(s,o+=4),A=r(s,o+=4),g=r(s,o+2),p=r(s,o+4);o+=6;var m=i(s,o+=8);o+=4,o+=A+g+p,UZIP._readLocal(s,m,a,c,d,t);}return a},UZIP._readLocal=function(e,t,r,i,o,a){var s=UZIP.bin.readUshort,f=UZIP.bin.readUint;f(e,t),s(e,t+=4),s(e,t+=2);var l=s(e,t+=2);f(e,t+=2),f(e,t+=4),t+=4;var c=s(e,t+=8),u=s(e,t+=2);t+=2;var h=UZIP.bin.readUTF8(e,t,c);if(t+=c,t+=u,a)r[h]={size:o,csize:i};else {var d=new Uint8Array(e.buffer,t);if(0==l)r[h]=new Uint8Array(d.buffer.slice(t,t+i));else {if(8!=l)throw "unknown compression method: "+l;var A=new Uint8Array(o);UZIP.inflateRaw(d,A),r[h]=A;}}},UZIP.inflateRaw=function(e,t){return UZIP.F.inflate(e,t)},UZIP.inflate=function(e,t){return UZIP.inflateRaw(new Uint8Array(e.buffer,e.byteOffset+2,e.length-6),t)},UZIP.deflate=function(e,t){null==t&&(t={level:6});var r=0,i=new Uint8Array(50+Math.floor(1.1*e.length));i[r]=120,i[r+1]=156,r+=2,r=UZIP.F.deflateRaw(e,i,r,t.level);var o=UZIP.adler(e,0,e.length);return i[r+0]=o>>>24&255,i[r+1]=o>>>16&255,i[r+2]=o>>>8&255,i[r+3]=o>>>0&255,new Uint8Array(i.buffer,0,r+4)},UZIP.deflateRaw=function(e,t){null==t&&(t={level:6});var r=new Uint8Array(50+Math.floor(1.1*e.length)),i=UZIP.F.deflateRaw(e,r,i,t.level);return new Uint8Array(r.buffer,0,i)},UZIP.encode=function(e,t){null==t&&(t=false);var r=0,i=UZIP.bin.writeUint,o=UZIP.bin.writeUshort,a={};for(var s in e){var f=!UZIP._noNeed(s)&&!t,l=e[s],c=UZIP.crc.crc(l,0,l.length);a[s]={cpr:f,usize:l.length,crc:c,file:f?UZIP.deflateRaw(l):l};}for(var s in a)r+=a[s].file.length+30+46+2*UZIP.bin.sizeUTF8(s);r+=22;var u=new Uint8Array(r),h=0,d=[];for(var s in a){var A=a[s];d.push(h),h=UZIP._writeHeader(u,h,s,A,0);}var g=0,p=h;for(var s in a){A=a[s];d.push(h),h=UZIP._writeHeader(u,h,s,A,1,d[g++]);}var m=h-p;return i(u,h,101010256),h+=4,o(u,h+=4,g),o(u,h+=2,g),i(u,h+=2,m),i(u,h+=4,p),h+=4,h+=2,u.buffer},UZIP._noNeed=function(e){var t=e.split(".").pop().toLowerCase();return -1!="png,jpg,jpeg,zip".indexOf(t)},UZIP._writeHeader=function(e,t,r,i,o,a){var s=UZIP.bin.writeUint,f=UZIP.bin.writeUshort,l=i.file;return s(e,t,0==o?67324752:33639248),t+=4,1==o&&(t+=2),f(e,t,20),f(e,t+=2,0),f(e,t+=2,i.cpr?8:0),s(e,t+=2,0),s(e,t+=4,i.crc),s(e,t+=4,l.length),s(e,t+=4,i.usize),f(e,t+=4,UZIP.bin.sizeUTF8(r)),f(e,t+=2,0),t+=2,1==o&&(t+=2,t+=2,s(e,t+=6,a),t+=4),t+=UZIP.bin.writeUTF8(e,t,r),0==o&&(e.set(l,t),t+=l.length),t},UZIP.crc={table:function(){for(var e=new Uint32Array(256),t=0;t<256;t++){for(var r=t,i=0;i<8;i++)1&r?r=3988292384^r>>>1:r>>>=1;e[t]=r;}return e}(),update:function(e,t,r,i){for(var o=0;o<i;o++)e=UZIP.crc.table[255&(e^t[r+o])]^e>>>8;return e},crc:function(e,t,r){return 4294967295^UZIP.crc.update(4294967295,e,t,r)}},UZIP.adler=function(e,t,r){for(var i=1,o=0,a=t,s=t+r;a<s;){for(var f=Math.min(a+5552,s);a<f;)o+=i+=e[a++];i%=65521,o%=65521;}return o<<16|i},UZIP.bin={readUshort:function(e,t){return e[t]|e[t+1]<<8},writeUshort:function(e,t,r){e[t]=255&r,e[t+1]=r>>8&255;},readUint:function(e,t){return 16777216*e[t+3]+(e[t+2]<<16|e[t+1]<<8|e[t])},writeUint:function(e,t,r){e[t]=255&r,e[t+1]=r>>8&255,e[t+2]=r>>16&255,e[t+3]=r>>24&255;},readASCII:function(e,t,r){for(var i="",o=0;o<r;o++)i+=String.fromCharCode(e[t+o]);return i},writeASCII:function(e,t,r){for(var i=0;i<r.length;i++)e[t+i]=r.charCodeAt(i);},pad:function(e){return e.length<2?"0"+e:e},readUTF8:function(e,t,r){for(var i,o="",a=0;a<r;a++)o+="%"+UZIP.bin.pad(e[t+a].toString(16));try{i=decodeURIComponent(o);}catch(i){return UZIP.bin.readASCII(e,t,r)}return i},writeUTF8:function(e,t,r){for(var i=r.length,o=0,a=0;a<i;a++){var s=r.charCodeAt(a);if(0==(4294967168&s))e[t+o]=s,o++;else if(0==(4294965248&s))e[t+o]=192|s>>6,e[t+o+1]=128|s>>0&63,o+=2;else if(0==(4294901760&s))e[t+o]=224|s>>12,e[t+o+1]=128|s>>6&63,e[t+o+2]=128|s>>0&63,o+=3;else {if(0!=(4292870144&s))throw "e";e[t+o]=240|s>>18,e[t+o+1]=128|s>>12&63,e[t+o+2]=128|s>>6&63,e[t+o+3]=128|s>>0&63,o+=4;}}return o},sizeUTF8:function(e){for(var t=e.length,r=0,i=0;i<t;i++){var o=e.charCodeAt(i);if(0==(4294967168&o))r++;else if(0==(4294965248&o))r+=2;else if(0==(4294901760&o))r+=3;else {if(0!=(4292870144&o))throw "e";r+=4;}}return r}},UZIP.F={},UZIP.F.deflateRaw=function(e,t,r,i){var o=[[0,0,0,0,0],[4,4,8,4,0],[4,5,16,8,0],[4,6,16,16,0],[4,10,16,32,0],[8,16,32,32,0],[8,16,128,128,0],[8,32,128,256,0],[32,128,258,1024,1],[32,258,258,4096,1]][i],a=UZIP.F.U,s=UZIP.F._goodIndex;var f=UZIP.F._putsE,l=0,c=r<<3,u=0,h=e.length;if(0==i){for(;l<h;){f(t,c,l+(_=Math.min(65535,h-l))==h?1:0),c=UZIP.F._copyExact(e,l,_,t,c+8),l+=_;}return c>>>3}var d=a.lits,A=a.strt,g=a.prev,p=0,m=0,w=0,v=0,b=0,y=0;for(h>2&&(A[y=UZIP.F._hash(e,0)]=0),l=0;l<h;l++){if(b=y,l+1<h-2){y=UZIP.F._hash(e,l+1);var E=l+1&32767;g[E]=A[y],A[y]=E;}if(u<=l){(p>14e3||m>26697)&&h-l>100&&(u<l&&(d[p]=l-u,p+=2,u=l),c=UZIP.F._writeBlock(l==h-1||u==h?1:0,d,p,v,e,w,l-w,t,c),p=m=v=0,w=l);var F=0;l<h-2&&(F=UZIP.F._bestMatch(e,l,g,b,Math.min(o[2],h-l),o[3]));var _=F>>>16,B=65535&F;if(0!=F){B=65535&F;var U=s(_=F>>>16,a.of0);a.lhst[257+U]++;var C=s(B,a.df0);a.dhst[C]++,v+=a.exb[U]+a.dxb[C],d[p]=_<<23|l-u,d[p+1]=B<<16|U<<8|C,p+=2,u=l+_;}else a.lhst[e[l]]++;m++;}}for(w==l&&0!=e.length||(u<l&&(d[p]=l-u,p+=2,u=l),c=UZIP.F._writeBlock(1,d,p,v,e,w,l-w,t,c),p=0,m=0,p=m=v=0,w=l);0!=(7&c);)c++;return c>>>3},UZIP.F._bestMatch=function(e,t,r,i,o,a){var s=32767&t,f=r[s],l=s-f+32768&32767;if(f==s||i!=UZIP.F._hash(e,t-l))return 0;for(var c=0,u=0,h=Math.min(32767,t);l<=h&&0!=--a&&f!=s;){if(0==c||e[t+c]==e[t+c-l]){var d=UZIP.F._howLong(e,t,l);if(d>c){if(u=l,(c=d)>=o)break;l+2<d&&(d=l+2);for(var A=0,g=0;g<d-2;g++){var p=t-l+g+32768&32767,m=p-r[p]+32768&32767;m>A&&(A=m,f=p);}}}l+=(s=f)-(f=r[s])+32768&32767;}return c<<16|u},UZIP.F._howLong=function(e,t,r){if(e[t]!=e[t-r]||e[t+1]!=e[t+1-r]||e[t+2]!=e[t+2-r])return 0;var i=t,o=Math.min(e.length,t+258);for(t+=3;t<o&&e[t]==e[t-r];)t++;return t-i},UZIP.F._hash=function(e,t){return (e[t]<<8|e[t+1])+(e[t+2]<<4)&65535},UZIP.saved=0,UZIP.F._writeBlock=function(e,t,r,i,o,a,s,f,l){var c,u,h,d,A,g,p,m,w,v=UZIP.F.U,b=UZIP.F._putsF,y=UZIP.F._putsE;v.lhst[256]++,u=(c=UZIP.F.getTrees())[0],h=c[1],d=c[2],A=c[3],g=c[4],p=c[5],m=c[6],w=c[7];var E=32+(0==(l+3&7)?0:8-(l+3&7))+(s<<3),F=i+UZIP.F.contSize(v.fltree,v.lhst)+UZIP.F.contSize(v.fdtree,v.dhst),_=i+UZIP.F.contSize(v.ltree,v.lhst)+UZIP.F.contSize(v.dtree,v.dhst);_+=14+3*p+UZIP.F.contSize(v.itree,v.ihst)+(2*v.ihst[16]+3*v.ihst[17]+7*v.ihst[18]);for(var B=0;B<286;B++)v.lhst[B]=0;for(B=0;B<30;B++)v.dhst[B]=0;for(B=0;B<19;B++)v.ihst[B]=0;var U=E<F&&E<_?0:F<_?1:2;if(b(f,l,e),b(f,l+1,U),l+=3,0==U){for(;0!=(7&l);)l++;l=UZIP.F._copyExact(o,a,s,f,l);}else {var C,I;if(1==U&&(C=v.fltree,I=v.fdtree),2==U){UZIP.F.makeCodes(v.ltree,u),UZIP.F.revCodes(v.ltree,u),UZIP.F.makeCodes(v.dtree,h),UZIP.F.revCodes(v.dtree,h),UZIP.F.makeCodes(v.itree,d),UZIP.F.revCodes(v.itree,d),C=v.ltree,I=v.dtree,y(f,l,A-257),y(f,l+=5,g-1),y(f,l+=5,p-4),l+=4;for(var Q=0;Q<p;Q++)y(f,l+3*Q,v.itree[1+(v.ordr[Q]<<1)]);l+=3*p,l=UZIP.F._codeTiny(m,v.itree,f,l),l=UZIP.F._codeTiny(w,v.itree,f,l);}for(var M=a,x=0;x<r;x+=2){for(var S=t[x],R=S>>>23,T=M+(8388607&S);M<T;)l=UZIP.F._writeLit(o[M++],C,f,l);if(0!=R){var O=t[x+1],P=O>>16,H=O>>8&255,L=255&O;y(f,l=UZIP.F._writeLit(257+H,C,f,l),R-v.of0[H]),l+=v.exb[H],b(f,l=UZIP.F._writeLit(L,I,f,l),P-v.df0[L]),l+=v.dxb[L],M+=R;}}l=UZIP.F._writeLit(256,C,f,l);}return l},UZIP.F._copyExact=function(e,t,r,i,o){var a=o>>>3;return i[a]=r,i[a+1]=r>>>8,i[a+2]=255-i[a],i[a+3]=255-i[a+1],a+=4,i.set(new Uint8Array(e.buffer,t,r),a),o+(r+4<<3)},UZIP.F.getTrees=function(){for(var e=UZIP.F.U,t=UZIP.F._hufTree(e.lhst,e.ltree,15),r=UZIP.F._hufTree(e.dhst,e.dtree,15),i=[],o=UZIP.F._lenCodes(e.ltree,i),a=[],s=UZIP.F._lenCodes(e.dtree,a),f=0;f<i.length;f+=2)e.ihst[i[f]]++;for(f=0;f<a.length;f+=2)e.ihst[a[f]]++;for(var l=UZIP.F._hufTree(e.ihst,e.itree,7),c=19;c>4&&0==e.itree[1+(e.ordr[c-1]<<1)];)c--;return [t,r,l,o,s,c,i,a]},UZIP.F.getSecond=function(e){for(var t=[],r=0;r<e.length;r+=2)t.push(e[r+1]);return t},UZIP.F.nonZero=function(e){for(var t="",r=0;r<e.length;r+=2)0!=e[r+1]&&(t+=(r>>1)+",");return t},UZIP.F.contSize=function(e,t){for(var r=0,i=0;i<t.length;i++)r+=t[i]*e[1+(i<<1)];return r},UZIP.F._codeTiny=function(e,t,r,i){for(var o=0;o<e.length;o+=2){var a=e[o],s=e[o+1];i=UZIP.F._writeLit(a,t,r,i);var f=16==a?2:17==a?3:7;a>15&&(UZIP.F._putsE(r,i,s,f),i+=f);}return i},UZIP.F._lenCodes=function(e,t){for(var r=e.length;2!=r&&0==e[r-1];)r-=2;for(var i=0;i<r;i+=2){var o=e[i+1],a=i+3<r?e[i+3]:-1,s=i+5<r?e[i+5]:-1,f=0==i?-1:e[i-1];if(0==o&&a==o&&s==o){for(var l=i+5;l+2<r&&e[l+2]==o;)l+=2;(c=Math.min(l+1-i>>>1,138))<11?t.push(17,c-3):t.push(18,c-11),i+=2*c-2;}else if(o==f&&a==o&&s==o){for(l=i+5;l+2<r&&e[l+2]==o;)l+=2;var c=Math.min(l+1-i>>>1,6);t.push(16,c-3),i+=2*c-2;}else t.push(o,0);}return r>>>1},UZIP.F._hufTree=function(e,t,r){var i=[],o=e.length,a=t.length,s=0;for(s=0;s<a;s+=2)t[s]=0,t[s+1]=0;for(s=0;s<o;s++)0!=e[s]&&i.push({lit:s,f:e[s]});var f=i.length,l=i.slice(0);if(0==f)return 0;if(1==f){var c=i[0].lit;l=0==c?1:0;return t[1+(c<<1)]=1,t[1+(l<<1)]=1,1}i.sort((function(e,t){return e.f-t.f}));var u=i[0],h=i[1],d=0,A=1,g=2;for(i[0]={lit:-1,f:u.f+h.f,l:u,r:h,d:0};A!=f-1;)u=d!=A&&(g==f||i[d].f<i[g].f)?i[d++]:i[g++],h=d!=A&&(g==f||i[d].f<i[g].f)?i[d++]:i[g++],i[A++]={lit:-1,f:u.f+h.f,l:u,r:h};var p=UZIP.F.setDepth(i[A-1],0);for(p>r&&(UZIP.F.restrictDepth(l,r,p),p=r),s=0;s<f;s++)t[1+(l[s].lit<<1)]=l[s].d;return p},UZIP.F.setDepth=function(e,t){return -1!=e.lit?(e.d=t,t):Math.max(UZIP.F.setDepth(e.l,t+1),UZIP.F.setDepth(e.r,t+1))},UZIP.F.restrictDepth=function(e,t,r){var i=0,o=1<<r-t,a=0;for(e.sort((function(e,t){return t.d==e.d?e.f-t.f:t.d-e.d})),i=0;i<e.length&&e[i].d>t;i++){var s=e[i].d;e[i].d=t,a+=o-(1<<r-s);}for(a>>>=r-t;a>0;){(s=e[i].d)<t?(e[i].d++,a-=1<<t-s-1):i++;}for(;i>=0;i--)e[i].d==t&&a<0&&(e[i].d--,a++);0!=a&&console.log("debt left");},UZIP.F._goodIndex=function(e,t){var r=0;return t[16|r]<=e&&(r|=16),t[8|r]<=e&&(r|=8),t[4|r]<=e&&(r|=4),t[2|r]<=e&&(r|=2),t[1|r]<=e&&(r|=1),r},UZIP.F._writeLit=function(e,t,r,i){return UZIP.F._putsF(r,i,t[e<<1]),i+t[1+(e<<1)]},UZIP.F.inflate=function(e,t){var r=Uint8Array;if(3==e[0]&&0==e[1])return t||new r(0);var i=UZIP.F,o=i._bitsF,a=i._bitsE,s=i._decodeTiny,f=i.makeCodes,l=i.codes2map,c=i._get17,u=i.U,h=null==t;h&&(t=new r(e.length>>>2<<3));for(var d,A,g=0,p=0,m=0,w=0,v=0,b=0,y=0,E=0,F=0;0==g;)if(g=o(e,F,1),p=o(e,F+1,2),F+=3,0!=p){if(h&&(t=UZIP.F._check(t,E+(1<<17))),1==p&&(d=u.flmap,A=u.fdmap,b=511,y=31),2==p){m=a(e,F,5)+257,w=a(e,F+5,5)+1,v=a(e,F+10,4)+4,F+=14;for(var _=0;_<38;_+=2)u.itree[_]=0,u.itree[_+1]=0;var B=1;for(_=0;_<v;_++){var U=a(e,F+3*_,3);u.itree[1+(u.ordr[_]<<1)]=U,U>B&&(B=U);}F+=3*v,f(u.itree,B),l(u.itree,B,u.imap),d=u.lmap,A=u.dmap,F=s(u.imap,(1<<B)-1,m+w,e,F,u.ttree);var C=i._copyOut(u.ttree,0,m,u.ltree);b=(1<<C)-1;var I=i._copyOut(u.ttree,m,w,u.dtree);y=(1<<I)-1,f(u.ltree,C),l(u.ltree,C,d),f(u.dtree,I),l(u.dtree,I,A);}for(;;){var Q=d[c(e,F)&b];F+=15&Q;var M=Q>>>4;if(M>>>8==0)t[E++]=M;else {if(256==M)break;var x=E+M-254;if(M>264){var S=u.ldef[M-257];x=E+(S>>>3)+a(e,F,7&S),F+=7&S;}var R=A[c(e,F)&y];F+=15&R;var T=R>>>4,O=u.ddef[T],P=(O>>>4)+o(e,F,15&O);for(F+=15&O,h&&(t=UZIP.F._check(t,E+(1<<17)));E<x;)t[E]=t[E++-P],t[E]=t[E++-P],t[E]=t[E++-P],t[E]=t[E++-P];E=x;}}}else {0!=(7&F)&&(F+=8-(7&F));var H=4+(F>>>3),L=e[H-4]|e[H-3]<<8;h&&(t=UZIP.F._check(t,E+L)),t.set(new r(e.buffer,e.byteOffset+H,L),E),F=H+L<<3,E+=L;}return t.length==E?t:t.slice(0,E)},UZIP.F._check=function(e,t){var r=e.length;if(t<=r)return e;var i=new Uint8Array(Math.max(r<<1,t));return i.set(e,0),i},UZIP.F._decodeTiny=function(e,t,r,i,o,a){for(var s=UZIP.F._bitsE,f=UZIP.F._get17,l=0;l<r;){var c=e[f(i,o)&t];o+=15&c;var u=c>>>4;if(u<=15)a[l]=u,l++;else {var h=0,d=0;16==u?(d=3+s(i,o,2),o+=2,h=a[l-1]):17==u?(d=3+s(i,o,3),o+=3):18==u&&(d=11+s(i,o,7),o+=7);for(var A=l+d;l<A;)a[l]=h,l++;}}return o},UZIP.F._copyOut=function(e,t,r,i){for(var o=0,a=0,s=i.length>>>1;a<r;){var f=e[a+t];i[a<<1]=0,i[1+(a<<1)]=f,f>o&&(o=f),a++;}for(;a<s;)i[a<<1]=0,i[1+(a<<1)]=0,a++;return o},UZIP.F.makeCodes=function(e,t){for(var r,i,o,a,s=UZIP.F.U,f=e.length,l=s.bl_count,c=0;c<=t;c++)l[c]=0;for(c=1;c<f;c+=2)l[e[c]]++;var u=s.next_code;for(r=0,l[0]=0,i=1;i<=t;i++)r=r+l[i-1]<<1,u[i]=r;for(o=0;o<f;o+=2)0!=(a=e[o+1])&&(e[o]=u[a],u[a]++);},UZIP.F.codes2map=function(e,t,r){for(var i=e.length,o=UZIP.F.U.rev15,a=0;a<i;a+=2)if(0!=e[a+1])for(var s=a>>1,f=e[a+1],l=s<<4|f,c=t-f,u=e[a]<<c,h=u+(1<<c);u!=h;){r[o[u]>>>15-t]=l,u++;}},UZIP.F.revCodes=function(e,t){for(var r=UZIP.F.U.rev15,i=15-t,o=0;o<e.length;o+=2){var a=e[o]<<t-e[o+1];e[o]=r[a]>>>i;}},UZIP.F._putsE=function(e,t,r){r<<=7&t;var i=t>>>3;e[i]|=r,e[i+1]|=r>>>8;},UZIP.F._putsF=function(e,t,r){r<<=7&t;var i=t>>>3;e[i]|=r,e[i+1]|=r>>>8,e[i+2]|=r>>>16;},UZIP.F._bitsE=function(e,t,r){return (e[t>>>3]|e[1+(t>>>3)]<<8)>>>(7&t)&(1<<r)-1},UZIP.F._bitsF=function(e,t,r){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16)>>>(7&t)&(1<<r)-1},UZIP.F._get17=function(e,t){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16)>>>(7&t)},UZIP.F._get25=function(e,t){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16|e[3+(t>>>3)]<<24)>>>(7&t)},UZIP.F.U=(r=Uint16Array,i=Uint32Array,{next_code:new r(16),bl_count:new r(16),ordr:[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],of0:[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,999,999,999],exb:[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0],ldef:new r(32),df0:[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,65535,65535],dxb:[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0],ddef:new i(32),flmap:new r(512),fltree:[],fdmap:new r(32),fdtree:[],lmap:new r(32768),ltree:[],ttree:[],dmap:new r(32768),dtree:[],imap:new r(512),itree:[],rev15:new r(32768),lhst:new i(286),dhst:new i(30),ihst:new i(19),lits:new i(15e3),strt:new r(65536),prev:new r(32768)}),function(){for(var e=UZIP.F.U,t=0;t<32768;t++){var r=t;r=(4278255360&(r=(4042322160&(r=(3435973836&(r=(2863311530&r)>>>1|(1431655765&r)<<1))>>>2|(858993459&r)<<2))>>>4|(252645135&r)<<4))>>>8|(16711935&r)<<8,e.rev15[t]=(r>>>16|r<<16)>>>17;}function pushV(e,t,r){for(;0!=t--;)e.push(0,r);}for(t=0;t<32;t++)e.ldef[t]=e.of0[t]<<3|e.exb[t],e.ddef[t]=e.df0[t]<<4|e.dxb[t];pushV(e.fltree,144,8),pushV(e.fltree,112,9),pushV(e.fltree,24,7),pushV(e.fltree,8,8),UZIP.F.makeCodes(e.fltree,9),UZIP.F.codes2map(e.fltree,9,e.flmap),UZIP.F.revCodes(e.fltree,9),pushV(e.fdtree,32,5),UZIP.F.makeCodes(e.fdtree,5),UZIP.F.codes2map(e.fdtree,5,e.fdmap),UZIP.F.revCodes(e.fdtree,5),pushV(e.itree,19,0),pushV(e.ltree,286,0),pushV(e.dtree,30,0),pushV(e.ttree,320,0);}();}();var UZIP=_mergeNamespaces$1({__proto__:null,default:e},[e]);const UPNG=function(){var e={nextZero(e,t){for(;0!=e[t];)t++;return t},readUshort:(e,t)=>e[t]<<8|e[t+1],writeUshort(e,t,r){e[t]=r>>8&255,e[t+1]=255&r;},readUint:(e,t)=>16777216*e[t]+(e[t+1]<<16|e[t+2]<<8|e[t+3]),writeUint(e,t,r){e[t]=r>>24&255,e[t+1]=r>>16&255,e[t+2]=r>>8&255,e[t+3]=255&r;},readASCII(e,t,r){let i="";for(let o=0;o<r;o++)i+=String.fromCharCode(e[t+o]);return i},writeASCII(e,t,r){for(let i=0;i<r.length;i++)e[t+i]=r.charCodeAt(i);},readBytes(e,t,r){const i=[];for(let o=0;o<r;o++)i.push(e[t+o]);return i},pad:e=>e.length<2?`0${e}`:e,readUTF8(t,r,i){let o,a="";for(let o=0;o<i;o++)a+=`%${e.pad(t[r+o].toString(16))}`;try{o=decodeURIComponent(a);}catch(o){return e.readASCII(t,r,i)}return o}};function decodeImage(t,r,i,o){const a=r*i,s=_getBPP(o),f=Math.ceil(r*s/8),l=new Uint8Array(4*a),c=new Uint32Array(l.buffer),{ctype:u}=o,{depth:h}=o,d=e.readUshort;if(6==u){const e=a<<2;if(8==h)for(var A=0;A<e;A+=4)l[A]=t[A],l[A+1]=t[A+1],l[A+2]=t[A+2],l[A+3]=t[A+3];if(16==h)for(A=0;A<e;A++)l[A]=t[A<<1];}else if(2==u){const e=o.tabs.tRNS;if(null==e){if(8==h)for(A=0;A<a;A++){var g=3*A;c[A]=255<<24|t[g+2]<<16|t[g+1]<<8|t[g];}if(16==h)for(A=0;A<a;A++){g=6*A;c[A]=255<<24|t[g+4]<<16|t[g+2]<<8|t[g];}}else {var p=e[0];const r=e[1],i=e[2];if(8==h)for(A=0;A<a;A++){var m=A<<2;g=3*A;c[A]=255<<24|t[g+2]<<16|t[g+1]<<8|t[g],t[g]==p&&t[g+1]==r&&t[g+2]==i&&(l[m+3]=0);}if(16==h)for(A=0;A<a;A++){m=A<<2,g=6*A;c[A]=255<<24|t[g+4]<<16|t[g+2]<<8|t[g],d(t,g)==p&&d(t,g+2)==r&&d(t,g+4)==i&&(l[m+3]=0);}}}else if(3==u){const e=o.tabs.PLTE,s=o.tabs.tRNS,c=s?s.length:0;if(1==h)for(var w=0;w<i;w++){var v=w*f,b=w*r;for(A=0;A<r;A++){m=b+A<<2;var y=3*(E=t[v+(A>>3)]>>7-((7&A)<<0)&1);l[m]=e[y],l[m+1]=e[y+1],l[m+2]=e[y+2],l[m+3]=E<c?s[E]:255;}}if(2==h)for(w=0;w<i;w++)for(v=w*f,b=w*r,A=0;A<r;A++){m=b+A<<2,y=3*(E=t[v+(A>>2)]>>6-((3&A)<<1)&3);l[m]=e[y],l[m+1]=e[y+1],l[m+2]=e[y+2],l[m+3]=E<c?s[E]:255;}if(4==h)for(w=0;w<i;w++)for(v=w*f,b=w*r,A=0;A<r;A++){m=b+A<<2,y=3*(E=t[v+(A>>1)]>>4-((1&A)<<2)&15);l[m]=e[y],l[m+1]=e[y+1],l[m+2]=e[y+2],l[m+3]=E<c?s[E]:255;}if(8==h)for(A=0;A<a;A++){var E;m=A<<2,y=3*(E=t[A]);l[m]=e[y],l[m+1]=e[y+1],l[m+2]=e[y+2],l[m+3]=E<c?s[E]:255;}}else if(4==u){if(8==h)for(A=0;A<a;A++){m=A<<2;var F=t[_=A<<1];l[m]=F,l[m+1]=F,l[m+2]=F,l[m+3]=t[_+1];}if(16==h)for(A=0;A<a;A++){var _;m=A<<2,F=t[_=A<<2];l[m]=F,l[m+1]=F,l[m+2]=F,l[m+3]=t[_+2];}}else if(0==u)for(p=o.tabs.tRNS?o.tabs.tRNS:-1,w=0;w<i;w++){const e=w*f,i=w*r;if(1==h)for(var B=0;B<r;B++){var U=(F=255*(t[e+(B>>>3)]>>>7-(7&B)&1))==255*p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}else if(2==h)for(B=0;B<r;B++){U=(F=85*(t[e+(B>>>2)]>>>6-((3&B)<<1)&3))==85*p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}else if(4==h)for(B=0;B<r;B++){U=(F=17*(t[e+(B>>>1)]>>>4-((1&B)<<2)&15))==17*p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}else if(8==h)for(B=0;B<r;B++){U=(F=t[e+B])==p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}else if(16==h)for(B=0;B<r;B++){F=t[e+(B<<1)],U=d(t,e+(B<<1))==p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}}return l}function _decompress(e,r,i,o){const a=_getBPP(e),s=Math.ceil(i*a/8),f=new Uint8Array((s+1+e.interlace)*o);return r=e.tabs.CgBI?t(r,f):_inflate(r,f),0==e.interlace?r=_filterZero(r,e,0,i,o):1==e.interlace&&(r=function _readInterlace(e,t){const r=t.width,i=t.height,o=_getBPP(t),a=o>>3,s=Math.ceil(r*o/8),f=new Uint8Array(i*s);let l=0;const c=[0,0,4,0,2,0,1],u=[0,4,0,2,0,1,0],h=[8,8,8,4,4,2,2],d=[8,8,4,4,2,2,1];let A=0;for(;A<7;){const p=h[A],m=d[A];let w=0,v=0,b=c[A];for(;b<i;)b+=p,v++;let y=u[A];for(;y<r;)y+=m,w++;const E=Math.ceil(w*o/8);_filterZero(e,t,l,w,v);let F=0,_=c[A];for(;_<i;){let t=u[A],i=l+F*E<<3;for(;t<r;){var g;if(1==o)g=(g=e[i>>3])>>7-(7&i)&1,f[_*s+(t>>3)]|=g<<7-((7&t)<<0);if(2==o)g=(g=e[i>>3])>>6-(7&i)&3,f[_*s+(t>>2)]|=g<<6-((3&t)<<1);if(4==o)g=(g=e[i>>3])>>4-(7&i)&15,f[_*s+(t>>1)]|=g<<4-((1&t)<<2);if(o>=8){const r=_*s+t*a;for(let t=0;t<a;t++)f[r+t]=e[(i>>3)+t];}i+=o,t+=m;}F++,_+=p;}w*v!=0&&(l+=v*(1+E)),A+=1;}return f}(r,e)),r}function _inflate(e,r){return t(new Uint8Array(e.buffer,2,e.length-6),r)}var t=function(){const e={H:{}};return e.H.N=function(t,r){const i=Uint8Array;let o,a,s=0,f=0,l=0,c=0,u=0,h=0,d=0,A=0,g=0;if(3==t[0]&&0==t[1])return r||new i(0);const p=e.H,m=p.b,w=p.e,v=p.R,b=p.n,y=p.A,E=p.Z,F=p.m,_=null==r;for(_&&(r=new i(t.length>>>2<<5));0==s;)if(s=m(t,g,1),f=m(t,g+1,2),g+=3,0!=f){if(_&&(r=e.H.W(r,A+(1<<17))),1==f&&(o=F.J,a=F.h,h=511,d=31),2==f){l=w(t,g,5)+257,c=w(t,g+5,5)+1,u=w(t,g+10,4)+4,g+=14;let e=1;for(var B=0;B<38;B+=2)F.Q[B]=0,F.Q[B+1]=0;for(B=0;B<u;B++){const r=w(t,g+3*B,3);F.Q[1+(F.X[B]<<1)]=r,r>e&&(e=r);}g+=3*u,b(F.Q,e),y(F.Q,e,F.u),o=F.w,a=F.d,g=v(F.u,(1<<e)-1,l+c,t,g,F.v);const r=p.V(F.v,0,l,F.C);h=(1<<r)-1;const i=p.V(F.v,l,c,F.D);d=(1<<i)-1,b(F.C,r),y(F.C,r,o),b(F.D,i),y(F.D,i,a);}for(;;){const e=o[E(t,g)&h];g+=15&e;const i=e>>>4;if(i>>>8==0)r[A++]=i;else {if(256==i)break;{let e=A+i-254;if(i>264){const r=F.q[i-257];e=A+(r>>>3)+w(t,g,7&r),g+=7&r;}const o=a[E(t,g)&d];g+=15&o;const s=o>>>4,f=F.c[s],l=(f>>>4)+m(t,g,15&f);for(g+=15&f;A<e;)r[A]=r[A++-l],r[A]=r[A++-l],r[A]=r[A++-l],r[A]=r[A++-l];A=e;}}}}else {0!=(7&g)&&(g+=8-(7&g));const o=4+(g>>>3),a=t[o-4]|t[o-3]<<8;_&&(r=e.H.W(r,A+a)),r.set(new i(t.buffer,t.byteOffset+o,a),A),g=o+a<<3,A+=a;}return r.length==A?r:r.slice(0,A)},e.H.W=function(e,t){const r=e.length;if(t<=r)return e;const i=new Uint8Array(r<<1);return i.set(e,0),i},e.H.R=function(t,r,i,o,a,s){const f=e.H.e,l=e.H.Z;let c=0;for(;c<i;){const e=t[l(o,a)&r];a+=15&e;const i=e>>>4;if(i<=15)s[c]=i,c++;else {let e=0,t=0;16==i?(t=3+f(o,a,2),a+=2,e=s[c-1]):17==i?(t=3+f(o,a,3),a+=3):18==i&&(t=11+f(o,a,7),a+=7);const r=c+t;for(;c<r;)s[c]=e,c++;}}return a},e.H.V=function(e,t,r,i){let o=0,a=0;const s=i.length>>>1;for(;a<r;){const r=e[a+t];i[a<<1]=0,i[1+(a<<1)]=r,r>o&&(o=r),a++;}for(;a<s;)i[a<<1]=0,i[1+(a<<1)]=0,a++;return o},e.H.n=function(t,r){const i=e.H.m,o=t.length;let a,s,f;let l;const c=i.j;for(var u=0;u<=r;u++)c[u]=0;for(u=1;u<o;u+=2)c[t[u]]++;const h=i.K;for(a=0,c[0]=0,s=1;s<=r;s++)a=a+c[s-1]<<1,h[s]=a;for(f=0;f<o;f+=2)l=t[f+1],0!=l&&(t[f]=h[l],h[l]++);},e.H.A=function(t,r,i){const o=t.length,a=e.H.m.r;for(let e=0;e<o;e+=2)if(0!=t[e+1]){const o=e>>1,s=t[e+1],f=o<<4|s,l=r-s;let c=t[e]<<l;const u=c+(1<<l);for(;c!=u;){i[a[c]>>>15-r]=f,c++;}}},e.H.l=function(t,r){const i=e.H.m.r,o=15-r;for(let e=0;e<t.length;e+=2){const a=t[e]<<r-t[e+1];t[e]=i[a]>>>o;}},e.H.M=function(e,t,r){r<<=7&t;const i=t>>>3;e[i]|=r,e[i+1]|=r>>>8;},e.H.I=function(e,t,r){r<<=7&t;const i=t>>>3;e[i]|=r,e[i+1]|=r>>>8,e[i+2]|=r>>>16;},e.H.e=function(e,t,r){return (e[t>>>3]|e[1+(t>>>3)]<<8)>>>(7&t)&(1<<r)-1},e.H.b=function(e,t,r){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16)>>>(7&t)&(1<<r)-1},e.H.Z=function(e,t){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16)>>>(7&t)},e.H.i=function(e,t){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16|e[3+(t>>>3)]<<24)>>>(7&t)},e.H.m=function(){const e=Uint16Array,t=Uint32Array;return {K:new e(16),j:new e(16),X:[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],S:[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,999,999,999],T:[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0],q:new e(32),p:[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,65535,65535],z:[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0],c:new t(32),J:new e(512),_:[],h:new e(32),$:[],w:new e(32768),C:[],v:[],d:new e(32768),D:[],u:new e(512),Q:[],r:new e(32768),s:new t(286),Y:new t(30),a:new t(19),t:new t(15e3),k:new e(65536),g:new e(32768)}}(),function(){const t=e.H.m;for(var r=0;r<32768;r++){let e=r;e=(2863311530&e)>>>1|(1431655765&e)<<1,e=(3435973836&e)>>>2|(858993459&e)<<2,e=(4042322160&e)>>>4|(252645135&e)<<4,e=(4278255360&e)>>>8|(16711935&e)<<8,t.r[r]=(e>>>16|e<<16)>>>17;}function n(e,t,r){for(;0!=t--;)e.push(0,r);}for(r=0;r<32;r++)t.q[r]=t.S[r]<<3|t.T[r],t.c[r]=t.p[r]<<4|t.z[r];n(t._,144,8),n(t._,112,9),n(t._,24,7),n(t._,8,8),e.H.n(t._,9),e.H.A(t._,9,t.J),e.H.l(t._,9),n(t.$,32,5),e.H.n(t.$,5),e.H.A(t.$,5,t.h),e.H.l(t.$,5),n(t.Q,19,0),n(t.C,286,0),n(t.D,30,0),n(t.v,320,0);}(),e.H.N}();function _getBPP(e){return [1,null,3,1,2,null,4][e.ctype]*e.depth}function _filterZero(e,t,r,i,o){let a=_getBPP(t);const s=Math.ceil(i*a/8);let f,l;a=Math.ceil(a/8);let c=e[r],u=0;if(c>1&&(e[r]=[0,0,1][c-2]),3==c)for(u=a;u<s;u++)e[u+1]=e[u+1]+(e[u+1-a]>>>1)&255;for(let t=0;t<o;t++)if(f=r+t*s,l=f+t+1,c=e[l-1],u=0,0==c)for(;u<s;u++)e[f+u]=e[l+u];else if(1==c){for(;u<a;u++)e[f+u]=e[l+u];for(;u<s;u++)e[f+u]=e[l+u]+e[f+u-a];}else if(2==c)for(;u<s;u++)e[f+u]=e[l+u]+e[f+u-s];else if(3==c){for(;u<a;u++)e[f+u]=e[l+u]+(e[f+u-s]>>>1);for(;u<s;u++)e[f+u]=e[l+u]+(e[f+u-s]+e[f+u-a]>>>1);}else {for(;u<a;u++)e[f+u]=e[l+u]+_paeth(0,e[f+u-s],0);for(;u<s;u++)e[f+u]=e[l+u]+_paeth(e[f+u-a],e[f+u-s],e[f+u-a-s]);}return e}function _paeth(e,t,r){const i=e+t-r,o=i-e,a=i-t,s=i-r;return o*o<=a*a&&o*o<=s*s?e:a*a<=s*s?t:r}function _IHDR(t,r,i){i.width=e.readUint(t,r),r+=4,i.height=e.readUint(t,r),r+=4,i.depth=t[r],r++,i.ctype=t[r],r++,i.compress=t[r],r++,i.filter=t[r],r++,i.interlace=t[r],r++;}function _copyTile(e,t,r,i,o,a,s,f,l){const c=Math.min(t,o),u=Math.min(r,a);let h=0,d=0;for(let r=0;r<u;r++)for(let a=0;a<c;a++)if(s>=0&&f>=0?(h=r*t+a<<2,d=(f+r)*o+s+a<<2):(h=(-f+r)*t-s+a<<2,d=r*o+a<<2),0==l)i[d]=e[h],i[d+1]=e[h+1],i[d+2]=e[h+2],i[d+3]=e[h+3];else if(1==l){var A=e[h+3]*(1/255),g=e[h]*A,p=e[h+1]*A,m=e[h+2]*A,w=i[d+3]*(1/255),v=i[d]*w,b=i[d+1]*w,y=i[d+2]*w;const t=1-A,r=A+w*t,o=0==r?0:1/r;i[d+3]=255*r,i[d+0]=(g+v*t)*o,i[d+1]=(p+b*t)*o,i[d+2]=(m+y*t)*o;}else if(2==l){A=e[h+3],g=e[h],p=e[h+1],m=e[h+2],w=i[d+3],v=i[d],b=i[d+1],y=i[d+2];A==w&&g==v&&p==b&&m==y?(i[d]=0,i[d+1]=0,i[d+2]=0,i[d+3]=0):(i[d]=g,i[d+1]=p,i[d+2]=m,i[d+3]=A);}else if(3==l){A=e[h+3],g=e[h],p=e[h+1],m=e[h+2],w=i[d+3],v=i[d],b=i[d+1],y=i[d+2];if(A==w&&g==v&&p==b&&m==y)continue;if(A<220&&w>20)return false}return true}return {decode:function decode(r){const i=new Uint8Array(r);let o=8;const a=e,s=a.readUshort,f=a.readUint,l={tabs:{},frames:[]},c=new Uint8Array(i.length);let u,h=0,d=0;const A=[137,80,78,71,13,10,26,10];for(var g=0;g<8;g++)if(i[g]!=A[g])throw "The input is not a PNG file!";for(;o<i.length;){const e=a.readUint(i,o);o+=4;const r=a.readASCII(i,o,4);if(o+=4,"IHDR"==r)_IHDR(i,o,l);else if("iCCP"==r){for(var p=o;0!=i[p];)p++;a.readASCII(i,o,p-o);const s=i.slice(p+2,o+e);let f=null;try{f=_inflate(s);}catch(e){f=t(s);}l.tabs[r]=f;}else if("CgBI"==r)l.tabs[r]=i.slice(o,o+4);else if("IDAT"==r){for(g=0;g<e;g++)c[h+g]=i[o+g];h+=e;}else if("acTL"==r)l.tabs[r]={num_frames:f(i,o),num_plays:f(i,o+4)},u=new Uint8Array(i.length);else if("fcTL"==r){if(0!=d)(E=l.frames[l.frames.length-1]).data=_decompress(l,u.slice(0,d),E.rect.width,E.rect.height),d=0;const e={x:f(i,o+12),y:f(i,o+16),width:f(i,o+4),height:f(i,o+8)};let t=s(i,o+22);t=s(i,o+20)/(0==t?100:t);const r={rect:e,delay:Math.round(1e3*t),dispose:i[o+24],blend:i[o+25]};l.frames.push(r);}else if("fdAT"==r){for(g=0;g<e-4;g++)u[d+g]=i[o+g+4];d+=e-4;}else if("pHYs"==r)l.tabs[r]=[a.readUint(i,o),a.readUint(i,o+4),i[o+8]];else if("cHRM"==r){l.tabs[r]=[];for(g=0;g<8;g++)l.tabs[r].push(a.readUint(i,o+4*g));}else if("tEXt"==r||"zTXt"==r){null==l.tabs[r]&&(l.tabs[r]={});var m=a.nextZero(i,o),w=a.readASCII(i,o,m-o),v=o+e-m-1;if("tEXt"==r)y=a.readASCII(i,m+1,v);else {var b=_inflate(i.slice(m+2,m+2+v));y=a.readUTF8(b,0,b.length);}l.tabs[r][w]=y;}else if("iTXt"==r){null==l.tabs[r]&&(l.tabs[r]={});m=0,p=o;m=a.nextZero(i,p);w=a.readASCII(i,p,m-p);const t=i[p=m+1];var y;p+=2,m=a.nextZero(i,p),a.readASCII(i,p,m-p),p=m+1,m=a.nextZero(i,p),a.readUTF8(i,p,m-p);v=e-((p=m+1)-o);if(0==t)y=a.readUTF8(i,p,v);else {b=_inflate(i.slice(p,p+v));y=a.readUTF8(b,0,b.length);}l.tabs[r][w]=y;}else if("PLTE"==r)l.tabs[r]=a.readBytes(i,o,e);else if("hIST"==r){const e=l.tabs.PLTE.length/3;l.tabs[r]=[];for(g=0;g<e;g++)l.tabs[r].push(s(i,o+2*g));}else if("tRNS"==r)3==l.ctype?l.tabs[r]=a.readBytes(i,o,e):0==l.ctype?l.tabs[r]=s(i,o):2==l.ctype&&(l.tabs[r]=[s(i,o),s(i,o+2),s(i,o+4)]);else if("gAMA"==r)l.tabs[r]=a.readUint(i,o)/1e5;else if("sRGB"==r)l.tabs[r]=i[o];else if("bKGD"==r)0==l.ctype||4==l.ctype?l.tabs[r]=[s(i,o)]:2==l.ctype||6==l.ctype?l.tabs[r]=[s(i,o),s(i,o+2),s(i,o+4)]:3==l.ctype&&(l.tabs[r]=i[o]);else if("IEND"==r)break;o+=e,a.readUint(i,o),o+=4;}var E;return 0!=d&&((E=l.frames[l.frames.length-1]).data=_decompress(l,u.slice(0,d),E.rect.width,E.rect.height)),l.data=_decompress(l,c,l.width,l.height),delete l.compress,delete l.interlace,delete l.filter,l},toRGBA8:function toRGBA8(e){const t=e.width,r=e.height;if(null==e.tabs.acTL)return [decodeImage(e.data,t,r,e).buffer];const i=[];null==e.frames[0].data&&(e.frames[0].data=e.data);const o=t*r*4,a=new Uint8Array(o),s=new Uint8Array(o),f=new Uint8Array(o);for(let c=0;c<e.frames.length;c++){const u=e.frames[c],h=u.rect.x,d=u.rect.y,A=u.rect.width,g=u.rect.height,p=decodeImage(u.data,A,g,e);if(0!=c)for(var l=0;l<o;l++)f[l]=a[l];if(0==u.blend?_copyTile(p,A,g,a,t,r,h,d,0):1==u.blend&&_copyTile(p,A,g,a,t,r,h,d,1),i.push(a.buffer.slice(0)),0==u.dispose);else if(1==u.dispose)_copyTile(s,A,g,a,t,r,h,d,0);else if(2==u.dispose)for(l=0;l<o;l++)a[l]=f[l];}return i},_paeth:_paeth,_copyTile:_copyTile,_bin:e}}();!function(){const{_copyTile:e}=UPNG,{_bin:t}=UPNG,r=UPNG._paeth;var i={table:function(){const e=new Uint32Array(256);for(let t=0;t<256;t++){let r=t;for(let e=0;e<8;e++)1&r?r=3988292384^r>>>1:r>>>=1;e[t]=r;}return e}(),update(e,t,r,o){for(let a=0;a<o;a++)e=i.table[255&(e^t[r+a])]^e>>>8;return e},crc:(e,t,r)=>4294967295^i.update(4294967295,e,t,r)};function addErr(e,t,r,i){t[r]+=e[0]*i>>4,t[r+1]+=e[1]*i>>4,t[r+2]+=e[2]*i>>4,t[r+3]+=e[3]*i>>4;}function N(e){return Math.max(0,Math.min(255,e))}function D(e,t){const r=e[0]-t[0],i=e[1]-t[1],o=e[2]-t[2],a=e[3]-t[3];return r*r+i*i+o*o+a*a}function dither(e,t,r,i,o,a,s){null==s&&(s=1);const f=i.length,l=[];for(var c=0;c<f;c++){const e=i[c];l.push([e>>>0&255,e>>>8&255,e>>>16&255,e>>>24&255]);}for(c=0;c<f;c++){let e=4294967295;for(var u=0,h=0;h<f;h++){var d=D(l[c],l[h]);h!=c&&d<e&&(e=d,u=h);}}const A=new Uint32Array(o.buffer),g=new Int16Array(t*r*4),p=[0,8,2,10,12,4,14,6,3,11,1,9,15,7,13,5];for(c=0;c<p.length;c++)p[c]=255*((p[c]+.5)/16-.5);for(let o=0;o<r;o++)for(let w=0;w<t;w++){var m;c=4*(o*t+w);if(2!=s)m=[N(e[c]+g[c]),N(e[c+1]+g[c+1]),N(e[c+2]+g[c+2]),N(e[c+3]+g[c+3])];else {d=p[4*(3&o)+(3&w)];m=[N(e[c]+d),N(e[c+1]+d),N(e[c+2]+d),N(e[c+3]+d)];}u=0;let v=16777215;for(h=0;h<f;h++){const e=D(m,l[h]);e<v&&(v=e,u=h);}const b=l[u],y=[m[0]-b[0],m[1]-b[1],m[2]-b[2],m[3]-b[3]];1==s&&(w!=t-1&&addErr(y,g,c+4,7),o!=r-1&&(0!=w&&addErr(y,g,c+4*t-4,3),addErr(y,g,c+4*t,5),w!=t-1&&addErr(y,g,c+4*t+4,1))),a[c>>2]=u,A[c>>2]=i[u];}}function _main(e,r,o,a,s){null==s&&(s={});const{crc:f}=i,l=t.writeUint,c=t.writeUshort,u=t.writeASCII;let h=8;const d=e.frames.length>1;let A,g=false,p=33+(d?20:0);if(null!=s.sRGB&&(p+=13),null!=s.pHYs&&(p+=21),null!=s.iCCP&&(A=pako.deflate(s.iCCP),p+=21+A.length+4),3==e.ctype){for(var m=e.plte.length,w=0;w<m;w++)e.plte[w]>>>24!=255&&(g=true);p+=8+3*m+4+(g?8+1*m+4:0);}for(var v=0;v<e.frames.length;v++){d&&(p+=38),p+=(F=e.frames[v]).cimg.length+12,0!=v&&(p+=4);}p+=12;const b=new Uint8Array(p),y=[137,80,78,71,13,10,26,10];for(w=0;w<8;w++)b[w]=y[w];if(l(b,h,13),h+=4,u(b,h,"IHDR"),h+=4,l(b,h,r),h+=4,l(b,h,o),h+=4,b[h]=e.depth,h++,b[h]=e.ctype,h++,b[h]=0,h++,b[h]=0,h++,b[h]=0,h++,l(b,h,f(b,h-17,17)),h+=4,null!=s.sRGB&&(l(b,h,1),h+=4,u(b,h,"sRGB"),h+=4,b[h]=s.sRGB,h++,l(b,h,f(b,h-5,5)),h+=4),null!=s.iCCP){const e=13+A.length;l(b,h,e),h+=4,u(b,h,"iCCP"),h+=4,u(b,h,"ICC profile"),h+=11,h+=2,b.set(A,h),h+=A.length,l(b,h,f(b,h-(e+4),e+4)),h+=4;}if(null!=s.pHYs&&(l(b,h,9),h+=4,u(b,h,"pHYs"),h+=4,l(b,h,s.pHYs[0]),h+=4,l(b,h,s.pHYs[1]),h+=4,b[h]=s.pHYs[2],h++,l(b,h,f(b,h-13,13)),h+=4),d&&(l(b,h,8),h+=4,u(b,h,"acTL"),h+=4,l(b,h,e.frames.length),h+=4,l(b,h,null!=s.loop?s.loop:0),h+=4,l(b,h,f(b,h-12,12)),h+=4),3==e.ctype){l(b,h,3*(m=e.plte.length)),h+=4,u(b,h,"PLTE"),h+=4;for(w=0;w<m;w++){const t=3*w,r=e.plte[w],i=255&r,o=r>>>8&255,a=r>>>16&255;b[h+t+0]=i,b[h+t+1]=o,b[h+t+2]=a;}if(h+=3*m,l(b,h,f(b,h-3*m-4,3*m+4)),h+=4,g){l(b,h,m),h+=4,u(b,h,"tRNS"),h+=4;for(w=0;w<m;w++)b[h+w]=e.plte[w]>>>24&255;h+=m,l(b,h,f(b,h-m-4,m+4)),h+=4;}}let E=0;for(v=0;v<e.frames.length;v++){var F=e.frames[v];d&&(l(b,h,26),h+=4,u(b,h,"fcTL"),h+=4,l(b,h,E++),h+=4,l(b,h,F.rect.width),h+=4,l(b,h,F.rect.height),h+=4,l(b,h,F.rect.x),h+=4,l(b,h,F.rect.y),h+=4,c(b,h,a[v]),h+=2,c(b,h,1e3),h+=2,b[h]=F.dispose,h++,b[h]=F.blend,h++,l(b,h,f(b,h-30,30)),h+=4);const t=F.cimg;l(b,h,(m=t.length)+(0==v?0:4)),h+=4;const r=h;u(b,h,0==v?"IDAT":"fdAT"),h+=4,0!=v&&(l(b,h,E++),h+=4),b.set(t,h),h+=m,l(b,h,f(b,r,h-r)),h+=4;}return l(b,h,0),h+=4,u(b,h,"IEND"),h+=4,l(b,h,f(b,h-4,4)),h+=4,b.buffer}function compressPNG(e,t,r){for(let i=0;i<e.frames.length;i++){const o=e.frames[i];const a=o.rect.height,s=new Uint8Array(a*o.bpl+a);o.cimg=_filterZero(o.img,a,o.bpp,o.bpl,s,t,r);}}function compress(t,r,i,o,a){const s=a[0],f=a[1],l=a[2],c=a[3],u=a[4],h=a[5];let d=6,A=8,g=255;for(var p=0;p<t.length;p++){const e=new Uint8Array(t[p]);for(var m=e.length,w=0;w<m;w+=4)g&=e[w+3];}const v=255!=g,b=function framize(t,r,i,o,a,s){const f=[];for(var l=0;l<t.length;l++){const h=new Uint8Array(t[l]),A=new Uint32Array(h.buffer);var c;let g=0,p=0,m=r,w=i,v=o?1:0;if(0!=l){const b=s||o||1==l||0!=f[l-2].dispose?1:2;let y=0,E=1e9;for(let e=0;e<b;e++){var u=new Uint8Array(t[l-1-e]);const o=new Uint32Array(t[l-1-e]);let s=r,f=i,c=-1,h=-1;for(let e=0;e<i;e++)for(let t=0;t<r;t++){A[d=e*r+t]!=o[d]&&(t<s&&(s=t),t>c&&(c=t),e<f&&(f=e),e>h&&(h=e));} -1==c&&(s=f=c=h=0),a&&(1==(1&s)&&s--,1==(1&f)&&f--);const v=(c-s+1)*(h-f+1);v<E&&(E=v,y=e,g=s,p=f,m=c-s+1,w=h-f+1);}u=new Uint8Array(t[l-1-y]);1==y&&(f[l-1].dispose=2),c=new Uint8Array(m*w*4),e(u,r,i,c,m,w,-g,-p,0),v=e(h,r,i,c,m,w,-g,-p,3)?1:0,1==v?_prepareDiff(h,r,i,c,{x:g,y:p,width:m,height:w}):e(h,r,i,c,m,w,-g,-p,0);}else c=h.slice(0);f.push({rect:{x:g,y:p,width:m,height:w},img:c,blend:v,dispose:0});}if(o)for(l=0;l<f.length;l++){if(1==(A=f[l]).blend)continue;const e=A.rect,o=f[l-1].rect,s=Math.min(e.x,o.x),c=Math.min(e.y,o.y),u={x:s,y:c,width:Math.max(e.x+e.width,o.x+o.width)-s,height:Math.max(e.y+e.height,o.y+o.height)-c};f[l-1].dispose=1,l-1!=0&&_updateFrame(t,r,i,f,l-1,u,a),_updateFrame(t,r,i,f,l,u,a);}let h=0;if(1!=t.length)for(var d=0;d<f.length;d++){var A;h+=(A=f[d]).rect.width*A.rect.height;}return f}(t,r,i,s,f,l),y={},E=[],F=[];if(0!=o){const e=[];for(w=0;w<b.length;w++)e.push(b[w].img.buffer);const t=function concatRGBA(e){let t=0;for(var r=0;r<e.length;r++)t+=e[r].byteLength;const i=new Uint8Array(t);let o=0;for(r=0;r<e.length;r++){const t=new Uint8Array(e[r]),a=t.length;for(let e=0;e<a;e+=4){let r=t[e],a=t[e+1],s=t[e+2];const f=t[e+3];0==f&&(r=a=s=0),i[o+e]=r,i[o+e+1]=a,i[o+e+2]=s,i[o+e+3]=f;}o+=a;}return i.buffer}(e),r=quantize(t,o);for(w=0;w<r.plte.length;w++)E.push(r.plte[w].est.rgba);let i=0;for(w=0;w<b.length;w++){const e=(B=b[w]).img.length;var _=new Uint8Array(r.inds.buffer,i>>2,e>>2);F.push(_);const t=new Uint8Array(r.abuf,i,e);h&&dither(B.img,B.rect.width,B.rect.height,E,t,_),B.img.set(t),i+=e;}}else for(p=0;p<b.length;p++){var B=b[p];const e=new Uint32Array(B.img.buffer);var U=B.rect.width;m=e.length,_=new Uint8Array(m);F.push(_);for(w=0;w<m;w++){const t=e[w];if(0!=w&&t==e[w-1])_[w]=_[w-1];else if(w>U&&t==e[w-U])_[w]=_[w-U];else {let e=y[t];if(null==e&&(y[t]=e=E.length,E.push(t),E.length>=300))break;_[w]=e;}}}const C=E.length;C<=256&&0==u&&(A=C<=2?1:C<=4?2:C<=16?4:8,A=Math.max(A,c));for(p=0;p<b.length;p++){(B=b[p]).rect.x;U=B.rect.width;const e=B.rect.height;let t=B.img;let r=4*U,i=4;if(C<=256&&0==u){r=Math.ceil(A*U/8);var I=new Uint8Array(r*e);const o=F[p];for(let t=0;t<e;t++){w=t*r;const e=t*U;if(8==A)for(var Q=0;Q<U;Q++)I[w+Q]=o[e+Q];else if(4==A)for(Q=0;Q<U;Q++)I[w+(Q>>1)]|=o[e+Q]<<4-4*(1&Q);else if(2==A)for(Q=0;Q<U;Q++)I[w+(Q>>2)]|=o[e+Q]<<6-2*(3&Q);else if(1==A)for(Q=0;Q<U;Q++)I[w+(Q>>3)]|=o[e+Q]<<7-1*(7&Q);}t=I,d=3,i=1;}else if(0==v&&1==b.length){I=new Uint8Array(U*e*3);const o=U*e;for(w=0;w<o;w++){const e=3*w,r=4*w;I[e]=t[r],I[e+1]=t[r+1],I[e+2]=t[r+2];}t=I,d=2,i=3,r=3*U;}B.img=t,B.bpl=r,B.bpp=i;}return {ctype:d,depth:A,plte:E,frames:b}}function _updateFrame(t,r,i,o,a,s,f){const l=Uint8Array,c=Uint32Array,u=new l(t[a-1]),h=new c(t[a-1]),d=a+1<t.length?new l(t[a+1]):null,A=new l(t[a]),g=new c(A.buffer);let p=r,m=i,w=-1,v=-1;for(let e=0;e<s.height;e++)for(let t=0;t<s.width;t++){const i=s.x+t,f=s.y+e,l=f*r+i,c=g[l];0==c||0==o[a-1].dispose&&h[l]==c&&(null==d||0!=d[4*l+3])||(i<p&&(p=i),i>w&&(w=i),f<m&&(m=f),f>v&&(v=f));} -1==w&&(p=m=w=v=0),f&&(1==(1&p)&&p--,1==(1&m)&&m--),s={x:p,y:m,width:w-p+1,height:v-m+1};const b=o[a];b.rect=s,b.blend=1,b.img=new Uint8Array(s.width*s.height*4),0==o[a-1].dispose?(e(u,r,i,b.img,s.width,s.height,-s.x,-s.y,0),_prepareDiff(A,r,i,b.img,s)):e(A,r,i,b.img,s.width,s.height,-s.x,-s.y,0);}function _prepareDiff(t,r,i,o,a){e(t,r,i,o,a.width,a.height,-a.x,-a.y,2);}function _filterZero(e,t,r,i,o,a,s){const f=[];let l,c=[0,1,2,3,4];-1!=a?c=[a]:(t*i>5e5||1==r)&&(c=[0]),s&&(l={level:0});const u=UZIP;for(var h=0;h<c.length;h++){for(let a=0;a<t;a++)_filterLine(o,e,a,i,r,c[h]);f.push(u.deflate(o,l));}let d,A=1e9;for(h=0;h<f.length;h++)f[h].length<A&&(d=h,A=f[h].length);return f[d]}function _filterLine(e,t,i,o,a,s){const f=i*o;let l=f+i;if(e[l]=s,l++,0==s)if(o<500)for(var c=0;c<o;c++)e[l+c]=t[f+c];else e.set(new Uint8Array(t.buffer,f,o),l);else if(1==s){for(c=0;c<a;c++)e[l+c]=t[f+c];for(c=a;c<o;c++)e[l+c]=t[f+c]-t[f+c-a]+256&255;}else if(0==i){for(c=0;c<a;c++)e[l+c]=t[f+c];if(2==s)for(c=a;c<o;c++)e[l+c]=t[f+c];if(3==s)for(c=a;c<o;c++)e[l+c]=t[f+c]-(t[f+c-a]>>1)+256&255;if(4==s)for(c=a;c<o;c++)e[l+c]=t[f+c]-r(t[f+c-a],0,0)+256&255;}else {if(2==s)for(c=0;c<o;c++)e[l+c]=t[f+c]+256-t[f+c-o]&255;if(3==s){for(c=0;c<a;c++)e[l+c]=t[f+c]+256-(t[f+c-o]>>1)&255;for(c=a;c<o;c++)e[l+c]=t[f+c]+256-(t[f+c-o]+t[f+c-a]>>1)&255;}if(4==s){for(c=0;c<a;c++)e[l+c]=t[f+c]+256-r(0,t[f+c-o],0)&255;for(c=a;c<o;c++)e[l+c]=t[f+c]+256-r(t[f+c-a],t[f+c-o],t[f+c-a-o])&255;}}}function quantize(e,t){const r=new Uint8Array(e),i=r.slice(0),o=new Uint32Array(i.buffer),a=getKDtree(i,t),s=a[0],f=a[1],l=r.length,c=new Uint8Array(l>>2);let u;if(r.length<2e7)for(var h=0;h<l;h+=4){u=getNearest(s,d=r[h]*(1/255),A=r[h+1]*(1/255),g=r[h+2]*(1/255),p=r[h+3]*(1/255)),c[h>>2]=u.ind,o[h>>2]=u.est.rgba;}else for(h=0;h<l;h+=4){var d=r[h]*(1/255),A=r[h+1]*(1/255),g=r[h+2]*(1/255),p=r[h+3]*(1/255);for(u=s;u.left;)u=planeDst(u.est,d,A,g,p)<=0?u.left:u.right;c[h>>2]=u.ind,o[h>>2]=u.est.rgba;}return {abuf:i.buffer,inds:c,plte:f}}function getKDtree(e,t,r){null==r&&(r=1e-4);const i=new Uint32Array(e.buffer),o={i0:0,i1:e.length,bst:null,est:null,tdst:0,left:null,right:null};o.bst=stats(e,o.i0,o.i1),o.est=estats(o.bst);const a=[o];for(;a.length<t;){let t=0,o=0;for(var s=0;s<a.length;s++)a[s].est.L>t&&(t=a[s].est.L,o=s);if(t<r)break;const f=a[o],l=splitPixels(e,i,f.i0,f.i1,f.est.e,f.est.eMq255);if(f.i0>=l||f.i1<=l){f.est.L=0;continue}const c={i0:f.i0,i1:l,bst:null,est:null,tdst:0,left:null,right:null};c.bst=stats(e,c.i0,c.i1),c.est=estats(c.bst);const u={i0:l,i1:f.i1,bst:null,est:null,tdst:0,left:null,right:null};u.bst={R:[],m:[],N:f.bst.N-c.bst.N};for(s=0;s<16;s++)u.bst.R[s]=f.bst.R[s]-c.bst.R[s];for(s=0;s<4;s++)u.bst.m[s]=f.bst.m[s]-c.bst.m[s];u.est=estats(u.bst),f.left=c,f.right=u,a[o]=c,a.push(u);}a.sort(((e,t)=>t.bst.N-e.bst.N));for(s=0;s<a.length;s++)a[s].ind=s;return [o,a]}function getNearest(e,t,r,i,o){if(null==e.left)return e.tdst=function dist(e,t,r,i,o){const a=t-e[0],s=r-e[1],f=i-e[2],l=o-e[3];return a*a+s*s+f*f+l*l}(e.est.q,t,r,i,o),e;const a=planeDst(e.est,t,r,i,o);let s=e.left,f=e.right;a>0&&(s=e.right,f=e.left);const l=getNearest(s,t,r,i,o);if(l.tdst<=a*a)return l;const c=getNearest(f,t,r,i,o);return c.tdst<l.tdst?c:l}function planeDst(e,t,r,i,o){const{e:a}=e;return a[0]*t+a[1]*r+a[2]*i+a[3]*o-e.eMq}function splitPixels(e,t,r,i,o,a){for(i-=4;r<i;){for(;vecDot(e,r,o)<=a;)r+=4;for(;vecDot(e,i,o)>a;)i-=4;if(r>=i)break;const s=t[r>>2];t[r>>2]=t[i>>2],t[i>>2]=s,r+=4,i-=4;}for(;vecDot(e,r,o)>a;)r-=4;return r+4}function vecDot(e,t,r){return e[t]*r[0]+e[t+1]*r[1]+e[t+2]*r[2]+e[t+3]*r[3]}function stats(e,t,r){const i=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],o=[0,0,0,0],a=r-t>>2;for(let a=t;a<r;a+=4){const t=e[a]*(1/255),r=e[a+1]*(1/255),s=e[a+2]*(1/255),f=e[a+3]*(1/255);o[0]+=t,o[1]+=r,o[2]+=s,o[3]+=f,i[0]+=t*t,i[1]+=t*r,i[2]+=t*s,i[3]+=t*f,i[5]+=r*r,i[6]+=r*s,i[7]+=r*f,i[10]+=s*s,i[11]+=s*f,i[15]+=f*f;}return i[4]=i[1],i[8]=i[2],i[9]=i[6],i[12]=i[3],i[13]=i[7],i[14]=i[11],{R:i,m:o,N:a}}function estats(e){const{R:t}=e,{m:r}=e,{N:i}=e,a=r[0],s=r[1],f=r[2],l=r[3],c=0==i?0:1/i,u=[t[0]-a*a*c,t[1]-a*s*c,t[2]-a*f*c,t[3]-a*l*c,t[4]-s*a*c,t[5]-s*s*c,t[6]-s*f*c,t[7]-s*l*c,t[8]-f*a*c,t[9]-f*s*c,t[10]-f*f*c,t[11]-f*l*c,t[12]-l*a*c,t[13]-l*s*c,t[14]-l*f*c,t[15]-l*l*c],h=u,d=o;let A=[Math.random(),Math.random(),Math.random(),Math.random()],g=0,p=0;if(0!=i)for(let e=0;e<16&&(A=d.multVec(h,A),p=Math.sqrt(d.dot(A,A)),A=d.sml(1/p,A),!(0!=e&&Math.abs(p-g)<1e-9));e++)g=p;const m=[a*c,s*c,f*c,l*c];return {Cov:u,q:m,e:A,L:g,eMq255:d.dot(d.sml(255,m),A),eMq:d.dot(A,m),rgba:(Math.round(255*m[3])<<24|Math.round(255*m[2])<<16|Math.round(255*m[1])<<8|Math.round(255*m[0])<<0)>>>0}}var o={multVec:(e,t)=>[e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3]*t[3],e[4]*t[0]+e[5]*t[1]+e[6]*t[2]+e[7]*t[3],e[8]*t[0]+e[9]*t[1]+e[10]*t[2]+e[11]*t[3],e[12]*t[0]+e[13]*t[1]+e[14]*t[2]+e[15]*t[3]],dot:(e,t)=>e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3]*t[3],sml:(e,t)=>[e*t[0],e*t[1],e*t[2],e*t[3]]};UPNG.encode=function encode(e,t,r,i,o,a,s){null==i&&(i=0),null==s&&(s=false);const f=compress(e,t,r,i,[false,false,false,0,s,false]);return compressPNG(f,-1),_main(f,t,r,o,a)},UPNG.encodeLL=function encodeLL(e,t,r,i,o,a,s,f){const l={ctype:0+(1==i?0:2)+(0==o?0:4),depth:a,frames:[]},c=(i+o)*a,u=c*t;for(let i=0;i<e.length;i++)l.frames.push({rect:{x:0,y:0,width:t,height:r},img:new Uint8Array(e[i]),blend:0,dispose:1,bpp:Math.ceil(c/8),bpl:Math.ceil(u/8)});return compressPNG(l,0,true),_main(l,t,r,s,f)},UPNG.encode.compress=compress,UPNG.encode.dither=dither,UPNG.quantize=quantize,UPNG.quantize.getKDtree=getKDtree,UPNG.quantize.getNearest=getNearest;}();const r={toArrayBuffer(e,t){const i=e.width,o=e.height,a=i<<2,s=e.getContext("2d").getImageData(0,0,i,o),f=new Uint32Array(s.data.buffer),l=(32*i+31)/32<<2,c=l*o,u=122+c,h=new ArrayBuffer(u),d=new DataView(h),A=1<<20;let g,p,m,w,v=A,b=0,y=0,E=0;function set16(e){d.setUint16(y,e,true),y+=2;}function set32(e){d.setUint32(y,e,true),y+=4;}function seek(e){y+=e;}set16(19778),set32(u),seek(4),set32(122),set32(108),set32(i),set32(-o>>>0),set16(1),set16(32),set32(3),set32(c),set32(2835),set32(2835),seek(8),set32(16711680),set32(65280),set32(255),set32(4278190080),set32(1466527264),function convert(){for(;b<o&&v>0;){for(w=122+b*l,g=0;g<a;)v--,p=f[E++],m=p>>>24,d.setUint32(w+g,p<<8|m),g+=4;b++;}E<f.length?(v=A,setTimeout(convert,r._dly)):t(h);}();},toBlob(e,t){this.toArrayBuffer(e,(e=>{t(new Blob([e],{type:"image/bmp"}));}));},_dly:9};var i={CHROME:"CHROME",FIREFOX:"FIREFOX",DESKTOP_SAFARI:"DESKTOP_SAFARI",IE:"IE",IOS:"IOS",ETC:"ETC"},o={[i.CHROME]:16384,[i.FIREFOX]:11180,[i.DESKTOP_SAFARI]:16384,[i.IE]:8192,[i.IOS]:4096,[i.ETC]:8192};const a="undefined"!=typeof window,s="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,f=a&&window.cordova&&window.cordova.require&&window.cordova.require("cordova/modulemapper"),CustomFile=(a||s)&&(f&&f.getOriginalSymbol(window,"File")||"undefined"!=typeof File&&File),CustomFileReader=(a||s)&&(f&&f.getOriginalSymbol(window,"FileReader")||"undefined"!=typeof FileReader&&FileReader);function getFilefromDataUrl(e,t,r=Date.now()){return new Promise((i=>{const o=e.split(","),a=o[0].match(/:(.*?);/)[1],s=globalThis.atob(o[1]);let f=s.length;const l=new Uint8Array(f);for(;f--;)l[f]=s.charCodeAt(f);const c=new Blob([l],{type:a});c.name=t,c.lastModified=r,i(c);}))}function getDataUrlFromFile(e){return new Promise(((t,r)=>{const i=new CustomFileReader;i.onload=()=>t(i.result),i.onerror=e=>r(e),i.readAsDataURL(e);}))}function loadImage(e){return new Promise(((t,r)=>{const i=new Image;i.onload=()=>t(i),i.onerror=e=>r(e),i.src=e;}))}function getBrowserName(){if(void 0!==getBrowserName.cachedResult)return getBrowserName.cachedResult;let e=i.ETC;const{userAgent:t}=navigator;return /Chrom(e|ium)/i.test(t)?e=i.CHROME:/iP(ad|od|hone)/i.test(t)&&/WebKit/i.test(t)?e=i.IOS:/Safari/i.test(t)?e=i.DESKTOP_SAFARI:/Firefox/i.test(t)?e=i.FIREFOX:(/MSIE/i.test(t)||true==!!document.documentMode)&&(e=i.IE),getBrowserName.cachedResult=e,getBrowserName.cachedResult}function approximateBelowMaximumCanvasSizeOfBrowser(e,t){const r=getBrowserName(),i=o[r];let a=e,s=t,f=a*s;const l=a>s?s/a:a/s;for(;f>i*i;){const e=(i+a)/2,t=(i+s)/2;e<t?(s=t,a=t*l):(s=e*l,a=e),f=a*s;}return {width:a,height:s}}function getNewCanvasAndCtx(e,t){let r,i;try{if(r=new OffscreenCanvas(e,t),i=r.getContext("2d"),null===i)throw new Error("getContext of OffscreenCanvas returns null")}catch(e){r=document.createElement("canvas"),i=r.getContext("2d");}return r.width=e,r.height=t,[r,i]}function drawImageInCanvas(e,t){const{width:r,height:i}=approximateBelowMaximumCanvasSizeOfBrowser(e.width,e.height),[o,a]=getNewCanvasAndCtx(r,i);return t&&/jpe?g/.test(t)&&(a.fillStyle="white",a.fillRect(0,0,o.width,o.height)),a.drawImage(e,0,0,o.width,o.height),o}function isIOS(){return void 0!==isIOS.cachedResult||(isIOS.cachedResult=["iPad Simulator","iPhone Simulator","iPod Simulator","iPad","iPhone","iPod"].includes(navigator.platform)||navigator.userAgent.includes("Mac")&&"undefined"!=typeof document&&"ontouchend"in document),isIOS.cachedResult}function drawFileInCanvas(e,t={}){return new Promise((function(r,o){let a,s;var $Try_2_Post=function(){try{return s=drawImageInCanvas(a,t.fileType||e.type),r([a,s])}catch(e){return o(e)}},$Try_2_Catch=function(t){try{var $Try_3_Catch=function(e){try{throw e}catch(e){return o(e)}};try{let t;return getDataUrlFromFile(e).then((function(e){try{return t=e,loadImage(t).then((function(e){try{return a=e,function(){try{return $Try_2_Post()}catch(e){return o(e)}}()}catch(e){return $Try_3_Catch(e)}}),$Try_3_Catch)}catch(e){return $Try_3_Catch(e)}}),$Try_3_Catch)}catch(e){$Try_3_Catch(e);}}catch(e){return o(e)}};try{if(isIOS()||[i.DESKTOP_SAFARI,i.MOBILE_SAFARI].includes(getBrowserName()))throw new Error("Skip createImageBitmap on IOS and Safari");return createImageBitmap(e).then((function(e){try{return a=e,$Try_2_Post()}catch(e){return $Try_2_Catch()}}),$Try_2_Catch)}catch(e){$Try_2_Catch();}}))}function canvasToFile(e,t,i,o,a=1){return new Promise((function(s,f){let l;if("image/png"===t){let c,u,h;return c=e.getContext("2d"),({data:u}=c.getImageData(0,0,e.width,e.height)),h=UPNG.encode([u.buffer],e.width,e.height,4096*a),l=new Blob([h],{type:t}),l.name=i,l.lastModified=o,$If_4.call(this)}{if("image/bmp"===t)return new Promise((t=>r.toBlob(e,t))).then(function(e){try{return l=e,l.name=i,l.lastModified=o,$If_5.call(this)}catch(e){return f(e)}}.bind(this),f);{if("function"==typeof OffscreenCanvas&&e instanceof OffscreenCanvas)return e.convertToBlob({type:t,quality:a}).then(function(e){try{return l=e,l.name=i,l.lastModified=o,$If_6.call(this)}catch(e){return f(e)}}.bind(this),f);{let d;return d=e.toDataURL(t,a),getFilefromDataUrl(d,i,o).then(function(e){try{return l=e,$If_6.call(this)}catch(e){return f(e)}}.bind(this),f)}function $If_6(){return $If_5.call(this)}}function $If_5(){return $If_4.call(this)}}function $If_4(){return s(l)}}))}function cleanupCanvasMemory(e){e.width=0,e.height=0;}function isAutoOrientationInBrowser(){return new Promise((function(e,t){let i,o,a,s;return void 0!==isAutoOrientationInBrowser.cachedResult?e(isAutoOrientationInBrowser.cachedResult):(getFilefromDataUrl("data:image/jpeg;base64,/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAAAAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/xABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q==","test.jpg",Date.now()).then((function(r){try{return i=r,drawFileInCanvas(i).then((function(r){try{return o=r[1],canvasToFile(o,i.type,i.name,i.lastModified).then((function(r){try{return a=r,cleanupCanvasMemory(o),drawFileInCanvas(a).then((function(r){try{return s=r[0],isAutoOrientationInBrowser.cachedResult=1===s.width&&2===s.height,e(isAutoOrientationInBrowser.cachedResult)}catch(e){return t(e)}}),t)}catch(e){return t(e)}}),t)}catch(e){return t(e)}}),t)}catch(e){return t(e)}}),t))}))}function getExifOrientation(e){return new Promise(((t,r)=>{const i=new CustomFileReader;i.onload=e=>{const r=new DataView(e.target.result);if(65496!=r.getUint16(0,false))return t(-2);const i=r.byteLength;let o=2;for(;o<i;){if(r.getUint16(o+2,false)<=8)return t(-1);const e=r.getUint16(o,false);if(o+=2,65505==e){if(1165519206!=r.getUint32(o+=2,false))return t(-1);const e=18761==r.getUint16(o+=6,false);o+=r.getUint32(o+4,e);const i=r.getUint16(o,e);o+=2;for(let a=0;a<i;a++)if(274==r.getUint16(o+12*a,e))return t(r.getUint16(o+12*a+8,e))}else {if(65280!=(65280&e))break;o+=r.getUint16(o,false);}}return t(-1)},i.onerror=e=>r(e),i.readAsArrayBuffer(e);}))}function handleMaxWidthOrHeight(e,t){const{width:r}=e,{height:i}=e,{maxWidthOrHeight:o}=t;let a,s=e;return isFinite(o)&&(r>o||i>o)&&([s,a]=getNewCanvasAndCtx(r,i),r>i?(s.width=o,s.height=i/r*o):(s.width=r/i*o,s.height=o),a.drawImage(e,0,0,s.width,s.height),cleanupCanvasMemory(e)),s}function followExifOrientation(e,t){const{width:r}=e,{height:i}=e,[o,a]=getNewCanvasAndCtx(r,i);switch(t>4&&t<9?(o.width=i,o.height=r):(o.width=r,o.height=i),t){case 2:a.transform(-1,0,0,1,r,0);break;case 3:a.transform(-1,0,0,-1,r,i);break;case 4:a.transform(1,0,0,-1,0,i);break;case 5:a.transform(0,1,1,0,0,0);break;case 6:a.transform(0,1,-1,0,i,0);break;case 7:a.transform(0,-1,-1,0,i,r);break;case 8:a.transform(0,-1,1,0,0,r);}return a.drawImage(e,0,0,r,i),cleanupCanvasMemory(e),o}function compress(e,t,r=0){return new Promise((function(i,o){let a,s,f,l,c,u,h,d,A,g,p,m,w,v,b,y,E,F,_,B;function incProgress(e=5){if(t.signal&&t.signal.aborted)throw t.signal.reason;a+=e,t.onProgress(Math.min(a,100));}function setProgress(e){if(t.signal&&t.signal.aborted)throw t.signal.reason;a=Math.min(Math.max(e,a),100),t.onProgress(a);}return a=r,s=t.maxIteration||10,f=1024*t.maxSizeMB*1024,incProgress(),drawFileInCanvas(e,t).then(function(r){try{return [,l]=r,incProgress(),c=handleMaxWidthOrHeight(l,t),incProgress(),new Promise((function(r,i){var o;if(!(o=t.exifOrientation))return getExifOrientation(e).then(function(e){try{return o=e,$If_2.call(this)}catch(e){return i(e)}}.bind(this),i);function $If_2(){return r(o)}return $If_2.call(this)})).then(function(r){try{return u=r,incProgress(),isAutoOrientationInBrowser().then(function(r){try{return h=r?c:followExifOrientation(c,u),incProgress(),d=t.initialQuality||1,A=t.fileType||e.type,canvasToFile(h,A,e.name,e.lastModified,d).then(function(r){try{{if(g=r,incProgress(),p=g.size>f,m=g.size>e.size,!p&&!m)return setProgress(100),i(g);var a;function $Loop_3(){if(s--&&(b>f||b>w)){let t,r;return t=B?.95*_.width:_.width,r=B?.95*_.height:_.height,[E,F]=getNewCanvasAndCtx(t,r),F.drawImage(_,0,0,t,r),d*="image/png"===A?.85:.95,canvasToFile(E,A,e.name,e.lastModified,d).then((function(e){try{return y=e,cleanupCanvasMemory(_),_=E,b=y.size,setProgress(Math.min(99,Math.floor((v-b)/(v-f)*100))),$Loop_3}catch(e){return o(e)}}),o)}return [1]}return w=e.size,v=g.size,b=v,_=h,B=!t.alwaysKeepResolution&&p,(a=function(e){for(;e;){if(e.then)return void e.then(a,o);try{if(e.pop){if(e.length)return e.pop()?$Loop_3_exit.call(this):e;e=$Loop_3;}else e=e.call(this);}catch(e){return o(e)}}}.bind(this))($Loop_3);function $Loop_3_exit(){return cleanupCanvasMemory(_),cleanupCanvasMemory(E),cleanupCanvasMemory(c),cleanupCanvasMemory(h),cleanupCanvasMemory(l),setProgress(100),i(y)}}}catch(u){return o(u)}}.bind(this),o)}catch(e){return o(e)}}.bind(this),o)}catch(e){return o(e)}}.bind(this),o)}catch(e){return o(e)}}.bind(this),o)}))}const l="\nlet scriptImported = false\nself.addEventListener('message', async (e) => {\n const { file, id, imageCompressionLibUrl, options } = e.data\n options.onProgress = (progress) => self.postMessage({ progress, id })\n try {\n if (!scriptImported) {\n // console.log('[worker] importScripts', imageCompressionLibUrl)\n self.importScripts(imageCompressionLibUrl)\n scriptImported = true\n }\n // console.log('[worker] self', self)\n const compressedFile = await imageCompression(file, options)\n self.postMessage({ file: compressedFile, id })\n } catch (e) {\n // console.error('[worker] error', e)\n self.postMessage({ error: e.message + '\\n' + e.stack, id })\n }\n})\n";let c;function compressOnWebWorker(e,t){return new Promise(((r,i)=>{c||(c=function createWorkerScriptURL(e){const t=[];return t.push(e),URL.createObjectURL(new Blob(t))}(l));const o=new Worker(c);o.addEventListener("message",(function handler(e){if(t.signal&&t.signal.aborted)o.terminate();else if(void 0===e.data.progress){if(e.data.error)return i(new Error(e.data.error)),void o.terminate();r(e.data.file),o.terminate();}else t.onProgress(e.data.progress);})),o.addEventListener("error",i),t.signal&&t.signal.addEventListener("abort",(()=>{i(t.signal.reason),o.terminate();})),o.postMessage({file:e,imageCompressionLibUrl:t.libURL,options:{...t,onProgress:void 0,signal:void 0}});}))}function imageCompression(e,t){return new Promise((function(r,i){let o,a,s,f,l,c;if(o={...t},s=0,({onProgress:f}=o),o.maxSizeMB=o.maxSizeMB||Number.POSITIVE_INFINITY,l="boolean"!=typeof o.useWebWorker||o.useWebWorker,delete o.useWebWorker,o.onProgress=e=>{s=e,"function"==typeof f&&f(s);},!(e instanceof Blob||e instanceof CustomFile))return i(new Error("The file given is not an instance of Blob or File"));if(!/^image/.test(e.type))return i(new Error("The file given is not an image"));if(c="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,!l||"function"!=typeof Worker||c)return compress(e,o).then(function(e){try{return a=e,$If_4.call(this)}catch(e){return i(e)}}.bind(this),i);var u=function(){try{return $If_4.call(this)}catch(e){return i(e)}}.bind(this),$Try_1_Catch=function(t){try{return compress(e,o).then((function(e){try{return a=e,u()}catch(e){return i(e)}}),i)}catch(e){return i(e)}};try{return o.libURL=o.libURL||"https://cdn.jsdelivr.net/npm/browser-image-compression@2.0.2/dist/browser-image-compression.js",compressOnWebWorker(e,o).then((function(e){try{return a=e,u()}catch(e){return $Try_1_Catch()}}),$Try_1_Catch)}catch(e){$Try_1_Catch();}function $If_4(){try{a.name=e.name,a.lastModified=e.lastModified;}catch(e){}try{o.preserveExif&&"image/jpeg"===e.type&&(!o.fileType||o.fileType&&o.fileType===e.type)&&(a=copyExifWithoutOrientation(e,a));}catch(e){}return r(a)}}))}imageCompression.getDataUrlFromFile=getDataUrlFromFile,imageCompression.getFilefromDataUrl=getFilefromDataUrl,imageCompression.loadImage=loadImage,imageCompression.drawImageInCanvas=drawImageInCanvas,imageCompression.drawFileInCanvas=drawFileInCanvas,imageCompression.canvasToFile=canvasToFile,imageCompression.getExifOrientation=getExifOrientation,imageCompression.handleMaxWidthOrHeight=handleMaxWidthOrHeight,imageCompression.followExifOrientation=followExifOrientation,imageCompression.cleanupCanvasMemory=cleanupCanvasMemory,imageCompression.isAutoOrientationInBrowser=isAutoOrientationInBrowser,imageCompression.approximateBelowMaximumCanvasSizeOfBrowser=approximateBelowMaximumCanvasSizeOfBrowser,imageCompression.copyExifWithoutOrientation=copyExifWithoutOrientation,imageCompression.getBrowserName=getBrowserName,imageCompression.version="2.0.2";
|
|
15849
|
+
|
|
15850
|
+
class KritzelImageTool extends KritzelBaseTool {
|
|
15851
|
+
fileInput = null;
|
|
15852
|
+
maxCompressionSize = 300;
|
|
15124
15853
|
constructor(core) {
|
|
15125
15854
|
super(core);
|
|
15855
|
+
this.setupFileInput();
|
|
15126
15856
|
}
|
|
15127
|
-
|
|
15128
|
-
this.
|
|
15129
|
-
this.initialMouseY = 0;
|
|
15130
|
-
this.initialSize = { x: 0, y: 0, width: 0, height: 0 };
|
|
15131
|
-
this.newSize = { x: 0, y: 0, width: 0, height: 0 };
|
|
15132
|
-
this.hasResized = false;
|
|
15857
|
+
onActivate() {
|
|
15858
|
+
this.openFilePicker();
|
|
15133
15859
|
}
|
|
15134
|
-
|
|
15135
|
-
if (
|
|
15136
|
-
|
|
15137
|
-
const selectionGroup = this._core.store.selectionGroup;
|
|
15138
|
-
if (selectionGroup && this._core.store.state.isResizeHandleSelected) {
|
|
15139
|
-
const clientX = event.clientX - this._core.store.offsetX;
|
|
15140
|
-
const clientY = event.clientY - this._core.store.offsetY;
|
|
15141
|
-
this._core.store.state.isResizing = true;
|
|
15142
|
-
this.initialMouseX = clientX;
|
|
15143
|
-
this.initialMouseY = clientY;
|
|
15144
|
-
this.initialSize.width = selectionGroup.width;
|
|
15145
|
-
this.initialSize.height = selectionGroup.height;
|
|
15146
|
-
this.initialSize.x = selectionGroup.translateX;
|
|
15147
|
-
this.initialSize.y = selectionGroup.translateY;
|
|
15148
|
-
}
|
|
15149
|
-
}
|
|
15150
|
-
}
|
|
15151
|
-
if (event.pointerType === 'touch') {
|
|
15152
|
-
const activePointers = Array.from(this._core.store.state.pointers.values());
|
|
15153
|
-
const firstTouch = activePointers[0];
|
|
15154
|
-
if (!firstTouch) {
|
|
15155
|
-
return;
|
|
15156
|
-
}
|
|
15157
|
-
if (activePointers.length === 1) {
|
|
15158
|
-
const selectionGroup = this._core.store.selectionGroup;
|
|
15159
|
-
if (selectionGroup && this._core.store.state.isResizeHandleSelected) {
|
|
15160
|
-
const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
|
|
15161
|
-
const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
|
|
15162
|
-
this._core.store.state.isResizing = true;
|
|
15163
|
-
this.initialMouseX = clientX;
|
|
15164
|
-
this.initialMouseY = clientY;
|
|
15165
|
-
this.initialSize.width = selectionGroup.width;
|
|
15166
|
-
this.initialSize.height = selectionGroup.height;
|
|
15167
|
-
this.initialSize.x = selectionGroup.translateX;
|
|
15168
|
-
this.initialSize.y = selectionGroup.translateY;
|
|
15169
|
-
clearTimeout(this._core.store.state.longTouchTimeout);
|
|
15170
|
-
}
|
|
15171
|
-
}
|
|
15860
|
+
openFilePicker() {
|
|
15861
|
+
if (this._core.store.isDisabled) {
|
|
15862
|
+
return;
|
|
15172
15863
|
}
|
|
15864
|
+
this.fileInput.click();
|
|
15173
15865
|
}
|
|
15174
|
-
|
|
15175
|
-
|
|
15176
|
-
|
|
15177
|
-
|
|
15178
|
-
|
|
15179
|
-
|
|
15180
|
-
|
|
15181
|
-
|
|
15182
|
-
const resizeDeltaX = Math.abs(dx);
|
|
15183
|
-
const resizeDeltaY = Math.abs(dy);
|
|
15184
|
-
const resizeThreshold = 5;
|
|
15185
|
-
if (resizeDeltaX > resizeThreshold || resizeDeltaY > resizeThreshold) {
|
|
15186
|
-
this.hasResized = true;
|
|
15187
|
-
}
|
|
15188
|
-
if (!this.hasResized) {
|
|
15189
|
-
return;
|
|
15190
|
-
}
|
|
15191
|
-
const rotation = selectionGroup.rotation;
|
|
15192
|
-
const sin = Math.sin(rotation);
|
|
15193
|
-
const cos = Math.cos(rotation);
|
|
15194
|
-
const activeScale = selectionGroup.scale || this._core.store.state.scale;
|
|
15195
|
-
// Calculate delta in local unrotated space
|
|
15196
|
-
// We rotate the screen delta by -rotation to align with the object's axes
|
|
15197
|
-
const localDx = dx * cos + dy * sin;
|
|
15198
|
-
const localDy = -dx * sin + dy * cos;
|
|
15199
|
-
// Calculate the center of the selection group before resize
|
|
15200
|
-
const initialCenterX = this.initialSize.x + this.initialSize.width / activeScale / 2;
|
|
15201
|
-
const initialCenterY = this.initialSize.y + this.initialSize.height / activeScale / 2;
|
|
15202
|
-
// The center moves by half of the screen delta (scaled)
|
|
15203
|
-
// This is true regardless of rotation because the resize happens symmetrically around the center
|
|
15204
|
-
// relative to the fixed point logic
|
|
15205
|
-
const newCenterX = initialCenterX + dx / activeScale / 2;
|
|
15206
|
-
const newCenterY = initialCenterY + dy / activeScale / 2;
|
|
15207
|
-
switch (this._core.store.state.resizeHandleType) {
|
|
15208
|
-
case KritzelHandleType.TopLeft:
|
|
15209
|
-
this.newSize.width = this.initialSize.width - localDx;
|
|
15210
|
-
this.newSize.height = this.initialSize.height - localDy;
|
|
15211
|
-
break;
|
|
15212
|
-
case KritzelHandleType.TopRight:
|
|
15213
|
-
this.newSize.width = this.initialSize.width + localDx;
|
|
15214
|
-
this.newSize.height = this.initialSize.height - localDy;
|
|
15215
|
-
break;
|
|
15216
|
-
case KritzelHandleType.BottomLeft:
|
|
15217
|
-
this.newSize.width = this.initialSize.width - localDx;
|
|
15218
|
-
this.newSize.height = this.initialSize.height + localDy;
|
|
15219
|
-
break;
|
|
15220
|
-
case KritzelHandleType.BottomRight:
|
|
15221
|
-
this.newSize.width = this.initialSize.width + localDx;
|
|
15222
|
-
this.newSize.height = this.initialSize.height + localDy;
|
|
15223
|
-
break;
|
|
15224
|
-
}
|
|
15225
|
-
this.newSize.x = newCenterX - this.newSize.width / activeScale / 2;
|
|
15226
|
-
this.newSize.y = newCenterY - this.newSize.height / activeScale / 2;
|
|
15227
|
-
selectionGroup.resize(this.newSize.x, this.newSize.y, this.newSize.width, this.newSize.height);
|
|
15228
|
-
}
|
|
15229
|
-
}
|
|
15230
|
-
if (event.pointerType === 'touch') {
|
|
15231
|
-
const activePointers = Array.from(this._core.store.state.pointers.values());
|
|
15232
|
-
const oneFingerTouch = activePointers[0];
|
|
15233
|
-
if (!oneFingerTouch) {
|
|
15234
|
-
return;
|
|
15235
|
-
}
|
|
15236
|
-
const selectionGroup = this._core.store.selectionGroup;
|
|
15237
|
-
if (this._core.store.state.isResizing && selectionGroup) {
|
|
15238
|
-
const clientX = Math.round(oneFingerTouch.clientX - this._core.store.offsetX);
|
|
15239
|
-
const clientY = Math.round(oneFingerTouch.clientY - this._core.store.offsetY);
|
|
15240
|
-
const dx = clientX - this.initialMouseX;
|
|
15241
|
-
const dy = clientY - this.initialMouseY;
|
|
15242
|
-
const resizeDeltaX = Math.abs(dx);
|
|
15243
|
-
const resizeDeltaY = Math.abs(dy);
|
|
15244
|
-
const resizeThreshold = 5;
|
|
15245
|
-
if (resizeDeltaX > resizeThreshold || resizeDeltaY > resizeThreshold) {
|
|
15246
|
-
clearTimeout(this._core.store.state.longTouchTimeout);
|
|
15247
|
-
this.hasResized = true;
|
|
15248
|
-
}
|
|
15249
|
-
if (!this.hasResized) {
|
|
15250
|
-
return;
|
|
15251
|
-
}
|
|
15252
|
-
const rotation = selectionGroup.rotation;
|
|
15253
|
-
const sin = Math.sin(rotation);
|
|
15254
|
-
const cos = Math.cos(rotation);
|
|
15255
|
-
const activeScale = selectionGroup.scale || this._core.store.state.scale;
|
|
15256
|
-
const localDx = dx * cos + dy * sin;
|
|
15257
|
-
const localDy = -dx * sin + dy * cos;
|
|
15258
|
-
const initialCenterX = this.initialSize.x + this.initialSize.width / activeScale / 2;
|
|
15259
|
-
const initialCenterY = this.initialSize.y + this.initialSize.height / activeScale / 2;
|
|
15260
|
-
const newCenterX = initialCenterX + dx / activeScale / 2;
|
|
15261
|
-
const newCenterY = initialCenterY + dy / activeScale / 2;
|
|
15262
|
-
switch (this._core.store.state.resizeHandleType) {
|
|
15263
|
-
case KritzelHandleType.TopLeft:
|
|
15264
|
-
this.newSize.width = this.initialSize.width - localDx;
|
|
15265
|
-
this.newSize.height = this.initialSize.height - localDy;
|
|
15266
|
-
break;
|
|
15267
|
-
case KritzelHandleType.TopRight:
|
|
15268
|
-
this.newSize.width = this.initialSize.width + localDx;
|
|
15269
|
-
this.newSize.height = this.initialSize.height - localDy;
|
|
15270
|
-
break;
|
|
15271
|
-
case KritzelHandleType.BottomLeft:
|
|
15272
|
-
this.newSize.width = this.initialSize.width - localDx;
|
|
15273
|
-
this.newSize.height = this.initialSize.height + localDy;
|
|
15274
|
-
break;
|
|
15275
|
-
case KritzelHandleType.BottomRight:
|
|
15276
|
-
this.newSize.width = this.initialSize.width + localDx;
|
|
15277
|
-
this.newSize.height = this.initialSize.height + localDy;
|
|
15278
|
-
break;
|
|
15279
|
-
}
|
|
15280
|
-
this.newSize.x = newCenterX - this.newSize.width / activeScale / 2;
|
|
15281
|
-
this.newSize.y = newCenterY - this.newSize.height / activeScale / 2;
|
|
15282
|
-
selectionGroup.resize(this.newSize.x, this.newSize.y, this.newSize.width, this.newSize.height);
|
|
15283
|
-
}
|
|
15284
|
-
}
|
|
15866
|
+
setupFileInput() {
|
|
15867
|
+
this.fileInput = document.createElement('input');
|
|
15868
|
+
this.fileInput.type = 'file';
|
|
15869
|
+
this.fileInput.accept = 'image/*';
|
|
15870
|
+
this.fileInput.style.display = 'none';
|
|
15871
|
+
this.fileInput.addEventListener('change', this.handleFileSelect.bind(this));
|
|
15872
|
+
this.fileInput.addEventListener('cancel', this.handleCancel.bind(this));
|
|
15873
|
+
document.body.appendChild(this.fileInput);
|
|
15285
15874
|
}
|
|
15286
|
-
|
|
15287
|
-
|
|
15288
|
-
|
|
15289
|
-
|
|
15290
|
-
|
|
15291
|
-
|
|
15292
|
-
|
|
15293
|
-
|
|
15294
|
-
|
|
15295
|
-
|
|
15296
|
-
|
|
15875
|
+
handleFileSelect(event) {
|
|
15876
|
+
const input = event.target;
|
|
15877
|
+
if (input.files && input.files[0]) {
|
|
15878
|
+
const file = input.files[0];
|
|
15879
|
+
imageCompression(file, {
|
|
15880
|
+
maxWidthOrHeight: this.maxCompressionSize,
|
|
15881
|
+
})
|
|
15882
|
+
.then(compressedFile => {
|
|
15883
|
+
this.readFile(compressedFile);
|
|
15884
|
+
})
|
|
15885
|
+
.catch(error => {
|
|
15886
|
+
console.error('Error during image compression or processing:', error);
|
|
15887
|
+
this.handleCancel();
|
|
15888
|
+
});
|
|
15297
15889
|
}
|
|
15298
|
-
|
|
15299
|
-
|
|
15300
|
-
|
|
15301
|
-
|
|
15302
|
-
|
|
15303
|
-
|
|
15304
|
-
this._core.store.state.hasObjectsChanged = true;
|
|
15305
|
-
}
|
|
15306
|
-
this.reset();
|
|
15307
|
-
clearTimeout(this._core.store.state.longTouchTimeout);
|
|
15308
|
-
}
|
|
15890
|
+
else {
|
|
15891
|
+
console.info('File selection cancelled by user.');
|
|
15892
|
+
this.handleCancel();
|
|
15893
|
+
}
|
|
15894
|
+
if (input) {
|
|
15895
|
+
input.value = '';
|
|
15309
15896
|
}
|
|
15310
15897
|
}
|
|
15898
|
+
readFile(file) {
|
|
15899
|
+
const reader = new FileReader();
|
|
15900
|
+
reader.onload = e => {
|
|
15901
|
+
const img = new Image();
|
|
15902
|
+
img.src = e.target?.result;
|
|
15903
|
+
img.onload = () => this.createKritzelImage(img);
|
|
15904
|
+
};
|
|
15905
|
+
reader.readAsDataURL(file);
|
|
15906
|
+
}
|
|
15907
|
+
createKritzelImage(img) {
|
|
15908
|
+
const image = KritzelImage.create(this._core);
|
|
15909
|
+
const { scaledWidth, scaledHeight } = image.calculateScaledDimensions(img);
|
|
15910
|
+
image.src = img.src;
|
|
15911
|
+
image.width = scaledWidth;
|
|
15912
|
+
image.height = scaledHeight;
|
|
15913
|
+
image.zIndex = this._core.store.currentZIndex;
|
|
15914
|
+
image.centerInViewport();
|
|
15915
|
+
this.addImageToStore(image);
|
|
15916
|
+
return image;
|
|
15917
|
+
}
|
|
15918
|
+
addImageToStore(image) {
|
|
15919
|
+
this._core.addObject(image);
|
|
15920
|
+
this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
|
|
15921
|
+
this._core.selectObjects([image]);
|
|
15922
|
+
this._core.engine.emitObjectsChange();
|
|
15923
|
+
}
|
|
15924
|
+
handleCancel() {
|
|
15925
|
+
this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
|
|
15926
|
+
}
|
|
15311
15927
|
}
|
|
15312
15928
|
|
|
15313
|
-
class
|
|
15314
|
-
|
|
15315
|
-
|
|
15316
|
-
|
|
15317
|
-
|
|
15929
|
+
class KritzelTextTool extends KritzelBaseTool {
|
|
15930
|
+
fontFamily = 'Arial';
|
|
15931
|
+
fontSize = 16;
|
|
15932
|
+
fontColor = '#000000';
|
|
15933
|
+
palette = [
|
|
15934
|
+
'#000000',
|
|
15935
|
+
'#FFFFFF',
|
|
15936
|
+
'#FF0000',
|
|
15937
|
+
'#00FF00',
|
|
15938
|
+
'#0000FF',
|
|
15939
|
+
'#FFFF00',
|
|
15940
|
+
'#FF00FF',
|
|
15941
|
+
'#00FFFF',
|
|
15942
|
+
'#808080',
|
|
15943
|
+
'#C0C0C0',
|
|
15944
|
+
'#800000',
|
|
15945
|
+
'#008000',
|
|
15946
|
+
'#000080',
|
|
15947
|
+
'#808000',
|
|
15948
|
+
'#800080',
|
|
15949
|
+
];
|
|
15318
15950
|
constructor(core) {
|
|
15319
15951
|
super(core);
|
|
15320
15952
|
}
|
|
15321
|
-
reset() {
|
|
15322
|
-
this.initialRotation = 0;
|
|
15323
|
-
this.rotation = 0;
|
|
15324
|
-
this.unchangedObjects = [];
|
|
15325
|
-
}
|
|
15326
15953
|
handlePointerDown(event) {
|
|
15954
|
+
if (event.cancelable) {
|
|
15955
|
+
event.preventDefault();
|
|
15956
|
+
}
|
|
15327
15957
|
if (event.pointerType === 'mouse') {
|
|
15328
|
-
|
|
15329
|
-
|
|
15330
|
-
|
|
15331
|
-
|
|
15332
|
-
|
|
15333
|
-
|
|
15334
|
-
|
|
15335
|
-
const centerY = selectionGroup.translateY + selectionGroup.height / 2 / this._core.store.state.scale;
|
|
15336
|
-
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
15337
|
-
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
15338
|
-
this.initialSelectionGroupRotation = selectionGroup.rotation;
|
|
15339
|
-
this.initialRotation = Math.atan2(centerY - cursorY, centerX - cursorX) - selectionGroup.rotation;
|
|
15340
|
-
this.unchangedObjects = selectionGroup.objects.map(obj => obj.clone());
|
|
15341
|
-
}
|
|
15958
|
+
const path = event.composedPath().slice(1);
|
|
15959
|
+
const objectElement = path.find(element => element.classList && element.classList.contains('object'));
|
|
15960
|
+
const object = this._core.findObjectById(objectElement?.id);
|
|
15961
|
+
const activeText = this._core.store.activeText;
|
|
15962
|
+
if (activeText === null && object instanceof KritzelText) {
|
|
15963
|
+
object.edit(event);
|
|
15964
|
+
return;
|
|
15342
15965
|
}
|
|
15343
|
-
|
|
15966
|
+
if (activeText !== null && object instanceof KritzelText) {
|
|
15967
|
+
activeText.save();
|
|
15968
|
+
object.edit(event);
|
|
15969
|
+
return;
|
|
15970
|
+
}
|
|
15971
|
+
if (activeText !== null && object instanceof KritzelText === false) {
|
|
15972
|
+
this._core.resetActiveText();
|
|
15973
|
+
this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
|
|
15974
|
+
return;
|
|
15975
|
+
}
|
|
15976
|
+
if (KritzelEventHelper.isLeftClick(event) === false) {
|
|
15977
|
+
return;
|
|
15978
|
+
}
|
|
15979
|
+
const clientX = event.clientX - this._core.store.offsetX;
|
|
15980
|
+
const clientY = event.clientY - this._core.store.offsetY;
|
|
15981
|
+
const text = KritzelText.create(this._core, this.fontSize, this.fontFamily);
|
|
15982
|
+
text.fontColor = this.fontColor;
|
|
15983
|
+
text.translateX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
15984
|
+
text.translateY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
15985
|
+
text.zIndex = this._core.store.currentZIndex;
|
|
15986
|
+
this._core.store.state.objects.insert(text);
|
|
15987
|
+
this._core.rerender();
|
|
15988
|
+
text.edit(event);
|
|
15989
|
+
}
|
|
15344
15990
|
if (event.pointerType === 'touch') {
|
|
15345
15991
|
const activePointers = Array.from(this._core.store.state.pointers.values());
|
|
15346
|
-
const
|
|
15347
|
-
|
|
15992
|
+
const path = event.composedPath().slice(1);
|
|
15993
|
+
const objectElement = path.find(element => element.classList && element.classList.contains('object'));
|
|
15994
|
+
const object = this._core.findObjectById(objectElement?.id);
|
|
15995
|
+
const activeText = this._core.store.activeText;
|
|
15996
|
+
if (activeText === null && object instanceof KritzelText) {
|
|
15997
|
+
object.edit(event);
|
|
15348
15998
|
return;
|
|
15349
15999
|
}
|
|
15350
|
-
if (
|
|
16000
|
+
if (activeText !== null && object instanceof KritzelText) {
|
|
16001
|
+
activeText.save();
|
|
16002
|
+
object.edit(event);
|
|
16003
|
+
return;
|
|
16004
|
+
}
|
|
16005
|
+
if (activeText !== null && object instanceof KritzelText === false) {
|
|
16006
|
+
this._core.resetActiveText();
|
|
16007
|
+
this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
|
|
16008
|
+
return;
|
|
16009
|
+
}
|
|
16010
|
+
if (activePointers.length > 1) {
|
|
16011
|
+
return;
|
|
16012
|
+
}
|
|
16013
|
+
const clientX = Math.round(activePointers[0].clientX - this._core.store.offsetX);
|
|
16014
|
+
const clientY = Math.round(activePointers[0].clientY - this._core.store.offsetY);
|
|
16015
|
+
const text = KritzelText.create(this._core, this.fontSize, this.fontFamily);
|
|
16016
|
+
text.fontColor = this.fontColor;
|
|
16017
|
+
text.translateX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
16018
|
+
text.translateY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
16019
|
+
text.zIndex = this._core.store.currentZIndex;
|
|
16020
|
+
this._core.store.state.objects.insert(text);
|
|
16021
|
+
this._core.rerender();
|
|
16022
|
+
text.edit(event);
|
|
16023
|
+
}
|
|
16024
|
+
}
|
|
16025
|
+
handlePointerUp(event) {
|
|
16026
|
+
if (event.cancelable) {
|
|
16027
|
+
event.preventDefault();
|
|
16028
|
+
}
|
|
16029
|
+
this._core.store.activeText?.edit(event);
|
|
16030
|
+
}
|
|
16031
|
+
}
|
|
16032
|
+
|
|
16033
|
+
class KritzelIconRegistry {
|
|
16034
|
+
static registry = new Map();
|
|
16035
|
+
static register(name, svgContent) {
|
|
16036
|
+
if (this.registry.has(name)) {
|
|
16037
|
+
console.warn(`[IconRegistry] Icon "${name}" is already registered. It will be overwritten.`);
|
|
16038
|
+
}
|
|
16039
|
+
this.registry.set(name, svgContent);
|
|
16040
|
+
}
|
|
16041
|
+
static get(name) {
|
|
16042
|
+
return this.registry.get(name);
|
|
16043
|
+
}
|
|
16044
|
+
static registerIcons(icons) {
|
|
16045
|
+
for (const name in icons) {
|
|
16046
|
+
if (Object.prototype.hasOwnProperty.call(icons, name)) {
|
|
16047
|
+
this.register(name, icons[name]);
|
|
16048
|
+
}
|
|
16049
|
+
}
|
|
16050
|
+
}
|
|
16051
|
+
static has(name) {
|
|
16052
|
+
return this.registry.has(name);
|
|
16053
|
+
}
|
|
16054
|
+
}
|
|
16055
|
+
KritzelIconRegistry.registerIcons({
|
|
16056
|
+
'cursor': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z"/></svg>',
|
|
16057
|
+
'pen': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/></svg>',
|
|
16058
|
+
'arrow': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="19" x2="19" y2="5"/><path d="M15 5h4v4"/></svg>',
|
|
16059
|
+
'highlighter': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-highlighter-icon lucide-highlighter"><path d="m9 11-6 6v3h9l3-3"/><path d="m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4"/></svg>',
|
|
16060
|
+
'eraser': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21"/><path d="M22 21H7"/><path d="m5 11 9 9"/></svg>',
|
|
16061
|
+
'type': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 7 4 4 20 4 20 7"/><line x1="9" x2="15" y1="20" y2="20"/><line x1="12" x2="12" y1="4" y2="20"/></svg>',
|
|
16062
|
+
'image': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg>',
|
|
16063
|
+
'chevron-down': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>',
|
|
16064
|
+
'chevron-up': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m18 15-6-6-6 6"/></svg>',
|
|
16065
|
+
'copy': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy-icon lucide-copy"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>',
|
|
16066
|
+
'paste': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-clipboard-paste-icon lucide-clipboard-paste"><path d="M11 14h10"/><path d="M16 4h2a2 2 0 0 1 2 2v1.344"/><path d="m17 18 4-4-4-4"/><path d="M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 1.793-1.113"/><rect x="8" y="2" width="8" height="4" rx="1"/></svg>',
|
|
16067
|
+
'cut': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-scissors-icon lucide-scissors"><circle cx="6" cy="6" r="3"/><path d="M8.12 8.12 12 12"/><path d="M20 4 8.12 15.88"/><circle cx="6" cy="18" r="3"/><path d="M14.8 14.8 20 20"/></svg>',
|
|
16068
|
+
'delete': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-trash2-icon lucide-trash-2"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/></svg>',
|
|
16069
|
+
'bring-to-front': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-to-line-icon lucide-arrow-up-to-line"><path d="M5 3h14"/><path d="m18 13-6-6-6 6"/><path d="M12 7v14"/></svg>',
|
|
16070
|
+
'send-to-back': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down-to-line-icon lucide-arrow-down-to-line"><path d="M12 17V3"/><path d="m6 11 6 6 6-6"/><path d="M19 21H5"/></svg>',
|
|
16071
|
+
'select-all': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-mouse-pointer-icon lucide-square-mouse-pointer"><path d="M12.034 12.681a.498.498 0 0 1 .647-.647l9 3.5a.5.5 0 0 1-.033.943l-3.444 1.068a1 1 0 0 0-.66.66l-1.067 3.443a.5.5 0 0 1-.943.033z"/><path d="M21 11V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h6"/></svg>',
|
|
16072
|
+
'download': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download-icon lucide-download"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>',
|
|
16073
|
+
'undo': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-undo-icon lucide-undo"><path d="M3 7v6h6"/><path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"/></svg>',
|
|
16074
|
+
'redo': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-redo-icon lucide-redo"><path d="M21 7v6h-6"/><path d="M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3l3 2.7"/></svg>',
|
|
16075
|
+
'plus': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-plus-icon lucide-plus"><path d="M5 12h14"/><path d="M12 5v14"/></svg>',
|
|
16076
|
+
'ellipsis-vertical': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-ellipsis-vertical-icon lucide-ellipsis-vertical"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>',
|
|
16077
|
+
'x': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>',
|
|
16078
|
+
'check': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-icon lucide-check"><path d="M20 6 9 17l-5-5"/></svg>',
|
|
16079
|
+
'move-vertical': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-move-vertical-icon lucide-move-vertical" version="1.1" id="svg3"> <defs id="defs3" /> <path d="M12 2v20" id="path1" /> <path style="fill:#ffffff;stroke-width:2.5;stroke-dasharray:none;stroke-linejoin:round;paint-order:stroke fill markers" d="m 11.735575,22.661865 c -0.09259,-0.02798 -0.204674,-0.07661 -0.249076,-0.108068 -0.04441,-0.03147 -1.167275,-0.979853 -2.4952713,-2.10755 -1.8557024,-1.57581 -2.4300904,-2.079639 -2.4817336,-2.17687 -0.086514,-0.162885 -0.089504,-0.422449 -0.00664,-0.576334 0.1483053,-0.275409 0.437667,-0.436207 0.7830634,-0.435147 0.3692925,0.0011 0.3517326,-0.01122 2.168748,1.525599 L 11.12348,20.194964 V 11.999996 3.8050256 L 9.4546663,5.2164943 C 7.6376509,6.7533118 7.6552109,6.7409594 7.2859184,6.7420935 6.6681409,6.7439906 6.253658,6.1955854 6.5159903,5.723396 6.5738626,5.6192278 7.1368766,5.1267427 9.0629381,3.4955044 11.738128,1.2298067 11.640395,1.3026868 12.00355,1.3026868 c 0.363154,0 0.265421,-0.07288 2.940611,2.1928176 1.926062,1.6312383 2.489076,2.1237234 2.546948,2.2278916 0.262332,0.4721894 -0.15215,1.0205946 -0.769928,1.0186975 -0.369293,-0.00114 -0.351733,0.011218 -2.168748,-1.5255992 L 12.88362,3.8050256 v 8.1949704 8.194968 l 1.668813,-1.411469 c 1.817015,-1.536817 1.799455,-1.524464 2.168748,-1.525599 0.617772,-0.0019 1.032269,0.546521 0.769928,1.018687 -0.103474,0.18623 -4.919006,4.273935 -5.130582,4.355136 -0.20796,0.07981 -0.425829,0.09033 -0.624952,0.03014 z" id="path4" /> </svg>',
|
|
16080
|
+
'hand': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-hand-icon lucide-hand" version="1.1" id="svg4"> <defs id="defs4" /> <path d="M18 11V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2" id="path1" /> <path d="M14 10V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2v2" id="path2" /> <path d="M10 10.5V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2v8" id="path3" /> <path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15" id="path4" /> <path style="fill:#ffffff;stroke-width:0.0423032" d="M 11.478261,21.222477 C 9.6854059,21.139331 8.3341788,20.647346 7.1297169,19.639159 6.9698298,19.505327 5.949644,18.508708 4.8626374,17.42445 3.0780314,15.644357 2.8775851,15.435367 2.7968392,15.270602 2.5896561,14.847836 2.616354,14.436649 2.8771894,14.033085 c 0.136522,-0.211226 0.3837159,-0.398688 0.6367632,-0.482897 0.1529977,-0.05091 0.2326803,-0.05992 0.4470908,-0.05054 0.2250407,0.0098 0.2876577,0.02439 0.4554568,0.105827 0.1747854,0.08483 0.2933718,0.192702 1.1675186,1.062064 0.7182021,0.714271 1.0062041,0.982633 1.0998825,1.024878 0.1680197,0.07577 0.4756439,0.07817 0.6368269,0.005 0.1370772,-0.06226 0.2976691,-0.220818 0.3691296,-0.364463 0.072561,-0.145855 0.071573,-0.543545 -0.00169,-0.681911 -0.028747,-0.05429 -0.2476439,-0.296131 -0.4864385,-0.537426 l -0.4341716,-0.438718 0.00753,-3.989014 0.00753,-3.9890137 0.089246,-0.1883791 c 0.244787,-0.516692 0.7711718,-0.809716 1.3059705,-0.7269981 0.3973687,0.061462 0.7569953,0.3284904 0.9363122,0.6952277 l 0.093853,0.1919473 0.014101,2.4958872 0.014101,2.4958877 0.067385,0.149123 c 0.1186861,0.262654 0.4140438,0.457222 0.6940724,0.457222 0.2764172,0 0.5690532,-0.187563 0.6965992,-0.446482 l 0.06486,-0.131661 0.0141,-3.4970626 0.0141,-3.4970623 0.08982,-0.1896121 C 11.096301,3.0422103 11.506844,2.7755634 12,2.7755634 c 0.493156,0 0.903699,0.2666469 1.122868,0.7293016 l 0.08982,0.1896121 0.0141,3.2432432 c 0.01405,3.2315947 0.01432,3.2437077 0.07397,3.3726737 0.124721,0.269649 0.355908,0.424566 0.661411,0.443206 0.237954,0.01452 0.429018,-0.0627 0.591626,-0.239109 0.223655,-0.242637 0.208338,-0.06565 0.224113,-2.5896966 l 0.0141,-2.2561693 0.09385,-0.1919473 c 0.179317,-0.3667373 0.538944,-0.6337662 0.936313,-0.6952277 0.609359,-0.09425 1.208067,0.3054956 1.370981,0.9153772 0.03013,0.1127929 0.03773,0.6662436 0.038,2.7657391 3.74e-4,2.9328416 -0.008,2.8034316 0.197044,3.0364016 0.234927,0.266892 0.603828,0.337117 0.920407,0.175213 0.181933,-0.09304 0.329759,-0.261686 0.376309,-0.4293 0.01848,-0.06654 0.02929,-0.683932 0.0295,-1.684364 1.78e-4,-0.8783075 0.01239,-1.6530128 0.02751,-1.745346 0.08579,-0.5238478 0.505382,-0.9420803 1.039546,-1.0361716 0.607538,-0.1070155 1.25615,0.3485846 1.385876,0.973471 0.02211,0.1064847 0.02843,1.1397236 0.02169,3.5455556 -0.0093,3.324725 -0.01078,3.403075 -0.07062,3.770606 -0.126399,0.776213 -0.328814,1.41352 -0.669031,2.106456 -0.36657,0.746612 -0.72118,1.250303 -1.297841,1.843464 -1.185731,1.21966 -2.604527,1.933174 -4.300822,2.162889 -0.38234,0.05178 -2.604621,0.0785 -3.412456,0.04104 z" id="path16" /> </svg>',
|
|
16081
|
+
'hand-grab': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-hand-grab-icon lucide-hand-grab" version="1.1" id="svg5"> <defs id="defs5" /> <path d="M18 11.5V9a2 2 0 0 0-2-2a2 2 0 0 0-2 2v1.4" id="path1" /> <path d="M14 10V8a2 2 0 0 0-2-2a2 2 0 0 0-2 2v2" id="path2" /> <path d="M10 9.9V9a2 2 0 0 0-2-2a2 2 0 0 0-2 2v5" id="path3" /> <path d="M6 14a2 2 0 0 0-2-2a2 2 0 0 0-2 2" id="path4" /> <path d="M18 11a2 2 0 1 1 4 0v3a8 8 0 0 1-8 8h-4a8 8 0 0 1-8-8 2 2 0 1 1 4 0" id="path5" /> <path style="fill:#ffffff;stroke-width:0.0423032" d="M 9.5887192,21.221364 C 8.0526856,21.128375 6.6533474,20.587008 5.4571093,19.622953 5.3019976,19.497947 5.0101058,19.225967 4.8084606,19.018552 4.2300382,18.423579 3.8474097,17.879011 3.4960953,17.150756 3.1017715,16.333345 2.9014937,15.633198 2.8052391,14.735605 c -0.097577,-0.909922 -0.019834,-1.263323 0.3569794,-1.622742 0.753018,-0.718257 1.9463784,-0.256949 2.0619296,0.797066 0.037839,0.345154 0.089701,0.477674 0.2472582,0.631806 0.2464521,0.241095 0.5958008,0.287445 0.9036474,0.119894 0.1478567,-0.08047 0.2303871,-0.16866 0.3266699,-0.349056 0.050118,-0.0939 0.05337,-0.2326 0.066784,-2.848413 L 6.7826087,8.7144536 6.8458627,8.559342 C 6.9736603,8.2459558 7.2463773,7.9734987 7.5581669,7.8477164 c 0.1253804,-0.050581 0.2118709,-0.062705 0.4512338,-0.063254 0.275392,-6.312e-4 0.3094658,0.00564 0.4867271,0.089609 0.264676,0.1253746 0.4771417,0.334346 0.6070085,0.5970252 l 0.1048543,0.212087 0.017338,0.7488899 c 0.019395,0.8377165 0.026343,0.8713745 0.2242006,1.0860245 0.1591067,0.172611 0.3522021,0.249908 0.5883018,0.235502 0.305503,-0.01864 0.53669,-0.173557 0.661411,-0.443206 0.05734,-0.123963 0.0605,-0.181852 0.07486,-1.3703236 0.01626,-1.3456629 0.0141,-1.3267758 0.182667,-1.5938092 0.257891,-0.4085268 0.77099,-0.6442307 1.228943,-0.5645435 0.471012,0.08196 0.850379,0.4102836 0.989676,0.8565175 0.04663,0.149383 0.0514,0.2860903 0.0514,1.4721724 0,0.8344844 0.01108,1.3591794 0.03064,1.4503554 0.04109,0.191585 0.179844,0.390224 0.342505,0.490321 0.27721,0.170587 0.693425,0.126085 0.92529,-0.09893 0.228986,-0.222224 0.234357,-0.25046 0.251582,-1.3227071 l 0.0152,-0.9463484 0.104855,-0.2120434 c 0.129872,-0.2626357 0.342372,-0.471626 0.607008,-0.5969817 0.177262,-0.083967 0.211335,-0.09024 0.486727,-0.089609 0.239363,5.486e-4 0.325854,0.012673 0.451234,0.063254 0.31179,0.1257823 0.584507,0.3982394 0.712304,0.7116256 0.0621,0.1522783 0.06351,0.1824139 0.07736,1.649824 0.01408,1.492435 0.0142,1.494914 0.07947,1.627432 0.0771,0.156535 0.2216,0.291061 0.395058,0.367789 0.09695,0.04289 0.169789,0.05275 0.325757,0.04411 0.230544,-0.01277 0.363779,-0.06826 0.506411,-0.21089 0.177062,-0.177061 0.198302,-0.25307 0.219359,-0.784959 0.02002,-0.505773 0.05012,-0.6549 0.175297,-0.868492 0.498409,-0.850471 1.728484,-0.8041941 2.173478,0.08177 0.131751,0.262312 0.134447,0.313526 0.122615,2.328965 -0.0104,1.771638 -0.01517,1.923481 -0.0714,2.273746 -0.123689,0.770512 -0.327108,1.411813 -0.668163,2.106456 -0.36657,0.746612 -0.72118,1.250303 -1.297841,1.843464 -1.180268,1.214041 -2.600612,1.930754 -4.287083,2.163284 -0.28697,0.03957 -0.685446,0.04782 -2.636536,0.05459 -1.26416,0.0044 -2.4698005,-0.0024 -2.6792012,-0.01507 z" id="path6" /> </svg>',
|
|
16082
|
+
'mouse-pointer': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-mouse-pointer2-icon lucide-mouse-pointer-2" version="1.1" id="svg1"> <defs id="defs1" /> <path d="M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z" id="path1" /> <path style="fill:#ffffff;stroke-width:0.0972651;stroke-linejoin:round;paint-order:stroke fill markers" d="M 7.8509196,12.314844 C 6.127071,8.1920568 4.7235325,4.8125504 4.7319449,4.8048299 c 0.01847,-0.016951 15.4787861,6.0801241 15.4610371,6.0973581 -0.0069,0.0067 -1.32633,0.341226 -2.93219,0.743509 -1.858829,0.465652 -3.011462,0.772855 -3.172161,0.845453 -0.688426,0.310999 -1.245788,0.879167 -1.498145,1.527185 -0.06251,0.160512 -0.440389,1.529576 -0.839733,3.042364 -0.399346,1.512788 -0.734966,2.750433 -0.745822,2.750322 -0.01086,-1.09e-4 -1.430163,-3.373391 -3.1540114,-7.496177 z" id="path2" /> <path style="fill:#ffffff;stroke-width:0.0705053;stroke-linejoin:round;paint-order:stroke fill markers" d="M 10.777831,19.240865 C 9.3899089,15.977493 4.7567738,4.8648478 4.7760705,4.8455511 c 0.013084,-0.013084 4.7618079,1.8507077 10.7773965,4.2299406 4.467495,1.7669453 4.558837,1.8037523 4.533848,1.8269883 -0.01183,0.011 -0.96064,0.257045 -2.108467,0.546768 -3.445281,0.869623 -3.777157,0.960825 -4.075205,1.119907 -0.597122,0.31871 -1.103666,0.864485 -1.309236,1.410635 -0.09642,0.256167 -0.312339,1.032806 -0.943226,3.392713 -0.641066,2.39798 -0.629171,2.354884 -0.649929,2.354849 -0.0091,-1.5e-5 -0.10963,-0.218934 -0.223421,-0.486487 z" id="path6" /> </svg>',
|
|
16083
|
+
'pointer': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-pointer-icon lucide-pointer" version="1.1" id="svg5"> <defs id="defs5" /> <path d="M22 14a8 8 0 0 1-8 8" id="path1" /> <path d="M18 11v-1a2 2 0 0 0-2-2a2 2 0 0 0-2 2" id="path2" /> <path d="M14 10V9a2 2 0 0 0-2-2a2 2 0 0 0-2 2v1" id="path3" /> <path d="M10 9.5V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2v10" id="path4" /> <path d="M18 11a2 2 0 1 1 4 0v3a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15" id="path5" /> <path style="fill:#ffffff;stroke-width:0.0930233;stroke-linejoin:round;paint-order:stroke fill markers" d="M 11.2,21.203517 C 9.5092536,21.041493 8.4838422,20.667261 7.3116279,19.784423 7.1888372,19.691945 6.139155,18.670017 4.9790008,17.513473 2.5649891,15.106974 2.6562686,15.220427 2.6869266,14.664621 c 0.020277,-0.367608 0.1272548,-0.601802 0.3822766,-0.836873 0.2609241,-0.240511 0.4631523,-0.319555 0.8191689,-0.320185 0.509345,-9.02e-4 0.5815472,0.04845 1.7102825,1.168976 0.532833,0.528958 1.0223742,0.98871 1.0878697,1.021671 0.1699959,0.08555 0.4741793,0.07658 0.6733897,-0.01985 0.303161,-0.146758 0.4720469,-0.543298 0.3791811,-0.890307 -0.036373,-0.135913 -0.1250717,-0.246717 -0.506083,-0.632208 l -0.4619807,-0.467411 0.00983,-5.002356 0.00983,-5.0023561 0.080405,-0.1739784 C 6.9849846,3.2633288 7.2288893,3.0140593 7.4738043,2.8937849 7.6531903,2.8056908 7.7297209,2.7906977 8,2.7906977 c 0.2702791,0 0.3468097,0.014993 0.5261957,0.1030872 0.244915,0.1202744 0.4888197,0.3695439 0.6027014,0.6159576 0.079998,0.1730976 0.080499,0.1905559 0.09901,3.448397 0.020472,3.6031325 0.00418,3.3665775 0.2478407,3.5980265 0.2810043,0.266918 0.6822132,0.283308 1.0083162,0.04119 0.232339,-0.172501 0.25913,-0.280173 0.275933,-1.1089849 0.01456,-0.7180841 0.01869,-0.7520168 0.117805,-0.9674419 0.454637,-0.9881395 1.789759,-0.9881395 2.244396,0 0.09912,0.2154251 0.103248,0.2493578 0.117805,0.9674419 0.0168,0.8288119 0.04359,0.9364839 0.275933,1.1089849 0.326103,0.242117 0.727312,0.225727 1.008316,-0.04119 0.160426,-0.152384 0.218977,-0.294978 0.250819,-0.610832 C 14.845142,9.2502593 15.336916,8.7840275 16,8.7840275 c 0.478554,0 0.847992,0.2240614 1.082489,0.6565225 l 0.126813,0.2338686 0.01869,0.7813954 c 0.02069,0.865066 0.03258,0.913549 0.276595,1.127795 0.239244,0.210059 0.56665,0.239738 0.88018,0.07979 0.219744,-0.112104 0.324993,-0.301479 0.383515,-0.690054 0.06437,-0.427406 0.125794,-0.56694 0.357358,-0.811785 C 19.38604,9.8862229 19.614832,9.7882106 20,9.7869898 c 0.380153,-0.0012 0.636641,0.1145641 0.905451,0.4086842 0.318537,0.348531 0.317259,0.340596 0.332734,2.064791 0.02055,2.290001 -0.04576,2.983129 -0.383655,4.009983 -0.878739,2.670467 -3.284504,4.613238 -6.090273,4.918188 -0.322415,0.03504 -3.227312,0.04717 -3.564257,0.01488 z" id="path6" /> <path style="fill:#ffffff;stroke-width:0.0705053;stroke-linejoin:round;paint-order:stroke fill markers" d="m 11.942854,21.190957 c -1.015003,-0.01704 -1.131723,-0.02688 -1.806535,-0.1522 C 9.5352183,20.927122 8.8225664,20.681951 8.319624,20.413766 7.7570149,20.113766 7.3758537,19.827607 6.7836127,19.260598 5.774345,18.294328 3.1746828,15.68636 2.994305,15.459184 2.7288345,15.124838 2.6970674,15.049109 2.7026798,14.763988 c 0.00827,-0.420163 0.1162058,-0.679868 0.3844475,-0.925026 0.2382303,-0.217729 0.416008,-0.293073 0.7303102,-0.309513 0.5101451,-0.02668 0.6329728,0.04799 1.5378542,0.934977 0.8479274,0.831158 1.2429087,1.199656 1.342282,1.252283 0.11819,0.06259 0.4442817,0.06081 0.6122724,-0.0033 0.1482348,-0.05661 0.3410696,-0.248342 0.4107195,-0.408368 0.063024,-0.144802 0.06179,-0.498 -0.00216,-0.618376 C 7.6917315,14.63641 7.4736023,14.388937 7.2336742,14.136678 L 6.797441,13.678026 6.7970753,8.6903755 6.7967098,3.7027253 6.9021002,3.502167 C 7.0320375,3.2548956 7.2343175,3.0487286 7.4743519,2.9189189 c 0.1797064,-0.097185 0.1877434,-0.098707 0.5209477,-0.098707 0.3169743,0 0.349151,0.00524 0.5076381,0.082665 0.2190605,0.1070166 0.4871239,0.3748528 0.5931342,0.592632 l 0.081613,0.1676584 0.019992,1.3834579 c 0.010995,0.7609018 0.021403,2.0560783 0.023128,2.87817 0.00341,1.6233162 0.028863,2.2876938 0.093598,2.4426258 0.089047,0.213119 0.4255042,0.419889 0.683247,0.419889 0.1563141,0 0.3731941,-0.08265 0.5187231,-0.197689 0.205546,-0.162474 0.225694,-0.250142 0.256177,-1.1146856 C 10.79418,8.8613623 10.8052,8.7492253 10.85668,8.6186115 11.053264,8.1198873 11.50183,7.7978848 12,7.7978848 c 0.49817,0 0.946736,0.3220025 1.143315,0.8207259 0.05148,0.1306138 0.0625,0.2427508 0.08413,0.8563239 0.02897,0.8216044 0.05497,0.9507864 0.218548,1.0859664 0.341252,0.282005 0.756809,0.293493 1.057036,0.02922 0.169206,-0.148941 0.234219,-0.2921 0.288276,-0.6347773 0.103115,-0.6536616 0.472737,-1.0621772 1.030077,-1.1384664 0.547738,-0.074975 0.986339,0.1608863 1.276145,0.6862578 l 0.105758,0.1917223 7.68e-4,0.3699606 c 0.0011,0.553623 0.04553,1.179579 0.09141,1.289176 0.04428,0.105777 0.219161,0.276182 0.364423,0.355092 0.06913,0.03755 0.155512,0.05067 0.333059,0.05055 0.204058,-1.3e-4 0.260695,-0.01118 0.380729,-0.07426 0.239564,-0.12591 0.320531,-0.265583 0.407786,-0.703455 0.08382,-0.420648 0.125337,-0.527172 0.279883,-0.718174 0.321102,-0.3968443 0.77117,-0.5469022 1.259123,-0.4198065 0.21344,0.055594 0.39756,0.1739295 0.578279,0.3716645 0.286251,0.313205 0.299377,0.389392 0.324444,1.883099 0.02888,1.721047 -0.03379,2.808669 -0.20314,3.525265 -0.307591,1.301561 -0.970113,2.493177 -1.91312,3.440943 -1.060413,1.065767 -2.34115,1.742909 -3.849589,2.035329 -0.348478,0.06755 -0.462014,0.07524 -1.340342,0.09078 -0.527787,0.0093 -1.416154,0.0093 -1.974148,-6.4e-5 z" id="path7" /> </svg>'
|
|
16084
|
+
});
|
|
16085
|
+
|
|
16086
|
+
class KritzelCursorHelper {
|
|
16087
|
+
static _pointerCursor = null;
|
|
16088
|
+
/**
|
|
16089
|
+
* Returns the custom pointer cursor CSS value.
|
|
16090
|
+
* This can be used instead of `cursor: pointer` for consistent styling.
|
|
16091
|
+
*/
|
|
16092
|
+
static getPointerCursor() {
|
|
16093
|
+
if (!this._pointerCursor) {
|
|
16094
|
+
this._pointerCursor = this.getCursor({ iconName: 'pointer' });
|
|
16095
|
+
}
|
|
16096
|
+
return this._pointerCursor;
|
|
16097
|
+
}
|
|
16098
|
+
/**
|
|
16099
|
+
* Returns a custom cursor CSS value with support for rotation.
|
|
16100
|
+
* The icon is retrieved from the KritzelIconRegistry.
|
|
16101
|
+
*/
|
|
16102
|
+
static getCursor(options) {
|
|
16103
|
+
const iconName = options.iconName === 'default' ? 'mouse-pointer' : options.iconName;
|
|
16104
|
+
const iconSvg = KritzelIconRegistry.get(iconName);
|
|
16105
|
+
if (!iconSvg) {
|
|
16106
|
+
console.warn(`Icon "${iconName}" not found in registry.`);
|
|
16107
|
+
return 'auto';
|
|
16108
|
+
}
|
|
16109
|
+
const size = options.size || 24;
|
|
16110
|
+
const rotation = options.rotation || 0;
|
|
16111
|
+
const color = options.color || 'black';
|
|
16112
|
+
// Default cursor (mouse-pointer) has hotspot at (4, 4), others at center
|
|
16113
|
+
const hotspot = options.hotspot || (options.iconName === 'default' ? { x: 4, y: 4 } : { x: size / 2, y: size / 2 });
|
|
16114
|
+
// Modify the SVG string to set size and color
|
|
16115
|
+
// We replace width and height to match the requested size
|
|
16116
|
+
// We replace currentColor with the requested color
|
|
16117
|
+
let content = iconSvg
|
|
16118
|
+
.replace(/width="\d+"/, `width="${size}"`)
|
|
16119
|
+
.replace(/height="\d+"/, `height="${size}"`)
|
|
16120
|
+
.replace(/currentColor/g, color);
|
|
16121
|
+
// Create the SVG string
|
|
16122
|
+
// We rotate around the center of the SVG canvas
|
|
16123
|
+
const center = size / 2;
|
|
16124
|
+
// We use a group to apply the rotation
|
|
16125
|
+
const svg = `
|
|
16126
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">
|
|
16127
|
+
<g transform="rotate(${rotation} ${center} ${center})">
|
|
16128
|
+
${content}
|
|
16129
|
+
</g>
|
|
16130
|
+
</svg>
|
|
16131
|
+
`;
|
|
16132
|
+
// Encode the SVG for use in a data URI
|
|
16133
|
+
// We need to be careful with encoding to ensure it works across browsers
|
|
16134
|
+
const encodedSvg = encodeURIComponent(svg.replace(/\s+/g, ' ').trim())
|
|
16135
|
+
.replace(/'/g, '%27')
|
|
16136
|
+
.replace(/"/g, '%22');
|
|
16137
|
+
const dataUri = `data:image/svg+xml;charset=utf-8,${encodedSvg}`;
|
|
16138
|
+
// Return the cursor style string
|
|
16139
|
+
return `url('${dataUri}') ${hotspot.x} ${hotspot.y}, auto`;
|
|
16140
|
+
}
|
|
16141
|
+
}
|
|
16142
|
+
|
|
16143
|
+
class KritzelBaseHandler {
|
|
16144
|
+
_core;
|
|
16145
|
+
constructor(core) {
|
|
16146
|
+
this._core = core;
|
|
16147
|
+
}
|
|
16148
|
+
}
|
|
16149
|
+
|
|
16150
|
+
class KritzelMoveHandler extends KritzelBaseHandler {
|
|
16151
|
+
dragStartX;
|
|
16152
|
+
dragStartY;
|
|
16153
|
+
startX;
|
|
16154
|
+
startY;
|
|
16155
|
+
endX;
|
|
16156
|
+
endY;
|
|
16157
|
+
hasMoved = false;
|
|
16158
|
+
trackedPointerId = null;
|
|
16159
|
+
constructor(core) {
|
|
16160
|
+
super(core);
|
|
16161
|
+
}
|
|
16162
|
+
reset() {
|
|
16163
|
+
this.dragStartX = 0;
|
|
16164
|
+
this.dragStartY = 0;
|
|
16165
|
+
this.startX = 0;
|
|
16166
|
+
this.startY = 0;
|
|
16167
|
+
this.endX = 0;
|
|
16168
|
+
this.endY = 0;
|
|
16169
|
+
this.hasMoved = false;
|
|
16170
|
+
this.trackedPointerId = null;
|
|
16171
|
+
}
|
|
16172
|
+
cancelPendingDrag() {
|
|
16173
|
+
this._core.store.state.isDragging = false;
|
|
16174
|
+
this.reset();
|
|
16175
|
+
}
|
|
16176
|
+
handlePointerDown(event) {
|
|
16177
|
+
if (event.pointerType === 'mouse') {
|
|
16178
|
+
if (KritzelEventHelper.isLeftClick(event)) {
|
|
15351
16179
|
const selectionGroup = this._core.store.selectionGroup;
|
|
15352
|
-
if (selectionGroup && this._core.store.state.isRotationHandleSelected) {
|
|
15353
|
-
const clientX =
|
|
15354
|
-
const clientY =
|
|
15355
|
-
this._core.store.state.
|
|
15356
|
-
|
|
15357
|
-
|
|
15358
|
-
|
|
15359
|
-
|
|
15360
|
-
this.
|
|
15361
|
-
|
|
15362
|
-
|
|
15363
|
-
|
|
16180
|
+
if (selectionGroup?.isSelected && !this._core.store.state.isResizeHandleSelected && !this._core.store.state.isRotationHandleSelected && !this._core.store.state.isLineHandleSelected) {
|
|
16181
|
+
const clientX = event.clientX - this._core.store.offsetX;
|
|
16182
|
+
const clientY = event.clientY - this._core.store.offsetY;
|
|
16183
|
+
this._core.store.state.isDragging = true;
|
|
16184
|
+
this.dragStartX = clientX;
|
|
16185
|
+
this.dragStartY = clientY;
|
|
16186
|
+
this.startX = this.dragStartX;
|
|
16187
|
+
this.startY = this.dragStartY;
|
|
16188
|
+
this.trackedPointerId = event.pointerId;
|
|
16189
|
+
}
|
|
16190
|
+
else {
|
|
16191
|
+
this.trackedPointerId = null;
|
|
16192
|
+
}
|
|
16193
|
+
}
|
|
16194
|
+
else {
|
|
16195
|
+
this.trackedPointerId = null;
|
|
16196
|
+
}
|
|
16197
|
+
}
|
|
16198
|
+
if (event.pointerType === 'touch') {
|
|
16199
|
+
const activePointers = Array.from(this._core.store.state.pointers.values());
|
|
16200
|
+
if (this._core.store.state.pointers.size === 1) {
|
|
16201
|
+
if (this._core.store.state.isScaling) {
|
|
16202
|
+
this.trackedPointerId = null;
|
|
16203
|
+
return;
|
|
16204
|
+
}
|
|
16205
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
16206
|
+
if (selectionGroup?.isSelected && !this._core.store.state.isResizeHandleSelected && !this._core.store.state.isRotationHandleSelected && !this._core.store.state.isLineHandleSelected) {
|
|
16207
|
+
const x = Math.round(activePointers[0].clientX - this._core.store.offsetX);
|
|
16208
|
+
const y = Math.round(activePointers[0].clientY - this._core.store.offsetY);
|
|
16209
|
+
this.dragStartX = x;
|
|
16210
|
+
this.dragStartY = y;
|
|
16211
|
+
this.startX = x;
|
|
16212
|
+
this.startY = y;
|
|
16213
|
+
this.trackedPointerId = event.pointerId;
|
|
16214
|
+
}
|
|
16215
|
+
else {
|
|
16216
|
+
this.trackedPointerId = null;
|
|
15364
16217
|
}
|
|
15365
16218
|
}
|
|
15366
16219
|
}
|
|
15367
16220
|
}
|
|
15368
16221
|
handlePointerMove(event) {
|
|
16222
|
+
if (this.trackedPointerId === null || this.trackedPointerId !== event.pointerId) {
|
|
16223
|
+
return;
|
|
16224
|
+
}
|
|
15369
16225
|
if (event.pointerType === 'mouse') {
|
|
15370
16226
|
const selectionGroup = this._core.store.selectionGroup;
|
|
15371
|
-
if (this._core.store.state.
|
|
16227
|
+
if (this._core.store.state.isDragging && selectionGroup) {
|
|
15372
16228
|
const clientX = event.clientX - this._core.store.offsetX;
|
|
15373
16229
|
const clientY = event.clientY - this._core.store.offsetY;
|
|
15374
|
-
|
|
15375
|
-
|
|
15376
|
-
const
|
|
15377
|
-
const
|
|
15378
|
-
const
|
|
15379
|
-
|
|
15380
|
-
|
|
16230
|
+
this.endX = clientX;
|
|
16231
|
+
this.endY = clientY;
|
|
16232
|
+
const moveDeltaX = Math.abs(clientX - this.startX);
|
|
16233
|
+
const moveDeltaY = Math.abs(clientY - this.startY);
|
|
16234
|
+
const moveThreshold = 5;
|
|
16235
|
+
if (moveDeltaX > moveThreshold || moveDeltaY > moveThreshold) {
|
|
16236
|
+
selectionGroup.move(clientX, clientY, this.dragStartX, this.dragStartY);
|
|
16237
|
+
this.dragStartX = clientX;
|
|
16238
|
+
this.dragStartY = clientY;
|
|
16239
|
+
this.hasMoved = true;
|
|
16240
|
+
}
|
|
15381
16241
|
}
|
|
15382
16242
|
}
|
|
15383
16243
|
if (event.pointerType === 'touch') {
|
|
15384
16244
|
const activePointers = Array.from(this._core.store.state.pointers.values());
|
|
15385
|
-
const firstTouch = activePointers[0];
|
|
15386
|
-
if (!firstTouch) {
|
|
15387
|
-
return;
|
|
15388
|
-
}
|
|
15389
16245
|
const selectionGroup = this._core.store.selectionGroup;
|
|
15390
|
-
if (this._core.store.state.
|
|
15391
|
-
|
|
15392
|
-
|
|
15393
|
-
|
|
15394
|
-
|
|
15395
|
-
const
|
|
15396
|
-
const
|
|
15397
|
-
|
|
15398
|
-
this.
|
|
15399
|
-
|
|
15400
|
-
|
|
16246
|
+
if (this._core.store.state.pointers.size === 1 &&
|
|
16247
|
+
selectionGroup &&
|
|
16248
|
+
!this._core.store.state.isResizeHandleSelected &&
|
|
16249
|
+
!this._core.store.state.isRotationHandleSelected &&
|
|
16250
|
+
!this._core.store.state.isScaling) {
|
|
16251
|
+
const x = Math.round(activePointers[0].clientX - this._core.store.offsetX);
|
|
16252
|
+
const y = Math.round(activePointers[0].clientY - this._core.store.offsetY);
|
|
16253
|
+
this._core.store.state.isDragging = true;
|
|
16254
|
+
this.endX = x;
|
|
16255
|
+
this.endY = y;
|
|
16256
|
+
const moveDeltaX = Math.abs(x - this.startX);
|
|
16257
|
+
const moveDeltaY = Math.abs(y - this.startY);
|
|
16258
|
+
const moveThreshold = 5;
|
|
16259
|
+
if (moveDeltaX > moveThreshold || moveDeltaY > moveThreshold) {
|
|
16260
|
+
clearTimeout(this._core.store.state.longTouchTimeout);
|
|
16261
|
+
selectionGroup.move(x, y, this.dragStartX, this.dragStartY);
|
|
16262
|
+
this.dragStartX = x;
|
|
16263
|
+
this.dragStartY = y;
|
|
16264
|
+
this.hasMoved = true;
|
|
16265
|
+
}
|
|
15401
16266
|
}
|
|
15402
16267
|
}
|
|
15403
16268
|
}
|
|
15404
16269
|
handlePointerUp(event) {
|
|
16270
|
+
if (this.trackedPointerId === null || this.trackedPointerId !== event.pointerId) {
|
|
16271
|
+
return;
|
|
16272
|
+
}
|
|
15405
16273
|
if (event.pointerType === 'mouse') {
|
|
15406
|
-
if (this._core.store.state.
|
|
15407
|
-
this._core.store.
|
|
15408
|
-
this.
|
|
15409
|
-
|
|
15410
|
-
|
|
15411
|
-
|
|
16274
|
+
if (this._core.store.state.isDragging) {
|
|
16275
|
+
this._core.store.state.isDragging = false;
|
|
16276
|
+
if (this.hasMoved) {
|
|
16277
|
+
this._core.store.selectionGroup.update();
|
|
16278
|
+
this._core.engine.emitObjectsChange();
|
|
16279
|
+
this._core.store.state.hasObjectsChanged = true;
|
|
16280
|
+
}
|
|
15412
16281
|
}
|
|
15413
16282
|
}
|
|
15414
16283
|
if (event.pointerType === 'touch') {
|
|
15415
|
-
if (this._core.store.state.
|
|
15416
|
-
this._core.store.
|
|
15417
|
-
this.
|
|
15418
|
-
|
|
15419
|
-
|
|
15420
|
-
|
|
15421
|
-
|
|
16284
|
+
if (this._core.store.state.isDragging) {
|
|
16285
|
+
this._core.store.state.isDragging = false;
|
|
16286
|
+
if (this.hasMoved) {
|
|
16287
|
+
this._core.store.selectionGroup.update();
|
|
16288
|
+
this._core.engine.emitObjectsChange();
|
|
16289
|
+
this._core.store.state.hasObjectsChanged = true;
|
|
16290
|
+
}
|
|
15422
16291
|
}
|
|
15423
16292
|
}
|
|
16293
|
+
this.reset();
|
|
15424
16294
|
}
|
|
15425
16295
|
}
|
|
15426
16296
|
|
|
15427
|
-
|
|
15428
|
-
|
|
15429
|
-
|
|
15430
|
-
|
|
15431
|
-
|
|
15432
|
-
|
|
15433
|
-
|
|
15434
|
-
object.scale = core.store.state.scale;
|
|
15435
|
-
object.zIndex = 99999;
|
|
15436
|
-
object.backgroundColor = 'var(--kritzel-selection-box-background-color, rgba(0, 122, 255, 0.2))';
|
|
15437
|
-
object.borderColor = 'var(--kritzel-selection-box-border-color, rgba(0, 122, 255, 0.5))';
|
|
15438
|
-
object.borderWidth = 2;
|
|
15439
|
-
object.height = 0;
|
|
15440
|
-
object.width = 0;
|
|
15441
|
-
return object;
|
|
15442
|
-
}
|
|
15443
|
-
}
|
|
16297
|
+
var KritzelHandleType;
|
|
16298
|
+
(function (KritzelHandleType) {
|
|
16299
|
+
KritzelHandleType["TopLeft"] = "top-left";
|
|
16300
|
+
KritzelHandleType["TopRight"] = "top-right";
|
|
16301
|
+
KritzelHandleType["BottomLeft"] = "bottom-left";
|
|
16302
|
+
KritzelHandleType["BottomRight"] = "bottom-right";
|
|
16303
|
+
})(KritzelHandleType || (KritzelHandleType = {}));
|
|
15444
16304
|
|
|
15445
|
-
class
|
|
15446
|
-
|
|
15447
|
-
|
|
15448
|
-
|
|
15449
|
-
|
|
15450
|
-
|
|
15451
|
-
|
|
15452
|
-
|
|
15453
|
-
maxX;
|
|
15454
|
-
minY;
|
|
15455
|
-
maxY;
|
|
15456
|
-
// Getter to retrieve actual objects from the store by their IDs
|
|
15457
|
-
get objects() {
|
|
15458
|
-
return this.objectIds
|
|
15459
|
-
.map(id => {
|
|
15460
|
-
const found = this._core.store.state.objects.filter(obj => obj.id === id);
|
|
15461
|
-
return found.length > 0 ? found[0] : null;
|
|
15462
|
-
})
|
|
15463
|
-
.filter(obj => obj !== null);
|
|
15464
|
-
}
|
|
15465
|
-
get length() {
|
|
15466
|
-
return this.objectIds.length;
|
|
16305
|
+
class KritzelResizeHandler extends KritzelBaseHandler {
|
|
16306
|
+
initialMouseX = 0;
|
|
16307
|
+
initialMouseY = 0;
|
|
16308
|
+
initialSize = { x: 0, y: 0, width: 0, height: 0 };
|
|
16309
|
+
newSize = { x: 0, y: 0, width: 0, height: 0 };
|
|
16310
|
+
hasResized = false;
|
|
16311
|
+
constructor(core) {
|
|
16312
|
+
super(core);
|
|
15467
16313
|
}
|
|
15468
|
-
|
|
15469
|
-
|
|
15470
|
-
|
|
15471
|
-
|
|
15472
|
-
|
|
15473
|
-
|
|
15474
|
-
object.zIndex = 99999;
|
|
15475
|
-
return object;
|
|
16314
|
+
reset() {
|
|
16315
|
+
this.initialMouseX = 0;
|
|
16316
|
+
this.initialMouseY = 0;
|
|
16317
|
+
this.initialSize = { x: 0, y: 0, width: 0, height: 0 };
|
|
16318
|
+
this.newSize = { x: 0, y: 0, width: 0, height: 0 };
|
|
16319
|
+
this.hasResized = false;
|
|
15476
16320
|
}
|
|
15477
|
-
|
|
15478
|
-
|
|
15479
|
-
|
|
15480
|
-
|
|
16321
|
+
handlePointerDown(event) {
|
|
16322
|
+
if (event.pointerType === 'mouse') {
|
|
16323
|
+
if (KritzelEventHelper.isLeftClick(event)) {
|
|
16324
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
16325
|
+
if (selectionGroup && this._core.store.state.isResizeHandleSelected) {
|
|
16326
|
+
const clientX = event.clientX - this._core.store.offsetX;
|
|
16327
|
+
const clientY = event.clientY - this._core.store.offsetY;
|
|
16328
|
+
this._core.store.state.isResizing = true;
|
|
16329
|
+
this.initialMouseX = clientX;
|
|
16330
|
+
this.initialMouseY = clientY;
|
|
16331
|
+
this.initialSize.width = selectionGroup.width;
|
|
16332
|
+
this.initialSize.height = selectionGroup.height;
|
|
16333
|
+
this.initialSize.x = selectionGroup.translateX;
|
|
16334
|
+
this.initialSize.y = selectionGroup.translateY;
|
|
16335
|
+
}
|
|
16336
|
+
}
|
|
15481
16337
|
}
|
|
15482
|
-
|
|
15483
|
-
this.
|
|
16338
|
+
if (event.pointerType === 'touch') {
|
|
16339
|
+
const activePointers = Array.from(this._core.store.state.pointers.values());
|
|
16340
|
+
const firstTouch = activePointers[0];
|
|
16341
|
+
if (!firstTouch) {
|
|
16342
|
+
return;
|
|
16343
|
+
}
|
|
16344
|
+
if (activePointers.length === 1) {
|
|
16345
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
16346
|
+
if (selectionGroup && this._core.store.state.isResizeHandleSelected) {
|
|
16347
|
+
const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
|
|
16348
|
+
const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
|
|
16349
|
+
this._core.store.state.isResizing = true;
|
|
16350
|
+
this.initialMouseX = clientX;
|
|
16351
|
+
this.initialMouseY = clientY;
|
|
16352
|
+
this.initialSize.width = selectionGroup.width;
|
|
16353
|
+
this.initialSize.height = selectionGroup.height;
|
|
16354
|
+
this.initialSize.x = selectionGroup.translateX;
|
|
16355
|
+
this.initialSize.y = selectionGroup.translateY;
|
|
16356
|
+
clearTimeout(this._core.store.state.longTouchTimeout);
|
|
16357
|
+
}
|
|
16358
|
+
}
|
|
15484
16359
|
}
|
|
15485
|
-
this.captureUnchangedSnapshots();
|
|
15486
|
-
this.refreshObjectDimensions();
|
|
15487
16360
|
}
|
|
15488
|
-
|
|
15489
|
-
|
|
15490
|
-
|
|
15491
|
-
|
|
15492
|
-
|
|
15493
|
-
|
|
15494
|
-
|
|
15495
|
-
|
|
15496
|
-
|
|
15497
|
-
|
|
15498
|
-
|
|
15499
|
-
|
|
15500
|
-
|
|
15501
|
-
|
|
15502
|
-
|
|
15503
|
-
|
|
15504
|
-
|
|
15505
|
-
|
|
15506
|
-
|
|
15507
|
-
|
|
15508
|
-
|
|
15509
|
-
|
|
15510
|
-
|
|
15511
|
-
|
|
15512
|
-
|
|
15513
|
-
|
|
15514
|
-
|
|
15515
|
-
|
|
15516
|
-
|
|
15517
|
-
|
|
15518
|
-
|
|
15519
|
-
|
|
15520
|
-
|
|
15521
|
-
|
|
15522
|
-
|
|
15523
|
-
|
|
15524
|
-
|
|
15525
|
-
|
|
15526
|
-
|
|
15527
|
-
|
|
15528
|
-
|
|
15529
|
-
|
|
15530
|
-
|
|
15531
|
-
|
|
15532
|
-
|
|
15533
|
-
|
|
15534
|
-
|
|
15535
|
-
|
|
15536
|
-
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
|
|
15540
|
-
|
|
15541
|
-
|
|
16361
|
+
handlePointerMove(event) {
|
|
16362
|
+
if (event.pointerType === 'mouse') {
|
|
16363
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
16364
|
+
if (this._core.store.state.isResizing && selectionGroup) {
|
|
16365
|
+
const clientX = event.clientX - this._core.store.offsetX;
|
|
16366
|
+
const clientY = event.clientY - this._core.store.offsetY;
|
|
16367
|
+
const dx = clientX - this.initialMouseX;
|
|
16368
|
+
const dy = clientY - this.initialMouseY;
|
|
16369
|
+
const resizeDeltaX = Math.abs(dx);
|
|
16370
|
+
const resizeDeltaY = Math.abs(dy);
|
|
16371
|
+
const resizeThreshold = 5;
|
|
16372
|
+
if (resizeDeltaX > resizeThreshold || resizeDeltaY > resizeThreshold) {
|
|
16373
|
+
this.hasResized = true;
|
|
16374
|
+
}
|
|
16375
|
+
if (!this.hasResized) {
|
|
16376
|
+
return;
|
|
16377
|
+
}
|
|
16378
|
+
const rotation = selectionGroup.rotation;
|
|
16379
|
+
const sin = Math.sin(rotation);
|
|
16380
|
+
const cos = Math.cos(rotation);
|
|
16381
|
+
const objectScale = selectionGroup.scale || 1;
|
|
16382
|
+
const currentScale = this._core.store.state.scale;
|
|
16383
|
+
// Calculate delta in local unrotated space
|
|
16384
|
+
// We rotate the screen delta by -rotation to align with the object's axes
|
|
16385
|
+
const localDx = (dx * cos + dy * sin) / currentScale;
|
|
16386
|
+
const localDy = (-dx * sin + dy * cos) / currentScale;
|
|
16387
|
+
// Calculate the center of the selection group before resize
|
|
16388
|
+
const initialCenterX = this.initialSize.x + this.initialSize.width / objectScale / 2;
|
|
16389
|
+
const initialCenterY = this.initialSize.y + this.initialSize.height / objectScale / 2;
|
|
16390
|
+
// The center moves by half of the screen delta (scaled)
|
|
16391
|
+
// This is true regardless of rotation because the resize happens symmetrically around the center
|
|
16392
|
+
// relative to the fixed point logic
|
|
16393
|
+
const newCenterX = initialCenterX + dx / currentScale / 2;
|
|
16394
|
+
const newCenterY = initialCenterY + dy / currentScale / 2;
|
|
16395
|
+
switch (this._core.store.state.resizeHandleType) {
|
|
16396
|
+
case KritzelHandleType.TopLeft:
|
|
16397
|
+
this.newSize.width = this.initialSize.width - localDx * objectScale;
|
|
16398
|
+
this.newSize.height = this.initialSize.height - localDy * objectScale;
|
|
16399
|
+
break;
|
|
16400
|
+
case KritzelHandleType.TopRight:
|
|
16401
|
+
this.newSize.width = this.initialSize.width + localDx * objectScale;
|
|
16402
|
+
this.newSize.height = this.initialSize.height - localDy * objectScale;
|
|
16403
|
+
break;
|
|
16404
|
+
case KritzelHandleType.BottomLeft:
|
|
16405
|
+
this.newSize.width = this.initialSize.width - localDx * objectScale;
|
|
16406
|
+
this.newSize.height = this.initialSize.height + localDy * objectScale;
|
|
16407
|
+
break;
|
|
16408
|
+
case KritzelHandleType.BottomRight:
|
|
16409
|
+
this.newSize.width = this.initialSize.width + localDx * objectScale;
|
|
16410
|
+
this.newSize.height = this.initialSize.height + localDy * objectScale;
|
|
16411
|
+
break;
|
|
16412
|
+
}
|
|
16413
|
+
this.newSize.x = newCenterX - this.newSize.width / objectScale / 2;
|
|
16414
|
+
this.newSize.y = newCenterY - this.newSize.height / objectScale / 2;
|
|
16415
|
+
selectionGroup.resize(this.newSize.x, this.newSize.y, this.newSize.width, this.newSize.height);
|
|
16416
|
+
}
|
|
15542
16417
|
}
|
|
15543
|
-
|
|
15544
|
-
|
|
15545
|
-
|
|
15546
|
-
|
|
15547
|
-
|
|
15548
|
-
|
|
15549
|
-
|
|
15550
|
-
|
|
15551
|
-
|
|
15552
|
-
|
|
16418
|
+
if (event.pointerType === 'touch') {
|
|
16419
|
+
const activePointers = Array.from(this._core.store.state.pointers.values());
|
|
16420
|
+
const oneFingerTouch = activePointers[0];
|
|
16421
|
+
if (!oneFingerTouch) {
|
|
16422
|
+
return;
|
|
16423
|
+
}
|
|
16424
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
16425
|
+
if (this._core.store.state.isResizing && selectionGroup) {
|
|
16426
|
+
const clientX = Math.round(oneFingerTouch.clientX - this._core.store.offsetX);
|
|
16427
|
+
const clientY = Math.round(oneFingerTouch.clientY - this._core.store.offsetY);
|
|
16428
|
+
const dx = clientX - this.initialMouseX;
|
|
16429
|
+
const dy = clientY - this.initialMouseY;
|
|
16430
|
+
const resizeDeltaX = Math.abs(dx);
|
|
16431
|
+
const resizeDeltaY = Math.abs(dy);
|
|
16432
|
+
const resizeThreshold = 5;
|
|
16433
|
+
if (resizeDeltaX > resizeThreshold || resizeDeltaY > resizeThreshold) {
|
|
16434
|
+
clearTimeout(this._core.store.state.longTouchTimeout);
|
|
16435
|
+
this.hasResized = true;
|
|
16436
|
+
}
|
|
16437
|
+
if (!this.hasResized) {
|
|
16438
|
+
return;
|
|
16439
|
+
}
|
|
16440
|
+
const rotation = selectionGroup.rotation;
|
|
16441
|
+
const sin = Math.sin(rotation);
|
|
16442
|
+
const cos = Math.cos(rotation);
|
|
16443
|
+
const objectScale = selectionGroup.scale || 1;
|
|
16444
|
+
const currentScale = this._core.store.state.scale;
|
|
16445
|
+
// Calculate delta in local unrotated space
|
|
16446
|
+
// We rotate the screen delta by -rotation to align with the object's axes
|
|
16447
|
+
const localDx = (dx * cos + dy * sin) / currentScale;
|
|
16448
|
+
const localDy = (-dx * sin + dy * cos) / currentScale;
|
|
16449
|
+
// Calculate the center of the selection group before resize
|
|
16450
|
+
const initialCenterX = this.initialSize.x + this.initialSize.width / objectScale / 2;
|
|
16451
|
+
const initialCenterY = this.initialSize.y + this.initialSize.height / objectScale / 2;
|
|
16452
|
+
// The center moves by half of the screen delta (scaled)
|
|
16453
|
+
// This is true regardless of rotation because the resize happens symmetrically around the center
|
|
16454
|
+
// relative to the fixed point logic
|
|
16455
|
+
const newCenterX = initialCenterX + dx / currentScale / 2;
|
|
16456
|
+
const newCenterY = initialCenterY + dy / currentScale / 2;
|
|
16457
|
+
switch (this._core.store.state.resizeHandleType) {
|
|
16458
|
+
case KritzelHandleType.TopLeft:
|
|
16459
|
+
this.newSize.width = this.initialSize.width - localDx * objectScale;
|
|
16460
|
+
this.newSize.height = this.initialSize.height - localDy * objectScale;
|
|
16461
|
+
break;
|
|
16462
|
+
case KritzelHandleType.TopRight:
|
|
16463
|
+
this.newSize.width = this.initialSize.width + localDx * objectScale;
|
|
16464
|
+
this.newSize.height = this.initialSize.height - localDy * objectScale;
|
|
16465
|
+
break;
|
|
16466
|
+
case KritzelHandleType.BottomLeft:
|
|
16467
|
+
this.newSize.width = this.initialSize.width - localDx * objectScale;
|
|
16468
|
+
this.newSize.height = this.initialSize.height + localDy * objectScale;
|
|
16469
|
+
break;
|
|
16470
|
+
case KritzelHandleType.BottomRight:
|
|
16471
|
+
this.newSize.width = this.initialSize.width + localDx * objectScale;
|
|
16472
|
+
this.newSize.height = this.initialSize.height + localDy * objectScale;
|
|
16473
|
+
break;
|
|
16474
|
+
}
|
|
16475
|
+
this.newSize.x = newCenterX - this.newSize.width / objectScale / 2;
|
|
16476
|
+
this.newSize.y = newCenterY - this.newSize.height / objectScale / 2;
|
|
16477
|
+
selectionGroup.resize(this.newSize.x, this.newSize.y, this.newSize.width, this.newSize.height);
|
|
16478
|
+
}
|
|
15553
16479
|
}
|
|
15554
|
-
return this;
|
|
15555
|
-
}
|
|
15556
|
-
update() {
|
|
15557
|
-
// Only update the selection group itself
|
|
15558
|
-
// Child objects are already updated during move/resize/rotate operations
|
|
15559
|
-
// Updating them again here would create redundant y.js updates
|
|
15560
|
-
this._core.store.state.objects.update(this);
|
|
15561
16480
|
}
|
|
15562
|
-
|
|
15563
|
-
|
|
15564
|
-
|
|
15565
|
-
|
|
15566
|
-
|
|
15567
|
-
|
|
15568
|
-
|
|
15569
|
-
|
|
15570
|
-
|
|
15571
|
-
|
|
15572
|
-
|
|
15573
|
-
|
|
15574
|
-
|
|
15575
|
-
|
|
15576
|
-
|
|
15577
|
-
|
|
16481
|
+
handlePointerUp(event) {
|
|
16482
|
+
if (event.pointerType === 'mouse') {
|
|
16483
|
+
if (this._core.store.state.isResizing) {
|
|
16484
|
+
this._core.store.state.isResizing = false;
|
|
16485
|
+
if (this.hasResized) {
|
|
16486
|
+
this._core.store.selectionGroup.update();
|
|
16487
|
+
this._core.engine.emitObjectsChange();
|
|
16488
|
+
this._core.store.state.hasObjectsChanged = true;
|
|
16489
|
+
}
|
|
16490
|
+
this.reset();
|
|
16491
|
+
}
|
|
16492
|
+
}
|
|
16493
|
+
if (event.pointerType === 'touch') {
|
|
16494
|
+
if (this._core.store.state.isResizing) {
|
|
16495
|
+
this._core.store.state.isResizing = false;
|
|
16496
|
+
if (this.hasResized) {
|
|
16497
|
+
this._core.store.selectionGroup.update();
|
|
16498
|
+
this._core.engine.emitObjectsChange();
|
|
16499
|
+
this._core.store.state.hasObjectsChanged = true;
|
|
16500
|
+
}
|
|
16501
|
+
this.reset();
|
|
16502
|
+
clearTimeout(this._core.store.state.longTouchTimeout);
|
|
16503
|
+
}
|
|
16504
|
+
}
|
|
15578
16505
|
}
|
|
15579
|
-
|
|
15580
|
-
|
|
15581
|
-
|
|
15582
|
-
|
|
15583
|
-
|
|
15584
|
-
|
|
15585
|
-
|
|
15586
|
-
|
|
15587
|
-
|
|
15588
|
-
const newCenterX = x + newTotalWidth / 2 / this.scale;
|
|
15589
|
-
const newCenterY = y + newTotalHeight / 2 / this.scale;
|
|
15590
|
-
const rotation = this.rotation;
|
|
15591
|
-
const cos = Math.cos(-rotation);
|
|
15592
|
-
const sin = Math.sin(-rotation);
|
|
15593
|
-
const cosR = Math.cos(rotation);
|
|
15594
|
-
const sinR = Math.sin(rotation);
|
|
15595
|
-
this._core.store.state.objects.transaction(() => {
|
|
15596
|
-
this.objects.forEach(child => {
|
|
15597
|
-
// Calculate child center
|
|
15598
|
-
const childCenterX = child.translateX + child.totalWidth / 2 / child.scale;
|
|
15599
|
-
const childCenterY = child.translateY + child.totalHeight / 2 / child.scale;
|
|
15600
|
-
// Vector from old group center to child center
|
|
15601
|
-
const dx = childCenterX - oldCenterX;
|
|
15602
|
-
const dy = childCenterY - oldCenterY;
|
|
15603
|
-
// Rotate to local space (align with group axes)
|
|
15604
|
-
const localX = dx * cos - dy * sin;
|
|
15605
|
-
const localY = dx * sin + dy * cos;
|
|
15606
|
-
// Scale in local space
|
|
15607
|
-
const scaledLocalX = localX * widthScaleFactor;
|
|
15608
|
-
const scaledLocalY = localY * heightScaleFactor;
|
|
15609
|
-
// Rotate back to world space
|
|
15610
|
-
const rotatedX = scaledLocalX * cosR - scaledLocalY * sinR;
|
|
15611
|
-
const rotatedY = scaledLocalX * sinR + scaledLocalY * cosR;
|
|
15612
|
-
// New child center
|
|
15613
|
-
const newChildCenterX = newCenterX + rotatedX;
|
|
15614
|
-
const newChildCenterY = newCenterY + rotatedY;
|
|
15615
|
-
// New child top-left
|
|
15616
|
-
// Calculate relative rotation
|
|
15617
|
-
const relativeRotation = child.rotation - rotation;
|
|
15618
|
-
const cosRel = Math.cos(relativeRotation);
|
|
15619
|
-
const sinRel = Math.sin(relativeRotation);
|
|
15620
|
-
// Project the group's scale factors onto the child's local axes
|
|
15621
|
-
// We use absolute values because scaling is magnitude-based
|
|
15622
|
-
// If the child is aligned (0 deg), cos=1, sin=0 -> scales match
|
|
15623
|
-
// If the child is 90 deg, cos=0, sin=1 -> scales swap
|
|
15624
|
-
const newChildWidthScale = Math.sqrt(Math.pow(widthScaleFactor * cosRel, 2) + Math.pow(heightScaleFactor * sinRel, 2));
|
|
15625
|
-
const newChildHeightScale = Math.sqrt(Math.pow(widthScaleFactor * sinRel, 2) + Math.pow(heightScaleFactor * cosRel, 2));
|
|
15626
|
-
const updatedWidth = child.width * newChildWidthScale;
|
|
15627
|
-
const updatedHeight = child.height * newChildHeightScale;
|
|
15628
|
-
const updatedTotalWidth = updatedWidth + child.padding * 2;
|
|
15629
|
-
const updatedTotalHeight = updatedHeight + child.padding * 2;
|
|
15630
|
-
const updatedX = newChildCenterX - updatedTotalWidth / 2 / child.scale;
|
|
15631
|
-
const updatedY = newChildCenterY - updatedTotalHeight / 2 / child.scale;
|
|
15632
|
-
child.resize(updatedX, updatedY, updatedWidth, updatedHeight);
|
|
15633
|
-
});
|
|
15634
|
-
// Refresh dimensions and update the SelectionGroup to propagate changes to other tabs
|
|
15635
|
-
this.refreshObjectDimensions();
|
|
15636
|
-
this.captureUnchangedSnapshots();
|
|
15637
|
-
this._core.store.state.objects.update(this);
|
|
15638
|
-
});
|
|
16506
|
+
}
|
|
16507
|
+
|
|
16508
|
+
class KritzelRotationHandler extends KritzelBaseHandler {
|
|
16509
|
+
initialRotation = 0;
|
|
16510
|
+
rotation = 0;
|
|
16511
|
+
unchangedObjects = [];
|
|
16512
|
+
initialSelectionGroupRotation = 0;
|
|
16513
|
+
constructor(core) {
|
|
16514
|
+
super(core);
|
|
15639
16515
|
}
|
|
15640
|
-
|
|
15641
|
-
this.
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
const angle = value - this.snapshotRotation;
|
|
15645
|
-
const cos = Math.cos(angle);
|
|
15646
|
-
const sin = Math.sin(angle);
|
|
15647
|
-
this._core.store.state.objects.transaction(() => {
|
|
15648
|
-
// Update the SelectionGroup itself to propagate rotation to other tabs
|
|
15649
|
-
this._core.store.state.objects.update(this);
|
|
15650
|
-
this.objects.forEach(child => {
|
|
15651
|
-
const unchangedSnapshot = this.unchangedObjectSnapshots.get(child.id);
|
|
15652
|
-
if (!unchangedSnapshot)
|
|
15653
|
-
return;
|
|
15654
|
-
const offsetX = this.getOffsetXToCenterFromSnapshot(unchangedSnapshot);
|
|
15655
|
-
const offsetY = this.getOffsetYToCenterFromSnapshot(unchangedSnapshot);
|
|
15656
|
-
const rotatedX = cos * offsetX - sin * offsetY;
|
|
15657
|
-
const rotatedY = sin * offsetX + cos * offsetY;
|
|
15658
|
-
child.translateX = centerX + rotatedX - child.totalWidth / 2 / child.scale;
|
|
15659
|
-
child.translateY = centerY + rotatedY - child.totalHeight / 2 / child.scale;
|
|
15660
|
-
child.rotate(this.objects.length === 1 ? value : unchangedSnapshot.rotation + angle);
|
|
15661
|
-
});
|
|
15662
|
-
});
|
|
16516
|
+
reset() {
|
|
16517
|
+
this.initialRotation = 0;
|
|
16518
|
+
this.rotation = 0;
|
|
16519
|
+
this.unchangedObjects = [];
|
|
15663
16520
|
}
|
|
15664
|
-
|
|
15665
|
-
|
|
15666
|
-
|
|
15667
|
-
|
|
15668
|
-
|
|
15669
|
-
|
|
15670
|
-
|
|
15671
|
-
|
|
15672
|
-
|
|
15673
|
-
|
|
15674
|
-
|
|
16521
|
+
handlePointerDown(event) {
|
|
16522
|
+
if (event.pointerType === 'mouse') {
|
|
16523
|
+
if (KritzelEventHelper.isLeftClick(event)) {
|
|
16524
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
16525
|
+
if (selectionGroup && this._core.store.state.isRotationHandleSelected) {
|
|
16526
|
+
const clientX = event.clientX - this._core.store.offsetX;
|
|
16527
|
+
const clientY = event.clientY - this._core.store.offsetY;
|
|
16528
|
+
this._core.store.state.isRotating = true;
|
|
16529
|
+
const objectScale = selectionGroup.scale || 1;
|
|
16530
|
+
const centerX = selectionGroup.translateX + selectionGroup.width / 2 / objectScale;
|
|
16531
|
+
const centerY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
16532
|
+
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
16533
|
+
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
16534
|
+
this.initialSelectionGroupRotation = selectionGroup.rotation;
|
|
16535
|
+
this.initialRotation = Math.atan2(centerY - cursorY, centerX - cursorX) - selectionGroup.rotation;
|
|
16536
|
+
this.unchangedObjects = selectionGroup.objects.map(obj => obj.clone());
|
|
16537
|
+
}
|
|
16538
|
+
}
|
|
16539
|
+
}
|
|
16540
|
+
if (event.pointerType === 'touch') {
|
|
16541
|
+
const activePointers = Array.from(this._core.store.state.pointers.values());
|
|
16542
|
+
const firstTouch = activePointers[0];
|
|
16543
|
+
if (!firstTouch) {
|
|
16544
|
+
return;
|
|
16545
|
+
}
|
|
16546
|
+
if (activePointers.length === 1) {
|
|
16547
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
16548
|
+
if (selectionGroup && this._core.store.state.isRotationHandleSelected) {
|
|
16549
|
+
const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
|
|
16550
|
+
const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
|
|
16551
|
+
this._core.store.state.isRotating = true;
|
|
16552
|
+
const objectScale = selectionGroup.scale || 1;
|
|
16553
|
+
const centerX = selectionGroup.translateX + selectionGroup.width / 2 / objectScale;
|
|
16554
|
+
const centerY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
16555
|
+
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
16556
|
+
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
16557
|
+
this.initialSelectionGroupRotation = selectionGroup.rotation;
|
|
16558
|
+
this.initialRotation = Math.atan2(centerY - cursorY, centerX - cursorX) - selectionGroup.rotation;
|
|
16559
|
+
this.unchangedObjects = selectionGroup.objects.map(obj => obj.clone());
|
|
16560
|
+
clearTimeout(this._core.store.state.longTouchTimeout);
|
|
16561
|
+
}
|
|
16562
|
+
}
|
|
15675
16563
|
}
|
|
15676
|
-
return selectionGroup;
|
|
15677
16564
|
}
|
|
15678
|
-
|
|
15679
|
-
if (
|
|
15680
|
-
const
|
|
15681
|
-
this.
|
|
15682
|
-
|
|
15683
|
-
|
|
15684
|
-
|
|
15685
|
-
|
|
15686
|
-
|
|
15687
|
-
|
|
15688
|
-
|
|
16565
|
+
handlePointerMove(event) {
|
|
16566
|
+
if (event.pointerType === 'mouse') {
|
|
16567
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
16568
|
+
if (this._core.store.state.isRotating && selectionGroup) {
|
|
16569
|
+
const clientX = event.clientX - this._core.store.offsetX;
|
|
16570
|
+
const clientY = event.clientY - this._core.store.offsetY;
|
|
16571
|
+
const objectScale = selectionGroup.scale || 1;
|
|
16572
|
+
const groupCenterX = selectionGroup.translateX + selectionGroup.width / 2 / objectScale;
|
|
16573
|
+
const groupCenterY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
16574
|
+
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
16575
|
+
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
16576
|
+
const currentRotation = Math.atan2(groupCenterY - cursorY, groupCenterX - cursorX);
|
|
16577
|
+
this.rotation = currentRotation - this.initialRotation;
|
|
16578
|
+
selectionGroup.rotate(this.rotation);
|
|
16579
|
+
}
|
|
15689
16580
|
}
|
|
15690
|
-
|
|
15691
|
-
const
|
|
15692
|
-
const
|
|
15693
|
-
|
|
15694
|
-
|
|
15695
|
-
|
|
15696
|
-
|
|
15697
|
-
|
|
15698
|
-
|
|
15699
|
-
const
|
|
15700
|
-
const
|
|
15701
|
-
|
|
15702
|
-
|
|
15703
|
-
|
|
15704
|
-
|
|
15705
|
-
|
|
15706
|
-
|
|
15707
|
-
|
|
15708
|
-
|
|
15709
|
-
|
|
15710
|
-
minY = ry;
|
|
15711
|
-
if (ry > maxY)
|
|
15712
|
-
maxY = ry;
|
|
15713
|
-
});
|
|
15714
|
-
});
|
|
15715
|
-
// Dimensions in world units (unrotated)
|
|
15716
|
-
const worldWidth = maxX - minX;
|
|
15717
|
-
const worldHeight = maxY - minY;
|
|
15718
|
-
this.width = (worldWidth - this.padding) * this.scale;
|
|
15719
|
-
this.height = (worldHeight - this.padding) * this.scale;
|
|
15720
|
-
// Center of the box in rotated space
|
|
15721
|
-
const cRx = (minX + maxX) / 2;
|
|
15722
|
-
const cRy = (minY + maxY) / 2;
|
|
15723
|
-
// Rotate center back to world space
|
|
15724
|
-
const cosR = Math.cos(rotation);
|
|
15725
|
-
const sinR = Math.sin(rotation);
|
|
15726
|
-
const cx = cRx * cosR - cRy * sinR;
|
|
15727
|
-
const cy = cRx * sinR + cRy * cosR;
|
|
15728
|
-
this.translateX = cx - (this.width / this.scale + 2 * this.padding) / 2;
|
|
15729
|
-
this.translateY = cy - (this.height / this.scale + 2 * this.padding) / 2;
|
|
16581
|
+
if (event.pointerType === 'touch') {
|
|
16582
|
+
const activePointers = Array.from(this._core.store.state.pointers.values());
|
|
16583
|
+
const firstTouch = activePointers[0];
|
|
16584
|
+
if (!firstTouch) {
|
|
16585
|
+
return;
|
|
16586
|
+
}
|
|
16587
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
16588
|
+
if (this._core.store.state.isRotating && selectionGroup) {
|
|
16589
|
+
const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
|
|
16590
|
+
const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
|
|
16591
|
+
const objectScale = selectionGroup.scale || 1;
|
|
16592
|
+
const groupCenterX = selectionGroup.translateX + selectionGroup.width / 2 / objectScale;
|
|
16593
|
+
const groupCenterY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
16594
|
+
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
16595
|
+
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
16596
|
+
const currentRotation = Math.atan2(groupCenterY - cursorY, groupCenterX - cursorX);
|
|
16597
|
+
this.rotation = currentRotation - this.initialRotation;
|
|
16598
|
+
selectionGroup.rotate(this.rotation);
|
|
16599
|
+
clearTimeout(this._core.store.state.longTouchTimeout);
|
|
16600
|
+
}
|
|
15730
16601
|
}
|
|
15731
|
-
this._core.store.state.objects.update(this);
|
|
15732
16602
|
}
|
|
15733
|
-
|
|
15734
|
-
|
|
15735
|
-
|
|
15736
|
-
|
|
16603
|
+
handlePointerUp(event) {
|
|
16604
|
+
if (event.pointerType === 'mouse') {
|
|
16605
|
+
if (this._core.store.state.isRotating) {
|
|
16606
|
+
this._core.store.selectionGroup.update();
|
|
16607
|
+
this._core.engine.emitObjectsChange();
|
|
16608
|
+
this._core.store.state.isRotating = false;
|
|
16609
|
+
this._core.store.state.hasObjectsChanged = true;
|
|
16610
|
+
this.reset();
|
|
16611
|
+
}
|
|
16612
|
+
}
|
|
16613
|
+
if (event.pointerType === 'touch') {
|
|
16614
|
+
if (this._core.store.state.isRotating) {
|
|
16615
|
+
this._core.store.selectionGroup.update();
|
|
16616
|
+
this._core.engine.emitObjectsChange();
|
|
16617
|
+
this._core.store.state.isRotating = false;
|
|
16618
|
+
this._core.store.state.hasObjectsChanged = true;
|
|
16619
|
+
this.reset();
|
|
16620
|
+
clearTimeout(this._core.store.state.longTouchTimeout);
|
|
16621
|
+
}
|
|
16622
|
+
}
|
|
15737
16623
|
}
|
|
15738
|
-
|
|
15739
|
-
|
|
15740
|
-
|
|
15741
|
-
|
|
16624
|
+
}
|
|
16625
|
+
|
|
16626
|
+
class KritzelSelectionBox extends KritzelBaseObject {
|
|
16627
|
+
__class__ = 'KritzelSelectionBox';
|
|
16628
|
+
static create(core) {
|
|
16629
|
+
const object = new KritzelSelectionBox();
|
|
16630
|
+
object._core = core;
|
|
16631
|
+
object.id = object.generateId();
|
|
16632
|
+
object.workspaceId = core.store.state.activeWorkspace.id;
|
|
16633
|
+
object.scale = core.store.state.scale;
|
|
16634
|
+
object.zIndex = 99999;
|
|
16635
|
+
object.backgroundColor = 'var(--kritzel-selection-box-background-color, rgba(0, 122, 255, 0.2))';
|
|
16636
|
+
object.borderColor = 'var(--kritzel-selection-box-border-color, rgba(0, 122, 255, 0.5))';
|
|
16637
|
+
object.borderWidth = 2;
|
|
16638
|
+
object.height = 0;
|
|
16639
|
+
object.width = 0;
|
|
16640
|
+
return object;
|
|
15742
16641
|
}
|
|
15743
16642
|
}
|
|
15744
16643
|
|
|
@@ -16000,12 +16899,393 @@ class KritzelHoverHandler extends KritzelBaseHandler {
|
|
|
16000
16899
|
}
|
|
16001
16900
|
}
|
|
16002
16901
|
|
|
16902
|
+
class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
16903
|
+
initialMouseX = 0;
|
|
16904
|
+
initialMouseY = 0;
|
|
16905
|
+
initialStartX = 0;
|
|
16906
|
+
initialStartY = 0;
|
|
16907
|
+
initialEndX = 0;
|
|
16908
|
+
initialEndY = 0;
|
|
16909
|
+
initialControlX;
|
|
16910
|
+
initialControlY;
|
|
16911
|
+
initialTranslateX = 0;
|
|
16912
|
+
initialTranslateY = 0;
|
|
16913
|
+
hasMoved = false;
|
|
16914
|
+
/** Current snap target during drag, if any */
|
|
16915
|
+
currentSnapTarget = null;
|
|
16916
|
+
constructor(core) {
|
|
16917
|
+
super(core);
|
|
16918
|
+
}
|
|
16919
|
+
reset() {
|
|
16920
|
+
this.initialMouseX = 0;
|
|
16921
|
+
this.initialMouseY = 0;
|
|
16922
|
+
this.initialStartX = 0;
|
|
16923
|
+
this.initialStartY = 0;
|
|
16924
|
+
this.initialEndX = 0;
|
|
16925
|
+
this.initialEndY = 0;
|
|
16926
|
+
this.initialControlX = undefined;
|
|
16927
|
+
this.initialControlY = undefined;
|
|
16928
|
+
this.initialTranslateX = 0;
|
|
16929
|
+
this.initialTranslateY = 0;
|
|
16930
|
+
this.hasMoved = false;
|
|
16931
|
+
this.currentSnapTarget = null;
|
|
16932
|
+
this._core.anchorManager.clearSnapCandidate();
|
|
16933
|
+
}
|
|
16934
|
+
handlePointerDown(event) {
|
|
16935
|
+
if (event.pointerType === 'mouse') {
|
|
16936
|
+
if (KritzelEventHelper.isLeftClick(event)) {
|
|
16937
|
+
this.startHandleDrag(event);
|
|
16938
|
+
}
|
|
16939
|
+
}
|
|
16940
|
+
if (event.pointerType === 'touch') {
|
|
16941
|
+
const activePointers = Array.from(this._core.store.state.pointers.values());
|
|
16942
|
+
if (activePointers.length === 1) {
|
|
16943
|
+
this.startHandleDrag(event);
|
|
16944
|
+
}
|
|
16945
|
+
}
|
|
16946
|
+
}
|
|
16947
|
+
startHandleDrag(event) {
|
|
16948
|
+
const line = this.getSelectedLine();
|
|
16949
|
+
if (!line || !this._core.store.state.isLineHandleSelected) {
|
|
16950
|
+
return;
|
|
16951
|
+
}
|
|
16952
|
+
const clientX = event.clientX - this._core.store.offsetX;
|
|
16953
|
+
const clientY = event.clientY - this._core.store.offsetY;
|
|
16954
|
+
this._core.store.state.isLineHandleDragging = true;
|
|
16955
|
+
this.initialMouseX = clientX;
|
|
16956
|
+
this.initialMouseY = clientY;
|
|
16957
|
+
// Store initial line state
|
|
16958
|
+
this.initialStartX = line.startX;
|
|
16959
|
+
this.initialStartY = line.startY;
|
|
16960
|
+
this.initialEndX = line.endX;
|
|
16961
|
+
this.initialEndY = line.endY;
|
|
16962
|
+
this.initialControlX = line.controlX;
|
|
16963
|
+
this.initialControlY = line.controlY;
|
|
16964
|
+
this.initialTranslateX = line.translateX;
|
|
16965
|
+
this.initialTranslateY = line.translateY;
|
|
16966
|
+
// Remove existing anchor for this endpoint when starting to drag
|
|
16967
|
+
const handleType = this._core.store.state.lineHandleType;
|
|
16968
|
+
if (handleType === 'start' || handleType === 'end') {
|
|
16969
|
+
this._core.anchorManager.removeAnchor(line.id, handleType);
|
|
16970
|
+
}
|
|
16971
|
+
clearTimeout(this._core.store.state.longTouchTimeout);
|
|
16972
|
+
}
|
|
16973
|
+
handlePointerMove(event) {
|
|
16974
|
+
const line = this.getSelectedLine();
|
|
16975
|
+
if (!line || !this._core.store.state.isLineHandleDragging) {
|
|
16976
|
+
return;
|
|
16977
|
+
}
|
|
16978
|
+
const clientX = event.clientX - this._core.store.offsetX;
|
|
16979
|
+
const clientY = event.clientY - this._core.store.offsetY;
|
|
16980
|
+
const dx = clientX - this.initialMouseX;
|
|
16981
|
+
const dy = clientY - this.initialMouseY;
|
|
16982
|
+
const moveThreshold = 3;
|
|
16983
|
+
if (Math.abs(dx) > moveThreshold || Math.abs(dy) > moveThreshold) {
|
|
16984
|
+
this.hasMoved = true;
|
|
16985
|
+
}
|
|
16986
|
+
if (!this.hasMoved) {
|
|
16987
|
+
return;
|
|
16988
|
+
}
|
|
16989
|
+
const viewportScale = this._core.store.state.scale;
|
|
16990
|
+
const objectScale = line.scale || 1;
|
|
16991
|
+
const rotation = line.rotation;
|
|
16992
|
+
// Convert mouse delta to world coordinates
|
|
16993
|
+
const worldDx = dx / viewportScale;
|
|
16994
|
+
const worldDy = dy / viewportScale;
|
|
16995
|
+
// Rotate the delta into the line's local coordinate system
|
|
16996
|
+
const cos = Math.cos(-rotation);
|
|
16997
|
+
const sin = Math.sin(-rotation);
|
|
16998
|
+
const localDx = (worldDx * cos - worldDy * sin) * objectScale;
|
|
16999
|
+
const localDy = (worldDx * sin + worldDy * cos) * objectScale;
|
|
17000
|
+
const handleType = this._core.store.state.lineHandleType;
|
|
17001
|
+
if (handleType === 'start') {
|
|
17002
|
+
// Calculate world position directly from mouse position for snap detection
|
|
17003
|
+
const worldX = (clientX - this._core.store.state.translateX) / viewportScale;
|
|
17004
|
+
const worldY = (clientY - this._core.store.state.translateY) / viewportScale;
|
|
17005
|
+
// Get other endpoint's anchor to prevent anchoring both to same object
|
|
17006
|
+
const otherAnchorId = line.endAnchor?.objectId;
|
|
17007
|
+
// Check for snap target
|
|
17008
|
+
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId);
|
|
17009
|
+
this.currentSnapTarget = snapTarget;
|
|
17010
|
+
if (snapTarget) {
|
|
17011
|
+
// Snap to target center - convert world coords to local
|
|
17012
|
+
const localCoords = this.worldToLineLocal(line, snapTarget.centerX, snapTarget.centerY);
|
|
17013
|
+
this.updateLineEndpoint(line, localCoords.x, localCoords.y, this.initialEndX, this.initialEndY);
|
|
17014
|
+
// Get the other endpoint's world position for edge intersection calculation
|
|
17015
|
+
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialEndX, this.initialEndY);
|
|
17016
|
+
// Calculate edge intersection point using AnchorManager to support curves
|
|
17017
|
+
const targetObject = this._core.store.allNonSelectionObjects.find(obj => obj.id === snapTarget.objectId);
|
|
17018
|
+
let edgeX;
|
|
17019
|
+
let edgeY;
|
|
17020
|
+
let t;
|
|
17021
|
+
if (targetObject) {
|
|
17022
|
+
const clipInfo = this._core.anchorManager.computeAnchorClipInfo(line, 'start', targetObject);
|
|
17023
|
+
if (clipInfo) {
|
|
17024
|
+
edgeX = clipInfo.worldX;
|
|
17025
|
+
edgeY = clipInfo.worldY;
|
|
17026
|
+
t = clipInfo.t;
|
|
17027
|
+
}
|
|
17028
|
+
}
|
|
17029
|
+
// Calculate control point in world coordinates if it exists
|
|
17030
|
+
let controlWorld = undefined;
|
|
17031
|
+
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
17032
|
+
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
17033
|
+
}
|
|
17034
|
+
// Update snap indicator
|
|
17035
|
+
this._core.anchorManager.setSnapCandidate({
|
|
17036
|
+
objectId: snapTarget.objectId,
|
|
17037
|
+
endpoint: 'start',
|
|
17038
|
+
centerX: snapTarget.centerX,
|
|
17039
|
+
centerY: snapTarget.centerY,
|
|
17040
|
+
lineEndpointX: otherEndpointWorld.x,
|
|
17041
|
+
lineEndpointY: otherEndpointWorld.y,
|
|
17042
|
+
controlX: controlWorld?.x,
|
|
17043
|
+
controlY: controlWorld?.y,
|
|
17044
|
+
t,
|
|
17045
|
+
edgeX,
|
|
17046
|
+
edgeY,
|
|
17047
|
+
lineStroke: line.stroke,
|
|
17048
|
+
lineStrokeWidth: line.strokeWidth / line.scale,
|
|
17049
|
+
arrowOffset: line.hasStartArrow ? line.getArrowSize('start') / line.scale : undefined,
|
|
17050
|
+
arrowStyle: line.hasStartArrow ? line.arrows?.start?.style : undefined,
|
|
17051
|
+
arrowFill: line.hasStartArrow ? line.getArrowFill('start') : undefined,
|
|
17052
|
+
});
|
|
17053
|
+
}
|
|
17054
|
+
else {
|
|
17055
|
+
// No snap - use regular position
|
|
17056
|
+
const newStartX = this.initialStartX + localDx;
|
|
17057
|
+
const newStartY = this.initialStartY + localDy;
|
|
17058
|
+
this.updateLineEndpoint(line, newStartX, newStartY, this.initialEndX, this.initialEndY);
|
|
17059
|
+
// Clear snap indicator
|
|
17060
|
+
this._core.anchorManager.clearSnapCandidate();
|
|
17061
|
+
}
|
|
17062
|
+
}
|
|
17063
|
+
else if (handleType === 'end') {
|
|
17064
|
+
// Calculate world position directly from mouse position for snap detection
|
|
17065
|
+
const worldX = (clientX - this._core.store.state.translateX) / viewportScale;
|
|
17066
|
+
const worldY = (clientY - this._core.store.state.translateY) / viewportScale;
|
|
17067
|
+
// Get other endpoint's anchor to prevent anchoring both to same object
|
|
17068
|
+
const otherAnchorId = line.startAnchor?.objectId;
|
|
17069
|
+
// Check for snap target
|
|
17070
|
+
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId);
|
|
17071
|
+
this.currentSnapTarget = snapTarget;
|
|
17072
|
+
if (snapTarget) {
|
|
17073
|
+
// Snap to target center - convert world coords to local
|
|
17074
|
+
const localCoords = this.worldToLineLocal(line, snapTarget.centerX, snapTarget.centerY);
|
|
17075
|
+
this.updateLineEndpoint(line, this.initialStartX, this.initialStartY, localCoords.x, localCoords.y);
|
|
17076
|
+
// Get the other endpoint's world position for edge intersection calculation
|
|
17077
|
+
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialStartX, this.initialStartY);
|
|
17078
|
+
// Calculate edge intersection point using AnchorManager to support curves
|
|
17079
|
+
const targetObject = this._core.store.allNonSelectionObjects.find(obj => obj.id === snapTarget.objectId);
|
|
17080
|
+
let edgeX;
|
|
17081
|
+
let edgeY;
|
|
17082
|
+
let t;
|
|
17083
|
+
if (targetObject) {
|
|
17084
|
+
const clipInfo = this._core.anchorManager.computeAnchorClipInfo(line, 'end', targetObject);
|
|
17085
|
+
if (clipInfo) {
|
|
17086
|
+
edgeX = clipInfo.worldX;
|
|
17087
|
+
edgeY = clipInfo.worldY;
|
|
17088
|
+
t = clipInfo.t;
|
|
17089
|
+
}
|
|
17090
|
+
}
|
|
17091
|
+
// Calculate control point in world coordinates if it exists
|
|
17092
|
+
let controlWorld = undefined;
|
|
17093
|
+
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
17094
|
+
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
17095
|
+
}
|
|
17096
|
+
// Update snap indicator
|
|
17097
|
+
this._core.anchorManager.setSnapCandidate({
|
|
17098
|
+
objectId: snapTarget.objectId,
|
|
17099
|
+
endpoint: 'end',
|
|
17100
|
+
centerX: snapTarget.centerX,
|
|
17101
|
+
centerY: snapTarget.centerY,
|
|
17102
|
+
lineEndpointX: otherEndpointWorld.x,
|
|
17103
|
+
lineEndpointY: otherEndpointWorld.y,
|
|
17104
|
+
controlX: controlWorld?.x,
|
|
17105
|
+
controlY: controlWorld?.y,
|
|
17106
|
+
t,
|
|
17107
|
+
edgeX,
|
|
17108
|
+
edgeY,
|
|
17109
|
+
lineStroke: line.stroke,
|
|
17110
|
+
lineStrokeWidth: line.strokeWidth / line.scale,
|
|
17111
|
+
arrowOffset: line.hasEndArrow ? line.getArrowSize('end') / line.scale : undefined,
|
|
17112
|
+
arrowStyle: line.hasEndArrow ? line.arrows?.end?.style : undefined,
|
|
17113
|
+
arrowFill: line.hasEndArrow ? line.getArrowFill('end') : undefined,
|
|
17114
|
+
});
|
|
17115
|
+
}
|
|
17116
|
+
else {
|
|
17117
|
+
// No snap - use regular position
|
|
17118
|
+
const newEndX = this.initialEndX + localDx;
|
|
17119
|
+
const newEndY = this.initialEndY + localDy;
|
|
17120
|
+
this.updateLineEndpoint(line, this.initialStartX, this.initialStartY, newEndX, newEndY);
|
|
17121
|
+
// Clear snap indicator
|
|
17122
|
+
this._core.anchorManager.clearSnapCandidate();
|
|
17123
|
+
}
|
|
17124
|
+
}
|
|
17125
|
+
else if (handleType === 'center') {
|
|
17126
|
+
const startControlX = this.initialControlX ?? (this.initialStartX + this.initialEndX) / 2;
|
|
17127
|
+
const startControlY = this.initialControlY ?? (this.initialStartY + this.initialEndY) / 2;
|
|
17128
|
+
const newControlX = startControlX + localDx * 2;
|
|
17129
|
+
const newControlY = startControlY + localDy * 2;
|
|
17130
|
+
// Check distance to line segment for snapping back to straight
|
|
17131
|
+
const x1 = line.startX;
|
|
17132
|
+
const y1 = line.startY;
|
|
17133
|
+
const x2 = line.endX;
|
|
17134
|
+
const y2 = line.endY;
|
|
17135
|
+
const A = newControlX - x1;
|
|
17136
|
+
const B = newControlY - y1;
|
|
17137
|
+
const C = x2 - x1;
|
|
17138
|
+
const D = y2 - y1;
|
|
17139
|
+
const dot = A * C + B * D;
|
|
17140
|
+
const len_sq = C * C + D * D;
|
|
17141
|
+
let param = -1;
|
|
17142
|
+
if (len_sq !== 0) {
|
|
17143
|
+
param = dot / len_sq;
|
|
17144
|
+
}
|
|
17145
|
+
let xx, yy;
|
|
17146
|
+
if (param < 0) {
|
|
17147
|
+
xx = x1;
|
|
17148
|
+
yy = y1;
|
|
17149
|
+
}
|
|
17150
|
+
else if (param > 1) {
|
|
17151
|
+
xx = x2;
|
|
17152
|
+
yy = y2;
|
|
17153
|
+
}
|
|
17154
|
+
else {
|
|
17155
|
+
xx = x1 + param * C;
|
|
17156
|
+
yy = y1 + param * D;
|
|
17157
|
+
}
|
|
17158
|
+
const dx = newControlX - xx;
|
|
17159
|
+
const dy = newControlY - yy;
|
|
17160
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
17161
|
+
// Threshold for snapping back to straight line
|
|
17162
|
+
const snapToStraightThreshold = 15;
|
|
17163
|
+
if (distance < snapToStraightThreshold) {
|
|
17164
|
+
line.updateControlPoint(undefined, undefined);
|
|
17165
|
+
}
|
|
17166
|
+
else {
|
|
17167
|
+
line.updateControlPoint(newControlX, newControlY);
|
|
17168
|
+
}
|
|
17169
|
+
}
|
|
17170
|
+
this._core.rerender();
|
|
17171
|
+
}
|
|
17172
|
+
/**
|
|
17173
|
+
* Converts world coordinates to a line's local coordinate system.
|
|
17174
|
+
*/
|
|
17175
|
+
worldToLineLocal(line, worldX, worldY) {
|
|
17176
|
+
const dx = (worldX - line.translateX) * line.scale;
|
|
17177
|
+
const dy = (worldY - line.translateY) * line.scale;
|
|
17178
|
+
const cx = line.totalWidth / 2;
|
|
17179
|
+
const cy = line.totalHeight / 2;
|
|
17180
|
+
const cos = Math.cos(-line.rotation);
|
|
17181
|
+
const sin = Math.sin(-line.rotation);
|
|
17182
|
+
const rotatedX = (dx - cx) * cos - (dy - cy) * sin + cx;
|
|
17183
|
+
const rotatedY = (dx - cx) * sin + (dy - cy) * cos + cy;
|
|
17184
|
+
return {
|
|
17185
|
+
x: rotatedX + line.x,
|
|
17186
|
+
y: rotatedY + line.y,
|
|
17187
|
+
};
|
|
17188
|
+
}
|
|
17189
|
+
/**
|
|
17190
|
+
* Converts a line's local coordinate system to world coordinates.
|
|
17191
|
+
*/
|
|
17192
|
+
lineLocalToWorld(line, localX, localY) {
|
|
17193
|
+
const px = localX - line.x;
|
|
17194
|
+
const py = localY - line.y;
|
|
17195
|
+
const cx = line.totalWidth / 2;
|
|
17196
|
+
const cy = line.totalHeight / 2;
|
|
17197
|
+
const cos = Math.cos(line.rotation);
|
|
17198
|
+
const sin = Math.sin(line.rotation);
|
|
17199
|
+
const rotatedX = (px - cx) * cos - (py - cy) * sin + cx;
|
|
17200
|
+
const rotatedY = (px - cx) * sin + (py - cy) * cos + cy;
|
|
17201
|
+
return {
|
|
17202
|
+
x: rotatedX / line.scale + line.translateX,
|
|
17203
|
+
y: rotatedY / line.scale + line.translateY,
|
|
17204
|
+
};
|
|
17205
|
+
}
|
|
17206
|
+
updateLineEndpoint(line, startX, startY, endX, endY) {
|
|
17207
|
+
// Calculate new bounding box using the static method that accounts for curve extrema
|
|
17208
|
+
const { minX, minY, maxX, maxY } = KritzelLine.calculateBoundingBox(startX, startY, endX, endY, line.controlX, line.controlY, line.strokeWidth);
|
|
17209
|
+
const newWidth = maxX - minX;
|
|
17210
|
+
const newHeight = maxY - minY;
|
|
17211
|
+
// Calculate old bounding box
|
|
17212
|
+
const { minX: oldMinX, minY: oldMinY, maxX: oldMaxX, maxY: oldMaxY, } = KritzelLine.calculateBoundingBox(this.initialStartX, this.initialStartY, this.initialEndX, this.initialEndY, this.initialControlX, this.initialControlY, line.strokeWidth);
|
|
17213
|
+
const oldWidth = oldMaxX - oldMinX;
|
|
17214
|
+
const oldHeight = oldMaxY - oldMinY;
|
|
17215
|
+
// Calculate center shift in local space
|
|
17216
|
+
const shiftX = minX - oldMinX;
|
|
17217
|
+
const shiftY = minY - oldMinY;
|
|
17218
|
+
const deltaCx = shiftX + (newWidth - oldWidth) / 2;
|
|
17219
|
+
const deltaCy = shiftY + (newHeight - oldHeight) / 2;
|
|
17220
|
+
// Convert the center shift to world coordinates
|
|
17221
|
+
const rotation = line.rotation;
|
|
17222
|
+
const cos = Math.cos(rotation);
|
|
17223
|
+
const sin = Math.sin(rotation);
|
|
17224
|
+
const scale = 1 / line.scale;
|
|
17225
|
+
const rotatedDeltaCx = deltaCx * cos - deltaCy * sin;
|
|
17226
|
+
const rotatedDeltaCy = deltaCx * sin + deltaCy * cos;
|
|
17227
|
+
// Calculate new translate
|
|
17228
|
+
const newTranslateX = this.initialTranslateX + scale * (oldWidth - newWidth) / 2 + scale * rotatedDeltaCx;
|
|
17229
|
+
const newTranslateY = this.initialTranslateY + scale * (oldHeight - newHeight) / 2 + scale * rotatedDeltaCy;
|
|
17230
|
+
// Update the line properties
|
|
17231
|
+
line.startX = startX;
|
|
17232
|
+
line.startY = startY;
|
|
17233
|
+
line.endX = endX;
|
|
17234
|
+
line.endY = endY;
|
|
17235
|
+
line.x = minX;
|
|
17236
|
+
line.y = minY;
|
|
17237
|
+
line.width = newWidth;
|
|
17238
|
+
line.height = newHeight;
|
|
17239
|
+
// Adjust translateX/Y to compensate for the bounding box shift
|
|
17240
|
+
line.translateX = newTranslateX;
|
|
17241
|
+
line.translateY = newTranslateY;
|
|
17242
|
+
// Clear cached adjusted points
|
|
17243
|
+
line._adjustedPoints = null;
|
|
17244
|
+
this._core.store.state.objects.update(line);
|
|
17245
|
+
}
|
|
17246
|
+
handlePointerUp(_event) {
|
|
17247
|
+
if (this._core.store.state.isLineHandleDragging) {
|
|
17248
|
+
const line = this.getSelectedLine();
|
|
17249
|
+
const handleType = this._core.store.state.lineHandleType;
|
|
17250
|
+
if (line && this.hasMoved) {
|
|
17251
|
+
// If we were snapped to a target, create the anchor
|
|
17252
|
+
if (this.currentSnapTarget && (handleType === 'start' || handleType === 'end')) {
|
|
17253
|
+
this._core.anchorManager.setAnchor(line.id, handleType, this.currentSnapTarget.objectId);
|
|
17254
|
+
}
|
|
17255
|
+
// Refresh the selection group dimensions after line endpoint change
|
|
17256
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
17257
|
+
if (selectionGroup) {
|
|
17258
|
+
selectionGroup.refreshObjectDimensions();
|
|
17259
|
+
selectionGroup.update();
|
|
17260
|
+
this._core.engine.emitObjectsChange();
|
|
17261
|
+
this._core.store.state.hasObjectsChanged = true;
|
|
17262
|
+
}
|
|
17263
|
+
}
|
|
17264
|
+
this._core.store.state.isLineHandleDragging = false;
|
|
17265
|
+
this.reset();
|
|
17266
|
+
this._core.rerender();
|
|
17267
|
+
}
|
|
17268
|
+
}
|
|
17269
|
+
getSelectedLine() {
|
|
17270
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
17271
|
+
if (!selectionGroup || selectionGroup.objects.length !== 1) {
|
|
17272
|
+
return null;
|
|
17273
|
+
}
|
|
17274
|
+
const object = selectionGroup.objects[0];
|
|
17275
|
+
if (object instanceof KritzelLine) {
|
|
17276
|
+
return object;
|
|
17277
|
+
}
|
|
17278
|
+
return null;
|
|
17279
|
+
}
|
|
17280
|
+
}
|
|
17281
|
+
|
|
16003
17282
|
class KritzelSelectionTool extends KritzelBaseTool {
|
|
16004
17283
|
selectionHandler;
|
|
16005
17284
|
moveHandler;
|
|
16006
17285
|
hoverHandler;
|
|
16007
17286
|
resizeHandler;
|
|
16008
17287
|
rotationHandler;
|
|
17288
|
+
lineHandleHandler;
|
|
16009
17289
|
constructor(core) {
|
|
16010
17290
|
super(core);
|
|
16011
17291
|
this.selectionHandler = new KritzelSelectionHandler(this._core);
|
|
@@ -16013,6 +17293,7 @@ class KritzelSelectionTool extends KritzelBaseTool {
|
|
|
16013
17293
|
this.hoverHandler = new KritzelHoverHandler(this._core);
|
|
16014
17294
|
this.resizeHandler = new KritzelResizeHandler(this._core);
|
|
16015
17295
|
this.rotationHandler = new KritzelRotationHandler(this._core);
|
|
17296
|
+
this.lineHandleHandler = new KritzelLineHandleHandler(this._core);
|
|
16016
17297
|
}
|
|
16017
17298
|
handlePointerDown(event) {
|
|
16018
17299
|
if (event.cancelable) {
|
|
@@ -16022,14 +17303,17 @@ class KritzelSelectionTool extends KritzelBaseTool {
|
|
|
16022
17303
|
if (KritzelEventHelper.isLeftClick(event)) {
|
|
16023
17304
|
this._core.store.state.isResizeHandleSelected = this.isHandleSelected(event);
|
|
16024
17305
|
this._core.store.state.isRotationHandleSelected = this.isRotationHandleSelected(event);
|
|
17306
|
+
this._core.store.state.isLineHandleSelected = this.isLineHandleSelected(event);
|
|
16025
17307
|
this._core.store.state.resizeHandleType = this.getHandleType(event);
|
|
17308
|
+
this._core.store.state.lineHandleType = this.getLineHandleType(event);
|
|
16026
17309
|
const selectedObject = this.getSelectedObject(event);
|
|
16027
17310
|
const selectionGroup = this._core.store.selectionGroup;
|
|
16028
17311
|
const isDifferentObject = selectedObject && selectionGroup && selectedObject.id !== selectionGroup.id;
|
|
16029
17312
|
if ((selectedObject === null || isDifferentObject) &&
|
|
16030
17313
|
selectionGroup &&
|
|
16031
17314
|
!this._core.store.state.isResizeHandleSelected &&
|
|
16032
|
-
!this._core.store.state.isRotationHandleSelected
|
|
17315
|
+
!this._core.store.state.isRotationHandleSelected &&
|
|
17316
|
+
!this._core.store.state.isLineHandleSelected) {
|
|
16033
17317
|
this._core.removeSelectionGroup();
|
|
16034
17318
|
this._core.rerender();
|
|
16035
17319
|
}
|
|
@@ -16037,6 +17321,7 @@ class KritzelSelectionTool extends KritzelBaseTool {
|
|
|
16037
17321
|
return;
|
|
16038
17322
|
}
|
|
16039
17323
|
}
|
|
17324
|
+
this.lineHandleHandler.handlePointerDown(event);
|
|
16040
17325
|
this.moveHandler.handlePointerDown(event);
|
|
16041
17326
|
this.selectionHandler.handlePointerDown(event);
|
|
16042
17327
|
this.resizeHandler.handlePointerDown(event);
|
|
@@ -16050,7 +17335,9 @@ class KritzelSelectionTool extends KritzelBaseTool {
|
|
|
16050
17335
|
if (this._core.store.state.pointers.size === 1) {
|
|
16051
17336
|
this._core.store.state.isResizeHandleSelected = this.isHandleSelected(event);
|
|
16052
17337
|
this._core.store.state.isRotationHandleSelected = this.isRotationHandleSelected(event);
|
|
17338
|
+
this._core.store.state.isLineHandleSelected = this.isLineHandleSelected(event);
|
|
16053
17339
|
this._core.store.state.resizeHandleType = this.getHandleType(event);
|
|
17340
|
+
this._core.store.state.lineHandleType = this.getLineHandleType(event);
|
|
16054
17341
|
const selectedObject = this.getSelectedObject(event);
|
|
16055
17342
|
const selectionGroup = this._core.store.selectionGroup;
|
|
16056
17343
|
const isDifferentObject = selectedObject && selectionGroup && selectedObject.id !== selectionGroup.id;
|
|
@@ -16060,11 +17347,13 @@ class KritzelSelectionTool extends KritzelBaseTool {
|
|
|
16060
17347
|
if ((selectedObject === null || isDifferentObject) &&
|
|
16061
17348
|
selectionGroup &&
|
|
16062
17349
|
!this._core.store.state.isResizeHandleSelected &&
|
|
16063
|
-
!this._core.store.state.isRotationHandleSelected
|
|
17350
|
+
!this._core.store.state.isRotationHandleSelected &&
|
|
17351
|
+
!this._core.store.state.isLineHandleSelected) {
|
|
16064
17352
|
this._core.removeSelectionGroup();
|
|
16065
17353
|
this._core.rerender();
|
|
16066
17354
|
}
|
|
16067
17355
|
}
|
|
17356
|
+
this.lineHandleHandler.handlePointerDown(event);
|
|
16068
17357
|
this.rotationHandler.handlePointerDown(event);
|
|
16069
17358
|
this.resizeHandler.handlePointerDown(event);
|
|
16070
17359
|
this.moveHandler.handlePointerDown(event);
|
|
@@ -16077,6 +17366,7 @@ class KritzelSelectionTool extends KritzelBaseTool {
|
|
|
16077
17366
|
event.preventDefault();
|
|
16078
17367
|
}
|
|
16079
17368
|
if (event.pointerType === 'mouse') {
|
|
17369
|
+
this.lineHandleHandler.handlePointerMove(event);
|
|
16080
17370
|
this.moveHandler.handlePointerMove(event);
|
|
16081
17371
|
this.hoverHandler.handlePointerMove(event);
|
|
16082
17372
|
this.selectionHandler.handlePointerMove(event);
|
|
@@ -16088,6 +17378,7 @@ class KritzelSelectionTool extends KritzelBaseTool {
|
|
|
16088
17378
|
if (this._core.store.state.isScaling === true) {
|
|
16089
17379
|
return;
|
|
16090
17380
|
}
|
|
17381
|
+
this.lineHandleHandler.handlePointerMove(event);
|
|
16091
17382
|
this.rotationHandler.handlePointerMove(event);
|
|
16092
17383
|
this.resizeHandler.handlePointerMove(event);
|
|
16093
17384
|
this.moveHandler.handlePointerMove(event);
|
|
@@ -16100,6 +17391,7 @@ class KritzelSelectionTool extends KritzelBaseTool {
|
|
|
16100
17391
|
event.preventDefault();
|
|
16101
17392
|
}
|
|
16102
17393
|
if (event.pointerType === 'mouse') {
|
|
17394
|
+
this.lineHandleHandler.handlePointerUp(event);
|
|
16103
17395
|
this.moveHandler.handlePointerUp(event);
|
|
16104
17396
|
this.resizeHandler.handlePointerUp(event);
|
|
16105
17397
|
this.rotationHandler.handlePointerUp(event);
|
|
@@ -16110,6 +17402,7 @@ class KritzelSelectionTool extends KritzelBaseTool {
|
|
|
16110
17402
|
if (this._core.store.state.isScaling === true) {
|
|
16111
17403
|
return;
|
|
16112
17404
|
}
|
|
17405
|
+
this.lineHandleHandler.handlePointerUp(event);
|
|
16113
17406
|
this.rotationHandler.handlePointerUp(event);
|
|
16114
17407
|
this.resizeHandler.handlePointerUp(event);
|
|
16115
17408
|
this.moveHandler.handlePointerUp(event);
|
|
@@ -16139,20 +17432,38 @@ class KritzelSelectionTool extends KritzelBaseTool {
|
|
|
16139
17432
|
const shadowRoot = this._core.store.state.host?.shadowRoot;
|
|
16140
17433
|
if (!shadowRoot)
|
|
16141
17434
|
return;
|
|
16142
|
-
const
|
|
16143
|
-
const handle =
|
|
17435
|
+
const elementsAtPoint = shadowRoot.elementsFromPoint(event.clientX, event.clientY);
|
|
17436
|
+
const handle = elementsAtPoint.find(el => el.classList.contains('resize-handle-overlay'));
|
|
16144
17437
|
return handle?.classList[1];
|
|
16145
17438
|
}
|
|
16146
17439
|
isHandleSelected(event) {
|
|
16147
17440
|
const shadowRoot = this._core.store.state.host?.shadowRoot;
|
|
16148
17441
|
if (!shadowRoot)
|
|
16149
17442
|
return false;
|
|
16150
|
-
const
|
|
16151
|
-
return
|
|
17443
|
+
const elementsAtPoint = shadowRoot.elementsFromPoint(event.clientX, event.clientY);
|
|
17444
|
+
return elementsAtPoint.some(el => el.classList.contains('resize-handle-overlay'));
|
|
17445
|
+
}
|
|
17446
|
+
isRotationHandleSelected(event) {
|
|
17447
|
+
const shadowRoot = this._core.store.state.host?.shadowRoot;
|
|
17448
|
+
if (!shadowRoot)
|
|
17449
|
+
return false;
|
|
17450
|
+
const elementsAtPoint = shadowRoot.elementsFromPoint(event.clientX, event.clientY);
|
|
17451
|
+
return elementsAtPoint.some(el => el.classList.contains('rotation-handle-overlay'));
|
|
17452
|
+
}
|
|
17453
|
+
isLineHandleSelected(event) {
|
|
17454
|
+
const shadowRoot = this._core.store.state.host?.shadowRoot;
|
|
17455
|
+
if (!shadowRoot)
|
|
17456
|
+
return false;
|
|
17457
|
+
const elementsAtPoint = shadowRoot.elementsFromPoint(event.clientX, event.clientY);
|
|
17458
|
+
return elementsAtPoint.some(el => el.classList.contains('selection-line-handle-overlay'));
|
|
16152
17459
|
}
|
|
16153
|
-
|
|
16154
|
-
const
|
|
16155
|
-
|
|
17460
|
+
getLineHandleType(event) {
|
|
17461
|
+
const shadowRoot = this._core.store.state.host?.shadowRoot;
|
|
17462
|
+
if (!shadowRoot)
|
|
17463
|
+
return;
|
|
17464
|
+
const elementsAtPoint = shadowRoot.elementsFromPoint(event.clientX, event.clientY);
|
|
17465
|
+
const handle = elementsAtPoint.find(el => el.classList.contains('selection-line-handle-overlay'));
|
|
17466
|
+
return handle?.classList[1];
|
|
16156
17467
|
}
|
|
16157
17468
|
}
|
|
16158
17469
|
|
|
@@ -31201,6 +32512,881 @@ class KritzelAppStateMap {
|
|
|
31201
32512
|
}
|
|
31202
32513
|
}
|
|
31203
32514
|
|
|
32515
|
+
class KritzelClassHelper {
|
|
32516
|
+
static isInstanceOf(object, className) {
|
|
32517
|
+
return !!object && object.__class__ === className;
|
|
32518
|
+
}
|
|
32519
|
+
}
|
|
32520
|
+
|
|
32521
|
+
/**
|
|
32522
|
+
* Manages anchor connections between line endpoints and other objects.
|
|
32523
|
+
* Maintains a runtime index for efficient reverse lookups and handles
|
|
32524
|
+
* snap detection during line endpoint dragging.
|
|
32525
|
+
*/
|
|
32526
|
+
class KritzelAnchorManager {
|
|
32527
|
+
_core;
|
|
32528
|
+
/**
|
|
32529
|
+
* Runtime index mapping objectId to the lines anchored to it.
|
|
32530
|
+
* This is derived from the anchor properties on lines and rebuilt as needed.
|
|
32531
|
+
*/
|
|
32532
|
+
_anchorIndex = new Map();
|
|
32533
|
+
constructor(core) {
|
|
32534
|
+
this._core = core;
|
|
32535
|
+
}
|
|
32536
|
+
// ============================================
|
|
32537
|
+
// Anchor CRUD Operations
|
|
32538
|
+
// ============================================
|
|
32539
|
+
/**
|
|
32540
|
+
* Sets an anchor from a line endpoint to a target object's center.
|
|
32541
|
+
* Updates both the line's anchor property and the internal index.
|
|
32542
|
+
*/
|
|
32543
|
+
setAnchor(lineId, endpoint, targetObjectId) {
|
|
32544
|
+
const line = this.getLineById(lineId);
|
|
32545
|
+
if (!line) {
|
|
32546
|
+
return;
|
|
32547
|
+
}
|
|
32548
|
+
// Prevent anchoring both endpoints to the same object
|
|
32549
|
+
if (endpoint === 'start' && line.endAnchor?.objectId === targetObjectId) {
|
|
32550
|
+
return;
|
|
32551
|
+
}
|
|
32552
|
+
if (endpoint === 'end' && line.startAnchor?.objectId === targetObjectId) {
|
|
32553
|
+
return;
|
|
32554
|
+
}
|
|
32555
|
+
// Remove existing anchor if any
|
|
32556
|
+
this.removeAnchor(lineId, endpoint);
|
|
32557
|
+
// Set the anchor on the line
|
|
32558
|
+
const anchor = { objectId: targetObjectId };
|
|
32559
|
+
if (endpoint === 'start') {
|
|
32560
|
+
line.startAnchor = anchor;
|
|
32561
|
+
}
|
|
32562
|
+
else {
|
|
32563
|
+
line.endAnchor = anchor;
|
|
32564
|
+
}
|
|
32565
|
+
// Update the index
|
|
32566
|
+
this.addToIndex(targetObjectId, lineId, endpoint);
|
|
32567
|
+
// Snap the endpoint to the target's center
|
|
32568
|
+
this.snapEndpointToObject(line, endpoint, targetObjectId);
|
|
32569
|
+
// Persist the change
|
|
32570
|
+
this._core.store.state.objects.update(line);
|
|
32571
|
+
}
|
|
32572
|
+
/**
|
|
32573
|
+
* Removes an anchor from a line endpoint.
|
|
32574
|
+
* Updates both the line's anchor property and the internal index.
|
|
32575
|
+
*/
|
|
32576
|
+
removeAnchor(lineId, endpoint) {
|
|
32577
|
+
const line = this.getLineById(lineId);
|
|
32578
|
+
if (!line) {
|
|
32579
|
+
return;
|
|
32580
|
+
}
|
|
32581
|
+
const anchor = endpoint === 'start' ? line.startAnchor : line.endAnchor;
|
|
32582
|
+
if (!anchor) {
|
|
32583
|
+
return;
|
|
32584
|
+
}
|
|
32585
|
+
// Remove from index
|
|
32586
|
+
this.removeFromIndex(anchor.objectId, lineId, endpoint);
|
|
32587
|
+
// Clear the anchor on the line
|
|
32588
|
+
if (endpoint === 'start') {
|
|
32589
|
+
line.startAnchor = undefined;
|
|
32590
|
+
}
|
|
32591
|
+
else {
|
|
32592
|
+
line.endAnchor = undefined;
|
|
32593
|
+
}
|
|
32594
|
+
}
|
|
32595
|
+
/**
|
|
32596
|
+
* Gets the anchor for a specific line endpoint.
|
|
32597
|
+
*/
|
|
32598
|
+
getAnchor(lineId, endpoint) {
|
|
32599
|
+
const line = this.getLineById(lineId);
|
|
32600
|
+
if (!line) {
|
|
32601
|
+
return null;
|
|
32602
|
+
}
|
|
32603
|
+
const anchor = endpoint === 'start' ? line.startAnchor : line.endAnchor;
|
|
32604
|
+
return anchor ?? null;
|
|
32605
|
+
}
|
|
32606
|
+
// ============================================
|
|
32607
|
+
// Query Operations
|
|
32608
|
+
// ============================================
|
|
32609
|
+
/**
|
|
32610
|
+
* Gets all lines that have an endpoint anchored to the specified object.
|
|
32611
|
+
*/
|
|
32612
|
+
getLinesAnchoredTo(objectId) {
|
|
32613
|
+
const entries = this._anchorIndex.get(objectId);
|
|
32614
|
+
return entries ? Array.from(entries) : [];
|
|
32615
|
+
}
|
|
32616
|
+
// ============================================
|
|
32617
|
+
// Position Updates
|
|
32618
|
+
// ============================================
|
|
32619
|
+
/**
|
|
32620
|
+
* Updates all line endpoints that are anchored to the specified object.
|
|
32621
|
+
* Should be called when an object moves.
|
|
32622
|
+
*/
|
|
32623
|
+
updateAnchorsForObject(objectId) {
|
|
32624
|
+
const entries = this.getLinesAnchoredTo(objectId);
|
|
32625
|
+
if (entries.length === 0) {
|
|
32626
|
+
return;
|
|
32627
|
+
}
|
|
32628
|
+
const targetObject = this.getObjectById(objectId);
|
|
32629
|
+
if (!targetObject) {
|
|
32630
|
+
return;
|
|
32631
|
+
}
|
|
32632
|
+
for (const entry of entries) {
|
|
32633
|
+
const line = this.getLineById(entry.lineId);
|
|
32634
|
+
if (!line) {
|
|
32635
|
+
continue;
|
|
32636
|
+
}
|
|
32637
|
+
this.snapEndpointToObject(line, entry.endpoint, objectId);
|
|
32638
|
+
}
|
|
32639
|
+
}
|
|
32640
|
+
/**
|
|
32641
|
+
* Snaps a line endpoint to an object's center.
|
|
32642
|
+
*/
|
|
32643
|
+
snapEndpointToObject(line, endpoint, targetObjectId) {
|
|
32644
|
+
const targetObject = this.getObjectById(targetObjectId);
|
|
32645
|
+
if (!targetObject) {
|
|
32646
|
+
return;
|
|
32647
|
+
}
|
|
32648
|
+
// Get target center in world coordinates
|
|
32649
|
+
const targetCenterX = targetObject.centerX;
|
|
32650
|
+
const targetCenterY = targetObject.centerY;
|
|
32651
|
+
// Convert world coordinates to line's local coordinate system
|
|
32652
|
+
const localCoords = this.worldToLineLocal(line, targetCenterX, targetCenterY);
|
|
32653
|
+
// Update the endpoint
|
|
32654
|
+
line.updateEndpoint(endpoint, localCoords.x, localCoords.y);
|
|
32655
|
+
}
|
|
32656
|
+
/**
|
|
32657
|
+
* Converts world coordinates to a line's local coordinate system.
|
|
32658
|
+
*/
|
|
32659
|
+
worldToLineLocal(line, worldX, worldY) {
|
|
32660
|
+
// Get line center in world coordinates
|
|
32661
|
+
const cx = line.centerX;
|
|
32662
|
+
const cy = line.centerY;
|
|
32663
|
+
// Translate to center
|
|
32664
|
+
const dx = worldX - cx;
|
|
32665
|
+
const dy = worldY - cy;
|
|
32666
|
+
// Apply inverse rotation
|
|
32667
|
+
const cos = Math.cos(-line.rotation);
|
|
32668
|
+
const sin = Math.sin(-line.rotation);
|
|
32669
|
+
const rotatedX = dx * cos - dy * sin;
|
|
32670
|
+
const rotatedY = dx * sin + dy * cos;
|
|
32671
|
+
// Calculate local coordinates relative to the unrotated bounding box top-left
|
|
32672
|
+
const relativeX = rotatedX + (line.totalWidth / 2) / line.scale;
|
|
32673
|
+
const relativeY = rotatedY + (line.totalHeight / 2) / line.scale;
|
|
32674
|
+
// Convert to internal coordinates
|
|
32675
|
+
const localX = relativeX * line.scale + line.x;
|
|
32676
|
+
const localY = relativeY * line.scale + line.y;
|
|
32677
|
+
return { x: localX, y: localY };
|
|
32678
|
+
}
|
|
32679
|
+
// ============================================
|
|
32680
|
+
// Snap Detection
|
|
32681
|
+
// ============================================
|
|
32682
|
+
/**
|
|
32683
|
+
* Finds the nearest object that can be snapped to within the snap threshold.
|
|
32684
|
+
* Returns null if no suitable snap target is found.
|
|
32685
|
+
*
|
|
32686
|
+
* @param worldX - X coordinate in world space
|
|
32687
|
+
* @param worldY - Y coordinate in world space
|
|
32688
|
+
* @param excludeLineId - ID of the line being edited (to exclude from snap targets)
|
|
32689
|
+
* @param otherEndpointAnchorId - ID of object anchored to the other endpoint (to prevent same-object anchoring)
|
|
32690
|
+
*/
|
|
32691
|
+
findSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId) {
|
|
32692
|
+
let bestTarget = null;
|
|
32693
|
+
let highestZIndex = -Infinity;
|
|
32694
|
+
const objects = this._core.store.allNonSelectionObjects;
|
|
32695
|
+
for (const object of objects) {
|
|
32696
|
+
// Skip the line being edited
|
|
32697
|
+
if (object.id === excludeLineId) {
|
|
32698
|
+
continue;
|
|
32699
|
+
}
|
|
32700
|
+
// Skip if this is the object anchored to the other endpoint
|
|
32701
|
+
if (otherEndpointAnchorId && object.id === otherEndpointAnchorId) {
|
|
32702
|
+
continue;
|
|
32703
|
+
}
|
|
32704
|
+
// Skip non-anchorable objects
|
|
32705
|
+
if (!this.isAnchorable(object)) {
|
|
32706
|
+
continue;
|
|
32707
|
+
}
|
|
32708
|
+
// Check if point is inside the object's rotated polygon
|
|
32709
|
+
const polygon = object.rotatedPolygon;
|
|
32710
|
+
const points = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
|
|
32711
|
+
if (KritzelGeometryHelper.isPointInPolygon({ x: worldX, y: worldY }, points)) {
|
|
32712
|
+
// If inside, check if this object is "above" the current best target
|
|
32713
|
+
if (object.zIndex > highestZIndex) {
|
|
32714
|
+
highestZIndex = object.zIndex;
|
|
32715
|
+
bestTarget = {
|
|
32716
|
+
objectId: object.id,
|
|
32717
|
+
centerX: object.centerX,
|
|
32718
|
+
centerY: object.centerY,
|
|
32719
|
+
};
|
|
32720
|
+
}
|
|
32721
|
+
}
|
|
32722
|
+
}
|
|
32723
|
+
return bestTarget;
|
|
32724
|
+
}
|
|
32725
|
+
/**
|
|
32726
|
+
* Sets the current snap candidate for visual feedback.
|
|
32727
|
+
*/
|
|
32728
|
+
setSnapCandidate(candidate) {
|
|
32729
|
+
this._core.store.state.snapCandidate = candidate;
|
|
32730
|
+
this._core.rerender();
|
|
32731
|
+
}
|
|
32732
|
+
/**
|
|
32733
|
+
* Gets the current snap candidate.
|
|
32734
|
+
*/
|
|
32735
|
+
getSnapCandidate() {
|
|
32736
|
+
return this._core.store.state.snapCandidate ?? null;
|
|
32737
|
+
}
|
|
32738
|
+
/**
|
|
32739
|
+
* Clears the snap candidate.
|
|
32740
|
+
*/
|
|
32741
|
+
clearSnapCandidate() {
|
|
32742
|
+
this._core.store.state.snapCandidate = null;
|
|
32743
|
+
this._core.rerender();
|
|
32744
|
+
}
|
|
32745
|
+
// ============================================
|
|
32746
|
+
// Render Data
|
|
32747
|
+
// ============================================
|
|
32748
|
+
/**
|
|
32749
|
+
* Gets render data for anchor lines visualization.
|
|
32750
|
+
* Returns null if there's no selected line with anchors or if snap is in progress.
|
|
32751
|
+
*/
|
|
32752
|
+
getAnchorLinesRenderData() {
|
|
32753
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
32754
|
+
if (!selectionGroup || selectionGroup.objects.length !== 1)
|
|
32755
|
+
return null;
|
|
32756
|
+
const selectedObject = selectionGroup.objects[0];
|
|
32757
|
+
if (!KritzelClassHelper.isInstanceOf(selectedObject, 'KritzelLine'))
|
|
32758
|
+
return null;
|
|
32759
|
+
const line = selectedObject;
|
|
32760
|
+
const startAnchorViz = this.computeAnchorVisualization(line, 'start');
|
|
32761
|
+
const endAnchorViz = this.computeAnchorVisualization(line, 'end');
|
|
32762
|
+
if (!startAnchorViz && !endAnchorViz)
|
|
32763
|
+
return null;
|
|
32764
|
+
const scale = this._core.store.state.scale;
|
|
32765
|
+
const lineStrokeWidth = line.strokeWidth / line.scale;
|
|
32766
|
+
const indicatorStrokeWidth = `${2 / scale}`;
|
|
32767
|
+
const dashLength = Math.max(lineStrokeWidth * 2, 4 / scale);
|
|
32768
|
+
const dashArray = `${dashLength} ${dashLength}`;
|
|
32769
|
+
const indicatorRadius = 8 / scale;
|
|
32770
|
+
return {
|
|
32771
|
+
lineStrokeWidth,
|
|
32772
|
+
indicatorStrokeWidth,
|
|
32773
|
+
dashArray,
|
|
32774
|
+
indicatorRadius,
|
|
32775
|
+
startAnchorViz,
|
|
32776
|
+
endAnchorViz,
|
|
32777
|
+
};
|
|
32778
|
+
}
|
|
32779
|
+
/**
|
|
32780
|
+
* Gets render data for snap indicator visualization.
|
|
32781
|
+
* Returns null if there's no snap candidate.
|
|
32782
|
+
*/
|
|
32783
|
+
getSnapIndicatorRenderData() {
|
|
32784
|
+
const snapCandidate = this.getSnapCandidate();
|
|
32785
|
+
if (!snapCandidate)
|
|
32786
|
+
return null;
|
|
32787
|
+
const scale = this._core.store.state.scale;
|
|
32788
|
+
const indicatorRadius = 8 / scale;
|
|
32789
|
+
const indicatorStrokeWidth = `${2 / scale}`;
|
|
32790
|
+
const lineStrokeWidth = snapCandidate.lineStrokeWidth
|
|
32791
|
+
? `${snapCandidate.lineStrokeWidth}`
|
|
32792
|
+
: `${4 / scale}`;
|
|
32793
|
+
const lineStrokeWidthNum = snapCandidate.lineStrokeWidth || (4 / scale);
|
|
32794
|
+
const dashLength = Math.max(lineStrokeWidthNum * 2, 4 / scale);
|
|
32795
|
+
const dashArray = `${dashLength} ${dashLength}`;
|
|
32796
|
+
const lineStroke = snapCandidate.lineStroke || '#000000';
|
|
32797
|
+
let solidLineEndX = snapCandidate.edgeX;
|
|
32798
|
+
let solidLineEndY = snapCandidate.edgeY;
|
|
32799
|
+
let arrowPoints;
|
|
32800
|
+
if (snapCandidate.arrowOffset && snapCandidate.edgeX !== undefined && snapCandidate.edgeY !== undefined) {
|
|
32801
|
+
const dx = snapCandidate.lineEndpointX - snapCandidate.edgeX;
|
|
32802
|
+
const dy = snapCandidate.lineEndpointY - snapCandidate.edgeY;
|
|
32803
|
+
const length = Math.sqrt(dx * dx + dy * dy);
|
|
32804
|
+
if (length > snapCandidate.arrowOffset) {
|
|
32805
|
+
solidLineEndX = snapCandidate.edgeX + (dx / length) * snapCandidate.arrowOffset;
|
|
32806
|
+
solidLineEndY = snapCandidate.edgeY + (dy / length) * snapCandidate.arrowOffset;
|
|
32807
|
+
}
|
|
32808
|
+
// Calculate arrow head points
|
|
32809
|
+
// Direction from line endpoint to edge (arrow direction)
|
|
32810
|
+
const arrowDx = snapCandidate.edgeX - snapCandidate.lineEndpointX;
|
|
32811
|
+
const arrowDy = snapCandidate.edgeY - snapCandidate.lineEndpointY;
|
|
32812
|
+
const arrowLengthTotal = Math.sqrt(arrowDx * arrowDx + arrowDy * arrowDy);
|
|
32813
|
+
if (arrowLengthTotal > 0) {
|
|
32814
|
+
const ux = arrowDx / arrowLengthTotal;
|
|
32815
|
+
const uy = arrowDy / arrowLengthTotal;
|
|
32816
|
+
// Perpendicular vector
|
|
32817
|
+
const px = -uy;
|
|
32818
|
+
const py = ux;
|
|
32819
|
+
// Arrow dimensions
|
|
32820
|
+
const arrowLength = snapCandidate.arrowOffset;
|
|
32821
|
+
const arrowWidth = arrowLength; // 1:1 ratio
|
|
32822
|
+
// Arrow tip at edge
|
|
32823
|
+
const tipX = snapCandidate.edgeX;
|
|
32824
|
+
const tipY = snapCandidate.edgeY;
|
|
32825
|
+
// Arrow base
|
|
32826
|
+
const baseX = tipX - ux * arrowLength;
|
|
32827
|
+
const baseY = tipY - uy * arrowLength;
|
|
32828
|
+
// Arrow wings
|
|
32829
|
+
const leftX = baseX + px * arrowWidth / 2;
|
|
32830
|
+
const leftY = baseY + py * arrowWidth / 2;
|
|
32831
|
+
const rightX = baseX - px * arrowWidth / 2;
|
|
32832
|
+
const rightY = baseY - py * arrowWidth / 2;
|
|
32833
|
+
arrowPoints = `${tipX},${tipY} ${leftX},${leftY} ${rightX},${rightY}`;
|
|
32834
|
+
}
|
|
32835
|
+
}
|
|
32836
|
+
return {
|
|
32837
|
+
indicatorRadius,
|
|
32838
|
+
indicatorStrokeWidth,
|
|
32839
|
+
lineStrokeWidth,
|
|
32840
|
+
dashArray,
|
|
32841
|
+
lineStroke,
|
|
32842
|
+
centerX: snapCandidate.centerX,
|
|
32843
|
+
centerY: snapCandidate.centerY,
|
|
32844
|
+
lineEndpointX: snapCandidate.lineEndpointX,
|
|
32845
|
+
lineEndpointY: snapCandidate.lineEndpointY,
|
|
32846
|
+
edgeX: snapCandidate.edgeX,
|
|
32847
|
+
edgeY: snapCandidate.edgeY,
|
|
32848
|
+
arrowOffset: snapCandidate.arrowOffset,
|
|
32849
|
+
arrowStyle: snapCandidate.arrowStyle,
|
|
32850
|
+
arrowFill: snapCandidate.arrowFill,
|
|
32851
|
+
solidLineEndX,
|
|
32852
|
+
solidLineEndY,
|
|
32853
|
+
arrowPoints,
|
|
32854
|
+
snapLinePath: (() => {
|
|
32855
|
+
if (snapCandidate.controlX !== undefined &&
|
|
32856
|
+
snapCandidate.controlY !== undefined &&
|
|
32857
|
+
snapCandidate.t !== undefined) {
|
|
32858
|
+
const startT = snapCandidate.endpoint === 'start' ? 1 - snapCandidate.t : snapCandidate.t;
|
|
32859
|
+
// Ensure meaningful range
|
|
32860
|
+
if (startT >= 1)
|
|
32861
|
+
return undefined;
|
|
32862
|
+
const segment = this.extractQuadraticSegment({ x: snapCandidate.lineEndpointX, y: snapCandidate.lineEndpointY }, { x: snapCandidate.controlX, y: snapCandidate.controlY }, { x: snapCandidate.centerX, y: snapCandidate.centerY }, startT, 1);
|
|
32863
|
+
return `M ${segment.start.x} ${segment.start.y} Q ${segment.control.x} ${segment.control.y} ${segment.end.x} ${segment.end.y}`;
|
|
32864
|
+
}
|
|
32865
|
+
return undefined;
|
|
32866
|
+
})(),
|
|
32867
|
+
};
|
|
32868
|
+
}
|
|
32869
|
+
// ============================================
|
|
32870
|
+
// Cleanup Operations
|
|
32871
|
+
// ============================================
|
|
32872
|
+
/**
|
|
32873
|
+
* Handles cleanup when an object is deleted.
|
|
32874
|
+
* Detaches all lines that were anchored to the deleted object.
|
|
32875
|
+
*/
|
|
32876
|
+
handleObjectDeleted(objectId) {
|
|
32877
|
+
const entries = this.getLinesAnchoredTo(objectId);
|
|
32878
|
+
for (const entry of entries) {
|
|
32879
|
+
this.removeAnchor(entry.lineId, entry.endpoint);
|
|
32880
|
+
// Update the line to persist the change
|
|
32881
|
+
const line = this.getLineById(entry.lineId);
|
|
32882
|
+
if (line) {
|
|
32883
|
+
this._core.store.state.objects.update(line);
|
|
32884
|
+
}
|
|
32885
|
+
}
|
|
32886
|
+
// Remove the object from the index
|
|
32887
|
+
this._anchorIndex.delete(objectId);
|
|
32888
|
+
}
|
|
32889
|
+
/**
|
|
32890
|
+
* Handles cleanup when a line is deleted.
|
|
32891
|
+
* Removes all anchor index entries for the line.
|
|
32892
|
+
*/
|
|
32893
|
+
handleLineDeleted(lineId) {
|
|
32894
|
+
const line = this.getLineById(lineId);
|
|
32895
|
+
if (!line) {
|
|
32896
|
+
return;
|
|
32897
|
+
}
|
|
32898
|
+
if (line.startAnchor) {
|
|
32899
|
+
this.removeFromIndex(line.startAnchor.objectId, lineId, 'start');
|
|
32900
|
+
}
|
|
32901
|
+
if (line.endAnchor) {
|
|
32902
|
+
this.removeFromIndex(line.endAnchor.objectId, lineId, 'end');
|
|
32903
|
+
}
|
|
32904
|
+
}
|
|
32905
|
+
// ============================================
|
|
32906
|
+
// Index Management
|
|
32907
|
+
// ============================================
|
|
32908
|
+
/**
|
|
32909
|
+
* Rebuilds the anchor index from all lines in the object map.
|
|
32910
|
+
* Should be called after loading/deserializing objects.
|
|
32911
|
+
*/
|
|
32912
|
+
rebuildIndex() {
|
|
32913
|
+
this._anchorIndex.clear();
|
|
32914
|
+
const objects = this._core.store.allObjects;
|
|
32915
|
+
for (const object of objects) {
|
|
32916
|
+
if (object instanceof KritzelLine) {
|
|
32917
|
+
if (object.startAnchor) {
|
|
32918
|
+
this.addToIndex(object.startAnchor.objectId, object.id, 'start');
|
|
32919
|
+
}
|
|
32920
|
+
if (object.endAnchor) {
|
|
32921
|
+
this.addToIndex(object.endAnchor.objectId, object.id, 'end');
|
|
32922
|
+
}
|
|
32923
|
+
}
|
|
32924
|
+
}
|
|
32925
|
+
}
|
|
32926
|
+
/**
|
|
32927
|
+
* Adds an entry to the anchor index.
|
|
32928
|
+
*/
|
|
32929
|
+
addToIndex(objectId, lineId, endpoint) {
|
|
32930
|
+
if (!this._anchorIndex.has(objectId)) {
|
|
32931
|
+
this._anchorIndex.set(objectId, new Set());
|
|
32932
|
+
}
|
|
32933
|
+
const entries = this._anchorIndex.get(objectId);
|
|
32934
|
+
// Check if entry already exists
|
|
32935
|
+
for (const entry of entries) {
|
|
32936
|
+
if (entry.lineId === lineId && entry.endpoint === endpoint) {
|
|
32937
|
+
return;
|
|
32938
|
+
}
|
|
32939
|
+
}
|
|
32940
|
+
entries.add({ lineId, endpoint });
|
|
32941
|
+
}
|
|
32942
|
+
/**
|
|
32943
|
+
* Removes an entry from the anchor index.
|
|
32944
|
+
*/
|
|
32945
|
+
removeFromIndex(objectId, lineId, endpoint) {
|
|
32946
|
+
const entries = this._anchorIndex.get(objectId);
|
|
32947
|
+
if (!entries) {
|
|
32948
|
+
return;
|
|
32949
|
+
}
|
|
32950
|
+
for (const entry of entries) {
|
|
32951
|
+
if (entry.lineId === lineId && entry.endpoint === endpoint) {
|
|
32952
|
+
entries.delete(entry);
|
|
32953
|
+
break;
|
|
32954
|
+
}
|
|
32955
|
+
}
|
|
32956
|
+
// Clean up empty sets
|
|
32957
|
+
if (entries.size === 0) {
|
|
32958
|
+
this._anchorIndex.delete(objectId);
|
|
32959
|
+
}
|
|
32960
|
+
}
|
|
32961
|
+
// ============================================
|
|
32962
|
+
// Helper Methods
|
|
32963
|
+
// ============================================
|
|
32964
|
+
/**
|
|
32965
|
+
* Gets a line by its ID.
|
|
32966
|
+
*/
|
|
32967
|
+
getLineById(lineId) {
|
|
32968
|
+
const objects = this._core.store.state.objects.filter(o => o.id === lineId);
|
|
32969
|
+
if (objects.length === 0) {
|
|
32970
|
+
return null;
|
|
32971
|
+
}
|
|
32972
|
+
const object = objects[0];
|
|
32973
|
+
return object instanceof KritzelLine ? object : null;
|
|
32974
|
+
}
|
|
32975
|
+
/**
|
|
32976
|
+
* Gets an object by its ID.
|
|
32977
|
+
*/
|
|
32978
|
+
getObjectById(objectId) {
|
|
32979
|
+
const objects = this._core.store.state.objects.filter(o => o.id === objectId);
|
|
32980
|
+
return objects.length > 0 ? objects[0] : null;
|
|
32981
|
+
}
|
|
32982
|
+
findAnchorTarget(line, endpoint) {
|
|
32983
|
+
const anchor = endpoint === 'start' ? line.startAnchor : line.endAnchor;
|
|
32984
|
+
if (!anchor) {
|
|
32985
|
+
return null;
|
|
32986
|
+
}
|
|
32987
|
+
return this._core.store.allNonSelectionObjects.find(obj => obj.id === anchor.objectId) ?? null;
|
|
32988
|
+
}
|
|
32989
|
+
// ============================================
|
|
32990
|
+
// Visualization Helpers
|
|
32991
|
+
// ============================================
|
|
32992
|
+
/**
|
|
32993
|
+
* Computes anchor visualization data for a line with anchors.
|
|
32994
|
+
* Returns edge intersection points for rendering dashed lines from edge to center.
|
|
32995
|
+
*/
|
|
32996
|
+
computeAnchorVisualization(line, endpoint) {
|
|
32997
|
+
const anchor = endpoint === 'start' ? line.startAnchor : line.endAnchor;
|
|
32998
|
+
if (!anchor)
|
|
32999
|
+
return null;
|
|
33000
|
+
const targetObject = this.findAnchorTarget(line, endpoint);
|
|
33001
|
+
if (!targetObject)
|
|
33002
|
+
return null;
|
|
33003
|
+
const clipInfo = this.computeAnchorClipInfo(line, endpoint, targetObject);
|
|
33004
|
+
if (!clipInfo)
|
|
33005
|
+
return null;
|
|
33006
|
+
const centerX = targetObject.centerX;
|
|
33007
|
+
const centerY = targetObject.centerY;
|
|
33008
|
+
return {
|
|
33009
|
+
edgeX: clipInfo.worldX,
|
|
33010
|
+
edgeY: clipInfo.worldY,
|
|
33011
|
+
centerX,
|
|
33012
|
+
centerY,
|
|
33013
|
+
pathD: this.buildAnchorPath(line, endpoint, clipInfo, targetObject) ?? undefined,
|
|
33014
|
+
};
|
|
33015
|
+
}
|
|
33016
|
+
/**
|
|
33017
|
+
* Computes a clipped line path that stops at anchor edges instead of going to anchor centers.
|
|
33018
|
+
* When arrows are present on anchored ends, the line is shortened so the arrow tip appears at the edge.
|
|
33019
|
+
* Returns the SVG path 'd' attribute string, or the original path if no clipping is needed.
|
|
33020
|
+
* @param line The line object
|
|
33021
|
+
* @param offsetByViewBox If true, subtracts line.x and line.y from coordinates (for selection UI)
|
|
33022
|
+
*/
|
|
33023
|
+
computeClippedLinePath(line, offsetByViewBox = false) {
|
|
33024
|
+
// Check for snap candidate first - this applies during drag operations even without anchors
|
|
33025
|
+
const snapCandidate = this.getSnapCandidate();
|
|
33026
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
33027
|
+
const hasActiveSnapCandidate = snapCandidate && selectionGroup && selectionGroup.objects.length === 1 && selectionGroup.objects[0].id === line.id;
|
|
33028
|
+
// If no anchors and no active snap candidate, return original path (with or without offset)
|
|
33029
|
+
if (!line.startAnchor && !line.endAnchor && !hasActiveSnapCandidate) {
|
|
33030
|
+
if (offsetByViewBox) {
|
|
33031
|
+
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
33032
|
+
return `M ${line.startX - line.x} ${line.startY - line.y} Q ${line.controlX - line.x} ${line.controlY - line.y} ${line.endX - line.x} ${line.endY - line.y}`;
|
|
33033
|
+
}
|
|
33034
|
+
return `M ${line.startX - line.x} ${line.startY - line.y} L ${line.endX - line.x} ${line.endY - line.y}`;
|
|
33035
|
+
}
|
|
33036
|
+
return line.d;
|
|
33037
|
+
}
|
|
33038
|
+
const startTarget = line.startAnchor ? this.findAnchorTarget(line, 'start') : null;
|
|
33039
|
+
const endTarget = line.endAnchor ? this.findAnchorTarget(line, 'end') : null;
|
|
33040
|
+
let startClip = startTarget ? this.computeAnchorClipInfo(line, 'start', startTarget) : null;
|
|
33041
|
+
let endClip = endTarget ? this.computeAnchorClipInfo(line, 'end', endTarget) : null;
|
|
33042
|
+
// Apply snap candidate if present and matches this line
|
|
33043
|
+
if (hasActiveSnapCandidate) {
|
|
33044
|
+
if (snapCandidate.edgeX !== undefined && snapCandidate.edgeY !== undefined) {
|
|
33045
|
+
const localEdge = this.lineWorldToLocal(line, snapCandidate.edgeX, snapCandidate.edgeY);
|
|
33046
|
+
const clipInfo = {
|
|
33047
|
+
worldX: snapCandidate.edgeX,
|
|
33048
|
+
worldY: snapCandidate.edgeY,
|
|
33049
|
+
localX: localEdge.x,
|
|
33050
|
+
localY: localEdge.y,
|
|
33051
|
+
t: snapCandidate.t
|
|
33052
|
+
};
|
|
33053
|
+
if (snapCandidate.endpoint === 'start') {
|
|
33054
|
+
startClip = clipInfo;
|
|
33055
|
+
}
|
|
33056
|
+
else {
|
|
33057
|
+
endClip = clipInfo;
|
|
33058
|
+
}
|
|
33059
|
+
}
|
|
33060
|
+
}
|
|
33061
|
+
// Apply offset if requested (for selection UI which uses coordinates relative to 0,0)
|
|
33062
|
+
const offsetX = offsetByViewBox ? line.x : 0;
|
|
33063
|
+
const offsetY = offsetByViewBox ? line.y : 0;
|
|
33064
|
+
// Handle curved lines by splitting the quadratic at the clipped parameters
|
|
33065
|
+
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
33066
|
+
let startT = startClip?.t ?? 0;
|
|
33067
|
+
let endT = endClip?.t ?? 1;
|
|
33068
|
+
// Adjust for start arrow
|
|
33069
|
+
if (startClip && line.hasStartArrow) {
|
|
33070
|
+
const arrowOffset = line.getArrowSize('start');
|
|
33071
|
+
const speed = this.evaluateDerivativeSpeedAtT(line, startT);
|
|
33072
|
+
if (speed > 0) {
|
|
33073
|
+
startT += arrowOffset / speed;
|
|
33074
|
+
}
|
|
33075
|
+
}
|
|
33076
|
+
// Adjust for end arrow
|
|
33077
|
+
if (endClip && line.hasEndArrow) {
|
|
33078
|
+
const arrowOffset = line.getArrowSize('end');
|
|
33079
|
+
const speed = this.evaluateDerivativeSpeedAtT(line, endT);
|
|
33080
|
+
if (speed > 0) {
|
|
33081
|
+
endT -= arrowOffset / speed;
|
|
33082
|
+
}
|
|
33083
|
+
}
|
|
33084
|
+
// Ensure valid range
|
|
33085
|
+
if (startT < 0)
|
|
33086
|
+
startT = 0;
|
|
33087
|
+
if (endT > 1)
|
|
33088
|
+
endT = 1;
|
|
33089
|
+
// Handle overlap or invalid range
|
|
33090
|
+
if (endT <= startT) {
|
|
33091
|
+
// If the arrow adjustment caused them to cross, or they were already crossed/close
|
|
33092
|
+
// We can either effectively hide the line, or just clamp them.
|
|
33093
|
+
// If we clamp them to the midpoint, the line becomes a point.
|
|
33094
|
+
// Let's ensure a minimal gap or just set them equal.
|
|
33095
|
+
const mid = (startT + endT) / 2;
|
|
33096
|
+
startT = mid;
|
|
33097
|
+
endT = mid;
|
|
33098
|
+
}
|
|
33099
|
+
const segment = this.extractQuadraticSegment({ x: line.startX, y: line.startY }, { x: line.controlX, y: line.controlY }, { x: line.endX, y: line.endY }, startT, endT);
|
|
33100
|
+
return `M ${segment.start.x - offsetX} ${segment.start.y - offsetY} Q ${segment.control.x - offsetX} ${segment.control.y - offsetY} ${segment.end.x - offsetX} ${segment.end.y - offsetY}`;
|
|
33101
|
+
}
|
|
33102
|
+
// Straight lines fall back to linear interpolation
|
|
33103
|
+
let startX = startClip?.localX ?? line.startX;
|
|
33104
|
+
let startY = startClip?.localY ?? line.startY;
|
|
33105
|
+
let endX = endClip?.localX ?? line.endX;
|
|
33106
|
+
let endY = endClip?.localY ?? line.endY;
|
|
33107
|
+
if (startClip && line.hasStartArrow) {
|
|
33108
|
+
const arrowOffset = line.getArrowSize('start');
|
|
33109
|
+
const dx = endX - startX;
|
|
33110
|
+
const dy = endY - startY;
|
|
33111
|
+
const length = Math.sqrt(dx * dx + dy * dy);
|
|
33112
|
+
if (length > arrowOffset) {
|
|
33113
|
+
startX += (dx / length) * arrowOffset;
|
|
33114
|
+
startY += (dy / length) * arrowOffset;
|
|
33115
|
+
}
|
|
33116
|
+
}
|
|
33117
|
+
if (endClip && line.hasEndArrow) {
|
|
33118
|
+
const arrowOffset = line.getArrowSize('end');
|
|
33119
|
+
const dx = startX - endX;
|
|
33120
|
+
const dy = startY - endY;
|
|
33121
|
+
const length = Math.sqrt(dx * dx + dy * dy);
|
|
33122
|
+
if (length > arrowOffset) {
|
|
33123
|
+
endX += (dx / length) * arrowOffset;
|
|
33124
|
+
endY += (dy / length) * arrowOffset;
|
|
33125
|
+
}
|
|
33126
|
+
}
|
|
33127
|
+
return `M ${startX - offsetX} ${startY - offsetY} L ${endX - offsetX} ${endY - offsetY}`;
|
|
33128
|
+
}
|
|
33129
|
+
computeAnchorClipInfo(line, endpoint, targetObject) {
|
|
33130
|
+
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
33131
|
+
return this.computeCurvedClipInfo(line, endpoint, targetObject);
|
|
33132
|
+
}
|
|
33133
|
+
return this.computeStraightClipInfo(line, endpoint, targetObject);
|
|
33134
|
+
}
|
|
33135
|
+
computeStraightClipInfo(line, endpoint, targetObject) {
|
|
33136
|
+
const reference = endpoint === 'start'
|
|
33137
|
+
? this.lineLocalToWorld(line, line.endX, line.endY)
|
|
33138
|
+
: this.lineLocalToWorld(line, line.startX, line.startY);
|
|
33139
|
+
const edgeIntersection = KritzelGeometryHelper.getLinePolygonIntersection(reference, { x: targetObject.centerX, y: targetObject.centerY }, targetObject.rotatedPolygon);
|
|
33140
|
+
if (!edgeIntersection) {
|
|
33141
|
+
return null;
|
|
33142
|
+
}
|
|
33143
|
+
const localEdge = this.lineWorldToLocal(line, edgeIntersection.x, edgeIntersection.y);
|
|
33144
|
+
const totalLength = Math.sqrt(Math.pow(line.endX - line.startX, 2) +
|
|
33145
|
+
Math.pow(line.endY - line.startY, 2));
|
|
33146
|
+
const distanceFromStart = Math.sqrt(Math.pow(localEdge.x - line.startX, 2) +
|
|
33147
|
+
Math.pow(localEdge.y - line.startY, 2));
|
|
33148
|
+
const t = totalLength > 0 ? distanceFromStart / totalLength : endpoint === 'start' ? 0 : 1;
|
|
33149
|
+
return {
|
|
33150
|
+
localX: localEdge.x,
|
|
33151
|
+
localY: localEdge.y,
|
|
33152
|
+
worldX: edgeIntersection.x,
|
|
33153
|
+
worldY: edgeIntersection.y,
|
|
33154
|
+
t,
|
|
33155
|
+
};
|
|
33156
|
+
}
|
|
33157
|
+
computeCurvedClipInfo(line, endpoint, targetObject) {
|
|
33158
|
+
const polygonPoints = this.getPolygonPoints(targetObject.rotatedPolygon);
|
|
33159
|
+
const exitPoint = this.findCurveExitPoint(line, endpoint, polygonPoints);
|
|
33160
|
+
if (exitPoint) {
|
|
33161
|
+
return exitPoint;
|
|
33162
|
+
}
|
|
33163
|
+
const reference = endpoint === 'start'
|
|
33164
|
+
? this.lineLocalToWorld(line, line.endX, line.endY)
|
|
33165
|
+
: this.lineLocalToWorld(line, line.startX, line.startY);
|
|
33166
|
+
const fallbackIntersection = KritzelGeometryHelper.getLinePolygonIntersection(reference, { x: targetObject.centerX, y: targetObject.centerY }, targetObject.rotatedPolygon);
|
|
33167
|
+
if (!fallbackIntersection) {
|
|
33168
|
+
return null;
|
|
33169
|
+
}
|
|
33170
|
+
const localEdge = this.lineWorldToLocal(line, fallbackIntersection.x, fallbackIntersection.y);
|
|
33171
|
+
const approxT = this.approximateParameterForWorldPoint(line, fallbackIntersection);
|
|
33172
|
+
return {
|
|
33173
|
+
localX: localEdge.x,
|
|
33174
|
+
localY: localEdge.y,
|
|
33175
|
+
worldX: fallbackIntersection.x,
|
|
33176
|
+
worldY: fallbackIntersection.y,
|
|
33177
|
+
t: approxT,
|
|
33178
|
+
};
|
|
33179
|
+
}
|
|
33180
|
+
findCurveExitPoint(line, endpoint, polygonPoints) {
|
|
33181
|
+
const steps = 64;
|
|
33182
|
+
const initialT = endpoint === 'start' ? 0 : 1;
|
|
33183
|
+
const initialSample = this.evaluateLineAtT(line, initialT);
|
|
33184
|
+
let prevInside = KritzelGeometryHelper.isPointInPolygon({ x: initialSample.worldX, y: initialSample.worldY }, polygonPoints);
|
|
33185
|
+
let prevT = initialT;
|
|
33186
|
+
for (let i = 1; i <= steps; i++) {
|
|
33187
|
+
const t = endpoint === 'start' ? i / steps : 1 - i / steps;
|
|
33188
|
+
const sample = this.evaluateLineAtT(line, t);
|
|
33189
|
+
const inside = KritzelGeometryHelper.isPointInPolygon({ x: sample.worldX, y: sample.worldY }, polygonPoints);
|
|
33190
|
+
if (prevInside && !inside) {
|
|
33191
|
+
const refinedT = this.refineCurveExitParameter(line, polygonPoints, prevT, t);
|
|
33192
|
+
const refinedPoint = this.evaluateLineAtT(line, refinedT);
|
|
33193
|
+
return {
|
|
33194
|
+
localX: refinedPoint.localX,
|
|
33195
|
+
localY: refinedPoint.localY,
|
|
33196
|
+
worldX: refinedPoint.worldX,
|
|
33197
|
+
worldY: refinedPoint.worldY,
|
|
33198
|
+
t: refinedT,
|
|
33199
|
+
};
|
|
33200
|
+
}
|
|
33201
|
+
prevInside = inside;
|
|
33202
|
+
prevT = t;
|
|
33203
|
+
}
|
|
33204
|
+
return null;
|
|
33205
|
+
}
|
|
33206
|
+
refineCurveExitParameter(line, polygonPoints, insideT, outsideT) {
|
|
33207
|
+
let tInside = insideT;
|
|
33208
|
+
let tOutside = outsideT;
|
|
33209
|
+
for (let i = 0; i < 8; i++) {
|
|
33210
|
+
const mid = (tInside + tOutside) / 2;
|
|
33211
|
+
const sample = this.evaluateLineAtT(line, mid);
|
|
33212
|
+
const inside = KritzelGeometryHelper.isPointInPolygon({ x: sample.worldX, y: sample.worldY }, polygonPoints);
|
|
33213
|
+
if (inside) {
|
|
33214
|
+
tInside = mid;
|
|
33215
|
+
}
|
|
33216
|
+
else {
|
|
33217
|
+
tOutside = mid;
|
|
33218
|
+
}
|
|
33219
|
+
}
|
|
33220
|
+
return (tInside + tOutside) / 2;
|
|
33221
|
+
}
|
|
33222
|
+
approximateParameterForWorldPoint(line, target) {
|
|
33223
|
+
const steps = 80;
|
|
33224
|
+
let bestT = 0;
|
|
33225
|
+
let bestDistance = Infinity;
|
|
33226
|
+
for (let i = 0; i <= steps; i++) {
|
|
33227
|
+
const t = i / steps;
|
|
33228
|
+
const sample = this.evaluateLineAtT(line, t);
|
|
33229
|
+
const distance = Math.hypot(sample.worldX - target.x, sample.worldY - target.y);
|
|
33230
|
+
if (distance < bestDistance) {
|
|
33231
|
+
bestDistance = distance;
|
|
33232
|
+
bestT = t;
|
|
33233
|
+
}
|
|
33234
|
+
}
|
|
33235
|
+
return bestT;
|
|
33236
|
+
}
|
|
33237
|
+
evaluateLineAtT(line, t) {
|
|
33238
|
+
const clampedT = Math.max(0, Math.min(1, t));
|
|
33239
|
+
let localX;
|
|
33240
|
+
let localY;
|
|
33241
|
+
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
33242
|
+
const oneMinusT = 1 - clampedT;
|
|
33243
|
+
localX = oneMinusT * oneMinusT * line.startX + 2 * oneMinusT * clampedT * line.controlX + clampedT * clampedT * line.endX;
|
|
33244
|
+
localY = oneMinusT * oneMinusT * line.startY + 2 * oneMinusT * clampedT * line.controlY + clampedT * clampedT * line.endY;
|
|
33245
|
+
}
|
|
33246
|
+
else {
|
|
33247
|
+
localX = line.startX + (line.endX - line.startX) * clampedT;
|
|
33248
|
+
localY = line.startY + (line.endY - line.startY) * clampedT;
|
|
33249
|
+
}
|
|
33250
|
+
const world = this.lineLocalToWorld(line, localX, localY);
|
|
33251
|
+
return { t: clampedT, localX, localY, worldX: world.x, worldY: world.y };
|
|
33252
|
+
}
|
|
33253
|
+
evaluateDerivativeSpeedAtT(line, t) {
|
|
33254
|
+
const clampedT = Math.max(0, Math.min(1, t));
|
|
33255
|
+
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
33256
|
+
// Quadratic Bezier derivative: B'(t) = 2(1-t)(P1-P0) + 2t(P2-P1)
|
|
33257
|
+
const p0x = line.startX;
|
|
33258
|
+
const p0y = line.startY;
|
|
33259
|
+
const p1x = line.controlX;
|
|
33260
|
+
const p1y = line.controlY;
|
|
33261
|
+
const p2x = line.endX;
|
|
33262
|
+
const p2y = line.endY;
|
|
33263
|
+
const dx = 2 * (1 - clampedT) * (p1x - p0x) + 2 * clampedT * (p2x - p1x);
|
|
33264
|
+
const dy = 2 * (1 - clampedT) * (p1y - p0y) + 2 * clampedT * (p2y - p1y);
|
|
33265
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
33266
|
+
}
|
|
33267
|
+
else {
|
|
33268
|
+
// Straight line speed is constant length
|
|
33269
|
+
const dx = line.endX - line.startX;
|
|
33270
|
+
const dy = line.endY - line.startY;
|
|
33271
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
33272
|
+
}
|
|
33273
|
+
}
|
|
33274
|
+
extractQuadraticSegment(start, control, end, tStart, tEnd) {
|
|
33275
|
+
let normalizedStart = Math.max(0, Math.min(1, tStart));
|
|
33276
|
+
let normalizedEnd = Math.max(0, Math.min(1, tEnd));
|
|
33277
|
+
if (normalizedEnd < normalizedStart) {
|
|
33278
|
+
const swap = normalizedStart;
|
|
33279
|
+
normalizedStart = normalizedEnd;
|
|
33280
|
+
normalizedEnd = swap;
|
|
33281
|
+
}
|
|
33282
|
+
let segment = { start, control, end };
|
|
33283
|
+
if (normalizedStart > 0) {
|
|
33284
|
+
const split = this.splitQuadraticSegment(segment, normalizedStart);
|
|
33285
|
+
segment = split.right;
|
|
33286
|
+
const remainingRange = 1 - normalizedStart;
|
|
33287
|
+
normalizedEnd = remainingRange > 0 ? (normalizedEnd - normalizedStart) / remainingRange : 1;
|
|
33288
|
+
}
|
|
33289
|
+
if (normalizedEnd < 1) {
|
|
33290
|
+
const split = this.splitQuadraticSegment(segment, normalizedEnd);
|
|
33291
|
+
segment = split.left;
|
|
33292
|
+
}
|
|
33293
|
+
return segment;
|
|
33294
|
+
}
|
|
33295
|
+
splitQuadraticSegment(segment, t) {
|
|
33296
|
+
const clampedT = Math.max(0, Math.min(1, t));
|
|
33297
|
+
const p0 = segment.start;
|
|
33298
|
+
const p1 = segment.control;
|
|
33299
|
+
const p2 = segment.end;
|
|
33300
|
+
const p01 = this.lerpPoint(p0, p1, clampedT);
|
|
33301
|
+
const p12 = this.lerpPoint(p1, p2, clampedT);
|
|
33302
|
+
const p012 = this.lerpPoint(p01, p12, clampedT);
|
|
33303
|
+
return {
|
|
33304
|
+
left: { start: p0, control: p01, end: p012 },
|
|
33305
|
+
right: { start: p012, control: p12, end: p2 },
|
|
33306
|
+
};
|
|
33307
|
+
}
|
|
33308
|
+
lerpPoint(a, b, t) {
|
|
33309
|
+
return {
|
|
33310
|
+
x: a.x + (b.x - a.x) * t,
|
|
33311
|
+
y: a.y + (b.y - a.y) * t,
|
|
33312
|
+
};
|
|
33313
|
+
}
|
|
33314
|
+
buildAnchorPath(line, endpoint, clipInfo, targetObject) {
|
|
33315
|
+
if (line.controlX === undefined || line.controlY === undefined || clipInfo.t === undefined) {
|
|
33316
|
+
return `M ${clipInfo.worldX} ${clipInfo.worldY} L ${targetObject.centerX} ${targetObject.centerY}`;
|
|
33317
|
+
}
|
|
33318
|
+
const startT = endpoint === 'start' ? 0 : clipInfo.t;
|
|
33319
|
+
const endT = endpoint === 'start' ? clipInfo.t : 1;
|
|
33320
|
+
if (endT <= startT) {
|
|
33321
|
+
return `M ${clipInfo.worldX} ${clipInfo.worldY} L ${targetObject.centerX} ${targetObject.centerY}`;
|
|
33322
|
+
}
|
|
33323
|
+
const segment = this.extractQuadraticSegment({ x: line.startX, y: line.startY }, { x: line.controlX, y: line.controlY }, { x: line.endX, y: line.endY }, startT, endT);
|
|
33324
|
+
const reverse = endpoint === 'start';
|
|
33325
|
+
return this.buildWorldQuadraticPath(line, segment, reverse);
|
|
33326
|
+
}
|
|
33327
|
+
buildWorldQuadraticPath(line, segment, reverse = false) {
|
|
33328
|
+
const startPoint = reverse ? segment.end : segment.start;
|
|
33329
|
+
const endPoint = reverse ? segment.start : segment.end;
|
|
33330
|
+
const controlPoint = segment.control;
|
|
33331
|
+
const startWorld = this.lineLocalToWorld(line, startPoint.x, startPoint.y);
|
|
33332
|
+
const controlWorld = this.lineLocalToWorld(line, controlPoint.x, controlPoint.y);
|
|
33333
|
+
const endWorld = this.lineLocalToWorld(line, endPoint.x, endPoint.y);
|
|
33334
|
+
return `M ${startWorld.x} ${startWorld.y} Q ${controlWorld.x} ${controlWorld.y} ${endWorld.x} ${endWorld.y}`;
|
|
33335
|
+
}
|
|
33336
|
+
getPolygonPoints(polygon) {
|
|
33337
|
+
return [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
|
|
33338
|
+
}
|
|
33339
|
+
/**
|
|
33340
|
+
* Converts local line coordinates to world coordinates.
|
|
33341
|
+
*/
|
|
33342
|
+
lineLocalToWorld(line, localX, localY) {
|
|
33343
|
+
const px = localX - line.x;
|
|
33344
|
+
const py = localY - line.y;
|
|
33345
|
+
const cx = line.totalWidth / 2;
|
|
33346
|
+
const cy = line.totalHeight / 2;
|
|
33347
|
+
const cos = Math.cos(line.rotation);
|
|
33348
|
+
const sin = Math.sin(line.rotation);
|
|
33349
|
+
const rotatedX = (px - cx) * cos - (py - cy) * sin + cx;
|
|
33350
|
+
const rotatedY = (px - cx) * sin + (py - cy) * cos + cy;
|
|
33351
|
+
return {
|
|
33352
|
+
x: rotatedX / line.scale + line.translateX,
|
|
33353
|
+
y: rotatedY / line.scale + line.translateY,
|
|
33354
|
+
};
|
|
33355
|
+
}
|
|
33356
|
+
/**
|
|
33357
|
+
* Converts world coordinates to line's local SVG coordinates.
|
|
33358
|
+
*/
|
|
33359
|
+
lineWorldToLocal(line, worldX, worldY) {
|
|
33360
|
+
const dx = (worldX - line.translateX) * line.scale;
|
|
33361
|
+
const dy = (worldY - line.translateY) * line.scale;
|
|
33362
|
+
const cx = line.totalWidth / 2;
|
|
33363
|
+
const cy = line.totalHeight / 2;
|
|
33364
|
+
const cos = Math.cos(-line.rotation);
|
|
33365
|
+
const sin = Math.sin(-line.rotation);
|
|
33366
|
+
const rotatedX = (dx - cx) * cos - (dy - cy) * sin + cx;
|
|
33367
|
+
const rotatedY = (dx - cx) * sin + (dy - cy) * cos + cy;
|
|
33368
|
+
return {
|
|
33369
|
+
x: rotatedX + line.x,
|
|
33370
|
+
y: rotatedY + line.y,
|
|
33371
|
+
};
|
|
33372
|
+
}
|
|
33373
|
+
/**
|
|
33374
|
+
* Checks if an object can be used as an anchor target.
|
|
33375
|
+
*/
|
|
33376
|
+
isAnchorable(object) {
|
|
33377
|
+
// Exclude selection-related objects
|
|
33378
|
+
if (object instanceof KritzelSelectionBox || object instanceof KritzelSelectionGroup) {
|
|
33379
|
+
return false;
|
|
33380
|
+
}
|
|
33381
|
+
// Exclude line objects - lines cannot be anchored to other lines
|
|
33382
|
+
if (object instanceof KritzelLine) {
|
|
33383
|
+
return false;
|
|
33384
|
+
}
|
|
33385
|
+
// All other visible objects can be anchored to
|
|
33386
|
+
return object.isVisible;
|
|
33387
|
+
}
|
|
33388
|
+
}
|
|
33389
|
+
|
|
31204
33390
|
const DEFAULT_BRUSH_CONFIG = {
|
|
31205
33391
|
type: 'pen',
|
|
31206
33392
|
color: '#000000',
|
|
@@ -31293,24 +33479,65 @@ const DEFAULT_TEXT_CONFIG = {
|
|
|
31293
33479
|
],
|
|
31294
33480
|
};
|
|
31295
33481
|
|
|
33482
|
+
const DEFAULT_LINE_TOOL_CONFIG = {
|
|
33483
|
+
color: '#000000',
|
|
33484
|
+
size: 4,
|
|
33485
|
+
palette: [
|
|
33486
|
+
'#000000',
|
|
33487
|
+
'#ff5252',
|
|
33488
|
+
'#ffbc00',
|
|
33489
|
+
'#00c853',
|
|
33490
|
+
'#0000FF',
|
|
33491
|
+
'#d500f9',
|
|
33492
|
+
'#fafafa',
|
|
33493
|
+
'#a52714',
|
|
33494
|
+
'#ee8100',
|
|
33495
|
+
'#558b2f',
|
|
33496
|
+
'#01579b',
|
|
33497
|
+
'#8e24aa',
|
|
33498
|
+
'#90a4ae',
|
|
33499
|
+
'#ff4081',
|
|
33500
|
+
'#ff6e40',
|
|
33501
|
+
'#aeea00',
|
|
33502
|
+
'#304ffe',
|
|
33503
|
+
'#7c4dff',
|
|
33504
|
+
'#cfd8dc',
|
|
33505
|
+
'#f8bbd0',
|
|
33506
|
+
'#ffccbc',
|
|
33507
|
+
'#f0f4c3',
|
|
33508
|
+
'#b3e5fc',
|
|
33509
|
+
'#e1bee7',
|
|
33510
|
+
],
|
|
33511
|
+
arrows: {
|
|
33512
|
+
end: { enabled: true, style: 'triangle' },
|
|
33513
|
+
},
|
|
33514
|
+
};
|
|
33515
|
+
|
|
31296
33516
|
exports.DEFAULT_BRUSH_CONFIG = DEFAULT_BRUSH_CONFIG;
|
|
33517
|
+
exports.DEFAULT_LINE_TOOL_CONFIG = DEFAULT_LINE_TOOL_CONFIG;
|
|
31297
33518
|
exports.DEFAULT_SYNC_CONFIG = DEFAULT_SYNC_CONFIG;
|
|
31298
33519
|
exports.DEFAULT_TEXT_CONFIG = DEFAULT_TEXT_CONFIG;
|
|
31299
33520
|
exports.Doc = Doc;
|
|
31300
33521
|
exports.HocuspocusProvider = HocuspocusProvider;
|
|
31301
33522
|
exports.HocuspocusProviderWebsocket = HocuspocusProviderWebsocket;
|
|
31302
33523
|
exports.IndexedDBSyncProvider = IndexedDBSyncProvider;
|
|
33524
|
+
exports.KritzelAnchorManager = KritzelAnchorManager;
|
|
31303
33525
|
exports.KritzelAppStateMap = KritzelAppStateMap;
|
|
31304
33526
|
exports.KritzelBaseHandler = KritzelBaseHandler;
|
|
31305
33527
|
exports.KritzelBaseObject = KritzelBaseObject;
|
|
31306
33528
|
exports.KritzelBaseTool = KritzelBaseTool;
|
|
31307
33529
|
exports.KritzelBrushTool = KritzelBrushTool;
|
|
33530
|
+
exports.KritzelClassHelper = KritzelClassHelper;
|
|
33531
|
+
exports.KritzelCursorHelper = KritzelCursorHelper;
|
|
31308
33532
|
exports.KritzelDevicesHelper = KritzelDevicesHelper;
|
|
31309
33533
|
exports.KritzelEraserTool = KritzelEraserTool;
|
|
31310
33534
|
exports.KritzelEventHelper = KritzelEventHelper;
|
|
33535
|
+
exports.KritzelIconRegistry = KritzelIconRegistry;
|
|
31311
33536
|
exports.KritzelImage = KritzelImage;
|
|
31312
33537
|
exports.KritzelImageTool = KritzelImageTool;
|
|
31313
33538
|
exports.KritzelKeyboardHelper = KritzelKeyboardHelper;
|
|
33539
|
+
exports.KritzelLine = KritzelLine;
|
|
33540
|
+
exports.KritzelLineTool = KritzelLineTool;
|
|
31314
33541
|
exports.KritzelPath = KritzelPath;
|
|
31315
33542
|
exports.KritzelSelectionBox = KritzelSelectionBox;
|
|
31316
33543
|
exports.KritzelSelectionGroup = KritzelSelectionGroup;
|
|
@@ -31351,6 +33578,6 @@ exports.varStorage = varStorage;
|
|
|
31351
33578
|
exports.writeVarString = writeVarString$2;
|
|
31352
33579
|
exports.writeVarUint = writeVarUint$2;
|
|
31353
33580
|
exports.writeVarUint8Array = writeVarUint8Array$2;
|
|
31354
|
-
//# sourceMappingURL=default-
|
|
33581
|
+
//# sourceMappingURL=default-line-tool.config-D1Ns0NmM.js.map
|
|
31355
33582
|
|
|
31356
|
-
//# sourceMappingURL=default-
|
|
33583
|
+
//# sourceMappingURL=default-line-tool.config-D1Ns0NmM.js.map
|