From f7b325c48c354d4a07bf431c67ad4f934a74c96b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mitja=20Bezen=C5=A1ek?= Date: Tue, 3 Oct 2023 16:21:07 +0200 Subject: [PATCH] Fix an issue with arrow creation. (#2004) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes an issue with creating arrows. Currently we create an arrow that has both `start` and `end` handles set to the same point. This causes `NaN` issues in some of our functions / svg rendering. After this change we only create the arrow after we start dragging, which ensures the start and the end handle won't have the same coordinates. This probably feels the best way to approach it: arrow of length 0 doesn't really make sense. Resolves [#2005](https://github.com/tldraw/tldraw/issues/2005) Before https://github.com/tldraw/tldraw/assets/2523721/6e83c17e-21bd-4e0a-826b-02fad9c21ec6 After https://github.com/tldraw/tldraw/assets/2523721/29359936-b673-4583-89c8-6d1728ab338c ### Change Type - [x] `patch` — Bug fix - [ ] `minor` — New feature - [ ] `major` — Breaking change - [ ] `dependencies` — Changes to package dependencies[^1] - [ ] `documentation` — Changes to the documentation only[^2] - [ ] `tests` — Changes to any test code only[^2] - [ ] `internal` — Any other changes that don't affect the published package[^2] - [ ] I don't know [^1]: publishes a `patch` release, for devDependencies use `internal` [^2]: will not publish a new version ### Test Plan 1. Create an arrow. 2. You should not see any errors in the console. --------- Co-authored-by: Steve Ruiz --- .../src/lib/editor/shapes/shared/arrow/curved-arrow.ts | 8 +++++++- .../src/lib/editor/shapes/shared/arrow/straight-arrow.ts | 8 +++++++- .../tldraw/src/lib/shapes/arrow/ArrowShapeTool.test.ts | 2 +- packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.tsx | 4 +++- .../tldraw/src/lib/shapes/arrow/toolStates/Pointing.ts | 9 +++++++-- 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/packages/editor/src/lib/editor/shapes/shared/arrow/curved-arrow.ts b/packages/editor/src/lib/editor/shapes/shared/arrow/curved-arrow.ts index 2fa24d777..76693ca2e 100644 --- a/packages/editor/src/lib/editor/shapes/shared/arrow/curved-arrow.ts +++ b/packages/editor/src/lib/editor/shapes/shared/arrow/curved-arrow.ts @@ -227,7 +227,8 @@ export function getCurvedArrowInfo( ) } - if (Vec2d.Dist(tA, tB) < MIN_ARROW_LENGTH) { + const distAB = Vec2d.Dist(tA, tB) + if (distAB < MIN_ARROW_LENGTH) { if (offsetA !== 0 && offsetB !== 0) { offsetA *= -1.5 offsetB *= -1.5 @@ -235,6 +236,11 @@ export function getCurvedArrowInfo( offsetA *= -2 } else if (offsetB !== 0) { offsetB *= -2 + } else { + if (distAB < 10) { + if (startShapeInfo) offsetA = -(10 - distAB) + else if (endShapeInfo) offsetB = -(10 - distAB) + } } } diff --git a/packages/editor/src/lib/editor/shapes/shared/arrow/straight-arrow.ts b/packages/editor/src/lib/editor/shapes/shared/arrow/straight-arrow.ts index 866ebe83a..9ac838d9c 100644 --- a/packages/editor/src/lib/editor/shapes/shared/arrow/straight-arrow.ts +++ b/packages/editor/src/lib/editor/shapes/shared/arrow/straight-arrow.ts @@ -106,7 +106,8 @@ export function getStraightArrowInfo(editor: Editor, shape: TLArrowShape): TLArr const tA = a.clone().add(u.clone().mul(offsetA * (didFlip ? -1 : 1))) const tB = b.clone().sub(u.clone().mul(offsetB * (didFlip ? -1 : 1))) - if (Vec2d.Dist(tA, tB) < MIN_ARROW_LENGTH) { + const distAB = Vec2d.Dist(tA, tB) + if (distAB < MIN_ARROW_LENGTH) { if (offsetA !== 0 && offsetB !== 0) { offsetA *= -1.5 offsetB *= -1.5 @@ -114,6 +115,11 @@ export function getStraightArrowInfo(editor: Editor, shape: TLArrowShape): TLArr offsetA *= -2 } else if (offsetB !== 0) { offsetB *= -2 + } else { + if (distAB < 10) { + if (startShapeInfo) offsetA = -(10 - distAB) + else if (endShapeInfo) offsetB = -(10 - distAB) + } } } diff --git a/packages/tldraw/src/lib/shapes/arrow/ArrowShapeTool.test.ts b/packages/tldraw/src/lib/shapes/arrow/ArrowShapeTool.test.ts index d4cd190db..9f74f5f1f 100644 --- a/packages/tldraw/src/lib/shapes/arrow/ArrowShapeTool.test.ts +++ b/packages/tldraw/src/lib/shapes/arrow/ArrowShapeTool.test.ts @@ -349,7 +349,7 @@ describe('When pointing an end shape', () => { y: 0, props: { start: { type: 'point', x: 0, y: 0 }, - end: { type: 'point', x: 0, y: 0 }, + end: { type: 'point', x: 2, y: 0 }, }, }) diff --git a/packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.tsx b/packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.tsx index a7587311b..43b1cf69a 100644 --- a/packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.tsx +++ b/packages/tldraw/src/lib/shapes/arrow/ArrowShapeUtil.tsx @@ -55,6 +55,8 @@ import { ArrowTextLabel } from './components/ArrowTextLabel' let globalRenderIndex = 0 +export const ARROW_END_OFFSET = 0.1 + /** @public */ export class ArrowShapeUtil extends ShapeUtil { static override type = 'arrow' as const @@ -78,7 +80,7 @@ export class ArrowShapeUtil extends ShapeUtil { labelColor: 'black', bend: 0, start: { type: 'point', x: 0, y: 0 }, - end: { type: 'point', x: 0, y: 0 }, + end: { type: 'point', x: 2, y: 0 }, arrowheadStart: 'none', arrowheadEnd: 'arrow', text: '', diff --git a/packages/tldraw/src/lib/shapes/arrow/toolStates/Pointing.ts b/packages/tldraw/src/lib/shapes/arrow/toolStates/Pointing.ts index bc4ca76f9..c4d25f6a2 100644 --- a/packages/tldraw/src/lib/shapes/arrow/toolStates/Pointing.ts +++ b/packages/tldraw/src/lib/shapes/arrow/toolStates/Pointing.ts @@ -127,12 +127,17 @@ export class Pointing extends StateNode { const handles = this.editor.getShapeHandles(shape) if (!handles) throw Error(`expected handles for arrow`) + const shapeWithOutEndOffset = { + ...shape, + props: { ...shape.props, end: { ...shape.props.end, x: 0, y: 0 } }, + } + // end update { const util = this.editor.getShapeUtil('arrow') const point = this.editor.getPointInShapeSpace(shape, this.editor.inputs.currentPagePoint) const endHandle = handles.find((h) => h.id === 'end')! - const change = util.onHandleChange?.(shape, { + const change = util.onHandleChange?.(shapeWithOutEndOffset, { handle: { ...endHandle, x: point.x, y: point.y }, isPrecise: false, // sure about that? }) @@ -150,7 +155,7 @@ export class Pointing extends StateNode { { const util = this.editor.getShapeUtil('arrow') const startHandle = handles.find((h) => h.id === 'start')! - const change = util.onHandleChange?.(shape, { + const change = util.onHandleChange?.(shapeWithOutEndOffset, { handle: { ...startHandle, x: 0, y: 0 }, isPrecise: this.didTimeout, // sure about that? })