@tldraw/editor 3.12.0-canary.ceec8eda0db0 → 3.12.0-canary.d0e07565d818

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 +165 -17
  2. package/dist-cjs/index.js +3 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/TldrawEditor.js +5 -0
  5. package/dist-cjs/lib/TldrawEditor.js.map +2 -2
  6. package/dist-cjs/lib/components/GeometryDebuggingView.js +2 -2
  7. package/dist-cjs/lib/components/GeometryDebuggingView.js.map +2 -2
  8. package/dist-cjs/lib/components/Shape.js +10 -14
  9. package/dist-cjs/lib/components/Shape.js.map +2 -2
  10. package/dist-cjs/lib/components/default-components/DefaultCanvas.js +10 -1
  11. package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
  12. package/dist-cjs/lib/editor/Editor.js +224 -32
  13. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  14. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js +1 -1
  15. package/dist-cjs/lib/editor/derivations/notVisibleShapes.js.map +2 -2
  16. package/dist-cjs/lib/editor/managers/FocusManager.js +1 -1
  17. package/dist-cjs/lib/editor/managers/FocusManager.js.map +2 -2
  18. package/dist-cjs/lib/editor/managers/FontManager.js +1 -1
  19. package/dist-cjs/lib/editor/managers/FontManager.js.map +2 -2
  20. package/dist-cjs/lib/editor/shapes/ShapeUtil.js +12 -0
  21. package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
  22. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +4 -13
  23. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +2 -2
  24. package/dist-cjs/lib/editor/tools/StateNode.js +4 -1
  25. package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
  26. package/dist-cjs/lib/editor/types/selection-types.js.map +1 -1
  27. package/dist-cjs/lib/exports/StyleEmbedder.js +19 -5
  28. package/dist-cjs/lib/exports/StyleEmbedder.js.map +2 -2
  29. package/dist-cjs/lib/exports/cssRules.js +127 -0
  30. package/dist-cjs/lib/exports/cssRules.js.map +7 -0
  31. package/dist-cjs/lib/exports/parseCss.js +0 -69
  32. package/dist-cjs/lib/exports/parseCss.js.map +2 -2
  33. package/dist-cjs/lib/hooks/useCanvasEvents.js +13 -8
  34. package/dist-cjs/lib/hooks/useCanvasEvents.js.map +3 -3
  35. package/dist-cjs/lib/hooks/useDocumentEvents.js +16 -0
  36. package/dist-cjs/lib/hooks/useDocumentEvents.js.map +2 -2
  37. package/dist-cjs/lib/hooks/useGestureEvents.js +12 -6
  38. package/dist-cjs/lib/hooks/useGestureEvents.js.map +2 -2
  39. package/dist-cjs/lib/license/Watermark.js +10 -20
  40. package/dist-cjs/lib/license/Watermark.js.map +2 -2
  41. package/dist-cjs/lib/primitives/geometry/Geometry2d.js +128 -16
  42. package/dist-cjs/lib/primitives/geometry/Geometry2d.js.map +3 -3
  43. package/dist-cjs/lib/primitives/geometry/Group2d.js +53 -10
  44. package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
  45. package/dist-cjs/lib/primitives/intersect.js +20 -0
  46. package/dist-cjs/lib/primitives/intersect.js.map +2 -2
  47. package/dist-cjs/lib/utils/debug-flags.js +2 -1
  48. package/dist-cjs/lib/utils/debug-flags.js.map +2 -2
  49. package/dist-cjs/lib/utils/reorderShapes.js +2 -8
  50. package/dist-cjs/lib/utils/reorderShapes.js.map +2 -2
  51. package/dist-cjs/version.js +3 -3
  52. package/dist-cjs/version.js.map +1 -1
  53. package/dist-esm/index.d.mts +165 -17
  54. package/dist-esm/index.mjs +8 -2
  55. package/dist-esm/index.mjs.map +2 -2
  56. package/dist-esm/lib/TldrawEditor.mjs +5 -0
  57. package/dist-esm/lib/TldrawEditor.mjs.map +2 -2
  58. package/dist-esm/lib/components/GeometryDebuggingView.mjs +3 -3
  59. package/dist-esm/lib/components/GeometryDebuggingView.mjs.map +2 -2
  60. package/dist-esm/lib/components/Shape.mjs +11 -15
  61. package/dist-esm/lib/components/Shape.mjs.map +2 -2
  62. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +10 -1
  63. package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
  64. package/dist-esm/lib/editor/Editor.mjs +225 -32
  65. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  66. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs +1 -1
  67. package/dist-esm/lib/editor/derivations/notVisibleShapes.mjs.map +2 -2
  68. package/dist-esm/lib/editor/managers/FocusManager.mjs +1 -1
  69. package/dist-esm/lib/editor/managers/FocusManager.mjs.map +2 -2
  70. package/dist-esm/lib/editor/managers/FontManager.mjs +1 -1
  71. package/dist-esm/lib/editor/managers/FontManager.mjs.map +2 -2
  72. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs +12 -0
  73. package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
  74. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +4 -13
  75. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +2 -2
  76. package/dist-esm/lib/editor/tools/StateNode.mjs +4 -1
  77. package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
  78. package/dist-esm/lib/exports/StyleEmbedder.mjs +21 -12
  79. package/dist-esm/lib/exports/StyleEmbedder.mjs.map +2 -2
  80. package/dist-esm/lib/exports/cssRules.mjs +107 -0
  81. package/dist-esm/lib/exports/cssRules.mjs.map +7 -0
  82. package/dist-esm/lib/exports/parseCss.mjs +0 -69
  83. package/dist-esm/lib/exports/parseCss.mjs.map +2 -2
  84. package/dist-esm/lib/hooks/useCanvasEvents.mjs +13 -8
  85. package/dist-esm/lib/hooks/useCanvasEvents.mjs.map +3 -3
  86. package/dist-esm/lib/hooks/useDocumentEvents.mjs +16 -0
  87. package/dist-esm/lib/hooks/useDocumentEvents.mjs.map +2 -2
  88. package/dist-esm/lib/hooks/useGestureEvents.mjs +12 -6
  89. package/dist-esm/lib/hooks/useGestureEvents.mjs.map +2 -2
  90. package/dist-esm/lib/license/Watermark.mjs +10 -20
  91. package/dist-esm/lib/license/Watermark.mjs.map +2 -2
  92. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs +132 -14
  93. package/dist-esm/lib/primitives/geometry/Geometry2d.mjs.map +2 -2
  94. package/dist-esm/lib/primitives/geometry/Group2d.mjs +54 -11
  95. package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
  96. package/dist-esm/lib/primitives/intersect.mjs +20 -0
  97. package/dist-esm/lib/primitives/intersect.mjs.map +2 -2
  98. package/dist-esm/lib/utils/debug-flags.mjs +2 -1
  99. package/dist-esm/lib/utils/debug-flags.mjs.map +2 -2
  100. package/dist-esm/lib/utils/reorderShapes.mjs +2 -8
  101. package/dist-esm/lib/utils/reorderShapes.mjs.map +2 -2
  102. package/dist-esm/version.mjs +3 -3
  103. package/dist-esm/version.mjs.map +1 -1
  104. package/editor.css +51 -30
  105. package/package.json +7 -7
  106. package/src/index.ts +11 -2
  107. package/src/lib/TldrawEditor.tsx +30 -3
  108. package/src/lib/components/GeometryDebuggingView.tsx +3 -3
  109. package/src/lib/components/Shape.tsx +15 -19
  110. package/src/lib/components/default-components/DefaultCanvas.tsx +6 -1
  111. package/src/lib/editor/Editor.ts +334 -39
  112. package/src/lib/editor/derivations/notVisibleShapes.ts +1 -1
  113. package/src/lib/editor/managers/FocusManager.ts +1 -1
  114. package/src/lib/editor/managers/FontManager.ts +1 -1
  115. package/src/lib/editor/shapes/ShapeUtil.ts +14 -0
  116. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +7 -15
  117. package/src/lib/editor/tools/StateNode.ts +6 -1
  118. package/src/lib/editor/types/selection-types.ts +3 -0
  119. package/src/lib/exports/StyleEmbedder.ts +25 -15
  120. package/src/lib/exports/cssRules.ts +126 -0
  121. package/src/lib/exports/parseCss.ts +0 -79
  122. package/src/lib/hooks/useCanvasEvents.ts +19 -8
  123. package/src/lib/hooks/useDocumentEvents.ts +18 -0
  124. package/src/lib/hooks/useGestureEvents.ts +12 -6
  125. package/src/lib/license/Watermark.tsx +18 -29
  126. package/src/lib/primitives/geometry/Geometry2d.ts +196 -16
  127. package/src/lib/primitives/geometry/Group2d.ts +75 -12
  128. package/src/lib/primitives/intersect.ts +41 -0
  129. package/src/lib/utils/debug-flags.ts +1 -0
  130. package/src/lib/utils/reorderShapes.ts +2 -9
  131. package/src/version.ts +3 -3
