@tldraw/editor 3.15.0-canary.21bb6b44433a → 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.
- package/dist-cjs/index.d.ts +103 -7
- package/dist-cjs/index.js +3 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/lib/components/SVGContainer.js +1 -1
- package/dist-cjs/lib/components/SVGContainer.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultBrush.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultBrush.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultCanvas.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultCollaboratorHint.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultCursor.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultCursor.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultGrid.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultGrid.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultHandles.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultHandles.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultShapeIndicator.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js +1 -1
- package/dist-cjs/lib/components/default-components/DefaultSnapIndictor.js.map +2 -2
- package/dist-cjs/lib/components/default-components/DefaultSpinner.js +27 -15
- package/dist-cjs/lib/components/default-components/DefaultSpinner.js.map +3 -3
- package/dist-cjs/lib/editor/Editor.js +88 -43
- package/dist-cjs/lib/editor/Editor.js.map +2 -2
- package/dist-cjs/lib/editor/managers/TextManager/TextManager.js +96 -101
- package/dist-cjs/lib/editor/managers/TextManager/TextManager.js.map +2 -2
- package/dist-cjs/lib/editor/shapes/ShapeUtil.js.map +2 -2
- package/dist-cjs/lib/editor/tools/StateNode.js +20 -1
- package/dist-cjs/lib/editor/tools/StateNode.js.map +2 -2
- package/dist-cjs/lib/editor/types/misc-types.js.map +1 -1
- package/dist-cjs/lib/hooks/useEditorComponents.js.map +1 -1
- package/dist-cjs/lib/license/Watermark.js +2 -2
- package/dist-cjs/lib/license/Watermark.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Arc2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Circle2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +3 -1
- package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/Ellipse2d.js +1 -1
- package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +2 -2
- package/dist-cjs/lib/primitives/geometry/geometry-constants.js +2 -2
- package/dist-cjs/lib/primitives/geometry/geometry-constants.js.map +2 -2
- package/dist-cjs/lib/primitives/intersect.js +4 -4
- package/dist-cjs/lib/primitives/intersect.js.map +2 -2
- package/dist-cjs/lib/primitives/utils.js +4 -0
- package/dist-cjs/lib/primitives/utils.js.map +2 -2
- package/dist-cjs/version.js +3 -3
- package/dist-cjs/version.js.map +1 -1
- package/dist-esm/index.d.mts +103 -7
- package/dist-esm/index.mjs +3 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/lib/components/SVGContainer.mjs +1 -1
- package/dist-esm/lib/components/SVGContainer.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultBrush.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultBrush.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultCanvas.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultCollaboratorHint.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultCursor.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultCursor.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultGrid.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultGrid.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultHandles.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultHandles.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultShapeIndicator.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs +1 -1
- package/dist-esm/lib/components/default-components/DefaultSnapIndictor.mjs.map +2 -2
- package/dist-esm/lib/components/default-components/DefaultSpinner.mjs +17 -15
- package/dist-esm/lib/components/default-components/DefaultSpinner.mjs.map +2 -2
- package/dist-esm/lib/editor/Editor.mjs +88 -43
- package/dist-esm/lib/editor/Editor.mjs.map +2 -2
- package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs +96 -101
- package/dist-esm/lib/editor/managers/TextManager/TextManager.mjs.map +2 -2
- package/dist-esm/lib/editor/shapes/ShapeUtil.mjs.map +2 -2
- package/dist-esm/lib/editor/tools/StateNode.mjs +20 -1
- package/dist-esm/lib/editor/tools/StateNode.mjs.map +2 -2
- package/dist-esm/lib/hooks/useEditorComponents.mjs.map +1 -1
- package/dist-esm/lib/license/Watermark.mjs +2 -2
- package/dist-esm/lib/license/Watermark.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Arc2d.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Circle2d.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +3 -1
- package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +2 -2
- package/dist-esm/lib/primitives/geometry/geometry-constants.mjs +2 -2
- package/dist-esm/lib/primitives/geometry/geometry-constants.mjs.map +2 -2
- package/dist-esm/lib/primitives/intersect.mjs +5 -5
- package/dist-esm/lib/primitives/intersect.mjs.map +2 -2
- package/dist-esm/lib/primitives/utils.mjs +4 -0
- package/dist-esm/lib/primitives/utils.mjs.map +2 -2
- package/dist-esm/version.mjs +3 -3
- package/dist-esm/version.mjs.map +1 -1
- package/editor.css +21 -27
- package/package.json +9 -8
- package/src/index.ts +2 -0
- package/src/lib/components/SVGContainer.tsx +1 -1
- package/src/lib/components/default-components/DefaultBrush.tsx +1 -1
- package/src/lib/components/default-components/DefaultCanvas.tsx +1 -1
- package/src/lib/components/default-components/DefaultCollaboratorHint.tsx +1 -1
- package/src/lib/components/default-components/DefaultCursor.tsx +1 -1
- package/src/lib/components/default-components/DefaultGrid.tsx +1 -1
- package/src/lib/components/default-components/DefaultHandles.tsx +5 -1
- package/src/lib/components/default-components/DefaultShapeIndicator.tsx +1 -1
- package/src/lib/components/default-components/DefaultSnapIndictor.tsx +1 -1
- package/src/lib/components/default-components/DefaultSpinner.tsx +12 -12
- package/src/lib/editor/Editor.test.ts +407 -0
- package/src/lib/editor/Editor.ts +106 -44
- package/src/lib/editor/managers/TextManager/TextManager.ts +108 -128
- package/src/lib/editor/shapes/ShapeUtil.ts +57 -0
- package/src/lib/editor/tools/StateNode.test.ts +285 -0
- package/src/lib/editor/tools/StateNode.ts +27 -1
- package/src/lib/editor/types/misc-types.ts +19 -0
- package/src/lib/hooks/useEditorComponents.tsx +1 -1
- package/src/lib/license/Watermark.tsx +2 -2
- package/src/lib/primitives/geometry/Arc2d.ts +2 -2
- package/src/lib/primitives/geometry/Circle2d.ts +2 -2
- package/src/lib/primitives/geometry/CubicBezier2d.ts +4 -1
- package/src/lib/primitives/geometry/Ellipse2d.ts +2 -2
- package/src/lib/primitives/geometry/geometry-constants.ts +2 -1
- package/src/lib/primitives/intersect.test.ts +57 -11
- package/src/lib/primitives/intersect.ts +12 -5
- package/src/lib/primitives/utils.ts +11 -0
- package/src/version.ts +3 -3
- 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 (
|
|
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:
|
|
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
|
|
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 {
|
|
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 =
|
|
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 {
|
|
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 =
|
|
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 =
|
|
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 {
|
|
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 =
|
|
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
|
-
|
|
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
|
|
33
|
+
if (approximately(ua_t, 0, precision) || approximately(ub_t, 0, precision)) return null // coincident
|
|
33
34
|
|
|
34
|
-
if (u_b
|
|
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 (
|
|
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.
|
|
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-
|
|
8
|
-
patch: '2025-07-
|
|
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
|
-
})
|