kritzel-stencil 0.3.25 → 0.3.27
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/index.cjs.js +1 -1
- package/dist/cjs/kritzel-active-users_45.cjs.entry.js +13 -8
- package/dist/cjs/{schema.constants-Np7j2Gz4.js → schema.constants-9v4Edzoe.js} +289 -108
- package/dist/collection/classes/handlers/line-handle.handler.js +59 -52
- package/dist/collection/classes/handlers/move.handler.js +2 -0
- package/dist/collection/classes/handlers/resize.handler.js +22 -0
- package/dist/collection/classes/handlers/rotation.handler.js +18 -6
- package/dist/collection/classes/managers/anchor.manager.js +108 -45
- package/dist/collection/classes/objects/selection-group.class.js +80 -5
- package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +10 -6
- package/dist/collection/configs/default-engine-config.js +1 -0
- package/dist/collection/constants/version.js +1 -1
- package/dist/components/index.js +1 -1
- package/dist/components/kritzel-controls.js +1 -1
- package/dist/components/kritzel-editor.js +1 -1
- package/dist/components/kritzel-engine.js +1 -1
- package/dist/components/kritzel-settings.js +1 -1
- package/dist/components/kritzel-tool-config.js +1 -1
- package/dist/components/{p-B5XGjTLd.js → p-BmKYyPpl.js} +1 -1
- package/dist/components/{p-DNDt6O6A.js → p-CRKezkZU.js} +1 -1
- package/dist/components/{p-Dw-t1qQc.js → p-DXYkgNAO.js} +1 -1
- package/dist/components/{p-DF0S4FqL.js → p-G3xSPZ0x.js} +1 -1
- package/dist/components/p-ioK7ttPL.js +9 -0
- package/dist/esm/index.js +2 -2
- package/dist/esm/kritzel-active-users_45.entry.js +13 -8
- package/dist/esm/{schema.constants-1SDhlkfS.js → schema.constants-BT66X_r5.js} +289 -108
- package/dist/stencil/index.esm.js +1 -1
- package/dist/stencil/p-9ca04088.entry.js +9 -0
- package/dist/stencil/{p-1SDhlkfS.js → p-BT66X_r5.js} +1 -1
- package/dist/stencil/stencil.esm.js +1 -1
- package/dist/types/classes/handlers/line-handle.handler.d.ts +1 -0
- package/dist/types/classes/handlers/resize.handler.d.ts +7 -0
- package/dist/types/classes/handlers/rotation.handler.d.ts +4 -0
- package/dist/types/classes/managers/anchor.manager.d.ts +35 -2
- package/dist/types/classes/objects/selection-group.class.d.ts +8 -1
- package/dist/types/constants/version.d.ts +1 -1
- package/dist/types/interfaces/anchor.interface.d.ts +12 -2
- package/dist/types/interfaces/engine-state.interface.d.ts +2 -1
- package/package.json +1 -1
- package/dist/components/p-6_95PFq4.js +0 -9
- package/dist/stencil/p-7ba7df62.entry.js +0 -9
|
@@ -20682,6 +20682,8 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
20682
20682
|
_lastChildPersistTime = 0;
|
|
20683
20683
|
/** Minimum interval (ms) between child Yjs persists during drag */
|
|
20684
20684
|
static CHILD_PERSIST_THROTTLE_MS = 100;
|
|
20685
|
+
/** Ignore tiny floating-point diffs when comparing refreshed bounds */
|
|
20686
|
+
static DIMENSION_EPSILON = 0.001;
|
|
20685
20687
|
/**
|
|
20686
20688
|
* Gets the array of object IDs contained in this selection group.
|
|
20687
20689
|
* @returns Array of string IDs for the selected objects
|
|
@@ -21055,6 +21057,30 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21055
21057
|
}
|
|
21056
21058
|
});
|
|
21057
21059
|
}
|
|
21060
|
+
/**
|
|
21061
|
+
* Returns true when the selection contains a line anchored to an object
|
|
21062
|
+
* that is not part of this selection group.
|
|
21063
|
+
*/
|
|
21064
|
+
hasExternalAnchoredLineConnections(cachedObjects) {
|
|
21065
|
+
const children = cachedObjects ?? this.objects;
|
|
21066
|
+
if (children.length === 0) {
|
|
21067
|
+
return false;
|
|
21068
|
+
}
|
|
21069
|
+
const selectedIds = new Set(children.map(child => child.id));
|
|
21070
|
+
for (const child of children) {
|
|
21071
|
+
if (!KritzelClassHelper.isInstanceOf(child, 'KritzelLine')) {
|
|
21072
|
+
continue;
|
|
21073
|
+
}
|
|
21074
|
+
const line = child;
|
|
21075
|
+
if (line.startAnchor && !selectedIds.has(line.startAnchor.objectId)) {
|
|
21076
|
+
return true;
|
|
21077
|
+
}
|
|
21078
|
+
if (line.endAnchor && !selectedIds.has(line.endAnchor.objectId)) {
|
|
21079
|
+
return true;
|
|
21080
|
+
}
|
|
21081
|
+
}
|
|
21082
|
+
return false;
|
|
21083
|
+
}
|
|
21058
21084
|
/**
|
|
21059
21085
|
* Resizes the selection group and scales all contained objects proportionally.
|
|
21060
21086
|
* Uses snapshot values to avoid compounding errors during continuous drag operations.
|
|
@@ -21147,8 +21173,13 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21147
21173
|
*/
|
|
21148
21174
|
rotate(value) {
|
|
21149
21175
|
this.rotation = value;
|
|
21150
|
-
|
|
21151
|
-
|
|
21176
|
+
// Use the snapshot center as the rotation pivot rather than the live center.
|
|
21177
|
+
// refreshObjectDimensions() below can grow the box when an anchored line is pinned
|
|
21178
|
+
// to an external object; keeping the pivot fixed prevents the children from drifting.
|
|
21179
|
+
const snapshotTotalWidth = this.snapshotWidth + this.padding * 2;
|
|
21180
|
+
const snapshotTotalHeight = this.snapshotHeight + this.padding * 2;
|
|
21181
|
+
const centerX = this.snapshotTranslateX + snapshotTotalWidth / 2 / this.scale;
|
|
21182
|
+
const centerY = this.snapshotTranslateY + snapshotTotalHeight / 2 / this.scale;
|
|
21152
21183
|
const angle = value - this.snapshotRotation;
|
|
21153
21184
|
const cos = Math.cos(angle);
|
|
21154
21185
|
const sin = Math.sin(angle);
|
|
@@ -21170,6 +21201,31 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21170
21201
|
child.translateY = centerY + rotatedY - child.totalHeight / 2 / child.scale;
|
|
21171
21202
|
child.rotate(childCount === 1 ? value : unchangedSnapshot.rotation + angle);
|
|
21172
21203
|
}
|
|
21204
|
+
// Re-sync any anchored lines so their stored geometry follows the rotated objects
|
|
21205
|
+
// (including lines anchored to an object outside this selection), then grow the
|
|
21206
|
+
// selection box to enclose the reshaped lines. Rotation sets state.isRotating
|
|
21207
|
+
// rather than state.isDragging, so the refresh must be triggered explicitly here.
|
|
21208
|
+
const anchorTargetIds = new Set();
|
|
21209
|
+
for (const child of children) {
|
|
21210
|
+
anchorTargetIds.add(child.id);
|
|
21211
|
+
if (KritzelClassHelper.isInstanceOf(child, 'KritzelLine')) {
|
|
21212
|
+
const line = child;
|
|
21213
|
+
if (line.startAnchor)
|
|
21214
|
+
anchorTargetIds.add(line.startAnchor.objectId);
|
|
21215
|
+
if (line.endAnchor)
|
|
21216
|
+
anchorTargetIds.add(line.endAnchor.objectId);
|
|
21217
|
+
}
|
|
21218
|
+
}
|
|
21219
|
+
let hasAnchoredLines = false;
|
|
21220
|
+
for (const targetId of anchorTargetIds) {
|
|
21221
|
+
if (this._core.anchorManager.getLinesAnchoredTo(targetId).length > 0) {
|
|
21222
|
+
hasAnchoredLines = true;
|
|
21223
|
+
this._core.anchorManager.updateAnchorsForObject(targetId);
|
|
21224
|
+
}
|
|
21225
|
+
}
|
|
21226
|
+
if (hasAnchoredLines) {
|
|
21227
|
+
this.refreshObjectDimensions(children);
|
|
21228
|
+
}
|
|
21173
21229
|
});
|
|
21174
21230
|
}
|
|
21175
21231
|
/**
|
|
@@ -21199,6 +21255,16 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21199
21255
|
* @param skipPersist If true, only updates local state without persisting to Yjs (used during undo/redo)
|
|
21200
21256
|
*/
|
|
21201
21257
|
refreshObjectDimensions(cachedObjects, skipPersist = false) {
|
|
21258
|
+
const previous = {
|
|
21259
|
+
minX: this.minX,
|
|
21260
|
+
minY: this.minY,
|
|
21261
|
+
maxX: this.maxX,
|
|
21262
|
+
maxY: this.maxY,
|
|
21263
|
+
width: this.width,
|
|
21264
|
+
height: this.height,
|
|
21265
|
+
translateX: this.translateX,
|
|
21266
|
+
translateY: this.translateY,
|
|
21267
|
+
};
|
|
21202
21268
|
const children = cachedObjects ?? this.objects;
|
|
21203
21269
|
if (children.length === 1) {
|
|
21204
21270
|
const obj = children[0];
|
|
@@ -21252,9 +21318,18 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21252
21318
|
this.translateX = cx - (this.width / this.scale + 2 * this.padding) / 2;
|
|
21253
21319
|
this.translateY = cy - (this.height / this.scale + 2 * this.padding) / 2;
|
|
21254
21320
|
}
|
|
21255
|
-
|
|
21321
|
+
const hasChanged = Math.abs(this.minX - previous.minX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21322
|
+
Math.abs(this.minY - previous.minY) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21323
|
+
Math.abs(this.maxX - previous.maxX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21324
|
+
Math.abs(this.maxY - previous.maxY) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21325
|
+
Math.abs(this.width - previous.width) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21326
|
+
Math.abs(this.height - previous.height) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21327
|
+
Math.abs(this.translateX - previous.translateX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21328
|
+
Math.abs(this.translateY - previous.translateY) > KritzelSelectionGroup.DIMENSION_EPSILON;
|
|
21329
|
+
if (!skipPersist && hasChanged) {
|
|
21256
21330
|
this._core.store.objects.update(this);
|
|
21257
21331
|
}
|
|
21332
|
+
return hasChanged;
|
|
21258
21333
|
}
|
|
21259
21334
|
/**
|
|
21260
21335
|
* Calculates the horizontal offset from the group's center to an object's center using snapshot data.
|
|
@@ -21264,7 +21339,7 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21264
21339
|
*/
|
|
21265
21340
|
getOffsetXToCenterFromSnapshot(snapshot) {
|
|
21266
21341
|
const objCenterX = snapshot.translateX + snapshot.totalWidth / snapshot.scale / 2;
|
|
21267
|
-
const groupCenterX = this.
|
|
21342
|
+
const groupCenterX = this.snapshotTranslateX + (this.snapshotWidth + this.padding * 2) / this.scale / 2;
|
|
21268
21343
|
return objCenterX - groupCenterX;
|
|
21269
21344
|
}
|
|
21270
21345
|
/**
|
|
@@ -21275,7 +21350,7 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21275
21350
|
*/
|
|
21276
21351
|
getOffsetYToCenterFromSnapshot(snapshot) {
|
|
21277
21352
|
const objCenterY = snapshot.translateY + snapshot.totalHeight / snapshot.scale / 2;
|
|
21278
|
-
const groupCenterY = this.
|
|
21353
|
+
const groupCenterY = this.snapshotTranslateY + (this.snapshotHeight + this.padding * 2) / this.scale / 2;
|
|
21279
21354
|
return objCenterY - groupCenterY;
|
|
21280
21355
|
}
|
|
21281
21356
|
/**
|
|
@@ -22648,6 +22723,7 @@ class KritzelMoveHandler extends KritzelBaseHandler {
|
|
|
22648
22723
|
this._core.store.state.isDragging = false;
|
|
22649
22724
|
if (this.hasMoved) {
|
|
22650
22725
|
this._core.store.selectionGroup.persistChildren();
|
|
22726
|
+
this._core.store.selectionGroup.refreshObjectDimensions(undefined, false);
|
|
22651
22727
|
this._core.store.selectionGroup.update();
|
|
22652
22728
|
this._core.engine.emitObjectsChange();
|
|
22653
22729
|
this._core.store.state.hasObjectsChanged = true;
|
|
@@ -22660,6 +22736,7 @@ class KritzelMoveHandler extends KritzelBaseHandler {
|
|
|
22660
22736
|
this._core.store.state.isDragging = false;
|
|
22661
22737
|
if (this.hasMoved) {
|
|
22662
22738
|
this._core.store.selectionGroup.persistChildren();
|
|
22739
|
+
this._core.store.selectionGroup.refreshObjectDimensions(undefined, false);
|
|
22663
22740
|
this._core.store.selectionGroup.update();
|
|
22664
22741
|
this._core.engine.emitObjectsChange();
|
|
22665
22742
|
this._core.store.state.hasObjectsChanged = true;
|
|
@@ -22772,6 +22849,22 @@ class KritzelResizeHandler extends KritzelBaseHandler {
|
|
|
22772
22849
|
this.newSize = { x: 0, y: 0, width: 0, height: 0 };
|
|
22773
22850
|
this.hasResized = false;
|
|
22774
22851
|
}
|
|
22852
|
+
/**
|
|
22853
|
+
* Rebase pointer and size baselines to the latest live selection bounds.
|
|
22854
|
+
* This prevents drift when anchored lines to external objects expand/shrink
|
|
22855
|
+
* the selection box while a resize gesture is in progress.
|
|
22856
|
+
*/
|
|
22857
|
+
rebaseResizeBaseline(selectionGroup, clientX, clientY) {
|
|
22858
|
+
this.initialMouseX = clientX;
|
|
22859
|
+
this.initialMouseY = clientY;
|
|
22860
|
+
this.initialSize.width = selectionGroup.width;
|
|
22861
|
+
this.initialSize.height = selectionGroup.height;
|
|
22862
|
+
this.initialSize.x = selectionGroup.translateX;
|
|
22863
|
+
this.initialSize.y = selectionGroup.translateY;
|
|
22864
|
+
}
|
|
22865
|
+
shouldRebaseResizeBaseline(selectionGroup) {
|
|
22866
|
+
return selectionGroup.hasExternalAnchoredLineConnections?.() === true;
|
|
22867
|
+
}
|
|
22775
22868
|
/**
|
|
22776
22869
|
* Handles the pointer down event to initiate a resize operation.
|
|
22777
22870
|
* Captures the initial mouse position and selection group dimensions when
|
|
@@ -22899,6 +22992,9 @@ class KritzelResizeHandler extends KritzelBaseHandler {
|
|
|
22899
22992
|
this.newSize.x = newCenterX - this.newSize.width / objectScale / 2;
|
|
22900
22993
|
this.newSize.y = newCenterY - this.newSize.height / objectScale / 2;
|
|
22901
22994
|
selectionGroup.resize(this.newSize.x, this.newSize.y, this.newSize.width, this.newSize.height);
|
|
22995
|
+
if (this.shouldRebaseResizeBaseline(selectionGroup)) {
|
|
22996
|
+
this.rebaseResizeBaseline(selectionGroup, clientX, clientY);
|
|
22997
|
+
}
|
|
22902
22998
|
}
|
|
22903
22999
|
}
|
|
22904
23000
|
if (event.pointerType === 'touch' || event.pointerType === 'pen') {
|
|
@@ -22965,6 +23061,9 @@ class KritzelResizeHandler extends KritzelBaseHandler {
|
|
|
22965
23061
|
this.newSize.x = newCenterX - this.newSize.width / objectScale / 2;
|
|
22966
23062
|
this.newSize.y = newCenterY - this.newSize.height / objectScale / 2;
|
|
22967
23063
|
selectionGroup.resize(this.newSize.x, this.newSize.y, this.newSize.width, this.newSize.height);
|
|
23064
|
+
if (this.shouldRebaseResizeBaseline(selectionGroup)) {
|
|
23065
|
+
this.rebaseResizeBaseline(selectionGroup, clientX, clientY);
|
|
23066
|
+
}
|
|
22968
23067
|
}
|
|
22969
23068
|
}
|
|
22970
23069
|
}
|
|
@@ -23052,6 +23151,10 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23052
23151
|
rotation = 0;
|
|
23053
23152
|
/** The rotation value of the selection group before the current rotation operation began. */
|
|
23054
23153
|
initialSelectionGroupRotation = 0;
|
|
23154
|
+
/** The X coordinate of the rotation pivot (group center) captured when rotation starts, in world space. */
|
|
23155
|
+
initialCenterX = 0;
|
|
23156
|
+
/** The Y coordinate of the rotation pivot (group center) captured when rotation starts, in world space. */
|
|
23157
|
+
initialCenterY = 0;
|
|
23055
23158
|
/**
|
|
23056
23159
|
* Creates an instance of KritzelRotationHandler.
|
|
23057
23160
|
* @param core - The KritzelCore instance that provides access to the store, engine, and other core functionalities.
|
|
@@ -23066,6 +23169,8 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23066
23169
|
reset() {
|
|
23067
23170
|
this.initialRotation = 0;
|
|
23068
23171
|
this.rotation = 0;
|
|
23172
|
+
this.initialCenterX = 0;
|
|
23173
|
+
this.initialCenterY = 0;
|
|
23069
23174
|
}
|
|
23070
23175
|
/**
|
|
23071
23176
|
* Handles the pointer down event to initiate a rotation operation.
|
|
@@ -23085,6 +23190,10 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23085
23190
|
const objectScale = selectionGroup.scale || 1;
|
|
23086
23191
|
const centerX = selectionGroup.translateX + selectionGroup.width / 2 / objectScale;
|
|
23087
23192
|
const centerY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
23193
|
+
// Capture the pivot once so it stays fixed even if the bounding box grows
|
|
23194
|
+
// mid-rotation (e.g. a line anchored to an object outside the selection).
|
|
23195
|
+
this.initialCenterX = centerX;
|
|
23196
|
+
this.initialCenterY = centerY;
|
|
23088
23197
|
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
23089
23198
|
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
23090
23199
|
this.initialSelectionGroupRotation = selectionGroup.rotation;
|
|
@@ -23108,6 +23217,10 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23108
23217
|
const objectScale = selectionGroup.scale || 1;
|
|
23109
23218
|
const centerX = selectionGroup.translateX + selectionGroup.width / 2 / objectScale;
|
|
23110
23219
|
const centerY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
23220
|
+
// Capture the pivot once so it stays fixed even if the bounding box grows
|
|
23221
|
+
// mid-rotation (e.g. a line anchored to an object outside the selection).
|
|
23222
|
+
this.initialCenterX = centerX;
|
|
23223
|
+
this.initialCenterY = centerY;
|
|
23111
23224
|
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
23112
23225
|
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
23113
23226
|
this.initialSelectionGroupRotation = selectionGroup.rotation;
|
|
@@ -23132,9 +23245,8 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23132
23245
|
if (this._core.store.state.isRotating && selectionGroup) {
|
|
23133
23246
|
const clientX = event.clientX - this._core.store.offsetX;
|
|
23134
23247
|
const clientY = event.clientY - this._core.store.offsetY;
|
|
23135
|
-
const
|
|
23136
|
-
const
|
|
23137
|
-
const groupCenterY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
23248
|
+
const groupCenterX = this.initialCenterX;
|
|
23249
|
+
const groupCenterY = this.initialCenterY;
|
|
23138
23250
|
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
23139
23251
|
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
23140
23252
|
const currentRotation = Math.atan2(groupCenterY - cursorY, groupCenterX - cursorX);
|
|
@@ -23152,9 +23264,8 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23152
23264
|
if (this._core.store.state.isRotating && selectionGroup) {
|
|
23153
23265
|
const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
|
|
23154
23266
|
const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
|
|
23155
|
-
const
|
|
23156
|
-
const
|
|
23157
|
-
const groupCenterY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
23267
|
+
const groupCenterX = this.initialCenterX;
|
|
23268
|
+
const groupCenterY = this.initialCenterY;
|
|
23158
23269
|
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
23159
23270
|
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
23160
23271
|
const currentRotation = Math.atan2(groupCenterY - cursorY, groupCenterX - cursorX);
|
|
@@ -23833,6 +23944,7 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
23833
23944
|
this.hasMoved = false;
|
|
23834
23945
|
this.currentSnapTarget = null;
|
|
23835
23946
|
this._core.anchorManager.clearSnapCandidate();
|
|
23947
|
+
this._core.anchorManager.clearSnapPreviewCandidate();
|
|
23836
23948
|
}
|
|
23837
23949
|
/**
|
|
23838
23950
|
* Handles pointer down events to initiate line handle dragging.
|
|
@@ -23935,15 +24047,19 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
23935
24047
|
const worldY = (clientY - this._core.store.state.translateY) / viewportScale;
|
|
23936
24048
|
// Get other endpoint's anchor to prevent anchoring both to same object
|
|
23937
24049
|
const otherAnchorId = line.endAnchor?.objectId;
|
|
24050
|
+
const previewTarget = this._core.anchorManager.findSnapPreviewTarget(worldX, worldY, line.id, otherAnchorId);
|
|
23938
24051
|
// Check for snap target
|
|
23939
|
-
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId);
|
|
24052
|
+
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId, event.pointerType);
|
|
23940
24053
|
this.currentSnapTarget = snapTarget;
|
|
24054
|
+
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialEndX, this.initialEndY);
|
|
24055
|
+
let controlWorld = undefined;
|
|
24056
|
+
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
24057
|
+
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
24058
|
+
}
|
|
23941
24059
|
if (snapTarget) {
|
|
23942
24060
|
// Snap to target center - convert world coords to local
|
|
23943
24061
|
const localCoords = this.worldToLineLocal(line, snapTarget.centerX, snapTarget.centerY);
|
|
23944
24062
|
this.updateLineEndpoint(line, localCoords.x, localCoords.y, this.initialEndX, this.initialEndY);
|
|
23945
|
-
// Get the other endpoint's world position for edge intersection calculation
|
|
23946
|
-
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialEndX, this.initialEndY);
|
|
23947
24063
|
// Calculate edge intersection point using AnchorManager to support curves
|
|
23948
24064
|
const targetObject = this._core.store.allNonSelectionObjects.find(obj => obj.id === snapTarget.objectId);
|
|
23949
24065
|
let edgeX;
|
|
@@ -23957,30 +24073,16 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
23957
24073
|
t = clipInfo.t;
|
|
23958
24074
|
}
|
|
23959
24075
|
}
|
|
23960
|
-
// Calculate control point in world coordinates if it exists
|
|
23961
|
-
let controlWorld = undefined;
|
|
23962
|
-
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
23963
|
-
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
23964
|
-
}
|
|
23965
24076
|
// Update snap indicator
|
|
23966
|
-
this._core.anchorManager.
|
|
23967
|
-
|
|
23968
|
-
|
|
23969
|
-
|
|
23970
|
-
|
|
23971
|
-
|
|
23972
|
-
|
|
23973
|
-
|
|
23974
|
-
|
|
23975
|
-
t,
|
|
23976
|
-
edgeX,
|
|
23977
|
-
edgeY,
|
|
23978
|
-
lineStroke: KritzelColorHelper.resolveThemeColor(line.stroke),
|
|
23979
|
-
lineStrokeWidth: line.strokeWidth / line.scale,
|
|
23980
|
-
arrowOffset: line.hasStartArrow ? line.getArrowSize('start') / line.scale : undefined,
|
|
23981
|
-
arrowStyle: line.hasStartArrow ? line.arrows?.start?.style : undefined,
|
|
23982
|
-
arrowFill: line.hasStartArrow ? line.getArrowFill('start') : undefined,
|
|
23983
|
-
});
|
|
24077
|
+
this._core.anchorManager.setSnapPreviewCandidate(null);
|
|
24078
|
+
this._core.anchorManager.setSnapCandidate(this.createIndicatorCandidate(line, 'start', snapTarget, otherEndpointWorld, controlWorld, { edgeX, edgeY, t }));
|
|
24079
|
+
}
|
|
24080
|
+
else if (previewTarget) {
|
|
24081
|
+
const newStartX = this.initialStartX + localDx;
|
|
24082
|
+
const newStartY = this.initialStartY + localDy;
|
|
24083
|
+
this.updateLineEndpoint(line, newStartX, newStartY, this.initialEndX, this.initialEndY);
|
|
24084
|
+
this._core.anchorManager.clearSnapCandidate();
|
|
24085
|
+
this._core.anchorManager.setSnapPreviewCandidate(this.createIndicatorCandidate(line, 'start', previewTarget, otherEndpointWorld, controlWorld));
|
|
23984
24086
|
}
|
|
23985
24087
|
else {
|
|
23986
24088
|
// No snap - use regular position
|
|
@@ -23989,6 +24091,7 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
23989
24091
|
this.updateLineEndpoint(line, newStartX, newStartY, this.initialEndX, this.initialEndY);
|
|
23990
24092
|
// Clear snap indicator
|
|
23991
24093
|
this._core.anchorManager.clearSnapCandidate();
|
|
24094
|
+
this._core.anchorManager.clearSnapPreviewCandidate();
|
|
23992
24095
|
}
|
|
23993
24096
|
}
|
|
23994
24097
|
else if (handleType === 'end') {
|
|
@@ -23997,15 +24100,19 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
23997
24100
|
const worldY = (clientY - this._core.store.state.translateY) / viewportScale;
|
|
23998
24101
|
// Get other endpoint's anchor to prevent anchoring both to same object
|
|
23999
24102
|
const otherAnchorId = line.startAnchor?.objectId;
|
|
24103
|
+
const previewTarget = this._core.anchorManager.findSnapPreviewTarget(worldX, worldY, line.id, otherAnchorId);
|
|
24000
24104
|
// Check for snap target
|
|
24001
|
-
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId);
|
|
24105
|
+
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId, event.pointerType);
|
|
24002
24106
|
this.currentSnapTarget = snapTarget;
|
|
24107
|
+
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialStartX, this.initialStartY);
|
|
24108
|
+
let controlWorld = undefined;
|
|
24109
|
+
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
24110
|
+
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
24111
|
+
}
|
|
24003
24112
|
if (snapTarget) {
|
|
24004
24113
|
// Snap to target center - convert world coords to local
|
|
24005
24114
|
const localCoords = this.worldToLineLocal(line, snapTarget.centerX, snapTarget.centerY);
|
|
24006
24115
|
this.updateLineEndpoint(line, this.initialStartX, this.initialStartY, localCoords.x, localCoords.y);
|
|
24007
|
-
// Get the other endpoint's world position for edge intersection calculation
|
|
24008
|
-
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialStartX, this.initialStartY);
|
|
24009
24116
|
// Calculate edge intersection point using AnchorManager to support curves
|
|
24010
24117
|
const targetObject = this._core.store.allNonSelectionObjects.find(obj => obj.id === snapTarget.objectId);
|
|
24011
24118
|
let edgeX;
|
|
@@ -24019,30 +24126,16 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
24019
24126
|
t = clipInfo.t;
|
|
24020
24127
|
}
|
|
24021
24128
|
}
|
|
24022
|
-
// Calculate control point in world coordinates if it exists
|
|
24023
|
-
let controlWorld = undefined;
|
|
24024
|
-
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
24025
|
-
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
24026
|
-
}
|
|
24027
24129
|
// Update snap indicator
|
|
24028
|
-
this._core.anchorManager.
|
|
24029
|
-
|
|
24030
|
-
|
|
24031
|
-
|
|
24032
|
-
|
|
24033
|
-
|
|
24034
|
-
|
|
24035
|
-
|
|
24036
|
-
|
|
24037
|
-
t,
|
|
24038
|
-
edgeX,
|
|
24039
|
-
edgeY,
|
|
24040
|
-
lineStroke: KritzelColorHelper.resolveThemeColor(line.stroke),
|
|
24041
|
-
lineStrokeWidth: line.strokeWidth / line.scale,
|
|
24042
|
-
arrowOffset: line.hasEndArrow ? line.getArrowSize('end') / line.scale : undefined,
|
|
24043
|
-
arrowStyle: line.hasEndArrow ? line.arrows?.end?.style : undefined,
|
|
24044
|
-
arrowFill: line.hasEndArrow ? line.getArrowFill('end') : undefined,
|
|
24045
|
-
});
|
|
24130
|
+
this._core.anchorManager.setSnapPreviewCandidate(null);
|
|
24131
|
+
this._core.anchorManager.setSnapCandidate(this.createIndicatorCandidate(line, 'end', snapTarget, otherEndpointWorld, controlWorld, { edgeX, edgeY, t }));
|
|
24132
|
+
}
|
|
24133
|
+
else if (previewTarget) {
|
|
24134
|
+
const newEndX = this.initialEndX + localDx;
|
|
24135
|
+
const newEndY = this.initialEndY + localDy;
|
|
24136
|
+
this.updateLineEndpoint(line, this.initialStartX, this.initialStartY, newEndX, newEndY);
|
|
24137
|
+
this._core.anchorManager.clearSnapCandidate();
|
|
24138
|
+
this._core.anchorManager.setSnapPreviewCandidate(this.createIndicatorCandidate(line, 'end', previewTarget, otherEndpointWorld, controlWorld));
|
|
24046
24139
|
}
|
|
24047
24140
|
else {
|
|
24048
24141
|
// No snap - use regular position
|
|
@@ -24051,9 +24144,12 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
24051
24144
|
this.updateLineEndpoint(line, this.initialStartX, this.initialStartY, newEndX, newEndY);
|
|
24052
24145
|
// Clear snap indicator
|
|
24053
24146
|
this._core.anchorManager.clearSnapCandidate();
|
|
24147
|
+
this._core.anchorManager.clearSnapPreviewCandidate();
|
|
24054
24148
|
}
|
|
24055
24149
|
}
|
|
24056
24150
|
else if (handleType === 'center') {
|
|
24151
|
+
this._core.anchorManager.clearSnapPreviewCandidate();
|
|
24152
|
+
this._core.anchorManager.clearSnapCandidate();
|
|
24057
24153
|
const startControlX = this.initialControlX ?? (this.initialStartX + this.initialEndX) / 2;
|
|
24058
24154
|
const startControlY = this.initialControlY ?? (this.initialStartY + this.initialEndY) / 2;
|
|
24059
24155
|
const newControlX = startControlX + localDx * 2;
|
|
@@ -24249,6 +24345,28 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
24249
24345
|
}
|
|
24250
24346
|
return null;
|
|
24251
24347
|
}
|
|
24348
|
+
createIndicatorCandidate(line, endpoint, target, otherEndpointWorld, controlWorld, clipInfo) {
|
|
24349
|
+
return {
|
|
24350
|
+
objectId: target.objectId,
|
|
24351
|
+
endpoint,
|
|
24352
|
+
centerX: target.centerX,
|
|
24353
|
+
centerY: target.centerY,
|
|
24354
|
+
lineEndpointX: otherEndpointWorld.x,
|
|
24355
|
+
lineEndpointY: otherEndpointWorld.y,
|
|
24356
|
+
controlX: controlWorld?.x,
|
|
24357
|
+
controlY: controlWorld?.y,
|
|
24358
|
+
t: clipInfo?.t,
|
|
24359
|
+
edgeX: clipInfo?.edgeX,
|
|
24360
|
+
edgeY: clipInfo?.edgeY,
|
|
24361
|
+
lineStroke: KritzelColorHelper.resolveThemeColor(line.stroke),
|
|
24362
|
+
lineStrokeWidth: line.strokeWidth / line.scale,
|
|
24363
|
+
arrowOffset: endpoint === 'start'
|
|
24364
|
+
? (line.hasStartArrow ? line.getArrowSize('start') / line.scale : undefined)
|
|
24365
|
+
: (line.hasEndArrow ? line.getArrowSize('end') / line.scale : undefined),
|
|
24366
|
+
arrowStyle: endpoint === 'start' ? line.arrows?.start?.style : line.arrows?.end?.style,
|
|
24367
|
+
arrowFill: endpoint === 'start' ? line.getArrowFill('start') : line.getArrowFill('end'),
|
|
24368
|
+
};
|
|
24369
|
+
}
|
|
24252
24370
|
}
|
|
24253
24371
|
|
|
24254
24372
|
/**
|
|
@@ -27890,6 +28008,14 @@ class KritzelAnchorManager {
|
|
|
27890
28008
|
}
|
|
27891
28009
|
this.snapEndpointToObject(line, entry.endpoint, objectId);
|
|
27892
28010
|
}
|
|
28011
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
28012
|
+
if (!selectionGroup || !this._core.store.state.isDragging) {
|
|
28013
|
+
return;
|
|
28014
|
+
}
|
|
28015
|
+
if (selectionGroup.hasExternalAnchoredLineConnections()) {
|
|
28016
|
+
// Keep the local drag overlay aligned with reshaped anchored lines without spamming Yjs.
|
|
28017
|
+
selectionGroup.refreshObjectDimensions(undefined, true);
|
|
28018
|
+
}
|
|
27893
28019
|
}
|
|
27894
28020
|
/**
|
|
27895
28021
|
* Snaps a line endpoint to an object's center position.
|
|
@@ -27956,8 +28082,25 @@ class KritzelAnchorManager {
|
|
|
27956
28082
|
* @param worldY - Y coordinate in world space
|
|
27957
28083
|
* @param excludeLineId - ID of the line being edited (to exclude from snap targets)
|
|
27958
28084
|
* @param otherEndpointAnchorId - ID of object anchored to the other endpoint (to prevent same-object anchoring)
|
|
28085
|
+
* @param pointerType - The pointer type driving the drag interaction.
|
|
28086
|
+
*/
|
|
28087
|
+
findSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId, pointerType = 'mouse') {
|
|
28088
|
+
return this.findBestSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId, object => {
|
|
28089
|
+
const snapRadius = this.getSnapRadius(object, pointerType);
|
|
28090
|
+
const dx = worldX - object.centerX;
|
|
28091
|
+
const dy = worldY - object.centerY;
|
|
28092
|
+
const distanceToCenter = Math.sqrt(dx * dx + dy * dy);
|
|
28093
|
+
return distanceToCenter <= snapRadius;
|
|
28094
|
+
});
|
|
28095
|
+
}
|
|
28096
|
+
/**
|
|
28097
|
+
* Finds the topmost anchorable object currently containing the dragged endpoint.
|
|
28098
|
+
* Used to drive preview UI before an actual snap engages.
|
|
27959
28099
|
*/
|
|
27960
|
-
|
|
28100
|
+
findSnapPreviewTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId) {
|
|
28101
|
+
return this.findBestSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId, () => true);
|
|
28102
|
+
}
|
|
28103
|
+
findBestSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId, predicate) {
|
|
27961
28104
|
let bestTarget = null;
|
|
27962
28105
|
let highestZIndex = -Infinity;
|
|
27963
28106
|
const objects = this._core.store.allNonSelectionObjects;
|
|
@@ -27977,28 +28120,30 @@ class KritzelAnchorManager {
|
|
|
27977
28120
|
// Check if point is inside the object's rotated polygon
|
|
27978
28121
|
const polygon = object.rotatedPolygon;
|
|
27979
28122
|
const points = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
|
|
27980
|
-
if (KritzelGeometryHelper.isPointInPolygon({ x: worldX, y: worldY }, points)) {
|
|
27981
|
-
|
|
27982
|
-
|
|
27983
|
-
|
|
27984
|
-
|
|
27985
|
-
|
|
27986
|
-
|
|
27987
|
-
|
|
27988
|
-
|
|
27989
|
-
|
|
27990
|
-
|
|
27991
|
-
|
|
27992
|
-
|
|
27993
|
-
objectId: object.id,
|
|
27994
|
-
centerX: object.centerX,
|
|
27995
|
-
centerY: object.centerY,
|
|
27996
|
-
};
|
|
27997
|
-
}
|
|
28123
|
+
if (!KritzelGeometryHelper.isPointInPolygon({ x: worldX, y: worldY }, points)) {
|
|
28124
|
+
continue;
|
|
28125
|
+
}
|
|
28126
|
+
if (!predicate(object)) {
|
|
28127
|
+
continue;
|
|
28128
|
+
}
|
|
28129
|
+
if (object.zIndex > highestZIndex) {
|
|
28130
|
+
highestZIndex = object.zIndex;
|
|
28131
|
+
bestTarget = {
|
|
28132
|
+
objectId: object.id,
|
|
28133
|
+
centerX: object.centerX,
|
|
28134
|
+
centerY: object.centerY,
|
|
28135
|
+
};
|
|
27998
28136
|
}
|
|
27999
28137
|
}
|
|
28000
28138
|
return bestTarget;
|
|
28001
28139
|
}
|
|
28140
|
+
getSnapRadius(object, pointerType) {
|
|
28141
|
+
const baseRadius = Math.min(object.width, object.height) / 5;
|
|
28142
|
+
if (pointerType === 'touch' || pointerType === 'pen') {
|
|
28143
|
+
return Math.max(baseRadius * 1.75, 24 / this.getSafeScale(this._core.store.state.scale));
|
|
28144
|
+
}
|
|
28145
|
+
return baseRadius;
|
|
28146
|
+
}
|
|
28002
28147
|
/**
|
|
28003
28148
|
* Sets the current snap candidate for visual feedback during line endpoint dragging.
|
|
28004
28149
|
* Updates the store state and triggers a rerender to show the snap indicator.
|
|
@@ -28009,6 +28154,17 @@ class KritzelAnchorManager {
|
|
|
28009
28154
|
this._core.store.state.snapCandidate = candidate;
|
|
28010
28155
|
this._core.rerender();
|
|
28011
28156
|
}
|
|
28157
|
+
/**
|
|
28158
|
+
* Sets the current snap preview candidate for early visual feedback during line dragging.
|
|
28159
|
+
* Preview state must remain separate from active snap state so line clipping and anchor
|
|
28160
|
+
* commit semantics only react to actual snaps.
|
|
28161
|
+
*
|
|
28162
|
+
* @param candidate - The preview candidate object, or null to clear.
|
|
28163
|
+
*/
|
|
28164
|
+
setSnapPreviewCandidate(candidate) {
|
|
28165
|
+
this._core.store.state.snapPreviewCandidate = candidate;
|
|
28166
|
+
this._core.rerender();
|
|
28167
|
+
}
|
|
28012
28168
|
/**
|
|
28013
28169
|
* Gets the current snap candidate from the store state.
|
|
28014
28170
|
*
|
|
@@ -28017,6 +28173,14 @@ class KritzelAnchorManager {
|
|
|
28017
28173
|
getSnapCandidate() {
|
|
28018
28174
|
return this._core.store.state.snapCandidate ?? null;
|
|
28019
28175
|
}
|
|
28176
|
+
/**
|
|
28177
|
+
* Gets the current snap preview candidate from the store state.
|
|
28178
|
+
*
|
|
28179
|
+
* @returns The current preview candidate if one exists, null otherwise.
|
|
28180
|
+
*/
|
|
28181
|
+
getSnapPreviewCandidate() {
|
|
28182
|
+
return this._core.store.state.snapPreviewCandidate ?? null;
|
|
28183
|
+
}
|
|
28020
28184
|
/**
|
|
28021
28185
|
* Clears the snap candidate from the store state and triggers a rerender.
|
|
28022
28186
|
* Should be called when snapping is cancelled or completed.
|
|
@@ -28025,6 +28189,13 @@ class KritzelAnchorManager {
|
|
|
28025
28189
|
this._core.store.state.snapCandidate = null;
|
|
28026
28190
|
this._core.rerender();
|
|
28027
28191
|
}
|
|
28192
|
+
/**
|
|
28193
|
+
* Clears the snap preview candidate from the store state and triggers a rerender.
|
|
28194
|
+
*/
|
|
28195
|
+
clearSnapPreviewCandidate() {
|
|
28196
|
+
this._core.store.state.snapPreviewCandidate = null;
|
|
28197
|
+
this._core.rerender();
|
|
28198
|
+
}
|
|
28028
28199
|
// ============================================
|
|
28029
28200
|
// Render Data
|
|
28030
28201
|
// ============================================
|
|
@@ -28078,33 +28249,43 @@ class KritzelAnchorManager {
|
|
|
28078
28249
|
* or null if there's no active snap candidate.
|
|
28079
28250
|
*/
|
|
28080
28251
|
getSnapIndicatorRenderData() {
|
|
28081
|
-
|
|
28082
|
-
|
|
28252
|
+
return this.getIndicatorRenderDataForCandidate(this.getSnapCandidate());
|
|
28253
|
+
}
|
|
28254
|
+
/**
|
|
28255
|
+
* Gets render data for snap preview visualization.
|
|
28256
|
+
*
|
|
28257
|
+
* @returns SnapIndicatorRenderData for the active preview candidate, or null if none exists.
|
|
28258
|
+
*/
|
|
28259
|
+
getSnapPreviewRenderData() {
|
|
28260
|
+
return this.getIndicatorRenderDataForCandidate(this.getSnapPreviewCandidate());
|
|
28261
|
+
}
|
|
28262
|
+
getIndicatorRenderDataForCandidate(candidate) {
|
|
28263
|
+
if (!candidate)
|
|
28083
28264
|
return null;
|
|
28084
|
-
if (!this.areFiniteNumbers(
|
|
28265
|
+
if (!this.areFiniteNumbers(candidate.centerX, candidate.centerY, candidate.lineEndpointX, candidate.lineEndpointY)) {
|
|
28085
28266
|
return null;
|
|
28086
28267
|
}
|
|
28087
28268
|
const scale = this.getSafeScale(this._core.store.state.scale);
|
|
28088
28269
|
const indicatorRadius = 8 / scale;
|
|
28089
28270
|
const indicatorStrokeWidth = `${2 / scale}`;
|
|
28090
|
-
const lineStrokeWidthNum = this.areFiniteNumbers(
|
|
28091
|
-
?
|
|
28271
|
+
const lineStrokeWidthNum = this.areFiniteNumbers(candidate.lineStrokeWidth) && candidate.lineStrokeWidth > 0
|
|
28272
|
+
? candidate.lineStrokeWidth
|
|
28092
28273
|
: (4 / scale);
|
|
28093
28274
|
const lineStrokeWidth = `${lineStrokeWidthNum}`;
|
|
28094
28275
|
const dashLength = Math.max(lineStrokeWidthNum * 2, 4 / scale);
|
|
28095
28276
|
const dashArray = `${dashLength} ${dashLength}`;
|
|
28096
|
-
const lineStroke =
|
|
28097
|
-
let edgeX = this.areFiniteNumbers(
|
|
28098
|
-
let edgeY = this.areFiniteNumbers(
|
|
28277
|
+
const lineStroke = candidate.lineStroke || '#000000';
|
|
28278
|
+
let edgeX = this.areFiniteNumbers(candidate.edgeX) ? candidate.edgeX : undefined;
|
|
28279
|
+
let edgeY = this.areFiniteNumbers(candidate.edgeY) ? candidate.edgeY : undefined;
|
|
28099
28280
|
let solidLineEndX = edgeX;
|
|
28100
28281
|
let solidLineEndY = edgeY;
|
|
28101
28282
|
let arrowPoints;
|
|
28102
|
-
const arrowOffset = this.areFiniteNumbers(
|
|
28103
|
-
?
|
|
28283
|
+
const arrowOffset = this.areFiniteNumbers(candidate.arrowOffset) && candidate.arrowOffset > 0
|
|
28284
|
+
? candidate.arrowOffset
|
|
28104
28285
|
: undefined;
|
|
28105
28286
|
if (arrowOffset !== undefined && edgeX !== undefined && edgeY !== undefined) {
|
|
28106
|
-
const dx =
|
|
28107
|
-
const dy =
|
|
28287
|
+
const dx = candidate.lineEndpointX - edgeX;
|
|
28288
|
+
const dy = candidate.lineEndpointY - edgeY;
|
|
28108
28289
|
const length = Math.sqrt(dx * dx + dy * dy);
|
|
28109
28290
|
if (length > arrowOffset) {
|
|
28110
28291
|
solidLineEndX = edgeX + (dx / length) * arrowOffset;
|
|
@@ -28112,8 +28293,8 @@ class KritzelAnchorManager {
|
|
|
28112
28293
|
}
|
|
28113
28294
|
// Calculate arrow head points
|
|
28114
28295
|
// Direction from line endpoint to edge (arrow direction)
|
|
28115
|
-
const arrowDx = edgeX -
|
|
28116
|
-
const arrowDy = edgeY -
|
|
28296
|
+
const arrowDx = edgeX - candidate.lineEndpointX;
|
|
28297
|
+
const arrowDy = edgeY - candidate.lineEndpointY;
|
|
28117
28298
|
const arrowLengthTotal = Math.sqrt(arrowDx * arrowDx + arrowDy * arrowDy);
|
|
28118
28299
|
if (arrowLengthTotal > 0) {
|
|
28119
28300
|
const ux = arrowDx / arrowLengthTotal;
|
|
@@ -28144,15 +28325,15 @@ class KritzelAnchorManager {
|
|
|
28144
28325
|
return null;
|
|
28145
28326
|
}
|
|
28146
28327
|
const snapLinePath = (() => {
|
|
28147
|
-
if (
|
|
28148
|
-
|
|
28149
|
-
|
|
28150
|
-
this.areFiniteNumbers(
|
|
28151
|
-
const startT =
|
|
28328
|
+
if (candidate.controlX !== undefined &&
|
|
28329
|
+
candidate.controlY !== undefined &&
|
|
28330
|
+
candidate.t !== undefined &&
|
|
28331
|
+
this.areFiniteNumbers(candidate.controlX, candidate.controlY, candidate.t)) {
|
|
28332
|
+
const startT = candidate.endpoint === 'start' ? 1 - candidate.t : candidate.t;
|
|
28152
28333
|
// Ensure meaningful range
|
|
28153
28334
|
if (startT >= 1)
|
|
28154
28335
|
return undefined;
|
|
28155
|
-
const segment = this.extractQuadraticSegment({ x:
|
|
28336
|
+
const segment = this.extractQuadraticSegment({ x: candidate.lineEndpointX, y: candidate.lineEndpointY }, { x: candidate.controlX, y: candidate.controlY }, { x: candidate.centerX, y: candidate.centerY }, startT, 1);
|
|
28156
28337
|
if (!this.areFiniteNumbers(segment.start.x, segment.start.y, segment.control.x, segment.control.y, segment.end.x, segment.end.y)) {
|
|
28157
28338
|
return undefined;
|
|
28158
28339
|
}
|
|
@@ -28166,15 +28347,15 @@ class KritzelAnchorManager {
|
|
|
28166
28347
|
lineStrokeWidth,
|
|
28167
28348
|
dashArray,
|
|
28168
28349
|
lineStroke,
|
|
28169
|
-
centerX:
|
|
28170
|
-
centerY:
|
|
28171
|
-
lineEndpointX:
|
|
28172
|
-
lineEndpointY:
|
|
28350
|
+
centerX: candidate.centerX,
|
|
28351
|
+
centerY: candidate.centerY,
|
|
28352
|
+
lineEndpointX: candidate.lineEndpointX,
|
|
28353
|
+
lineEndpointY: candidate.lineEndpointY,
|
|
28173
28354
|
edgeX,
|
|
28174
28355
|
edgeY,
|
|
28175
28356
|
arrowOffset,
|
|
28176
|
-
arrowStyle:
|
|
28177
|
-
arrowFill:
|
|
28357
|
+
arrowStyle: candidate.arrowStyle,
|
|
28358
|
+
arrowFill: candidate.arrowFill,
|
|
28178
28359
|
solidLineEndX,
|
|
28179
28360
|
solidLineEndY,
|
|
28180
28361
|
arrowPoints,
|