@@ -1,12 +1,51 @@
1
+ import { assert } from '@tldraw/utils'
1
2
  import { Box } from '../Box'
2
- import { Vec } from '../Vec'
3
- import { pointInPolygon } from '../utils'
3
+ import { Mat, MatModel } from '../Mat'
4
+ import { Vec, VecLike } from '../Vec'
5
+ import {
6
+ intersectCirclePolygon,
7
+ intersectCirclePolyline,
8
+ intersectLineSegmentPolygon,
9
+ intersectLineSegmentPolyline,
10
+ intersectPolys,
11
+ } from '../intersect'
12
+ import { approximately, pointInPolygon } from '../utils'
13
+
14
+ /**
15
+ * Filter geometry within a group.
16
+ *
17
+ * Filters are ignored when called directly on primitive geometries, but can be used to narrow down
18
+ * the results of an operation on `Group2d` geometries.
19
+ *
20
+ * @public
21
+ */
22
+ export interface Geometry2dFilters {
23
+ readonly includeLabels?: boolean
24
+ readonly includeInternal?: boolean
25
+ }
26
+
27
+ /** @public */
28
+ export const Geometry2dFilters: {
29
+ EXCLUDE_NON_STANDARD: Geometry2dFilters
30
+ INCLUDE_ALL: Geometry2dFilters
31
+ EXCLUDE_LABELS: Geometry2dFilters
32
+ EXCLUDE_INTERNAL: Geometry2dFilters
33
+ } = {
34
+ EXCLUDE_NON_STANDARD: {
35
+ includeLabels: false,
36
+ includeInternal: false,
37
+ },
38
+ INCLUDE_ALL: { includeLabels: true, includeInternal: true },
39
+ EXCLUDE_LABELS: { includeLabels: false, includeInternal: true },
40
+ EXCLUDE_INTERNAL: { includeLabels: true, includeInternal: false },
41
+ }
4
42
 
