diff --git a/packages/editor/api-report.md b/packages/editor/api-report.md index 6b5a73d95..2b8563890 100644 --- a/packages/editor/api-report.md +++ b/packages/editor/api-report.md @@ -514,7 +514,7 @@ export function degreesToRadians(d: number): number; export const DOUBLE_CLICK_DURATION = 450; // @internal (undocumented) -export const DRAG_DISTANCE = 4; +export const DRAG_DISTANCE = 16; // @public (undocumented) export const EASINGS: { diff --git a/packages/editor/src/lib/constants.ts b/packages/editor/src/lib/constants.ts index 43dcbfb10..233119932 100644 --- a/packages/editor/src/lib/constants.ts +++ b/packages/editor/src/lib/constants.ts @@ -34,10 +34,10 @@ export const DOUBLE_CLICK_DURATION = 450 export const MULTI_CLICK_DURATION = 200 /** @internal */ -export const COARSE_DRAG_DISTANCE = 6 +export const COARSE_DRAG_DISTANCE = 36 // 6 squared /** @internal */ -export const DRAG_DISTANCE = 4 +export const DRAG_DISTANCE = 16 // 4 squared /** @internal */ export const SVG_PADDING = 32 diff --git a/packages/editor/src/lib/editor/Editor.ts b/packages/editor/src/lib/editor/Editor.ts index 695471389..b93832f3b 100644 --- a/packages/editor/src/lib/editor/Editor.ts +++ b/packages/editor/src/lib/editor/Editor.ts @@ -8594,7 +8594,7 @@ export class Editor extends EventEmitter { if ( !inputs.isDragging && inputs.isPointing && - originPagePoint.dist(currentPagePoint) > + Vec.Dist2(originPagePoint, currentPagePoint) > (this.getInstanceState().isCoarsePointer ? COARSE_DRAG_DISTANCE : DRAG_DISTANCE) / this.getZoomLevel() ) { @@ -8684,7 +8684,7 @@ export class Editor extends EventEmitter { if ( !inputs.isDragging && inputs.isPointing && - originPagePoint.dist(currentPagePoint) > + Vec.Dist2(originPagePoint, currentPagePoint) > (this.getInstanceState().isCoarsePointer ? COARSE_DRAG_DISTANCE : DRAG_DISTANCE) / this.getZoomLevel() ) { diff --git a/packages/editor/src/lib/editor/managers/ClickManager.ts b/packages/editor/src/lib/editor/managers/ClickManager.ts index ef2c11111..ef5e53b10 100644 --- a/packages/editor/src/lib/editor/managers/ClickManager.ts +++ b/packages/editor/src/lib/editor/managers/ClickManager.ts @@ -227,7 +227,7 @@ export class ClickManager { if ( this._clickState !== 'idle' && this._clickScreenPoint && - this._clickScreenPoint.dist(this.editor.inputs.currentScreenPoint) > + Vec.Dist2(this._clickScreenPoint, this.editor.inputs.currentScreenPoint) > (this.editor.getInstanceState().isCoarsePointer ? COARSE_DRAG_DISTANCE : DRAG_DISTANCE) ) { this.cancelDoubleClickTimeout() diff --git a/packages/editor/src/lib/primitives/Vec.ts b/packages/editor/src/lib/primitives/Vec.ts index 82bf47395..e0ee4b717 100644 --- a/packages/editor/src/lib/primitives/Vec.ts +++ b/packages/editor/src/lib/primitives/Vec.ts @@ -308,19 +308,20 @@ export class Vec { static Per(A: VecLike): Vec { return new Vec(A.y, -A.x) } - - static Dist2(A: VecLike, B: VecLike): number { - return Vec.Sub(A, B).len2() - } - static Abs(A: VecLike): Vec { return new Vec(Math.abs(A.x), Math.abs(A.y)) } + // Get the distance between two points. static Dist(A: VecLike, B: VecLike): number { return Math.hypot(A.y - B.y, A.x - B.x) } + // Get the squared distance between two points. This is faster to calculate (no square root) so useful for "minimum distance" checks where the actual measurement does not matter. + static Dist2(A: VecLike, B: VecLike): number { + return (A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y) + } + /** * Dot product of two vectors which is used to calculate the angle between them. */ diff --git a/packages/editor/src/lib/primitives/geometry/Arc2d.ts b/packages/editor/src/lib/primitives/geometry/Arc2d.ts index 5feee17ef..3ca170a47 100644 --- a/packages/editor/src/lib/primitives/geometry/Arc2d.ts +++ b/packages/editor/src/lib/primitives/geometry/Arc2d.ts @@ -52,15 +52,16 @@ export class Arc2d extends Geometry2d { // Get the point (P) on the arc, then pick the nearest of A, B, and P const P = _center.clone().add(point.clone().sub(_center).uni().mul(radius)) - let distance = Infinity let nearest: Vec | undefined - for (const pt of [A, B, P]) { - if (point.dist(pt) < distance) { - nearest = pt - distance = point.dist(pt) + let dist = Infinity + let d: number + for (const p of [A, B, P]) { + d = Vec.Dist2(point, p) + if (d < dist) { + nearest = p + dist = d } } - if (!nearest) throw Error('nearest point not found') return nearest } diff --git a/packages/editor/src/lib/primitives/geometry/CubicBezier2d.ts b/packages/editor/src/lib/primitives/geometry/CubicBezier2d.ts index 9923891b7..912144378 100644 --- a/packages/editor/src/lib/primitives/geometry/CubicBezier2d.ts +++ b/packages/editor/src/lib/primitives/geometry/CubicBezier2d.ts @@ -55,9 +55,11 @@ export class CubicBezier2d extends Polyline2d { nearestPoint(A: Vec): Vec { let nearest: Vec | undefined let dist = Infinity + let d: number + let p: Vec for (const edge of this.segments) { - const p = edge.nearestPoint(A) - const d = p.dist(A) + p = edge.nearestPoint(A) + d = Vec.Dist2(p, A) if (d < dist) { nearest = p dist = d diff --git a/packages/editor/src/lib/primitives/geometry/CubicSpline2d.ts b/packages/editor/src/lib/primitives/geometry/CubicSpline2d.ts index 3c362b91b..8ff99bb0a 100644 --- a/packages/editor/src/lib/primitives/geometry/CubicSpline2d.ts +++ b/packages/editor/src/lib/primitives/geometry/CubicSpline2d.ts @@ -67,15 +67,16 @@ export class CubicSpline2d extends Geometry2d { nearestPoint(A: Vec): Vec { let nearest: Vec | undefined let dist = Infinity + let d: number + let p: Vec for (const segment of this.segments) { - const p = segment.nearestPoint(A) - const d = p.dist(A) + p = segment.nearestPoint(A) + d = Vec.Dist2(p, A) if (d < dist) { nearest = p dist = d } } - if (!nearest) throw Error('nearest point not found') return nearest } diff --git a/packages/editor/src/lib/primitives/geometry/Ellipse2d.ts b/packages/editor/src/lib/primitives/geometry/Ellipse2d.ts index d15b864af..a2340c298 100644 --- a/packages/editor/src/lib/primitives/geometry/Ellipse2d.ts +++ b/packages/editor/src/lib/primitives/geometry/Ellipse2d.ts @@ -76,15 +76,16 @@ export class Ellipse2d extends Geometry2d { nearestPoint(A: Vec): Vec { let nearest: Vec | undefined let dist = Infinity + let d: number + let p: Vec for (const edge of this.edges) { - const p = edge.nearestPoint(A) - const d = p.dist(A) + p = edge.nearestPoint(A) + d = Vec.Dist2(p, A) if (d < dist) { nearest = p dist = d } } - if (!nearest) throw Error('nearest point not found') return nearest } diff --git a/packages/editor/src/lib/primitives/geometry/Geometry2d.ts b/packages/editor/src/lib/primitives/geometry/Geometry2d.ts index e028de31e..b7df7a84d 100644 --- a/packages/editor/src/lib/primitives/geometry/Geometry2d.ts +++ b/packages/editor/src/lib/primitives/geometry/Geometry2d.ts @@ -55,14 +55,17 @@ export abstract class Geometry2d { } nearestPointOnLineSegment(A: Vec, B: Vec): Vec { - let distance = Infinity + const { vertices } = this let nearest: Vec | undefined - for (let i = 0; i < this.vertices.length; i++) { - const point = this.vertices[i] - const d = Vec.DistanceToLineSegment(A, B, point) - if (d < distance) { - distance = d - nearest = point + let dist = Infinity + let d: number + let p: Vec + for (let i = 0; i < vertices.length; i++) { + p = vertices[i] + d = Vec.DistanceToLineSegment(A, B, p) + if (d < dist) { + dist = d + nearest = p } } if (!nearest) throw Error('nearest point not found') diff --git a/packages/editor/src/lib/primitives/geometry/Group2d.ts b/packages/editor/src/lib/primitives/geometry/Group2d.ts index 3caeabfc8..b98f1ea99 100644 --- a/packages/editor/src/lib/primitives/geometry/Group2d.ts +++ b/packages/editor/src/lib/primitives/geometry/Group2d.ts @@ -30,8 +30,8 @@ export class Group2d extends Geometry2d { } override nearestPoint(point: Vec): Vec { - let d = Infinity - let p: Vec | undefined + let dist = Infinity + let nearest: Vec | undefined const { children } = this @@ -39,16 +39,18 @@ export class Group2d extends Geometry2d { throw Error('no children') } + let p: Vec + let d: number for (const child of children) { - const nearest = child.nearestPoint(point) - const dist = nearest.dist(point) - if (dist < d) { - d = dist - p = nearest + p = child.nearestPoint(point) + d = Vec.Dist2(p, point) + if (d < dist) { + dist = d + nearest = p } } - if (!p) throw Error('nearest point not found') - return p + if (!nearest) throw Error('nearest point not found') + return nearest } override distanceToPoint(point: Vec, hitInside = false) { diff --git a/packages/editor/src/lib/primitives/geometry/Polyline2d.ts b/packages/editor/src/lib/primitives/geometry/Polyline2d.ts index ffa541cb2..84c8e7471 100644 --- a/packages/editor/src/lib/primitives/geometry/Polyline2d.ts +++ b/packages/editor/src/lib/primitives/geometry/Polyline2d.ts @@ -51,18 +51,17 @@ export class Polyline2d extends Geometry2d { const { segments } = this let nearest = this.points[0] let dist = Infinity - let p: Vec // current point on segment let d: number // distance from A to p for (let i = 0; i < segments.length; i++) { p = segments[i].nearestPoint(A) - d = p.dist(A) + d = Vec.Dist2(p, A) if (d < dist) { nearest = p dist = d } } - + if (!nearest) throw Error('nearest point not found') return nearest } diff --git a/packages/store/api/api.json b/packages/store/api/api.json index d676a9c7e..cb4e90c11 100644 --- a/packages/store/api/api.json +++ b/packages/store/api/api.json @@ -4231,7 +4231,7 @@ { "kind": "Property", "canonicalReference": "@tldraw/store!Store#onBeforeChange:member", - "docComment": "/**\n * A callback before after each record's change.\n *\n * @param prev - The previous value, if any.\n *\n * @param next - The next value.\n */\n", + "docComment": "/**\n * A callback fired before each record's change.\n *\n * @param prev - The previous value, if any.\n *\n * @param next - The next value.\n */\n", "excerptTokens": [ { "kind": "Content", diff --git a/packages/tldraw/src/lib/shapes/draw/toolStates/Drawing.ts b/packages/tldraw/src/lib/shapes/draw/toolStates/Drawing.ts index 7ba9b582b..6946fec1a 100644 --- a/packages/tldraw/src/lib/shapes/draw/toolStates/Drawing.ts +++ b/packages/tldraw/src/lib/shapes/draw/toolStates/Drawing.ts @@ -309,7 +309,7 @@ export class Drawing extends StateNode { } const hasMovedFarEnough = - Vec.Dist(pagePointWhereNextSegmentChanged, inputs.currentPagePoint) > DRAG_DISTANCE + Vec.Dist2(pagePointWhereNextSegmentChanged, inputs.currentPagePoint) > DRAG_DISTANCE // Find the distance from where the pointer was when shift was released and // where it is now; if it's far enough away, then update the page point where @@ -382,7 +382,7 @@ export class Drawing extends StateNode { } const hasMovedFarEnough = - Vec.Dist(pagePointWhereNextSegmentChanged, inputs.currentPagePoint) > DRAG_DISTANCE + Vec.Dist2(pagePointWhereNextSegmentChanged, inputs.currentPagePoint) > DRAG_DISTANCE // Find the distance from where the pointer was when shift was released and // where it is now; if it's far enough away, then update the page point where