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
|
@@ -20704,6 +20704,8 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
20704
20704
|
_lastChildPersistTime = 0;
|
|
20705
20705
|
/** Minimum interval (ms) between child Yjs persists during drag */
|
|
20706
20706
|
static CHILD_PERSIST_THROTTLE_MS = 100;
|
|
20707
|
+
/** Ignore tiny floating-point diffs when comparing refreshed bounds */
|
|
20708
|
+
static DIMENSION_EPSILON = 0.001;
|
|
20707
20709
|
/**
|
|
20708
20710
|
* Gets the array of object IDs contained in this selection group.
|
|
20709
20711
|
* @returns Array of string IDs for the selected objects
|
|
@@ -21077,6 +21079,30 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21077
21079
|
}
|
|
21078
21080
|
});
|
|
21079
21081
|
}
|
|
21082
|
+
/**
|
|
21083
|
+
* Returns true when the selection contains a line anchored to an object
|
|
21084
|
+
* that is not part of this selection group.
|
|
21085
|
+
*/
|
|
21086
|
+
hasExternalAnchoredLineConnections(cachedObjects) {
|
|
21087
|
+
const children = cachedObjects ?? this.objects;
|
|
21088
|
+
if (children.length === 0) {
|
|
21089
|
+
return false;
|
|
21090
|
+
}
|
|
21091
|
+
const selectedIds = new Set(children.map(child => child.id));
|
|
21092
|
+
for (const child of children) {
|
|
21093
|
+
if (!KritzelClassHelper.isInstanceOf(child, 'KritzelLine')) {
|
|
21094
|
+
continue;
|
|
21095
|
+
}
|
|
21096
|
+
const line = child;
|
|
21097
|
+
if (line.startAnchor && !selectedIds.has(line.startAnchor.objectId)) {
|
|
21098
|
+
return true;
|
|
21099
|
+
}
|
|
21100
|
+
if (line.endAnchor && !selectedIds.has(line.endAnchor.objectId)) {
|
|
21101
|
+
return true;
|
|
21102
|
+
}
|
|
21103
|
+
}
|
|
21104
|
+
return false;
|
|
21105
|
+
}
|
|
21080
21106
|
/**
|
|
21081
21107
|
* Resizes the selection group and scales all contained objects proportionally.
|
|
21082
21108
|
* Uses snapshot values to avoid compounding errors during continuous drag operations.
|
|
@@ -21169,8 +21195,13 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21169
21195
|
*/
|
|
21170
21196
|
rotate(value) {
|
|
21171
21197
|
this.rotation = value;
|
|
21172
|
-
|
|
21173
|
-
|
|
21198
|
+
// Use the snapshot center as the rotation pivot rather than the live center.
|
|
21199
|
+
// refreshObjectDimensions() below can grow the box when an anchored line is pinned
|
|
21200
|
+
// to an external object; keeping the pivot fixed prevents the children from drifting.
|
|
21201
|
+
const snapshotTotalWidth = this.snapshotWidth + this.padding * 2;
|
|
21202
|
+
const snapshotTotalHeight = this.snapshotHeight + this.padding * 2;
|
|
21203
|
+
const centerX = this.snapshotTranslateX + snapshotTotalWidth / 2 / this.scale;
|
|
21204
|
+
const centerY = this.snapshotTranslateY + snapshotTotalHeight / 2 / this.scale;
|
|
21174
21205
|
const angle = value - this.snapshotRotation;
|
|
21175
21206
|
const cos = Math.cos(angle);
|
|
21176
21207
|
const sin = Math.sin(angle);
|
|
@@ -21192,6 +21223,31 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21192
21223
|
child.translateY = centerY + rotatedY - child.totalHeight / 2 / child.scale;
|
|
21193
21224
|
child.rotate(childCount === 1 ? value : unchangedSnapshot.rotation + angle);
|
|
21194
21225
|
}
|
|
21226
|
+
// Re-sync any anchored lines so their stored geometry follows the rotated objects
|
|
21227
|
+
// (including lines anchored to an object outside this selection), then grow the
|
|
21228
|
+
// selection box to enclose the reshaped lines. Rotation sets state.isRotating
|
|
21229
|
+
// rather than state.isDragging, so the refresh must be triggered explicitly here.
|
|
21230
|
+
const anchorTargetIds = new Set();
|
|
21231
|
+
for (const child of children) {
|
|
21232
|
+
anchorTargetIds.add(child.id);
|
|
21233
|
+
if (KritzelClassHelper.isInstanceOf(child, 'KritzelLine')) {
|
|
21234
|
+
const line = child;
|
|
21235
|
+
if (line.startAnchor)
|
|
21236
|
+
anchorTargetIds.add(line.startAnchor.objectId);
|
|
21237
|
+
if (line.endAnchor)
|
|
21238
|
+
anchorTargetIds.add(line.endAnchor.objectId);
|
|
21239
|
+
}
|
|
21240
|
+
}
|
|
21241
|
+
let hasAnchoredLines = false;
|
|
21242
|
+
for (const targetId of anchorTargetIds) {
|
|
21243
|
+
if (this._core.anchorManager.getLinesAnchoredTo(targetId).length > 0) {
|
|
21244
|
+
hasAnchoredLines = true;
|
|
21245
|
+
this._core.anchorManager.updateAnchorsForObject(targetId);
|
|
21246
|
+
}
|
|
21247
|
+
}
|
|
21248
|
+
if (hasAnchoredLines) {
|
|
21249
|
+
this.refreshObjectDimensions(children);
|
|
21250
|
+
}
|
|
21195
21251
|
});
|
|
21196
21252
|
}
|
|
21197
21253
|
/**
|
|
@@ -21221,6 +21277,16 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21221
21277
|
* @param skipPersist If true, only updates local state without persisting to Yjs (used during undo/redo)
|
|
21222
21278
|
*/
|
|
21223
21279
|
refreshObjectDimensions(cachedObjects, skipPersist = false) {
|
|
21280
|
+
const previous = {
|
|
21281
|
+
minX: this.minX,
|
|
21282
|
+
minY: this.minY,
|
|
21283
|
+
maxX: this.maxX,
|
|
21284
|
+
maxY: this.maxY,
|
|
21285
|
+
width: this.width,
|
|
21286
|
+
height: this.height,
|
|
21287
|
+
translateX: this.translateX,
|
|
21288
|
+
translateY: this.translateY,
|
|
21289
|
+
};
|
|
21224
21290
|
const children = cachedObjects ?? this.objects;
|
|
21225
21291
|
if (children.length === 1) {
|
|
21226
21292
|
const obj = children[0];
|
|
@@ -21274,9 +21340,18 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21274
21340
|
this.translateX = cx - (this.width / this.scale + 2 * this.padding) / 2;
|
|
21275
21341
|
this.translateY = cy - (this.height / this.scale + 2 * this.padding) / 2;
|
|
21276
21342
|
}
|
|
21277
|
-
|
|
21343
|
+
const hasChanged = Math.abs(this.minX - previous.minX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21344
|
+
Math.abs(this.minY - previous.minY) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21345
|
+
Math.abs(this.maxX - previous.maxX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21346
|
+
Math.abs(this.maxY - previous.maxY) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21347
|
+
Math.abs(this.width - previous.width) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21348
|
+
Math.abs(this.height - previous.height) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21349
|
+
Math.abs(this.translateX - previous.translateX) > KritzelSelectionGroup.DIMENSION_EPSILON ||
|
|
21350
|
+
Math.abs(this.translateY - previous.translateY) > KritzelSelectionGroup.DIMENSION_EPSILON;
|
|
21351
|
+
if (!skipPersist && hasChanged) {
|
|
21278
21352
|
this._core.store.objects.update(this);
|
|
21279
21353
|
}
|
|
21354
|
+
return hasChanged;
|
|
21280
21355
|
}
|
|
21281
21356
|
/**
|
|
21282
21357
|
* Calculates the horizontal offset from the group's center to an object's center using snapshot data.
|
|
@@ -21286,7 +21361,7 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21286
21361
|
*/
|
|
21287
21362
|
getOffsetXToCenterFromSnapshot(snapshot) {
|
|
21288
21363
|
const objCenterX = snapshot.translateX + snapshot.totalWidth / snapshot.scale / 2;
|
|
21289
|
-
const groupCenterX = this.
|
|
21364
|
+
const groupCenterX = this.snapshotTranslateX + (this.snapshotWidth + this.padding * 2) / this.scale / 2;
|
|
21290
21365
|
return objCenterX - groupCenterX;
|
|
21291
21366
|
}
|
|
21292
21367
|
/**
|
|
@@ -21297,7 +21372,7 @@ class KritzelSelectionGroup extends KritzelBaseObject {
|
|
|
21297
21372
|
*/
|
|
21298
21373
|
getOffsetYToCenterFromSnapshot(snapshot) {
|
|
21299
21374
|
const objCenterY = snapshot.translateY + snapshot.totalHeight / snapshot.scale / 2;
|
|
21300
|
-
const groupCenterY = this.
|
|
21375
|
+
const groupCenterY = this.snapshotTranslateY + (this.snapshotHeight + this.padding * 2) / this.scale / 2;
|
|
21301
21376
|
return objCenterY - groupCenterY;
|
|
21302
21377
|
}
|
|
21303
21378
|
/**
|
|
@@ -22670,6 +22745,7 @@ class KritzelMoveHandler extends KritzelBaseHandler {
|
|
|
22670
22745
|
this._core.store.state.isDragging = false;
|
|
22671
22746
|
if (this.hasMoved) {
|
|
22672
22747
|
this._core.store.selectionGroup.persistChildren();
|
|
22748
|
+
this._core.store.selectionGroup.refreshObjectDimensions(undefined, false);
|
|
22673
22749
|
this._core.store.selectionGroup.update();
|
|
22674
22750
|
this._core.engine.emitObjectsChange();
|
|
22675
22751
|
this._core.store.state.hasObjectsChanged = true;
|
|
@@ -22682,6 +22758,7 @@ class KritzelMoveHandler extends KritzelBaseHandler {
|
|
|
22682
22758
|
this._core.store.state.isDragging = false;
|
|
22683
22759
|
if (this.hasMoved) {
|
|
22684
22760
|
this._core.store.selectionGroup.persistChildren();
|
|
22761
|
+
this._core.store.selectionGroup.refreshObjectDimensions(undefined, false);
|
|
22685
22762
|
this._core.store.selectionGroup.update();
|
|
22686
22763
|
this._core.engine.emitObjectsChange();
|
|
22687
22764
|
this._core.store.state.hasObjectsChanged = true;
|
|
@@ -22794,6 +22871,22 @@ class KritzelResizeHandler extends KritzelBaseHandler {
|
|
|
22794
22871
|
this.newSize = { x: 0, y: 0, width: 0, height: 0 };
|
|
22795
22872
|
this.hasResized = false;
|
|
22796
22873
|
}
|
|
22874
|
+
/**
|
|
22875
|
+
* Rebase pointer and size baselines to the latest live selection bounds.
|
|
22876
|
+
* This prevents drift when anchored lines to external objects expand/shrink
|
|
22877
|
+
* the selection box while a resize gesture is in progress.
|
|
22878
|
+
*/
|
|
22879
|
+
rebaseResizeBaseline(selectionGroup, clientX, clientY) {
|
|
22880
|
+
this.initialMouseX = clientX;
|
|
22881
|
+
this.initialMouseY = clientY;
|
|
22882
|
+
this.initialSize.width = selectionGroup.width;
|
|
22883
|
+
this.initialSize.height = selectionGroup.height;
|
|
22884
|
+
this.initialSize.x = selectionGroup.translateX;
|
|
22885
|
+
this.initialSize.y = selectionGroup.translateY;
|
|
22886
|
+
}
|
|
22887
|
+
shouldRebaseResizeBaseline(selectionGroup) {
|
|
22888
|
+
return selectionGroup.hasExternalAnchoredLineConnections?.() === true;
|
|
22889
|
+
}
|
|
22797
22890
|
/**
|
|
22798
22891
|
* Handles the pointer down event to initiate a resize operation.
|
|
22799
22892
|
* Captures the initial mouse position and selection group dimensions when
|
|
@@ -22921,6 +23014,9 @@ class KritzelResizeHandler extends KritzelBaseHandler {
|
|
|
22921
23014
|
this.newSize.x = newCenterX - this.newSize.width / objectScale / 2;
|
|
22922
23015
|
this.newSize.y = newCenterY - this.newSize.height / objectScale / 2;
|
|
22923
23016
|
selectionGroup.resize(this.newSize.x, this.newSize.y, this.newSize.width, this.newSize.height);
|
|
23017
|
+
if (this.shouldRebaseResizeBaseline(selectionGroup)) {
|
|
23018
|
+
this.rebaseResizeBaseline(selectionGroup, clientX, clientY);
|
|
23019
|
+
}
|
|
22924
23020
|
}
|
|
22925
23021
|
}
|
|
22926
23022
|
if (event.pointerType === 'touch' || event.pointerType === 'pen') {
|
|
@@ -22987,6 +23083,9 @@ class KritzelResizeHandler extends KritzelBaseHandler {
|
|
|
22987
23083
|
this.newSize.x = newCenterX - this.newSize.width / objectScale / 2;
|
|
22988
23084
|
this.newSize.y = newCenterY - this.newSize.height / objectScale / 2;
|
|
22989
23085
|
selectionGroup.resize(this.newSize.x, this.newSize.y, this.newSize.width, this.newSize.height);
|
|
23086
|
+
if (this.shouldRebaseResizeBaseline(selectionGroup)) {
|
|
23087
|
+
this.rebaseResizeBaseline(selectionGroup, clientX, clientY);
|
|
23088
|
+
}
|
|
22990
23089
|
}
|
|
22991
23090
|
}
|
|
22992
23091
|
}
|
|
@@ -23074,6 +23173,10 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23074
23173
|
rotation = 0;
|
|
23075
23174
|
/** The rotation value of the selection group before the current rotation operation began. */
|
|
23076
23175
|
initialSelectionGroupRotation = 0;
|
|
23176
|
+
/** The X coordinate of the rotation pivot (group center) captured when rotation starts, in world space. */
|
|
23177
|
+
initialCenterX = 0;
|
|
23178
|
+
/** The Y coordinate of the rotation pivot (group center) captured when rotation starts, in world space. */
|
|
23179
|
+
initialCenterY = 0;
|
|
23077
23180
|
/**
|
|
23078
23181
|
* Creates an instance of KritzelRotationHandler.
|
|
23079
23182
|
* @param core - The KritzelCore instance that provides access to the store, engine, and other core functionalities.
|
|
@@ -23088,6 +23191,8 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23088
23191
|
reset() {
|
|
23089
23192
|
this.initialRotation = 0;
|
|
23090
23193
|
this.rotation = 0;
|
|
23194
|
+
this.initialCenterX = 0;
|
|
23195
|
+
this.initialCenterY = 0;
|
|
23091
23196
|
}
|
|
23092
23197
|
/**
|
|
23093
23198
|
* Handles the pointer down event to initiate a rotation operation.
|
|
@@ -23107,6 +23212,10 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23107
23212
|
const objectScale = selectionGroup.scale || 1;
|
|
23108
23213
|
const centerX = selectionGroup.translateX + selectionGroup.width / 2 / objectScale;
|
|
23109
23214
|
const centerY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
23215
|
+
// Capture the pivot once so it stays fixed even if the bounding box grows
|
|
23216
|
+
// mid-rotation (e.g. a line anchored to an object outside the selection).
|
|
23217
|
+
this.initialCenterX = centerX;
|
|
23218
|
+
this.initialCenterY = centerY;
|
|
23110
23219
|
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
23111
23220
|
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
23112
23221
|
this.initialSelectionGroupRotation = selectionGroup.rotation;
|
|
@@ -23130,6 +23239,10 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23130
23239
|
const objectScale = selectionGroup.scale || 1;
|
|
23131
23240
|
const centerX = selectionGroup.translateX + selectionGroup.width / 2 / objectScale;
|
|
23132
23241
|
const centerY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
23242
|
+
// Capture the pivot once so it stays fixed even if the bounding box grows
|
|
23243
|
+
// mid-rotation (e.g. a line anchored to an object outside the selection).
|
|
23244
|
+
this.initialCenterX = centerX;
|
|
23245
|
+
this.initialCenterY = centerY;
|
|
23133
23246
|
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
23134
23247
|
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
23135
23248
|
this.initialSelectionGroupRotation = selectionGroup.rotation;
|
|
@@ -23154,9 +23267,8 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23154
23267
|
if (this._core.store.state.isRotating && selectionGroup) {
|
|
23155
23268
|
const clientX = event.clientX - this._core.store.offsetX;
|
|
23156
23269
|
const clientY = event.clientY - this._core.store.offsetY;
|
|
23157
|
-
const
|
|
23158
|
-
const
|
|
23159
|
-
const groupCenterY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
23270
|
+
const groupCenterX = this.initialCenterX;
|
|
23271
|
+
const groupCenterY = this.initialCenterY;
|
|
23160
23272
|
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
23161
23273
|
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
23162
23274
|
const currentRotation = Math.atan2(groupCenterY - cursorY, groupCenterX - cursorX);
|
|
@@ -23174,9 +23286,8 @@ class KritzelRotationHandler extends KritzelBaseHandler {
|
|
|
23174
23286
|
if (this._core.store.state.isRotating && selectionGroup) {
|
|
23175
23287
|
const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
|
|
23176
23288
|
const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
|
|
23177
|
-
const
|
|
23178
|
-
const
|
|
23179
|
-
const groupCenterY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
|
|
23289
|
+
const groupCenterX = this.initialCenterX;
|
|
23290
|
+
const groupCenterY = this.initialCenterY;
|
|
23180
23291
|
const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
|
|
23181
23292
|
const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
|
|
23182
23293
|
const currentRotation = Math.atan2(groupCenterY - cursorY, groupCenterX - cursorX);
|
|
@@ -23855,6 +23966,7 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
23855
23966
|
this.hasMoved = false;
|
|
23856
23967
|
this.currentSnapTarget = null;
|
|
23857
23968
|
this._core.anchorManager.clearSnapCandidate();
|
|
23969
|
+
this._core.anchorManager.clearSnapPreviewCandidate();
|
|
23858
23970
|
}
|
|
23859
23971
|
/**
|
|
23860
23972
|
* Handles pointer down events to initiate line handle dragging.
|
|
@@ -23957,15 +24069,19 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
23957
24069
|
const worldY = (clientY - this._core.store.state.translateY) / viewportScale;
|
|
23958
24070
|
// Get other endpoint's anchor to prevent anchoring both to same object
|
|
23959
24071
|
const otherAnchorId = line.endAnchor?.objectId;
|
|
24072
|
+
const previewTarget = this._core.anchorManager.findSnapPreviewTarget(worldX, worldY, line.id, otherAnchorId);
|
|
23960
24073
|
// Check for snap target
|
|
23961
|
-
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId);
|
|
24074
|
+
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId, event.pointerType);
|
|
23962
24075
|
this.currentSnapTarget = snapTarget;
|
|
24076
|
+
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialEndX, this.initialEndY);
|
|
24077
|
+
let controlWorld = undefined;
|
|
24078
|
+
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
24079
|
+
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
24080
|
+
}
|
|
23963
24081
|
if (snapTarget) {
|
|
23964
24082
|
// Snap to target center - convert world coords to local
|
|
23965
24083
|
const localCoords = this.worldToLineLocal(line, snapTarget.centerX, snapTarget.centerY);
|
|
23966
24084
|
this.updateLineEndpoint(line, localCoords.x, localCoords.y, this.initialEndX, this.initialEndY);
|
|
23967
|
-
// Get the other endpoint's world position for edge intersection calculation
|
|
23968
|
-
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialEndX, this.initialEndY);
|
|
23969
24085
|
// Calculate edge intersection point using AnchorManager to support curves
|
|
23970
24086
|
const targetObject = this._core.store.allNonSelectionObjects.find(obj => obj.id === snapTarget.objectId);
|
|
23971
24087
|
let edgeX;
|
|
@@ -23979,30 +24095,16 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
23979
24095
|
t = clipInfo.t;
|
|
23980
24096
|
}
|
|
23981
24097
|
}
|
|
23982
|
-
// Calculate control point in world coordinates if it exists
|
|
23983
|
-
let controlWorld = undefined;
|
|
23984
|
-
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
23985
|
-
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
23986
|
-
}
|
|
23987
24098
|
// Update snap indicator
|
|
23988
|
-
this._core.anchorManager.
|
|
23989
|
-
|
|
23990
|
-
|
|
23991
|
-
|
|
23992
|
-
|
|
23993
|
-
|
|
23994
|
-
|
|
23995
|
-
|
|
23996
|
-
|
|
23997
|
-
t,
|
|
23998
|
-
edgeX,
|
|
23999
|
-
edgeY,
|
|
24000
|
-
lineStroke: KritzelColorHelper.resolveThemeColor(line.stroke),
|
|
24001
|
-
lineStrokeWidth: line.strokeWidth / line.scale,
|
|
24002
|
-
arrowOffset: line.hasStartArrow ? line.getArrowSize('start') / line.scale : undefined,
|
|
24003
|
-
arrowStyle: line.hasStartArrow ? line.arrows?.start?.style : undefined,
|
|
24004
|
-
arrowFill: line.hasStartArrow ? line.getArrowFill('start') : undefined,
|
|
24005
|
-
});
|
|
24099
|
+
this._core.anchorManager.setSnapPreviewCandidate(null);
|
|
24100
|
+
this._core.anchorManager.setSnapCandidate(this.createIndicatorCandidate(line, 'start', snapTarget, otherEndpointWorld, controlWorld, { edgeX, edgeY, t }));
|
|
24101
|
+
}
|
|
24102
|
+
else if (previewTarget) {
|
|
24103
|
+
const newStartX = this.initialStartX + localDx;
|
|
24104
|
+
const newStartY = this.initialStartY + localDy;
|
|
24105
|
+
this.updateLineEndpoint(line, newStartX, newStartY, this.initialEndX, this.initialEndY);
|
|
24106
|
+
this._core.anchorManager.clearSnapCandidate();
|
|
24107
|
+
this._core.anchorManager.setSnapPreviewCandidate(this.createIndicatorCandidate(line, 'start', previewTarget, otherEndpointWorld, controlWorld));
|
|
24006
24108
|
}
|
|
24007
24109
|
else {
|
|
24008
24110
|
// No snap - use regular position
|
|
@@ -24011,6 +24113,7 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
24011
24113
|
this.updateLineEndpoint(line, newStartX, newStartY, this.initialEndX, this.initialEndY);
|
|
24012
24114
|
// Clear snap indicator
|
|
24013
24115
|
this._core.anchorManager.clearSnapCandidate();
|
|
24116
|
+
this._core.anchorManager.clearSnapPreviewCandidate();
|
|
24014
24117
|
}
|
|
24015
24118
|
}
|
|
24016
24119
|
else if (handleType === 'end') {
|
|
@@ -24019,15 +24122,19 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
24019
24122
|
const worldY = (clientY - this._core.store.state.translateY) / viewportScale;
|
|
24020
24123
|
// Get other endpoint's anchor to prevent anchoring both to same object
|
|
24021
24124
|
const otherAnchorId = line.startAnchor?.objectId;
|
|
24125
|
+
const previewTarget = this._core.anchorManager.findSnapPreviewTarget(worldX, worldY, line.id, otherAnchorId);
|
|
24022
24126
|
// Check for snap target
|
|
24023
|
-
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId);
|
|
24127
|
+
const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId, event.pointerType);
|
|
24024
24128
|
this.currentSnapTarget = snapTarget;
|
|
24129
|
+
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialStartX, this.initialStartY);
|
|
24130
|
+
let controlWorld = undefined;
|
|
24131
|
+
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
24132
|
+
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
24133
|
+
}
|
|
24025
24134
|
if (snapTarget) {
|
|
24026
24135
|
// Snap to target center - convert world coords to local
|
|
24027
24136
|
const localCoords = this.worldToLineLocal(line, snapTarget.centerX, snapTarget.centerY);
|
|
24028
24137
|
this.updateLineEndpoint(line, this.initialStartX, this.initialStartY, localCoords.x, localCoords.y);
|
|
24029
|
-
// Get the other endpoint's world position for edge intersection calculation
|
|
24030
|
-
const otherEndpointWorld = this.lineLocalToWorld(line, this.initialStartX, this.initialStartY);
|
|
24031
24138
|
// Calculate edge intersection point using AnchorManager to support curves
|
|
24032
24139
|
const targetObject = this._core.store.allNonSelectionObjects.find(obj => obj.id === snapTarget.objectId);
|
|
24033
24140
|
let edgeX;
|
|
@@ -24041,30 +24148,16 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
24041
24148
|
t = clipInfo.t;
|
|
24042
24149
|
}
|
|
24043
24150
|
}
|
|
24044
|
-
// Calculate control point in world coordinates if it exists
|
|
24045
|
-
let controlWorld = undefined;
|
|
24046
|
-
if (line.controlX !== undefined && line.controlY !== undefined) {
|
|
24047
|
-
controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
|
|
24048
|
-
}
|
|
24049
24151
|
// Update snap indicator
|
|
24050
|
-
this._core.anchorManager.
|
|
24051
|
-
|
|
24052
|
-
|
|
24053
|
-
|
|
24054
|
-
|
|
24055
|
-
|
|
24056
|
-
|
|
24057
|
-
|
|
24058
|
-
|
|
24059
|
-
t,
|
|
24060
|
-
edgeX,
|
|
24061
|
-
edgeY,
|
|
24062
|
-
lineStroke: KritzelColorHelper.resolveThemeColor(line.stroke),
|
|
24063
|
-
lineStrokeWidth: line.strokeWidth / line.scale,
|
|
24064
|
-
arrowOffset: line.hasEndArrow ? line.getArrowSize('end') / line.scale : undefined,
|
|
24065
|
-
arrowStyle: line.hasEndArrow ? line.arrows?.end?.style : undefined,
|
|
24066
|
-
arrowFill: line.hasEndArrow ? line.getArrowFill('end') : undefined,
|
|
24067
|
-
});
|
|
24152
|
+
this._core.anchorManager.setSnapPreviewCandidate(null);
|
|
24153
|
+
this._core.anchorManager.setSnapCandidate(this.createIndicatorCandidate(line, 'end', snapTarget, otherEndpointWorld, controlWorld, { edgeX, edgeY, t }));
|
|
24154
|
+
}
|
|
24155
|
+
else if (previewTarget) {
|
|
24156
|
+
const newEndX = this.initialEndX + localDx;
|
|
24157
|
+
const newEndY = this.initialEndY + localDy;
|
|
24158
|
+
this.updateLineEndpoint(line, this.initialStartX, this.initialStartY, newEndX, newEndY);
|
|
24159
|
+
this._core.anchorManager.clearSnapCandidate();
|
|
24160
|
+
this._core.anchorManager.setSnapPreviewCandidate(this.createIndicatorCandidate(line, 'end', previewTarget, otherEndpointWorld, controlWorld));
|
|
24068
24161
|
}
|
|
24069
24162
|
else {
|
|
24070
24163
|
// No snap - use regular position
|
|
@@ -24073,9 +24166,12 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
24073
24166
|
this.updateLineEndpoint(line, this.initialStartX, this.initialStartY, newEndX, newEndY);
|
|
24074
24167
|
// Clear snap indicator
|
|
24075
24168
|
this._core.anchorManager.clearSnapCandidate();
|
|
24169
|
+
this._core.anchorManager.clearSnapPreviewCandidate();
|
|
24076
24170
|
}
|
|
24077
24171
|
}
|
|
24078
24172
|
else if (handleType === 'center') {
|
|
24173
|
+
this._core.anchorManager.clearSnapPreviewCandidate();
|
|
24174
|
+
this._core.anchorManager.clearSnapCandidate();
|
|
24079
24175
|
const startControlX = this.initialControlX ?? (this.initialStartX + this.initialEndX) / 2;
|
|
24080
24176
|
const startControlY = this.initialControlY ?? (this.initialStartY + this.initialEndY) / 2;
|
|
24081
24177
|
const newControlX = startControlX + localDx * 2;
|
|
@@ -24271,6 +24367,28 @@ class KritzelLineHandleHandler extends KritzelBaseHandler {
|
|
|
24271
24367
|
}
|
|
24272
24368
|
return null;
|
|
24273
24369
|
}
|
|
24370
|
+
createIndicatorCandidate(line, endpoint, target, otherEndpointWorld, controlWorld, clipInfo) {
|
|
24371
|
+
return {
|
|
24372
|
+
objectId: target.objectId,
|
|
24373
|
+
endpoint,
|
|
24374
|
+
centerX: target.centerX,
|
|
24375
|
+
centerY: target.centerY,
|
|
24376
|
+
lineEndpointX: otherEndpointWorld.x,
|
|
24377
|
+
lineEndpointY: otherEndpointWorld.y,
|
|
24378
|
+
controlX: controlWorld?.x,
|
|
24379
|
+
controlY: controlWorld?.y,
|
|
24380
|
+
t: clipInfo?.t,
|
|
24381
|
+
edgeX: clipInfo?.edgeX,
|
|
24382
|
+
edgeY: clipInfo?.edgeY,
|
|
24383
|
+
lineStroke: KritzelColorHelper.resolveThemeColor(line.stroke),
|
|
24384
|
+
lineStrokeWidth: line.strokeWidth / line.scale,
|
|
24385
|
+
arrowOffset: endpoint === 'start'
|
|
24386
|
+
? (line.hasStartArrow ? line.getArrowSize('start') / line.scale : undefined)
|
|
24387
|
+
: (line.hasEndArrow ? line.getArrowSize('end') / line.scale : undefined),
|
|
24388
|
+
arrowStyle: endpoint === 'start' ? line.arrows?.start?.style : line.arrows?.end?.style,
|
|
24389
|
+
arrowFill: endpoint === 'start' ? line.getArrowFill('start') : line.getArrowFill('end'),
|
|
24390
|
+
};
|
|
24391
|
+
}
|
|
24274
24392
|
}
|
|
24275
24393
|
|
|
24276
24394
|
/**
|
|
@@ -27912,6 +28030,14 @@ class KritzelAnchorManager {
|
|
|
27912
28030
|
}
|
|
27913
28031
|
this.snapEndpointToObject(line, entry.endpoint, objectId);
|
|
27914
28032
|
}
|
|
28033
|
+
const selectionGroup = this._core.store.selectionGroup;
|
|
28034
|
+
if (!selectionGroup || !this._core.store.state.isDragging) {
|
|
28035
|
+
return;
|
|
28036
|
+
}
|
|
28037
|
+
if (selectionGroup.hasExternalAnchoredLineConnections()) {
|
|
28038
|
+
// Keep the local drag overlay aligned with reshaped anchored lines without spamming Yjs.
|
|
28039
|
+
selectionGroup.refreshObjectDimensions(undefined, true);
|
|
28040
|
+
}
|
|
27915
28041
|
}
|
|
27916
28042
|
/**
|
|
27917
28043
|
* Snaps a line endpoint to an object's center position.
|
|
@@ -27978,8 +28104,25 @@ class KritzelAnchorManager {
|
|
|
27978
28104
|
* @param worldY - Y coordinate in world space
|
|
27979
28105
|
* @param excludeLineId - ID of the line being edited (to exclude from snap targets)
|
|
27980
28106
|
* @param otherEndpointAnchorId - ID of object anchored to the other endpoint (to prevent same-object anchoring)
|
|
28107
|
+
* @param pointerType - The pointer type driving the drag interaction.
|
|
28108
|
+
*/
|
|
28109
|
+
findSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId, pointerType = 'mouse') {
|
|
28110
|
+
return this.findBestSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId, object => {
|
|
28111
|
+
const snapRadius = this.getSnapRadius(object, pointerType);
|
|
28112
|
+
const dx = worldX - object.centerX;
|
|
28113
|
+
const dy = worldY - object.centerY;
|
|
28114
|
+
const distanceToCenter = Math.sqrt(dx * dx + dy * dy);
|
|
28115
|
+
return distanceToCenter <= snapRadius;
|
|
28116
|
+
});
|
|
28117
|
+
}
|
|
28118
|
+
/**
|
|
28119
|
+
* Finds the topmost anchorable object currently containing the dragged endpoint.
|
|
28120
|
+
* Used to drive preview UI before an actual snap engages.
|
|
27981
28121
|
*/
|
|
27982
|
-
|
|
28122
|
+
findSnapPreviewTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId) {
|
|
28123
|
+
return this.findBestSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId, () => true);
|
|
28124
|
+
}
|
|
28125
|
+
findBestSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId, predicate) {
|
|
27983
28126
|
let bestTarget = null;
|
|
27984
28127
|
let highestZIndex = -Infinity;
|
|
27985
28128
|
const objects = this._core.store.allNonSelectionObjects;
|
|
@@ -27999,28 +28142,30 @@ class KritzelAnchorManager {
|
|
|
27999
28142
|
// Check if point is inside the object's rotated polygon
|
|
28000
28143
|
const polygon = object.rotatedPolygon;
|
|
28001
28144
|
const points = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
|
|
28002
|
-
if (KritzelGeometryHelper.isPointInPolygon({ x: worldX, y: worldY }, points)) {
|
|
28003
|
-
|
|
28004
|
-
|
|
28005
|
-
|
|
28006
|
-
|
|
28007
|
-
|
|
28008
|
-
|
|
28009
|
-
|
|
28010
|
-
|
|
28011
|
-
|
|
28012
|
-
|
|
28013
|
-
|
|
28014
|
-
|
|
28015
|
-
objectId: object.id,
|
|
28016
|
-
centerX: object.centerX,
|
|
28017
|
-
centerY: object.centerY,
|
|
28018
|
-
};
|
|
28019
|
-
}
|
|
28145
|
+
if (!KritzelGeometryHelper.isPointInPolygon({ x: worldX, y: worldY }, points)) {
|
|
28146
|
+
continue;
|
|
28147
|
+
}
|
|
28148
|
+
if (!predicate(object)) {
|
|
28149
|
+
continue;
|
|
28150
|
+
}
|
|
28151
|
+
if (object.zIndex > highestZIndex) {
|
|
28152
|
+
highestZIndex = object.zIndex;
|
|
28153
|
+
bestTarget = {
|
|
28154
|
+
objectId: object.id,
|
|
28155
|
+
centerX: object.centerX,
|
|
28156
|
+
centerY: object.centerY,
|
|
28157
|
+
};
|
|
28020
28158
|
}
|
|
28021
28159
|
}
|
|
28022
28160
|
return bestTarget;
|
|
28023
28161
|
}
|
|
28162
|
+
getSnapRadius(object, pointerType) {
|
|
28163
|
+
const baseRadius = Math.min(object.width, object.height) / 5;
|
|
28164
|
+
if (pointerType === 'touch' || pointerType === 'pen') {
|
|
28165
|
+
return Math.max(baseRadius * 1.75, 24 / this.getSafeScale(this._core.store.state.scale));
|
|
28166
|
+
}
|
|
28167
|
+
return baseRadius;
|
|
28168
|
+
}
|
|
28024
28169
|
/**
|
|
28025
28170
|
* Sets the current snap candidate for visual feedback during line endpoint dragging.
|
|
28026
28171
|
* Updates the store state and triggers a rerender to show the snap indicator.
|
|
@@ -28031,6 +28176,17 @@ class KritzelAnchorManager {
|
|
|
28031
28176
|
this._core.store.state.snapCandidate = candidate;
|
|
28032
28177
|
this._core.rerender();
|
|
28033
28178
|
}
|
|
28179
|
+
/**
|
|
28180
|
+
* Sets the current snap preview candidate for early visual feedback during line dragging.
|
|
28181
|
+
* Preview state must remain separate from active snap state so line clipping and anchor
|
|
28182
|
+
* commit semantics only react to actual snaps.
|
|
28183
|
+
*
|
|
28184
|
+
* @param candidate - The preview candidate object, or null to clear.
|
|
28185
|
+
*/
|
|
28186
|
+
setSnapPreviewCandidate(candidate) {
|
|
28187
|
+
this._core.store.state.snapPreviewCandidate = candidate;
|
|
28188
|
+
this._core.rerender();
|
|
28189
|
+
}
|
|
28034
28190
|
/**
|
|
28035
28191
|
* Gets the current snap candidate from the store state.
|
|
28036
28192
|
*
|
|
@@ -28039,6 +28195,14 @@ class KritzelAnchorManager {
|
|
|
28039
28195
|
getSnapCandidate() {
|
|
28040
28196
|
return this._core.store.state.snapCandidate ?? null;
|
|
28041
28197
|
}
|
|
28198
|
+
/**
|
|
28199
|
+
* Gets the current snap preview candidate from the store state.
|
|
28200
|
+
*
|
|
28201
|
+
* @returns The current preview candidate if one exists, null otherwise.
|
|
28202
|
+
*/
|
|
28203
|
+
getSnapPreviewCandidate() {
|
|
28204
|
+
return this._core.store.state.snapPreviewCandidate ?? null;
|
|
28205
|
+
}
|
|
28042
28206
|
/**
|
|
28043
28207
|
* Clears the snap candidate from the store state and triggers a rerender.
|
|
28044
28208
|
* Should be called when snapping is cancelled or completed.
|
|
@@ -28047,6 +28211,13 @@ class KritzelAnchorManager {
|
|
|
28047
28211
|
this._core.store.state.snapCandidate = null;
|
|
28048
28212
|
this._core.rerender();
|
|
28049
28213
|
}
|
|
28214
|
+
/**
|
|
28215
|
+
* Clears the snap preview candidate from the store state and triggers a rerender.
|
|
28216
|
+
*/
|
|
28217
|
+
clearSnapPreviewCandidate() {
|
|
28218
|
+
this._core.store.state.snapPreviewCandidate = null;
|
|
28219
|
+
this._core.rerender();
|
|
28220
|
+
}
|
|
28050
28221
|
// ============================================
|
|
28051
28222
|
// Render Data
|
|
28052
28223
|
// ============================================
|
|
@@ -28100,33 +28271,43 @@ class KritzelAnchorManager {
|
|
|
28100
28271
|
* or null if there's no active snap candidate.
|
|
28101
28272
|
*/
|
|
28102
28273
|
getSnapIndicatorRenderData() {
|
|
28103
|
-
|
|
28104
|
-
|
|
28274
|
+
return this.getIndicatorRenderDataForCandidate(this.getSnapCandidate());
|
|
28275
|
+
}
|
|
28276
|
+
/**
|
|
28277
|
+
* Gets render data for snap preview visualization.
|
|
28278
|
+
*
|
|
28279
|
+
* @returns SnapIndicatorRenderData for the active preview candidate, or null if none exists.
|
|
28280
|
+
*/
|
|
28281
|
+
getSnapPreviewRenderData() {
|
|
28282
|
+
return this.getIndicatorRenderDataForCandidate(this.getSnapPreviewCandidate());
|
|
28283
|
+
}
|
|
28284
|
+
getIndicatorRenderDataForCandidate(candidate) {
|
|
28285
|
+
if (!candidate)
|
|
28105
28286
|
return null;
|
|
28106
|
-
if (!this.areFiniteNumbers(
|
|
28287
|
+
if (!this.areFiniteNumbers(candidate.centerX, candidate.centerY, candidate.lineEndpointX, candidate.lineEndpointY)) {
|
|
28107
28288
|
return null;
|
|
28108
28289
|
}
|
|
28109
28290
|
const scale = this.getSafeScale(this._core.store.state.scale);
|
|
28110
28291
|
const indicatorRadius = 8 / scale;
|
|
28111
28292
|
const indicatorStrokeWidth = `${2 / scale}`;
|
|
28112
|
-
const lineStrokeWidthNum = this.areFiniteNumbers(
|
|
28113
|
-
?
|
|
28293
|
+
const lineStrokeWidthNum = this.areFiniteNumbers(candidate.lineStrokeWidth) && candidate.lineStrokeWidth > 0
|
|
28294
|
+
? candidate.lineStrokeWidth
|
|
28114
28295
|
: (4 / scale);
|
|
28115
28296
|
const lineStrokeWidth = `${lineStrokeWidthNum}`;
|
|
28116
28297
|
const dashLength = Math.max(lineStrokeWidthNum * 2, 4 / scale);
|
|
28117
28298
|
const dashArray = `${dashLength} ${dashLength}`;
|
|
28118
|
-
const lineStroke =
|
|
28119
|
-
let edgeX = this.areFiniteNumbers(
|
|
28120
|
-
let edgeY = this.areFiniteNumbers(
|
|
28299
|
+
const lineStroke = candidate.lineStroke || '#000000';
|
|
28300
|
+
let edgeX = this.areFiniteNumbers(candidate.edgeX) ? candidate.edgeX : undefined;
|
|
28301
|
+
let edgeY = this.areFiniteNumbers(candidate.edgeY) ? candidate.edgeY : undefined;
|
|
28121
28302
|
let solidLineEndX = edgeX;
|
|
28122
28303
|
let solidLineEndY = edgeY;
|
|
28123
28304
|
let arrowPoints;
|
|
28124
|
-
const arrowOffset = this.areFiniteNumbers(
|
|
28125
|
-
?
|
|
28305
|
+
const arrowOffset = this.areFiniteNumbers(candidate.arrowOffset) && candidate.arrowOffset > 0
|
|
28306
|
+
? candidate.arrowOffset
|
|
28126
28307
|
: undefined;
|
|
28127
28308
|
if (arrowOffset !== undefined && edgeX !== undefined && edgeY !== undefined) {
|
|
28128
|
-
const dx =
|
|
28129
|
-
const dy =
|
|
28309
|
+
const dx = candidate.lineEndpointX - edgeX;
|
|
28310
|
+
const dy = candidate.lineEndpointY - edgeY;
|
|
28130
28311
|
const length = Math.sqrt(dx * dx + dy * dy);
|
|
28131
28312
|
if (length > arrowOffset) {
|
|
28132
28313
|
solidLineEndX = edgeX + (dx / length) * arrowOffset;
|
|
@@ -28134,8 +28315,8 @@ class KritzelAnchorManager {
|
|
|
28134
28315
|
}
|
|
28135
28316
|
// Calculate arrow head points
|
|
28136
28317
|
// Direction from line endpoint to edge (arrow direction)
|
|
28137
|
-
const arrowDx = edgeX -
|
|
28138
|
-
const arrowDy = edgeY -
|
|
28318
|
+
const arrowDx = edgeX - candidate.lineEndpointX;
|
|
28319
|
+
const arrowDy = edgeY - candidate.lineEndpointY;
|
|
28139
28320
|
const arrowLengthTotal = Math.sqrt(arrowDx * arrowDx + arrowDy * arrowDy);
|
|
28140
28321
|
if (arrowLengthTotal > 0) {
|
|
28141
28322
|
const ux = arrowDx / arrowLengthTotal;
|
|
@@ -28166,15 +28347,15 @@ class KritzelAnchorManager {
|
|
|
28166
28347
|
return null;
|
|
28167
28348
|
}
|
|
28168
28349
|
const snapLinePath = (() => {
|
|
28169
|
-
if (
|
|
28170
|
-
|
|
28171
|
-
|
|
28172
|
-
this.areFiniteNumbers(
|
|
28173
|
-
const startT =
|
|
28350
|
+
if (candidate.controlX !== undefined &&
|
|
28351
|
+
candidate.controlY !== undefined &&
|
|
28352
|
+
candidate.t !== undefined &&
|
|
28353
|
+
this.areFiniteNumbers(candidate.controlX, candidate.controlY, candidate.t)) {
|
|
28354
|
+
const startT = candidate.endpoint === 'start' ? 1 - candidate.t : candidate.t;
|
|
28174
28355
|
// Ensure meaningful range
|
|
28175
28356
|
if (startT >= 1)
|
|
28176
28357
|
return undefined;
|
|
28177
|
-
const segment = this.extractQuadraticSegment({ x:
|
|
28358
|
+
const segment = this.extractQuadraticSegment({ x: candidate.lineEndpointX, y: candidate.lineEndpointY }, { x: candidate.controlX, y: candidate.controlY }, { x: candidate.centerX, y: candidate.centerY }, startT, 1);
|
|
28178
28359
|
if (!this.areFiniteNumbers(segment.start.x, segment.start.y, segment.control.x, segment.control.y, segment.end.x, segment.end.y)) {
|
|
28179
28360
|
return undefined;
|
|
28180
28361
|
}
|
|
@@ -28188,15 +28369,15 @@ class KritzelAnchorManager {
|
|
|
28188
28369
|
lineStrokeWidth,
|
|
28189
28370
|
dashArray,
|
|
28190
28371
|
lineStroke,
|
|
28191
|
-
centerX:
|
|
28192
|
-
centerY:
|
|
28193
|
-
lineEndpointX:
|
|
28194
|
-
lineEndpointY:
|
|
28372
|
+
centerX: candidate.centerX,
|
|
28373
|
+
centerY: candidate.centerY,
|
|
28374
|
+
lineEndpointX: candidate.lineEndpointX,
|
|
28375
|
+
lineEndpointY: candidate.lineEndpointY,
|
|
28195
28376
|
edgeX,
|
|
28196
28377
|
edgeY,
|
|
28197
28378
|
arrowOffset,
|
|
28198
|
-
arrowStyle:
|
|
28199
|
-
arrowFill:
|
|
28379
|
+
arrowStyle: candidate.arrowStyle,
|
|
28380
|
+
arrowFill: candidate.arrowFill,
|
|
28200
28381
|
solidLineEndX,
|
|
28201
28382
|
solidLineEndY,
|
|
28202
28383
|
arrowPoints,
|