@tldraw/editor 3.15.0-canary.22a03ce9c171 → 3.15.0-canary.24182c470c85

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.
Files changed (131) hide show
  1. package/dist-cjs/index.d.ts +103 -7
  2. package/dist-cjs/index.js +3 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/components/SVGContainer.js +1 -1
  5. package/dist-cjs/lib/components/SVGContainer.js.map +2 -2
  6. package/dist-cjs/lib/components/default-components/DefaultBrush.js +1 -1
  7. package/dist-cjs/lib/components/default-components/DefaultBrush.js.map +2 -2
  8. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +1 -1
  9. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  10. package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js +1 -1
  11. package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js.map +2 -2
  12. package/dist-cjs/lib/components/default-components/DefaultCursor.js +1 -1
  13. package/dist-cjs/lib/components/default-components/DefaultCursor.js.map +2 -2
  14. package/dist-cjs/lib/components/default-components/DefaultGrid.js +1 -1
  15. package/dist-cjs/lib/components/default-components/DefaultGrid.js.map +2 -2
  16. package/dist-cjs/lib/components/default-components/DefaultHandles.js +1 -1
  17. package/dist-cjs/lib/components/default-components/DefaultHandles.js.map +2 -2
  18. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +1 -1
  19. package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
  20. package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js +1 -1
  21. package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js.map +2 -2
  22. package/dist-cjs/lib/components/default-components/DefaultSpinner.js +27 -15
  23. package/dist-cjs/lib/components/default-components/DefaultSpinner.js.map +3 -3
  24. package/dist-cjs/lib/editor/Editor.js +88 -43
  25. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  26. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +96 -101
  27. package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
  28. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  29. package/dist-cjs/lib/editor/tools/StateNode.js +20 -1
  30. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  31. package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
  32. package/dist-cjs/lib/hooks/useEditorComponents.js.map +1 -1
  33. package/dist-cjs/lib/license/Watermark.js +2 -2
  34. package/dist-cjs/lib/license/Watermark.js.map +2 -2
  35. package/dist-cjs/lib/primitives/geometry/Arc2d.js +1 -1
  36. package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
  37. package/dist-cjs/lib/primitives/geometry/Circle2d.js +1 -1
  38. package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +2 -2
  39. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +3 -1
  40. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
  41. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js +1 -1
  42. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +2 -2
  43. package/dist-cjs/lib/primitives/geometry/geometry-constants.js +2 -2
  44. package/dist-cjs/lib/primitives/geometry/geometry-constants.js.map +2 -2
  45. package/dist-cjs/lib/primitives/intersect.js +4 -4
  46. package/dist-cjs/lib/primitives/intersect.js.map +2 -2
  47. package/dist-cjs/lib/primitives/utils.js +4 -0
  48. package/dist-cjs/lib/primitives/utils.js.map +2 -2
  49. package/dist-cjs/version.js +3 -3
  50. package/dist-cjs/version.js.map +1 -1
  51. package/dist-esm/index.d.mts +103 -7
  52. package/dist-esm/index.mjs +3 -1
  53. package/dist-esm/index.mjs.map +2 -2
  54. package/dist-esm/lib/components/SVGContainer.mjs +1 -1
  55. package/dist-esm/lib/components/SVGContainer.mjs.map +2 -2
  56. package/dist-esm/lib/components/default-components/DefaultBrush.mjs +1 -1
  57. package/dist-esm/lib/components/default-components/DefaultBrush.mjs.map +2 -2
  58. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +1 -1
  59. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  60. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs +1 -1
  61. package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +2 -2
  62. package/dist-esm/lib/components/default-components/DefaultCursor.mjs +1 -1
  63. package/dist-esm/lib/components/default-components/DefaultCursor.mjs.map +2 -2
  64. package/dist-esm/lib/components/default-components/DefaultGrid.mjs +1 -1
  65. package/dist-esm/lib/components/default-components/DefaultGrid.mjs.map +2 -2
  66. package/dist-esm/lib/components/default-components/DefaultHandles.mjs +1 -1
  67. package/dist-esm/lib/components/default-components/DefaultHandles.mjs.map +2 -2
  68. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +1 -1
  69. package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
  70. package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs +1 -1
  71. package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs.map +2 -2
  72. package/dist-esm/lib/components/default-components/DefaultSpinner.mjs +17 -15
  73. package/dist-esm/lib/components/default-components/DefaultSpinner.mjs.map +2 -2
  74. package/dist-esm/lib/editor/Editor.mjs +88 -43
  75. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  76. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +96 -101
  77. package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
  78. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  79. package/dist-esm/lib/editor/tools/StateNode.mjs +20 -1
  80. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  81. package/dist-esm/lib/hooks/useEditorComponents.mjs.map +1 -1
  82. package/dist-esm/lib/license/Watermark.mjs +2 -2
  83. package/dist-esm/lib/license/Watermark.mjs.map +2 -2
  84. package/dist-esm/lib/primitives/geometry/Arc2d.mjs +2 -2
  85. package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
  86. package/dist-esm/lib/primitives/geometry/Circle2d.mjs +2 -2
  87. package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +2 -2
  88. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +3 -1
  89. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
  90. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs +2 -2
  91. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +2 -2
  92. package/dist-esm/lib/primitives/geometry/geometry-constants.mjs +2 -2
  93. package/dist-esm/lib/primitives/geometry/geometry-constants.mjs.map +2 -2
  94. package/dist-esm/lib/primitives/intersect.mjs +5 -5
  95. package/dist-esm/lib/primitives/intersect.mjs.map +2 -2
  96. package/dist-esm/lib/primitives/utils.mjs +4 -0
  97. package/dist-esm/lib/primitives/utils.mjs.map +2 -2
  98. package/dist-esm/version.mjs +3 -3
  99. package/dist-esm/version.mjs.map +1 -1
  100. package/editor.css +21 -27
  101. package/package.json +9 -8
  102. package/src/index.ts +2 -0
  103. package/src/lib/components/SVGContainer.tsx +1 -1
  104. package/src/lib/components/default-components/DefaultBrush.tsx +1 -1
  105. package/src/lib/components/default-components/DefaultCanvas.tsx +1 -1
  106. package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +1 -1
  107. package/src/lib/components/default-components/DefaultCursor.tsx +1 -1
  108. package/src/lib/components/default-components/DefaultGrid.tsx +1 -1
  109. package/src/lib/components/default-components/DefaultHandles.tsx +5 -1
  110. package/src/lib/components/default-components/DefaultShapeIndicator.tsx +1 -1
  111. package/src/lib/components/default-components/DefaultSnapIndictor.tsx +1 -1
  112. package/src/lib/components/default-components/DefaultSpinner.tsx +12 -12
  113. package/src/lib/editor/Editor.test.ts +407 -0
  114. package/src/lib/editor/Editor.ts +106 -44
  115. package/src/lib/editor/managers/TextManager/TextManager.ts +108 -128
  116. package/src/lib/editor/shapes/ShapeUtil.ts +57 -0
  117. package/src/lib/editor/tools/StateNode.test.ts +285 -0
  118. package/src/lib/editor/tools/StateNode.ts +27 -1
  119. package/src/lib/editor/types/misc-types.ts +19 -0
  120. package/src/lib/hooks/useEditorComponents.tsx +1 -1
  121. package/src/lib/license/Watermark.tsx +2 -2
  122. package/src/lib/primitives/geometry/Arc2d.ts +2 -2
  123. package/src/lib/primitives/geometry/Circle2d.ts +2 -2
  124. package/src/lib/primitives/geometry/CubicBezier2d.ts +4 -1
  125. package/src/lib/primitives/geometry/Ellipse2d.ts +2 -2
  126. package/src/lib/primitives/geometry/geometry-constants.ts +2 -1
  127. package/src/lib/primitives/intersect.test.ts +57 -11
  128. package/src/lib/primitives/intersect.ts +12 -5
  129. package/src/lib/primitives/utils.ts +11 -0
  130. package/src/version.ts +3 -3
  131. package/src/lib/test/currentToolIdMask.test.ts +0 -49