5
43
  /** @public */
6
44
  export interface Geometry2dOptions {
7
45
  isFilled: boolean
8
46
  isClosed: boolean
9
47
  isLabel?: boolean
48
+ isInternal?: boolean
10
49
  debugColor?: string
11
50
  ignore?: boolean
12
51
  }
@@ -16,6 +55,7 @@ export abstract class Geometry2d {
16
55
  isFilled = false
17
56
  isClosed = true
18
57
  isLabel = false
58
+ isInternal = false
19
59
  debugColor?: string
20
60
  ignore?: boolean
21
61
 
@@ -23,20 +63,23 @@ export abstract class Geometry2d {
23
63
  this.isFilled = opts.isFilled
24
64
  this.isClosed = opts.isClosed
25
65
  this.isLabel = opts.isLabel ?? false
66
+ this.isInternal = opts.isInternal ?? false
26
67
  this.debugColor = opts.debugColor
27
68
  this.ignore = opts.ignore
28
69
  }
29
70
 
30
- abstract getVertices(): Vec[]
71
+ isExcludedByFilter(filters?: Geometry2dFilters) {
72
+ if (!filters) return false
73
+ if (this.isLabel && !filters.includeLabels) return true
74
+ if (this.isInternal && !filters.includeInternal) return true
75
+ return false
76
+ }
31
77
 
32
- abstract nearestPoint(point: Vec): Vec
78
+ abstract getVertices(filters: Geometry2dFilters): Vec[]
33
79
 
34
- // hitTestPoint(point: Vec, margin = 0, hitInside = false) {
35
- // // We've removed the broad phase here; that should be done outside of the call
36
- // return this.distanceToPoint(point, hitInside) <= margin
37
- // }
80
+ abstract nearestPoint(point: Vec, _filters?: Geometry2dFilters): Vec
38
81
 
39
- hitTestPoint(point: Vec, margin = 0, hitInside = false) {
82
+ hitTestPoint(point: Vec, margin = 0, hitInside = false, _filters?: Geometry2dFilters) {
40
83
  // First check whether the point is inside
41
84
  if (this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices)) {
42
85
  return true
@@ -45,17 +88,17 @@ export abstract class Geometry2d {
45
88
  return Vec.Dist2(point, this.nearestPoint(point)) <= margin * margin
46
89
  }
47
90
 
48
- distanceToPoint(point: Vec, hitInside = false) {
91
+ distanceToPoint(point: Vec, hitInside = false, filters?: Geometry2dFilters) {
49
92
  return (
50
- point.dist(this.nearestPoint(point)) *
93
+ point.dist(this.nearestPoint(point, filters)) *
51
94
  (this.isClosed && (this.isFilled || hitInside) && pointInPolygon(point, this.vertices)
52
95
  ? -1
53
96
  : 1)
54
97
  )
55
98
  }
56
99
 
57
- distanceToLineSegment(A: Vec, B: Vec) {
58
- if (A.equals(B)) return this.distanceToPoint(A)
100
+ distanceToLineSegment(A: Vec, B: Vec, filters?: Geometry2dFilters) {
101
+ if (A.equals(B)) return this.distanceToPoint(A, false, filters)
59
102
  const { vertices } = this
60
103
  let nearest: Vec | undefined
61
104
  let dist = Infinity
@@ -73,10 +116,35 @@ export abstract class Geometry2d {
73
116
  return this.isClosed && this.isFilled && pointInPolygon(nearest, this.vertices) ? -dist : dist
74
117
  }
75
118
 
76
- hitTestLineSegment(A: Vec, B: Vec, distance = 0): boolean {
77
- return this.distanceToLineSegment(A, B) <= distance
119
+ hitTestLineSegment(A: Vec, B: Vec, distance = 0, filters?: Geometry2dFilters): boolean {
120
+ return this.distanceToLineSegment(A, B, filters) <= distance
121
+ }
122
+
123
+ intersectLineSegment(A: VecLike, B: VecLike, _filters?: Geometry2dFilters): VecLike[] {
124
+ const intersections = this.isClosed
125
+ ? intersectLineSegmentPolygon(A, B, this.vertices)
126
+ : intersectLineSegmentPolyline(A, B, this.vertices)
127
+
128
+ return intersections ?? []
129
+ }
130
+
131
+ intersectCircle(center: VecLike, radius: number, _filters?: Geometry2dFilters): VecLike[] {
132
+ const intersections = this.isClosed
133
+ ? intersectCirclePolygon(center, radius, this.vertices)
134
+ : intersectCirclePolyline(center, radius, this.vertices)
135
+
136
+ return intersections ?? []
137
+ }
138
+
139
+ intersectPolygon(polygon: VecLike[], _filters?: Geometry2dFilters): VecLike[] {
140
+ return intersectPolys(polygon, this.vertices, true, this.isClosed)
141
+ }
142
+
143
+ intersectPolyline(polyline: VecLike[], _filters?: Geometry2dFilters): VecLike[] {
144
+ return intersectPolys(polyline, this.vertices, false, this.isClosed)
78
145
  }
79
146
 
147
+ /** @deprecated Iterate the vertices instead. */
80
148
  nearestPointOnLineSegment(A: Vec, B: Vec): Vec {
81
149
  const { vertices } = this
82
150
  let nearest: Vec | undefined
@@ -105,12 +173,16 @@ export abstract class Geometry2d {
105
173
  )
106
174
  }
107
175
 
176
+ transform(transform: MatModel): Geometry2d {
177
+ return new TransformedGeometry2d(this, transform)
178
+ }
179
+
108
180
  private _vertices: Vec[] | undefined
109
181
 
110
182
  // eslint-disable-next-line no-restricted-syntax
111
183
  get vertices(): Vec[] {
112
184
  if (!this._vertices) {
113
- this._vertices = this.getVertices()
185
+ this._vertices = this.getVertices(Geometry2dFilters.EXCLUDE_LABELS)
114
186
  }
115
187
 
116
188
  return this._vertices
@@ -204,3 +276,111 @@ export abstract class Geometry2d {
204
276
 
205
277
  abstract getSvgPathData(first: boolean): string
206
278
  }
279
+
280
+ // =================================================================================================
281
+ // Because Geometry2d.transform depends on TransformedGeometry2d, we need to define it here instead
282
+ // of in its own files. This prevents a circular import error.
283
+ // =================================================================================================
284
+
285
+ /** @public */
286
+ export class TransformedGeometry2d extends Geometry2d {
287
+ private readonly inverse: MatModel
288
+ private readonly decomposed
289
+
290
+ constructor(
291
+ private readonly geometry: Geometry2d,
292
+ private readonly matrix: MatModel
293
+ ) {
294
+ super(geometry)
295
+ this.inverse = Mat.Inverse(matrix)
296
+ this.decomposed = Mat.Decompose(matrix)
297
+
298
+ assert(
299
+ approximately(this.decomposed.scaleX, this.decomposed.scaleY),
300
+ 'non-uniform scaling is not yet supported'
301
+ )
302
+ }
303
+
304
+ getVertices(filters: Geometry2dFilters): Vec[] {
305
+ return this.geometry.getVertices(filters).map((v) => Mat.applyToPoint(this.matrix, v))
306
+ }
307
+
308
+ nearestPoint(point: Vec, filters?: Geometry2dFilters): Vec {
309
+ return Mat.applyToPoint(
310
+ this.matrix,
311
+ this.geometry.nearestPoint(Mat.applyToPoint(this.inverse, point), filters)
312
+ )
313
+ }
314
+
315
+ override hitTestPoint(
316
+ point: Vec,
317
+ margin = 0,
318
+ hitInside?: boolean,
319
+ filters?: Geometry2dFilters
320
+ ): boolean {
321
+ return this.geometry.hitTestPoint(
322
+ Mat.applyToPoint(this.inverse, point),
323
+ margin / this.decomposed.scaleX,
324
+ hitInside,
325
+ filters
326
+ )
327
+ }
328
+
329
+ override distanceToPoint(point: Vec, hitInside = false, filters?: Geometry2dFilters) {
330
+ return (
331
+ this.geometry.distanceToPoint(Mat.applyToPoint(this.inverse, point), hitInside, filters) *
332
+ this.decomposed.scaleX
333
+ )
334
+ }
335
+
336
+ override distanceToLineSegment(A: Vec, B: Vec, filters?: Geometry2dFilters) {
337
+ return (
338
+ this.geometry.distanceToLineSegment(
339
+ Mat.applyToPoint(this.inverse, A),
340
+ Mat.applyToPoint(this.inverse, B),
341
+ filters
342
+ ) * this.decomposed.scaleX
343
+ )
344
+ }
345
+
346
+ override hitTestLineSegment(A: Vec, B: Vec, distance = 0, filters?: Geometry2dFilters): boolean {
347
+ return this.geometry.hitTestLineSegment(
348
+ Mat.applyToPoint(this.inverse, A),
349
+ Mat.applyToPoint(this.inverse, B),
350
+ distance / this.decomposed.scaleX,
351
+ filters
352
+ )
353
+ }
354
+
355
+ override intersectLineSegment(A: VecLike, B: VecLike, filters?: Geometry2dFilters) {
356
+ return this.geometry.intersectLineSegment(
357
+ Mat.applyToPoint(this.inverse, A),
358
+ Mat.applyToPoint(this.inverse, B),
359
+ filters
360
+ )
361
+ }
362
+
363
+ override intersectCircle(center: VecLike, radius: number, filters?: Geometry2dFilters) {
364
+ return this.geometry.intersectCircle(
365
+ Mat.applyToPoint(this.inverse, center),
366
+ radius / this.decomposed.scaleX,
367
+ filters
368
+ )
369
+ }
370
+
371
+ override intersectPolygon(polygon: VecLike[], filters?: Geometry2dFilters): VecLike[] {
372
+ return this.geometry.intersectPolygon(Mat.applyToPoints(this.inverse, polygon), filters)
373
+ }
374
+
375
+ override intersectPolyline(polyline: VecLike[], filters?: Geometry2dFilters): VecLike[] {
376
+ return this.geometry.intersectPolyline(Mat.applyToPoints(this.inverse, polyline), filters)
377
+ }
378
+
379
+ override transform(transform: MatModel): Geometry2d {
380
+ return new TransformedGeometry2d(this.geometry, Mat.Multiply(transform, this.matrix))
381
+ }
382
+
383
+ getSvgPathData(): string {
384
+ throw new Error('Cannot get SVG path data for transformed geometry.')
385
+ }
386
+ }
@@ -1,6 +1,8 @@
1
+ import { EMPTY_ARRAY } from '@tldraw/state'
1
2
  import { Box } from '../Box'
2
- import { Vec } from '../Vec'
3
- import { Geometry2d, Geometry2dOptions } from './Geometry2d'
3
+ import { Mat } from '../Mat'
4
+ import { Vec, VecLike } from '../Vec'
5
+ import { Geometry2d, Geometry2dFilters, Geometry2dOptions } from './Geometry2d'
4
6
 
5
7
  /** @public */
6
8
  export class Group2d extends Geometry2d {
@@ -25,11 +27,14 @@ export class Group2d extends Geometry2d {
25
27
  if (this.children.length === 0) throw Error('Group2d must have at least one child')
26
28
  }
27
29
 
28
- override getVertices(): Vec[] {
29
- return this.children.filter((c) => !c.isLabel).flatMap((c) => c.vertices)
30
+ override getVertices(filters: Geometry2dFilters): Vec[] {
31
+ if (this.isExcludedByFilter(filters)) return []
32
+ return this.children
33
+ .filter((c) => !c.isExcludedByFilter(filters))
34
+ .flatMap((c) => c.getVertices(filters))
30
35
  }
31
36
 
32
- override nearestPoint(point: Vec): Vec {
37
+ override nearestPoint(point: Vec, filters?: Geometry2dFilters): Vec {
33
38
  let dist = Infinity
34
39
  let nearest: Vec | undefined
35
40
 
@@ -42,7 +47,8 @@ export class Group2d extends Geometry2d {
42
47
  let p: Vec
43
48
  let d: number
44
49
  for (const child of children) {
45
- p = child.nearestPoint(point)
50
+ if (child.isExcludedByFilter(filters)) continue
51
+ p = child.nearestPoint(point, filters)
46
52
  d = Vec.Dist2(p, point)
47
53
  if (d < dist) {
48
54
  dist = d
@@ -53,18 +59,75 @@ export class Group2d extends Geometry2d {
53
59
  return nearest
54
60
  }
55
61
 
56
- override distanceToPoint(point: Vec, hitInside = false) {
57
- return Math.min(...this.children.map((c, i) => c.distanceToPoint(point, hitInside || i > 0)))
62
+ override distanceToPoint(point: Vec, hitInside = false, filters?: Geometry2dFilters) {
63
+ let smallestDistance = Infinity
64
+ for (const child of this.children) {
65
+ if (child.isExcludedByFilter(filters)) continue
66
+ const distance = child.distanceToPoint(point, hitInside, filters)
67
+ if (distance < smallestDistance) {
68
+ smallestDistance = distance
69
+ }
70
+ }
71
+ return smallestDistance
58
72
  }
59
73
 
60
- override hitTestPoint(point: Vec, margin: number, hitInside: boolean): boolean {
74
+ override hitTestPoint(
75
+ point: Vec,
76
+ margin: number,
77
+ hitInside: boolean,
78
+ filters = Geometry2dFilters.EXCLUDE_LABELS
79
+ ): boolean {
61
80
  return !!this.children
62
- .filter((c) => !c.isLabel)
81
+ .filter((c) => !c.isExcludedByFilter(filters))
63
82
  .find((c) => c.hitTestPoint(point, margin, hitInside))
64
83
  }
65
84
 
66
- override hitTestLineSegment(A: Vec, B: Vec, zoom: number): boolean {
67
- return !!this.children.filter((c) => !c.isLabel).find((c) => c.hitTestLineSegment(A, B, zoom))
85
+ override hitTestLineSegment(
86
+ A: Vec,
87
+ B: Vec,
88
+ zoom: number,
89
+ filters = Geometry2dFilters.EXCLUDE_LABELS
90
+ ): boolean {
91
+ return !!this.children
92
+ .filter((c) => !c.isExcludedByFilter(filters))
93
+ .find((c) => c.hitTestLineSegment(A, B, zoom))
94
+ }
95
+
96
+ override intersectLineSegment(A: VecLike, B: VecLike, filters?: Geometry2dFilters) {
97
+ return this.children.flatMap((child) => {
98
+ if (child.isExcludedByFilter(filters)) return EMPTY_ARRAY
99
+ return child.intersectLineSegment(A, B, filters)
100
+ })
101
+ }
102
+
103
+ override intersectCircle(center: VecLike, radius: number, filters?: Geometry2dFilters) {
104
+ return this.children.flatMap((child) => {
105
+ if (child.isExcludedByFilter(filters)) return EMPTY_ARRAY
106
+ return child.intersectCircle(center, radius, filters)
107
+ })
108
+ }
109
+
110
+ override intersectPolygon(polygon: VecLike[], filters?: Geometry2dFilters) {
111
+ return this.children.flatMap((child) => {
112
+ if (child.isExcludedByFilter(filters)) return EMPTY_ARRAY
113
+ return child.intersectPolygon(polygon, filters)
114
+ })
115
+ }
116
+
117
+ override intersectPolyline(polyline: VecLike[], filters?: Geometry2dFilters) {
118
+ return this.children.flatMap((child) => {
119
+ if (child.isExcludedByFilter(filters)) return EMPTY_ARRAY
120
+ return child.intersectPolyline(polyline, filters)
121
+ })
122
+ }
123
+
124
+ override transform(transform: Mat): Geometry2d {
125
+ return new Group2d({
126
+ children: this.children.map((c) => c.transform(transform)),
127
+ isLabel: this.isLabel,
128
+ debugColor: this.debugColor,
129
+ ignore: this.ignore,
130
+ })
68
131
  }
69
132
 
70
133
  getArea() {
@@ -293,6 +293,47 @@ export function intersectPolygonPolygon(
293
293
  return orderClockwise([...result.values()])
294
294
  }
295
295
 
296
+ /**
297
+ * Find all the points where `polyA` and `polyB` intersect and returns them in an undefined order.
298
+ * To find the polygon that's the intersection of polyA and polyB, use `intersectPolygonPolygon`
299
+ * instead, which orders the points and includes internal points.
300
+ *
301
+ * @param polyA - The first polygon.
302
+ * @param polyB - The second polygon.
303
+ * @param isAClosed - Whether `polyA` is a closed polygon or a polyline.
304
+ * @param isBClosed - Whether `polyB` is a closed polygon or a polyline.
305
+ * @public
306
+ */
307
+ export function intersectPolys(
308
+ polyA: VecLike[],
309
+ polyB: VecLike[],
310
+ isAClosed: boolean,
311
+ isBClosed: boolean
312
+ ): VecLike[] {
313
+ const result: Map<string, VecLike> = new Map()
314
+
315
+ // Add all intersection points to result
316
+ for (let i = 0, n = isAClosed ? polyA.length : polyA.length - 1; i < n; i++) {
317
+ const currentA = polyA[i]
318
+ const nextA = polyA[(i + 1) % polyA.length]
319
+
320
+ for (let j = 0, m = isBClosed ? polyB.length : polyB.length - 1; j < m; j++) {
321
+ const currentB = polyB[j]
322
+ const nextB = polyB[(j + 1) % polyB.length]
323
+ const intersection = intersectLineSegmentLineSegment(currentA, nextA, currentB, nextB)
324
+
325
+ if (intersection !== null) {
326
+ const id = getPointId(intersection)
327
+ if (!result.has(id)) {
328
+ result.set(id, intersection)
329
+ }
330
+ }
331
+ }
332
+ }
333
+
334
+ return [...result.values()]
335
+ }
336
+
296
337
  function getPointId(point: VecLike) {
297
338
  return `${point.x},${point.y}`
298
339
  }
@@ -53,6 +53,7 @@ export const debugFlags = {
53
53
  debugGeometry: createDebugValue('debugGeometry', { defaults: { all: false } }),
54
54
  hideShapes: createDebugValue('hideShapes', { defaults: { all: false } }),
55
55
  editOnType: createDebugValue('editOnType', { defaults: { all: false } }),
56
+ a11y: createDebugValue('a11y', { defaults: { all: false } }),
56
57
  } as const
57
58
 
58
59
  declare global {
@@ -154,24 +154,17 @@ function reorderToFront(moving: Set<TLShape>, children: TLShape[], changes: TLSh
154
154
  }
155
155
  }
156
156
 
157
- function getVerticesInPageSpace(editor: Editor, shape: TLShape) {
158
- const geo = editor.getShapeGeometry(shape)
159
- const pageTransform = editor.getShapePageTransform(shape)
160
- if (!geo || !pageTransform) return null
161
- return pageTransform.applyToPoints(geo.vertices)
162
- }
163
-
164
157
  function getOverlapChecker(editor: Editor, moving: Set<TLShape>) {
165
158
  const movingVertices = Array.from(moving)
166
159
  .map((shape) => {
167
- const vertices = getVerticesInPageSpace(editor, shape)
160
+ const vertices = editor.getShapePageGeometry(shape).vertices
168
161
  if (!vertices) return null
169
162
  return { shape, vertices }
170
163
  })
171
164
  .filter(Boolean) as { shape: TLShape; vertices: Vec[] }[]
172
165
 
173
166
  const isOverlapping = (child: TLShape) => {
174
- const vertices = getVerticesInPageSpace(editor, child)
167
+ const vertices = editor.getShapePageGeometry(child).vertices
175
168
  if (!vertices) return false
176
169
  return movingVertices.some((other) => {
177
170
  return polygonsIntersect(other.vertices, vertices)
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.12.0-canary.ceec8eda0db0'
4
+ export const version = '3.12.0-canary.d0e07565d818'
5
5
  export const publishDates = {
6
6
  major: '2024-09-13T14:36:29.063Z',
7
- minor: '2025-03-20T16:36:46.091Z',
8
- patch: '2025-03-20T16:36:46.091Z',
7
+ minor: '2025-04-15T10:00:23.888Z',
8
+ patch: '2025-04-15T10:00:23.888Z',
9
9
  }