@@ -62,7 +62,7 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
62
62
 
63
63
  this.parent = parent ?? ({} as any)
64
64
 
65
- if (this.parent) {
65
+ if (parent) {
66
66
  if (children && initial) {
67
67
  this.type = 'branch'
68
68
  this.initial = initial
@@ -238,6 +238,32 @@ export abstract class StateNode implements Partial<TLEventHandlers> {
238
238
  this._currentToolIdMask.set(id)
239
239
  }
240
240
 
241
+ /**
242
+ * Add a child node to this state node.
243
+ *
244
+ * @public
245
+ */
246
+ addChild(childConstructor: TLStateNodeConstructor): this {
247
+ if (this.type === 'leaf') {
248
+ throw new Error('StateNode.addChild: cannot add child to a leaf node')
249
+ }
250
+
251
+ // Initialize children if it's undefined (for root nodes without static children)
252
+ if (!this.children) {
253
+ this.children = {}
254
+ }
255
+
256
+ const child = new childConstructor(this.editor, this)
257
+
258
+ // Check if a child with this ID already exists
259
+ if (this.children[child.id]) {
260
+ throw new Error(`StateNode.addChild: a child with id '${child.id}' already exists`)
261
+ }
262
+
263
+ this.children[child.id] = child
264
+ return this
265
+ }
266
+
241
267
  onWheel?(info: TLWheelEventInfo): void
242
268
  onPointerDown?(info: TLPointerEventInfo): void
243
269
  onPointerMove?(info: TLPointerEventInfo): void
@@ -187,3 +187,22 @@ export interface TLCameraConstraints {
187
187
  y: 'free' | 'fixed' | 'inside' | 'outside' | 'contain'
188
188
  }
189
189
  }
190
+
191
+ /** @public */
192
+ export interface TLUpdatePointerOptions {
193
+ /** Whether to update the pointer immediately, rather than on the next tick. */
194
+ immediate?: boolean
195
+ /**
196
+ * The point, in screen-space, to update the pointer to. Defaults to the position of the last
197
+ * pointer event.
198
+ */
199
+ point?: VecLike
200
+ pointerId?: number
201
+ ctrlKey?: boolean
202
+ altKey?: boolean
203
+ shiftKey?: boolean
204
+ metaKey?: boolean
205
+ accelKey?: boolean
206
+ isPen?: boolean
207
+ button?: number
208
+ }
@@ -69,7 +69,7 @@ export interface TLEditorComponents {
69
69
  ShapeIndicator?: ComponentType<TLShapeIndicatorProps> | null
70
70
  ShapeIndicators?: ComponentType | null
71
71
  SnapIndicator?: ComponentType<TLSnapIndicatorProps> | null
72
- Spinner?: ComponentType | null
72
+ Spinner?: ComponentType<React.SVGProps<SVGSVGElement>> | null
73
73
  SvgDefs?: ComponentType | null
74
74
  ZoomBrush?: ComponentType<TLBrushProps> | null
75
75
 
@@ -143,7 +143,7 @@ To remove the watermark, please purchase a license at tldraw.dev.
143
143
  }
144
144
 
145
145
  .${className}:hover > button {
146
- animation: delayed_link 0.2s forwards ease-in-out;
146
+ animation: ${className}_delayed_link 0.2s forwards ease-in-out;
147
147
  animation-delay: 0.32s;
148
148
  }
149
149
 
@@ -153,7 +153,7 @@ To remove the watermark, please purchase a license at tldraw.dev.
153
153
  }
154
154
 
155
155
 
156
- @keyframes delayed_link {
156
+ @keyframes ${className}_delayed_link {
157
157
  0% {
158
158
  cursor: inherit;
159
159
  opacity: .38;
@@ -2,7 +2,7 @@ import { Vec, VecLike } from '../Vec'
2
2
  import { intersectLineSegmentCircle } from '../intersect'
3
3
  import { getArcMeasure, getPointInArcT, getPointOnCircle } from '../utils'
4
4
  import { Geometry2d, Geometry2dOptions } from './Geometry2d'
5
- import { getVerticesCountForLength } from './geometry-constants'
5
+ import { getVerticesCountForArcLength } from './geometry-constants'
6
6
 
7
7
  /** @public */
8
8
  export class Arc2d extends Geometry2d {
@@ -94,7 +94,7 @@ export class Arc2d extends Geometry2d {
94
94
  getVertices(): Vec[] {
95
95
  const { _center, _measure: measure, length, _radius: radius, _angleStart: angleStart } = this
96
96
  const vertices: Vec[] = []
97
- for (let i = 0, n = getVerticesCountForLength(Math.abs(length)); i < n + 1; i++) {
97
+ for (let i = 0, n = getVerticesCountForArcLength(Math.abs(length)); i < n + 1; i++) {
98
98
  const t = (i / n) * measure
99
99
  const angle = angleStart + t
100
100
  vertices.push(getPointOnCircle(_center, radius, angle))
@@ -3,7 +3,7 @@ import { Vec, VecLike } from '../Vec'
3
3
  import { intersectLineSegmentCircle } from '../intersect'
4
4
  import { PI2, getPointOnCircle } from '../utils'
5
5
  import { Geometry2d, Geometry2dOptions } from './Geometry2d'
6
- import { getVerticesCountForLength } from './geometry-constants'
6
+ import { getVerticesCountForArcLength } from './geometry-constants'
7
7
 
8
8
  /** @public */
9
9
  export class Circle2d extends Geometry2d {
@@ -36,7 +36,7 @@ export class Circle2d extends Geometry2d {
36
36
  const { _center, _radius: radius } = this
37
37
  const perimeter = PI2 * radius
38
38
  const vertices: Vec[] = []
39
- for (let i = 0, n = getVerticesCountForLength(perimeter); i < n; i++) {
39
+ for (let i = 0, n = getVerticesCountForArcLength(perimeter); i < n; i++) {
40
40
  const angle = (i / n) * PI2
41
41
  vertices.push(getPointOnCircle(_center, radius, angle))
42
42
  }
@@ -8,6 +8,7 @@ export class CubicBezier2d extends Polyline2d {
8
8
  private _b: Vec
9
9
  private _c: Vec
10
10
  private _d: Vec
11
+ private _resolution: number
11
12
 
12
13
  constructor(
13
14
  config: Omit<Geometry2dOptions, 'isFilled' | 'isClosed'> & {
@@ -15,6 +16,7 @@ export class CubicBezier2d extends Polyline2d {
15
16
  cp1: Vec
16
17
  cp2: Vec
17
18
  end: Vec
19
+ resolution?: number
18
20
  }
19
21
  ) {
20
22
  const { start: a, cp1: b, cp2: c, end: d } = config
@@ -24,13 +26,14 @@ export class CubicBezier2d extends Polyline2d {
24
26
  this._b = b
25
27
  this._c = c
26
28
  this._d = d
29
+ this._resolution = config.resolution ?? 10
27
30
  }
28
31
 
29
32
  override getVertices() {
30
33
  const vertices = [] as Vec[]
31
34
  const { _a: a, _b: b, _c: c, _d: d } = this
32
35
  // we'll always use ten vertices for each bezier curve
33
- for (let i = 0, n = 10; i <= n; i++) {
36
+ for (let i = 0, n = this._resolution; i <= n; i++) {
34
37
  const t = i / n
35
38
  vertices.push(
36
39
  new Vec(
@@ -3,7 +3,7 @@ import { Vec, VecLike } from '../Vec'
3
3
  import { PI, PI2, clamp, perimeterOfEllipse } from '../utils'
4
4
  import { Edge2d } from './Edge2d'
5
5
  import { Geometry2d, Geometry2dOptions } from './Geometry2d'
6
- import { getVerticesCountForLength } from './geometry-constants'
6
+ import { getVerticesCountForArcLength } from './geometry-constants'
7
7
 
8
8
  /** @public */
9
9
  export class Ellipse2d extends Geometry2d {
@@ -47,7 +47,7 @@ export class Ellipse2d extends Geometry2d {
47
47
  const q = Math.pow(cx - cy, 2) / Math.pow(cx + cy, 2)
48
48
  const p = PI * (cx + cy) * (1 + (3 * q) / (10 + Math.sqrt(4 - 3 * q)))
49
49
  // Number of points
50
- const len = getVerticesCountForLength(p)
50
+ const len = getVerticesCountForArcLength(p)
51
51
  // Size of step
52
52
  const step = PI2 / len
53
53
 
@@ -1,6 +1,7 @@
1
1
  const SPACING = 20
2
2
  const MIN_COUNT = 8
3
3
 
4
- export function getVerticesCountForLength(length: number, spacing = SPACING) {
4
+ /** @internal */
5
+ export function getVerticesCountForArcLength(length: number, spacing = SPACING) {
5
6
  return Math.max(MIN_COUNT, Math.ceil(length / spacing))
6
7
  }
@@ -181,6 +181,17 @@ describe('intersectLineSegmentLineSegment', () => {
181
181
  expect(result!.x).toBeCloseTo(5, 1)
182
182
  expect(result!.y).toBeCloseTo(5, 1)
183
183
  })
184
+
185
+ it('should find intersection when segments cross at endpoints (floating point error case)', () => {
186
+ const result = intersectLineSegmentLineSegment(
187
+ { x: 100, y: 100 },
188
+ { x: 20, y: 20 },
189
+ { x: 36.141160159025375, y: 31.811740238538057 },
190
+ { x: 34.14213562373095, y: 34.14213562373095 }
191
+ )
192
+
193
+ expect(result).not.toBeNull()
194
+ })
184
195
  })
185
196
 
186
197
  describe('edge cases', () => {
@@ -195,17 +206,6 @@ describe('intersectLineSegmentLineSegment', () => {
195
206
  expect(result).not.toBeNull()
196
207
  })
197
208
 
198
- it('should handle segments with very small coordinates', () => {
199
- const a1 = new Vec(1e-10, 1e-10)
200
- const a2 = new Vec(1e-9, 1e-9)
201
- const b1 = new Vec(1e-10, 1e-9)
202
- const b2 = new Vec(1e-9, 1e-10)
203
-
204
- const result = intersectLineSegmentLineSegment(a1, a2, b1, b2)
205
-
206
- expect(result).not.toBeNull()
207
- })
208
-
209
209
  it('should handle segments with very large coordinates', () => {
210
210
  const a1 = new Vec(1e10, 1e10)
211
211
  const a2 = new Vec(1e11, 1e11)
@@ -531,6 +531,52 @@ describe('intersectLineSegmentPolyline', () => {
531
531
  expect(sorted[3].x).toBeCloseTo(18, 5)
532
532
  sorted.forEach((pt) => expect(pt.y).toBeCloseTo(5, 5))
533
533
  })
534
+
535
+ // Test cases for vertex intersection issues
536
+ describe('vertex intersection edge cases', () => {
537
+ it('should detect intersection when line segment passes through polyline vertex', () => {
538
+ const a1 = new Vec(0, 5)
539
+ const a2 = new Vec(10, 5)
540
+ const points = [new Vec(5, 0), new Vec(5, 10)] // vertical line at x=5
541
+ const result = intersectLineSegmentPolyline(a1, a2, points)
542
+ expect(result).not.toBeNull()
543
+ expect(result!.length).toBe(1)
544
+ expect(result![0].x).toBeCloseTo(5, 5)
545
+ expect(result![0].y).toBeCloseTo(5, 5)
546
+ })
547
+
548
+ it('should detect intersection when line segment passes through polyline vertex at angle', () => {
549
+ const a1 = new Vec(0, 0)
550
+ const a2 = new Vec(10, 10)
551
+ const points = [new Vec(5, 0), new Vec(5, 10)] // vertical line at x=5
552
+ const result = intersectLineSegmentPolyline(a1, a2, points)
553
+ expect(result).not.toBeNull()
554
+ expect(result!.length).toBe(1)
555
+ expect(result![0].x).toBeCloseTo(5, 5)
556
+ expect(result![0].y).toBeCloseTo(5, 5)
557
+ })
558
+
559
+ it('should detect intersection when line segment passes through polyline vertex at middle', () => {
560
+ const a1 = new Vec(0, 5)
561
+ const a2 = new Vec(10, 5)
562
+ const points = [new Vec(0, 0), new Vec(5, 5), new Vec(10, 0)] // vertex at (5,5)
563
+ const result = intersectLineSegmentPolyline(a1, a2, points)
564
+ expect(result).not.toBeNull()
565
+ expect(result!.length).toBe(1)
566
+ expect(result![0].x).toBeCloseTo(5, 5)
567
+ expect(result![0].y).toBeCloseTo(5, 5)
568
+ })
569
+
570
+ it('should detect intersection when line segment passes through a polyline vertext (floating point error case)', () => {
571
+ const result = intersectLineSegmentPolyline({ x: 100, y: 100 }, { x: 20, y: 20 }, [
572
+ { x: 36.141160159025375, y: 31.811740238538057 },
573
+ { x: 34.14213562373095, y: 34.14213562373095 },
574
+ { x: 31.811740238538057, y: 36.141160159025375 },
575
+ ])
576
+
577
+ expect(result).not.toBeNull()
578
+ })
579
+ })
534
580
  })
535
581
 
536
582
  describe('intersectLineSegmentPolygon', () => {
@@ -1,5 +1,5 @@
1
1
  import { Box } from './Box'
2
- import { pointInPolygon } from './utils'
2
+ import { approximately, approximatelyLte, pointInPolygon } from './utils'
3
3
  import { Vec, VecLike } from './Vec'
4
4
 
5
5
  // need even more intersections? See https://gist.github.com/steveruizok/35c02d526c707003a5c79761bfb89a52
@@ -17,7 +17,8 @@ export function intersectLineSegmentLineSegment(
17
17
  a1: VecLike,
18
18
  a2: VecLike,
19
19
  b1: VecLike,
20
- b2: VecLike
20
+ b2: VecLike,
21
+ precision = 1e-10
21
22
  ) {
22
23
  const ABx = a1.x - b1.x
23
24
  const ABy = a1.y - b1.y
@@ -29,14 +30,19 @@ export function intersectLineSegmentLineSegment(
29
30
  const ub_t = AVx * ABy - AVy * ABx
30
31
  const u_b = BVy * AVx - BVx * AVy
31
32
 
32
- if (ua_t === 0 || ub_t === 0) return null // coincident
33
+ if (approximately(ua_t, 0, precision) || approximately(ub_t, 0, precision)) return null // coincident
33
34
 
34
- if (u_b === 0) return null // parallel
35
+ if (approximately(u_b, 0, precision)) return null // parallel
35
36
 
36
37
  if (u_b !== 0) {
37
38
  const ua = ua_t / u_b
38
39
  const ub = ub_t / u_b
39
- if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) {
40
+ if (
41
+ approximatelyLte(0, ua, precision) &&
42
+ approximatelyLte(ua, 1, precision) &&
43
+ approximatelyLte(0, ub, precision) &&
44
+ approximatelyLte(ub, 1, precision)
45
+ ) {
40
46
  return Vec.AddXY(a1, ua * AVx, ua * AVy)
41
47
  }
42
48
  }
@@ -125,6 +131,7 @@ export function intersectLineSegmentPolygon(a1: VecLike, a2: VecLike, points: Ve
125
131
  points[i - 1],
126
132
  points[i % points.length]
127
133
  )
134
+
128
135
  if (segmentIntersection) result.push(segmentIntersection)
129
136
  }
130
137
 
@@ -77,6 +77,17 @@ export function approximately(a: number, b: number, precision = 0.000001) {
77
77
  return Math.abs(a - b) <= precision
78
78
  }
79
79
 
80
+ /**
81
+ * Whether a number is approximately less than or equal to another number.
82
+ *
83
+ * @param a - The first number.
84
+ * @param b - The second number.
85
+ * @public
86
+ */
87
+ export function approximatelyLte(a: number, b: number, precision = 0.000001) {
88
+ return a < b || approximately(a, b, precision)
89
+ }
90
+
80
91
  /**
81
92
  * Find the approximate perimeter of an ellipse.
82
93
  *
package/src/version.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  // This file is automatically generated by internal/scripts/refresh-assets.ts.
2
2
  // Do not edit manually. Or do, I'm a comment, not a cop.
3
3
 
4
- export const version = '3.15.0-canary.22a03ce9c171'
4
+ export const version = '3.15.0-canary.24182c470c85'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-07-09T11:27:52.513Z',
8
- patch: '2025-07-09T11:27:52.513Z',
7
+ minor: '2025-07-29T14:40:12.366Z',
8
+ patch: '2025-07-29T14:40:12.366Z',
9
9
  }
@@ -1,49 +0,0 @@
1
- import { describe } from 'node:test'
2
- import { createTLStore } from '../config/createTLStore'
3
- import { Editor } from '../editor/Editor'
4
- import { StateNode } from '../editor/tools/StateNode'
5
-
6
- let editor: Editor
7
-
8
- class A extends StateNode {
9
- static override id = 'A'
10
- }
11
-
12
- class B extends StateNode {
13
- static override id = 'B'
14
- }
15
-
16
- class C extends StateNode {
17
- static override id = 'C'
18
-
19
- override onEnter() {
20
- this.setCurrentToolIdMask('A')
21
- }
22
- }
23
-
24
- beforeEach(() => {
25
- editor = new Editor({
26
- initialState: 'A',
27
- shapeUtils: [],
28
- bindingUtils: [],
29
- tools: [A, B, C],
30
- store: createTLStore({ shapeUtils: [], bindingUtils: [] }),
31
- getContainer: () => document.body,
32
- })
33
- })
34
-
35
- describe('current tool id mask', () => {
36
- it('starts with the correct tool id', () => {
37
- expect(editor.getCurrentToolId()).toBe('A')
38
- })
39
-
40
- it('updates the current tool id', () => {
41
- editor.setCurrentTool('B')
42
- expect(editor.getCurrentToolId()).toBe('B')
43
- })
44
-
45
- it('masks the current tool id', () => {
46
- editor.setCurrentTool('C')
47
- expect(editor.getCurrentToolId()).toBe('A')
48
- })
49
- })