@tldraw/editor 3.14.0-canary.b8a2a8b543ef → 3.14.0-canary.b8c5ab05b06a

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 (101) hide show
  1. package/dist-cjs/index.d.ts +51 -63
  2. package/dist-cjs/index.js +3 -1
  3. package/dist-cjs/index.js.map +2 -2
  4. package/dist-cjs/lib/editor/Editor.js +11 -47
  5. package/dist-cjs/lib/editor/Editor.js.map +2 -2
  6. package/dist-cjs/lib/editor/managers/FontManager.js +5 -1
  7. package/dist-cjs/lib/editor/managers/FontManager.js.map +2 -2
  8. package/dist-cjs/lib/primitives/Box.js +39 -33
  9. package/dist-cjs/lib/primitives/Box.js.map +2 -2
  10. package/dist-cjs/lib/primitives/Vec.js +13 -8
  11. package/dist-cjs/lib/primitives/Vec.js.map +2 -2
  12. package/dist-cjs/lib/primitives/geometry/Arc2d.js +41 -21
  13. package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
  14. package/dist-cjs/lib/primitives/geometry/Circle2d.js +11 -11
  15. package/dist-cjs/lib/primitives/geometry/Circle2d.js.map +2 -2
  16. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +13 -16
  17. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
  18. package/dist-cjs/lib/primitives/geometry/CubicSpline2d.js +4 -4
  19. package/dist-cjs/lib/primitives/geometry/CubicSpline2d.js.map +2 -2
  20. package/dist-cjs/lib/primitives/geometry/Edge2d.js +14 -17
  21. package/dist-cjs/lib/primitives/geometry/Edge2d.js.map +2 -2
  22. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js +10 -10
  23. package/dist-cjs/lib/primitives/geometry/Ellipse2d.js.map +2 -2
  24. package/dist-cjs/lib/primitives/geometry/Point2d.js +6 -6
  25. package/dist-cjs/lib/primitives/geometry/Point2d.js.map +2 -2
  26. package/dist-cjs/lib/primitives/geometry/Polygon2d.js +3 -0
  27. package/dist-cjs/lib/primitives/geometry/Polygon2d.js.map +2 -2
  28. package/dist-cjs/lib/primitives/geometry/Polyline2d.js +8 -5
  29. package/dist-cjs/lib/primitives/geometry/Polyline2d.js.map +2 -2
  30. package/dist-cjs/lib/primitives/geometry/Rectangle2d.js +22 -11
  31. package/dist-cjs/lib/primitives/geometry/Rectangle2d.js.map +2 -2
  32. package/dist-cjs/lib/primitives/geometry/Stadium2d.js +22 -22
  33. package/dist-cjs/lib/primitives/geometry/Stadium2d.js.map +2 -2
  34. package/dist-cjs/lib/utils/areShapesContentEqual.js +1 -1
  35. package/dist-cjs/lib/utils/areShapesContentEqual.js.map +2 -2
  36. package/dist-cjs/lib/utils/reorderShapes.js +11 -10
  37. package/dist-cjs/lib/utils/reorderShapes.js.map +2 -2
  38. package/dist-cjs/version.js +3 -3
  39. package/dist-cjs/version.js.map +1 -1
  40. package/dist-esm/index.d.mts +51 -63
  41. package/dist-esm/index.mjs +3 -1
  42. package/dist-esm/index.mjs.map +2 -2
  43. package/dist-esm/lib/editor/Editor.mjs +11 -47
  44. package/dist-esm/lib/editor/Editor.mjs.map +2 -2
  45. package/dist-esm/lib/editor/managers/FontManager.mjs +5 -1
  46. package/dist-esm/lib/editor/managers/FontManager.mjs.map +2 -2
  47. package/dist-esm/lib/primitives/Box.mjs +39 -33
  48. package/dist-esm/lib/primitives/Box.mjs.map +2 -2
  49. package/dist-esm/lib/primitives/Vec.mjs +13 -8
  50. package/dist-esm/lib/primitives/Vec.mjs.map +2 -2
  51. package/dist-esm/lib/primitives/geometry/Arc2d.mjs +41 -21
  52. package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
  53. package/dist-esm/lib/primitives/geometry/Circle2d.mjs +11 -11
  54. package/dist-esm/lib/primitives/geometry/Circle2d.mjs.map +2 -2
  55. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +13 -16
  56. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
  57. package/dist-esm/lib/primitives/geometry/CubicSpline2d.mjs +4 -4
  58. package/dist-esm/lib/primitives/geometry/CubicSpline2d.mjs.map +2 -2
  59. package/dist-esm/lib/primitives/geometry/Edge2d.mjs +14 -17
  60. package/dist-esm/lib/primitives/geometry/Edge2d.mjs.map +2 -2
  61. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs +11 -11
  62. package/dist-esm/lib/primitives/geometry/Ellipse2d.mjs.map +2 -2
  63. package/dist-esm/lib/primitives/geometry/Point2d.mjs +6 -6
  64. package/dist-esm/lib/primitives/geometry/Point2d.mjs.map +2 -2
  65. package/dist-esm/lib/primitives/geometry/Polygon2d.mjs +3 -0
  66. package/dist-esm/lib/primitives/geometry/Polygon2d.mjs.map +2 -2
  67. package/dist-esm/lib/primitives/geometry/Polyline2d.mjs +8 -5
  68. package/dist-esm/lib/primitives/geometry/Polyline2d.mjs.map +2 -2
  69. package/dist-esm/lib/primitives/geometry/Rectangle2d.mjs +22 -11
  70. package/dist-esm/lib/primitives/geometry/Rectangle2d.mjs.map +2 -2
  71. package/dist-esm/lib/primitives/geometry/Stadium2d.mjs +22 -22
  72. package/dist-esm/lib/primitives/geometry/Stadium2d.mjs.map +2 -2
  73. package/dist-esm/lib/utils/areShapesContentEqual.mjs +1 -1
  74. package/dist-esm/lib/utils/areShapesContentEqual.mjs.map +2 -2
  75. package/dist-esm/lib/utils/reorderShapes.mjs +11 -10
  76. package/dist-esm/lib/utils/reorderShapes.mjs.map +2 -2
  77. package/dist-esm/version.mjs +3 -3
  78. package/dist-esm/version.mjs.map +1 -1
  79. package/package.json +7 -7
  80. package/src/index.ts +1 -0
  81. package/src/lib/editor/Editor.ts +13 -48
  82. package/src/lib/editor/managers/FontManager.ts +5 -1
  83. package/src/lib/primitives/Box.test.ts +588 -7
  84. package/src/lib/primitives/Box.ts +41 -33
  85. package/src/lib/primitives/Vec.test.ts +2 -2
  86. package/src/lib/primitives/Vec.ts +13 -8
  87. package/src/lib/primitives/geometry/Arc2d.ts +42 -23
  88. package/src/lib/primitives/geometry/Circle2d.ts +12 -12
  89. package/src/lib/primitives/geometry/CubicBezier2d.test.ts +5 -0
  90. package/src/lib/primitives/geometry/CubicBezier2d.ts +13 -17
  91. package/src/lib/primitives/geometry/CubicSpline2d.ts +5 -5
  92. package/src/lib/primitives/geometry/Edge2d.ts +14 -18
  93. package/src/lib/primitives/geometry/Ellipse2d.ts +12 -13
  94. package/src/lib/primitives/geometry/Point2d.ts +6 -6
  95. package/src/lib/primitives/geometry/Polygon2d.ts +4 -0
  96. package/src/lib/primitives/geometry/Polyline2d.ts +10 -7
  97. package/src/lib/primitives/geometry/Rectangle2d.ts +24 -11
  98. package/src/lib/primitives/geometry/Stadium2d.ts +22 -23
  99. package/src/lib/utils/areShapesContentEqual.ts +2 -1
  100. package/src/lib/utils/reorderShapes.ts +10 -13
  101. package/src/version.ts +3 -3
@@ -2,13 +2,16 @@ import { Vec } from "../Vec.mjs";
2
2
  import { Edge2d } from "./Edge2d.mjs";
3
3
  import { Geometry2d } from "./Geometry2d.mjs";
4
4
  class Polyline2d extends Geometry2d {
5
- points;
5
+ _points;
6
+ _segments;
6
7
  constructor(config) {
7
8
  super({ isClosed: false, isFilled: false, ...config });
8
9
  const { points } = config;
9
- this.points = points;
10
+ this._points = points;
11
+ if (points.length < 2) {
12
+ throw new Error("Polyline2d: points must be an array of at least 2 points");
13
+ }
10
14
  }
11
- _segments;
12
15
  // eslint-disable-next-line no-restricted-syntax
13
16
  get segments() {
14
17
  if (!this._segments) {
@@ -29,11 +32,11 @@ class Polyline2d extends Geometry2d {
29
32
  return this.segments.reduce((acc, segment) => acc + segment.length, 0);
30
33
  }
31
34
  getVertices() {
32
- return this.points;
35
+ return this._points;
33
36
  }
34
37
  nearestPoint(A) {
35
38
  const { segments } = this;
36
- let nearest = this.points[0];
39
+ let nearest = this._points[0];
37
40
  let dist = Infinity;
38
41
  let p;
39
42
  let d;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/primitives/geometry/Polyline2d.ts"],
4
- "sourcesContent": ["import { Vec, VecLike } from '../Vec'\nimport { Edge2d } from './Edge2d'\nimport { Geometry2d, Geometry2dOptions } from './Geometry2d'\n\n/** @public */\nexport class Polyline2d extends Geometry2d {\n\tpoints: Vec[]\n\n\tconstructor(config: Omit<Geometry2dOptions, 'isFilled' | 'isClosed'> & { points: Vec[] }) {\n\t\tsuper({ isClosed: false, isFilled: false, ...config })\n\t\tconst { points } = config\n\t\tthis.points = points\n\t}\n\n\t_segments?: Edge2d[]\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tget segments() {\n\t\tif (!this._segments) {\n\t\t\tthis._segments = []\n\t\t\tconst { vertices } = this\n\t\t\tfor (let i = 0, n = vertices.length - 1; i < n; i++) {\n\t\t\t\tconst start = vertices[i]\n\t\t\t\tconst end = vertices[i + 1]\n\t\t\t\tthis._segments.push(new Edge2d({ start, end }))\n\t\t\t}\n\n\t\t\tif (this.isClosed) {\n\t\t\t\tthis._segments.push(new Edge2d({ start: vertices[vertices.length - 1], end: vertices[0] }))\n\t\t\t}\n\t\t}\n\n\t\treturn this._segments\n\t}\n\n\toverride getLength() {\n\t\treturn this.segments.reduce((acc, segment) => acc + segment.length, 0)\n\t}\n\n\tgetVertices() {\n\t\treturn this.points\n\t}\n\n\tnearestPoint(A: VecLike): Vec {\n\t\tconst { segments } = this\n\t\tlet nearest = this.points[0]\n\t\tlet dist = Infinity\n\t\tlet p: Vec // current point on segment\n\t\tlet d: number // distance from A to p\n\t\tfor (let i = 0; i < segments.length; i++) {\n\t\t\tp = segments[i].nearestPoint(A)\n\t\t\td = Vec.Dist2(p, A)\n\t\t\tif (d < dist) {\n\t\t\t\tnearest = p\n\t\t\t\tdist = d\n\t\t\t}\n\t\t}\n\t\tif (!nearest) throw Error('nearest point not found')\n\t\treturn nearest\n\t}\n\n\thitTestLineSegment(A: VecLike, B: VecLike, distance = 0): boolean {\n\t\tconst { segments } = this\n\t\tfor (let i = 0, n = segments.length; i < n; i++) {\n\t\t\tif (segments[i].hitTestLineSegment(A, B, distance)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\n\tgetSvgPathData(): string {\n\t\tconst { vertices } = this\n\t\tif (vertices.length < 2) return ''\n\t\treturn vertices.reduce((acc, vertex, i) => {\n\t\t\tif (i === 0) return `M ${vertex.x} ${vertex.y}`\n\t\t\treturn `${acc} L ${vertex.x} ${vertex.y}`\n\t\t}, '')\n\t}\n}\n"],
5
- "mappings": "AAAA,SAAS,WAAoB;AAC7B,SAAS,cAAc;AACvB,SAAS,kBAAqC;AAGvC,MAAM,mBAAmB,WAAW;AAAA,EAC1C;AAAA,EAEA,YAAY,QAA8E;AACzF,UAAM,EAAE,UAAU,OAAO,UAAU,OAAO,GAAG,OAAO,CAAC;AACrD,UAAM,EAAE,OAAO,IAAI;AACnB,SAAK,SAAS;AAAA,EACf;AAAA,EAEA;AAAA;AAAA,EAGA,IAAI,WAAW;AACd,QAAI,CAAC,KAAK,WAAW;AACpB,WAAK,YAAY,CAAC;AAClB,YAAM,EAAE,SAAS,IAAI;AACrB,eAAS,IAAI,GAAG,IAAI,SAAS,SAAS,GAAG,IAAI,GAAG,KAAK;AACpD,cAAM,QAAQ,SAAS,CAAC;AACxB,cAAM,MAAM,SAAS,IAAI,CAAC;AAC1B,aAAK,UAAU,KAAK,IAAI,OAAO,EAAE,OAAO,IAAI,CAAC,CAAC;AAAA,MAC/C;AAEA,UAAI,KAAK,UAAU;AAClB,aAAK,UAAU,KAAK,IAAI,OAAO,EAAE,OAAO,SAAS,SAAS,SAAS,CAAC,GAAG,KAAK,SAAS,CAAC,EAAE,CAAC,CAAC;AAAA,MAC3F;AAAA,IACD;AAEA,WAAO,KAAK;AAAA,EACb;AAAA,EAES,YAAY;AACpB,WAAO,KAAK,SAAS,OAAO,CAAC,KAAK,YAAY,MAAM,QAAQ,QAAQ,CAAC;AAAA,EACtE;AAAA,EAEA,cAAc;AACb,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,aAAa,GAAiB;AAC7B,UAAM,EAAE,SAAS,IAAI;AACrB,QAAI,UAAU,KAAK,OAAO,CAAC;AAC3B,QAAI,OAAO;AACX,QAAI;AACJ,QAAI;AACJ,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACzC,UAAI,SAAS,CAAC,EAAE,aAAa,CAAC;AAC9B,UAAI,IAAI,MAAM,GAAG,CAAC;AAClB,UAAI,IAAI,MAAM;AACb,kBAAU;AACV,eAAO;AAAA,MACR;AAAA,IACD;AACA,QAAI,CAAC,QAAS,OAAM,MAAM,yBAAyB;AACnD,WAAO;AAAA,EACR;AAAA,EAEA,mBAAmB,GAAY,GAAY,WAAW,GAAY;AACjE,UAAM,EAAE,SAAS,IAAI;AACrB,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,IAAI,GAAG,KAAK;AAChD,UAAI,SAAS,CAAC,EAAE,mBAAmB,GAAG,GAAG,QAAQ,GAAG;AACnD,eAAO;AAAA,MACR;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA,EAEA,iBAAyB;AACxB,UAAM,EAAE,SAAS,IAAI;AACrB,QAAI,SAAS,SAAS,EAAG,QAAO;AAChC,WAAO,SAAS,OAAO,CAAC,KAAK,QAAQ,MAAM;AAC1C,UAAI,MAAM,EAAG,QAAO,KAAK,OAAO,CAAC,IAAI,OAAO,CAAC;AAC7C,aAAO,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,OAAO,CAAC;AAAA,IACxC,GAAG,EAAE;AAAA,EACN;AACD;",
4
+ "sourcesContent": ["import { Vec, VecLike } from '../Vec'\nimport { Edge2d } from './Edge2d'\nimport { Geometry2d, Geometry2dOptions } from './Geometry2d'\n\n/** @public */\nexport class Polyline2d extends Geometry2d {\n\tprivate _points: Vec[]\n\tprivate _segments?: Edge2d[]\n\n\tconstructor(config: Omit<Geometry2dOptions, 'isFilled' | 'isClosed'> & { points: Vec[] }) {\n\t\tsuper({ isClosed: false, isFilled: false, ...config })\n\t\tconst { points } = config\n\t\tthis._points = points\n\n\t\tif (points.length < 2) {\n\t\t\tthrow new Error('Polyline2d: points must be an array of at least 2 points')\n\t\t}\n\t}\n\n\t// eslint-disable-next-line no-restricted-syntax\n\tprotected get segments() {\n\t\tif (!this._segments) {\n\t\t\tthis._segments = []\n\t\t\tconst { vertices } = this\n\t\t\tfor (let i = 0, n = vertices.length - 1; i < n; i++) {\n\t\t\t\tconst start = vertices[i]\n\t\t\t\tconst end = vertices[i + 1]\n\t\t\t\tthis._segments.push(new Edge2d({ start, end }))\n\t\t\t}\n\n\t\t\tif (this.isClosed) {\n\t\t\t\tthis._segments.push(new Edge2d({ start: vertices[vertices.length - 1], end: vertices[0] }))\n\t\t\t}\n\t\t}\n\n\t\treturn this._segments\n\t}\n\n\toverride getLength() {\n\t\treturn this.segments.reduce((acc, segment) => acc + segment.length, 0)\n\t}\n\n\tgetVertices() {\n\t\treturn this._points\n\t}\n\n\tnearestPoint(A: VecLike): Vec {\n\t\tconst { segments } = this\n\t\tlet nearest = this._points[0]\n\t\tlet dist = Infinity\n\t\tlet p: Vec // current point on segment\n\t\tlet d: number // distance from A to p\n\t\tfor (let i = 0; i < segments.length; i++) {\n\t\t\tp = segments[i].nearestPoint(A)\n\t\t\td = Vec.Dist2(p, A)\n\t\t\tif (d < dist) {\n\t\t\t\tnearest = p\n\t\t\t\tdist = d\n\t\t\t}\n\t\t}\n\t\tif (!nearest) throw Error('nearest point not found')\n\t\treturn nearest\n\t}\n\n\thitTestLineSegment(A: VecLike, B: VecLike, distance = 0): boolean {\n\t\tconst { segments } = this\n\t\tfor (let i = 0, n = segments.length; i < n; i++) {\n\t\t\tif (segments[i].hitTestLineSegment(A, B, distance)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\n\tgetSvgPathData(): string {\n\t\tconst { vertices } = this\n\t\tif (vertices.length < 2) return ''\n\t\treturn vertices.reduce((acc, vertex, i) => {\n\t\t\tif (i === 0) return `M ${vertex.x} ${vertex.y}`\n\t\t\treturn `${acc} L ${vertex.x} ${vertex.y}`\n\t\t}, '')\n\t}\n}\n"],
5
+ "mappings": "AAAA,SAAS,WAAoB;AAC7B,SAAS,cAAc;AACvB,SAAS,kBAAqC;AAGvC,MAAM,mBAAmB,WAAW;AAAA,EAClC;AAAA,EACA;AAAA,EAER,YAAY,QAA8E;AACzF,UAAM,EAAE,UAAU,OAAO,UAAU,OAAO,GAAG,OAAO,CAAC;AACrD,UAAM,EAAE,OAAO,IAAI;AACnB,SAAK,UAAU;AAEf,QAAI,OAAO,SAAS,GAAG;AACtB,YAAM,IAAI,MAAM,0DAA0D;AAAA,IAC3E;AAAA,EACD;AAAA;AAAA,EAGA,IAAc,WAAW;AACxB,QAAI,CAAC,KAAK,WAAW;AACpB,WAAK,YAAY,CAAC;AAClB,YAAM,EAAE,SAAS,IAAI;AACrB,eAAS,IAAI,GAAG,IAAI,SAAS,SAAS,GAAG,IAAI,GAAG,KAAK;AACpD,cAAM,QAAQ,SAAS,CAAC;AACxB,cAAM,MAAM,SAAS,IAAI,CAAC;AAC1B,aAAK,UAAU,KAAK,IAAI,OAAO,EAAE,OAAO,IAAI,CAAC,CAAC;AAAA,MAC/C;AAEA,UAAI,KAAK,UAAU;AAClB,aAAK,UAAU,KAAK,IAAI,OAAO,EAAE,OAAO,SAAS,SAAS,SAAS,CAAC,GAAG,KAAK,SAAS,CAAC,EAAE,CAAC,CAAC;AAAA,MAC3F;AAAA,IACD;AAEA,WAAO,KAAK;AAAA,EACb;AAAA,EAES,YAAY;AACpB,WAAO,KAAK,SAAS,OAAO,CAAC,KAAK,YAAY,MAAM,QAAQ,QAAQ,CAAC;AAAA,EACtE;AAAA,EAEA,cAAc;AACb,WAAO,KAAK;AAAA,EACb;AAAA,EAEA,aAAa,GAAiB;AAC7B,UAAM,EAAE,SAAS,IAAI;AACrB,QAAI,UAAU,KAAK,QAAQ,CAAC;AAC5B,QAAI,OAAO;AACX,QAAI;AACJ,QAAI;AACJ,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACzC,UAAI,SAAS,CAAC,EAAE,aAAa,CAAC;AAC9B,UAAI,IAAI,MAAM,GAAG,CAAC;AAClB,UAAI,IAAI,MAAM;AACb,kBAAU;AACV,eAAO;AAAA,MACR;AAAA,IACD;AACA,QAAI,CAAC,QAAS,OAAM,MAAM,yBAAyB;AACnD,WAAO;AAAA,EACR;AAAA,EAEA,mBAAmB,GAAY,GAAY,WAAW,GAAY;AACjE,UAAM,EAAE,SAAS,IAAI;AACrB,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,IAAI,GAAG,KAAK;AAChD,UAAI,SAAS,CAAC,EAAE,mBAAmB,GAAG,GAAG,QAAQ,GAAG;AACnD,eAAO;AAAA,MACR;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA,EAEA,iBAAyB;AACxB,UAAM,EAAE,SAAS,IAAI;AACrB,QAAI,SAAS,SAAS,EAAG,QAAO;AAChC,WAAO,SAAS,OAAO,CAAC,KAAK,QAAQ,MAAM;AAC1C,UAAI,MAAM,EAAG,QAAO,KAAK,OAAO,CAAC,IAAI,OAAO,CAAC;AAC7C,aAAO,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,OAAO,CAAC;AAAA,IACxC,GAAG,EAAE;AAAA,EACN;AACD;",
6
6
  "names": []
7
7
  }
@@ -2,10 +2,10 @@ import { Box } from "../Box.mjs";
2
2
  import { Vec } from "../Vec.mjs";
3
3
  import { Polygon2d } from "./Polygon2d.mjs";
4
4
  class Rectangle2d extends Polygon2d {
5
- x;
6
- y;
7
- w;
8
- h;
5
+ _x;
6
+ _y;
7
+ _w;
8
+ _h;
9
9
  constructor(config) {
10
10
  const { x = 0, y = 0, width, height } = config;
11
11
  super({
@@ -17,18 +17,29 @@ class Rectangle2d extends Polygon2d {
17
17
  new Vec(x, y + height)
18
18
  ]
19
19
  });
20
- this.x = x;
21
- this.y = y;
22
- this.w = width;
23
- this.h = height;
20
+ this._x = x;
21
+ this._y = y;
22
+ this._w = width;
23
+ this._h = height;
24
24
  }
25
25
  getBounds() {
26
- return new Box(this.x, this.y, this.w, this.h);
26
+ return new Box(this._x, this._y, this._w, this._h);
27
27
  }
28
28
  getSvgPathData() {
29
- const { x, y, w, h } = this;
30
- return `M${x},${y} h${w} v${h} h-${w}z`;
29
+ const { _x: x, _y: y, _w: w, _h: h } = this;
30
+ this.negativeZeroFix();
31
+ return `M${x},${y} h${w} v${h} h${-w}z`;
31
32
  }
33
+ negativeZeroFix() {
34
+ this._x = zeroFix(this._x);
35
+ this._y = zeroFix(this._y);
36
+ this._w = zeroFix(this._w);
37
+ this._h = zeroFix(this._h);
38
+ }
39
+ }
40
+ function zeroFix(value) {
41
+ if (Object.is(value, -0)) return 0;
42
+ return value;
32
43
  }
33
44
  export {
34
45
  Rectangle2d
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/primitives/geometry/Rectangle2d.ts"],
4
- "sourcesContent": ["import { Box } from '../Box'\nimport { Vec } from '../Vec'\nimport { Geometry2dOptions } from './Geometry2d'\nimport { Polygon2d } from './Polygon2d'\n\n/** @public */\nexport class Rectangle2d extends Polygon2d {\n\tx: number\n\ty: number\n\tw: number\n\th: number\n\n\tconstructor(\n\t\tconfig: Omit<Geometry2dOptions, 'isClosed'> & {\n\t\t\tx?: number\n\t\t\ty?: number\n\t\t\twidth: number\n\t\t\theight: number\n\t\t}\n\t) {\n\t\tconst { x = 0, y = 0, width, height } = config\n\t\tsuper({\n\t\t\t...config,\n\t\t\tpoints: [\n\t\t\t\tnew Vec(x, y),\n\t\t\t\tnew Vec(x + width, y),\n\t\t\t\tnew Vec(x + width, y + height),\n\t\t\t\tnew Vec(x, y + height),\n\t\t\t],\n\t\t})\n\t\tthis.x = x\n\t\tthis.y = y\n\t\tthis.w = width\n\t\tthis.h = height\n\t}\n\n\tgetBounds() {\n\t\treturn new Box(this.x, this.y, this.w, this.h)\n\t}\n\n\tgetSvgPathData(): string {\n\t\tconst { x, y, w, h } = this\n\t\treturn `M${x},${y} h${w} v${h} h-${w}z`\n\t}\n}\n"],
5
- "mappings": "AAAA,SAAS,WAAW;AACpB,SAAS,WAAW;AAEpB,SAAS,iBAAiB;AAGnB,MAAM,oBAAoB,UAAU;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,YACC,QAMC;AACD,UAAM,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,OAAO,IAAI;AACxC,UAAM;AAAA,MACL,GAAG;AAAA,MACH,QAAQ;AAAA,QACP,IAAI,IAAI,GAAG,CAAC;AAAA,QACZ,IAAI,IAAI,IAAI,OAAO,CAAC;AAAA,QACpB,IAAI,IAAI,IAAI,OAAO,IAAI,MAAM;AAAA,QAC7B,IAAI,IAAI,GAAG,IAAI,MAAM;AAAA,MACtB;AAAA,IACD,CAAC;AACD,SAAK,IAAI;AACT,SAAK,IAAI;AACT,SAAK,IAAI;AACT,SAAK,IAAI;AAAA,EACV;AAAA,EAEA,YAAY;AACX,WAAO,IAAI,IAAI,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAAA,EAC9C;AAAA,EAEA,iBAAyB;AACxB,UAAM,EAAE,GAAG,GAAG,GAAG,EAAE,IAAI;AACvB,WAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC;AAAA,EACrC;AACD;",
4
+ "sourcesContent": ["import { Box } from '../Box'\nimport { Vec } from '../Vec'\nimport { Geometry2dOptions } from './Geometry2d'\nimport { Polygon2d } from './Polygon2d'\n\n/** @public */\nexport class Rectangle2d extends Polygon2d {\n\tprivate _x: number\n\tprivate _y: number\n\tprivate _w: number\n\tprivate _h: number\n\n\tconstructor(\n\t\tconfig: Omit<Geometry2dOptions, 'isClosed'> & {\n\t\t\tx?: number\n\t\t\ty?: number\n\t\t\twidth: number\n\t\t\theight: number\n\t\t}\n\t) {\n\t\tconst { x = 0, y = 0, width, height } = config\n\t\tsuper({\n\t\t\t...config,\n\t\t\tpoints: [\n\t\t\t\tnew Vec(x, y),\n\t\t\t\tnew Vec(x + width, y),\n\t\t\t\tnew Vec(x + width, y + height),\n\t\t\t\tnew Vec(x, y + height),\n\t\t\t],\n\t\t})\n\t\tthis._x = x\n\t\tthis._y = y\n\t\tthis._w = width\n\t\tthis._h = height\n\t}\n\n\tgetBounds() {\n\t\treturn new Box(this._x, this._y, this._w, this._h)\n\t}\n\n\tgetSvgPathData(): string {\n\t\tconst { _x: x, _y: y, _w: w, _h: h } = this\n\t\tthis.negativeZeroFix()\n\t\treturn `M${x},${y} h${w} v${h} h${-w}z`\n\t}\n\n\tprivate negativeZeroFix() {\n\t\tthis._x = zeroFix(this._x)\n\t\tthis._y = zeroFix(this._y)\n\t\tthis._w = zeroFix(this._w)\n\t\tthis._h = zeroFix(this._h)\n\t}\n}\n\nfunction zeroFix(value: number) {\n\tif (Object.is(value, -0)) return 0\n\treturn value\n}\n"],
5
+ "mappings": "AAAA,SAAS,WAAW;AACpB,SAAS,WAAW;AAEpB,SAAS,iBAAiB;AAGnB,MAAM,oBAAoB,UAAU;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YACC,QAMC;AACD,UAAM,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,OAAO,IAAI;AACxC,UAAM;AAAA,MACL,GAAG;AAAA,MACH,QAAQ;AAAA,QACP,IAAI,IAAI,GAAG,CAAC;AAAA,QACZ,IAAI,IAAI,IAAI,OAAO,CAAC;AAAA,QACpB,IAAI,IAAI,IAAI,OAAO,IAAI,MAAM;AAAA,QAC7B,IAAI,IAAI,GAAG,IAAI,MAAM;AAAA,MACtB;AAAA,IACD,CAAC;AACD,SAAK,KAAK;AACV,SAAK,KAAK;AACV,SAAK,KAAK;AACV,SAAK,KAAK;AAAA,EACX;AAAA,EAEA,YAAY;AACX,WAAO,IAAI,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,EAAE;AAAA,EAClD;AAAA,EAEA,iBAAyB;AACxB,UAAM,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,EAAE,IAAI;AACvC,SAAK,gBAAgB;AACrB,WAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAAA,EACrC;AAAA,EAEQ,kBAAkB;AACzB,SAAK,KAAK,QAAQ,KAAK,EAAE;AACzB,SAAK,KAAK,QAAQ,KAAK,EAAE;AACzB,SAAK,KAAK,QAAQ,KAAK,EAAE;AACzB,SAAK,KAAK,QAAQ,KAAK,EAAE;AAAA,EAC1B;AACD;AAEA,SAAS,QAAQ,OAAe;AAC/B,MAAI,OAAO,GAAG,OAAO,EAAE,EAAG,QAAO;AACjC,SAAO;AACR;",
6
6
  "names": []
7
7
  }
@@ -9,58 +9,58 @@ class Stadium2d extends Geometry2d {
9
9
  super({ ...config, isClosed: true });
10
10
  this.config = config;
11
11
  const { width: w, height: h } = config;
12
- this.w = w;
13
- this.h = h;
12
+ this._w = w;
13
+ this._h = h;
14
14
  if (h > w) {
15
15
  const r = w / 2;
16
- this.a = new Arc2d({
16
+ this._a = new Arc2d({
17
17
  start: new Vec(0, r),
18
18
  end: new Vec(w, r),
19
19
  center: new Vec(w / 2, r),
20
20
  sweepFlag: 1,
21
21
  largeArcFlag: 1
22
22
  });
23
- this.b = new Edge2d({ start: new Vec(w, r), end: new Vec(w, h - r) });
24
- this.c = new Arc2d({
23
+ this._b = new Edge2d({ start: new Vec(w, r), end: new Vec(w, h - r) });
24
+ this._c = new Arc2d({
25
25
  start: new Vec(w, h - r),
26
26
  end: new Vec(0, h - r),
27
27
  center: new Vec(w / 2, h - r),
28
28
  sweepFlag: 1,
29
29
  largeArcFlag: 1
30
30
  });
31
- this.d = new Edge2d({ start: new Vec(0, h - r), end: new Vec(0, r) });
31
+ this._d = new Edge2d({ start: new Vec(0, h - r), end: new Vec(0, r) });
32
32
  } else {
33
33
  const r = h / 2;
34
- this.a = new Arc2d({
34
+ this._a = new Arc2d({
35
35
  start: new Vec(r, h),
36
36
  end: new Vec(r, 0),
37
37
  center: new Vec(r, r),
38
38
  sweepFlag: 1,
39
39
  largeArcFlag: 1
40
40
  });
41
- this.b = new Edge2d({ start: new Vec(r, 0), end: new Vec(w - r, 0) });
42
- this.c = new Arc2d({
41
+ this._b = new Edge2d({ start: new Vec(r, 0), end: new Vec(w - r, 0) });
42
+ this._c = new Arc2d({
43
43
  start: new Vec(w - r, 0),
44
44
  end: new Vec(w - r, h),
45
45
  center: new Vec(w - r, r),
46
46
  sweepFlag: 1,
47
47
  largeArcFlag: 1
48
48
  });
49
- this.d = new Edge2d({ start: new Vec(w - r, h), end: new Vec(r, h) });
49
+ this._d = new Edge2d({ start: new Vec(w - r, h), end: new Vec(r, h) });
50
50
  }
51
51
  }
52
- w;
53
- h;
54
- a;
55
- b;
56
- c;
57
- d;
52
+ _w;
53
+ _h;
54
+ _a;
55
+ _b;
56
+ _c;
57
+ _d;
58
58
  nearestPoint(A) {
59
59
  let nearest;
60
60
  let dist = Infinity;
61
61
  let _d;
62
62
  let p;
63
- const { a, b, c, d } = this;
63
+ const { _a: a, _b: b, _c: c, _d: d } = this;
64
64
  for (const part of [a, b, c, d]) {
65
65
  p = part.nearestPoint(A);
66
66
  _d = Vec.Dist2(p, A);
@@ -73,26 +73,26 @@ class Stadium2d extends Geometry2d {
73
73
  return nearest;
74
74
  }
75
75
  hitTestLineSegment(A, B) {
76
- const { a, b, c, d } = this;
76
+ const { _a: a, _b: b, _c: c, _d: d } = this;
77
77
  return [a, b, c, d].some((edge) => edge.hitTestLineSegment(A, B));
78
78
  }
79
79
  getVertices() {
80
- const { a, b, c, d } = this;
80
+ const { _a: a, _b: b, _c: c, _d: d } = this;
81
81
  return [a, b, c, d].reduce((a2, p) => {
82
82
  a2.push(...p.vertices);
83
83
  return a2;
84
84
  }, []);
85
85
  }
86
86
  getBounds() {
87
- return new Box(0, 0, this.w, this.h);
87
+ return new Box(0, 0, this._w, this._h);
88
88
  }
89
89
  getLength() {
90
- const { h, w } = this;
90
+ const { _h: h, _w: w } = this;
91
91
  if (h > w) return (PI * (w / 2) + (h - w)) * 2;
92
92
  else return (PI * (h / 2) + (w - h)) * 2;
93
93
  }
94
94
  getSvgPathData() {
95
- const { a, b, c, d } = this;
95
+ const { _a: a, _b: b, _c: c, _d: d } = this;
96
96
  return [a, b, c, d].map((p, i) => p.getSvgPathData(i === 0)).join(" ") + " Z";
97
97
  }
98
98
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/primitives/geometry/Stadium2d.ts"],
4
- "sourcesContent": ["import { Box } from '../Box'\nimport { Vec, VecLike } from '../Vec'\nimport { PI } from '../utils'\nimport { Arc2d } from './Arc2d'\nimport { Edge2d } from './Edge2d'\nimport { Geometry2d, Geometry2dOptions } from './Geometry2d'\n\n/** @public */\nexport class Stadium2d extends Geometry2d {\n\tw: number\n\th: number\n\n\ta: Arc2d\n\tb: Edge2d\n\tc: Arc2d\n\td: Edge2d\n\n\tconstructor(\n\t\tpublic config: Omit<Geometry2dOptions, 'isClosed'> & {\n\t\t\twidth: number\n\t\t\theight: number\n\t\t}\n\t) {\n\t\tsuper({ ...config, isClosed: true })\n\t\tconst { width: w, height: h } = config\n\t\tthis.w = w\n\t\tthis.h = h\n\n\t\tif (h > w) {\n\t\t\tconst r = w / 2\n\t\t\tthis.a = new Arc2d({\n\t\t\t\tstart: new Vec(0, r),\n\t\t\t\tend: new Vec(w, r),\n\t\t\t\tcenter: new Vec(w / 2, r),\n\t\t\t\tsweepFlag: 1,\n\t\t\t\tlargeArcFlag: 1,\n\t\t\t})\n\t\t\tthis.b = new Edge2d({ start: new Vec(w, r), end: new Vec(w, h - r) })\n\t\t\tthis.c = new Arc2d({\n\t\t\t\tstart: new Vec(w, h - r),\n\t\t\t\tend: new Vec(0, h - r),\n\t\t\t\tcenter: new Vec(w / 2, h - r),\n\t\t\t\tsweepFlag: 1,\n\t\t\t\tlargeArcFlag: 1,\n\t\t\t})\n\t\t\tthis.d = new Edge2d({ start: new Vec(0, h - r), end: new Vec(0, r) })\n\t\t} else {\n\t\t\tconst r = h / 2\n\t\t\tthis.a = new Arc2d({\n\t\t\t\tstart: new Vec(r, h),\n\t\t\t\tend: new Vec(r, 0),\n\t\t\t\tcenter: new Vec(r, r),\n\t\t\t\tsweepFlag: 1,\n\t\t\t\tlargeArcFlag: 1,\n\t\t\t})\n\t\t\tthis.b = new Edge2d({ start: new Vec(r, 0), end: new Vec(w - r, 0) })\n\t\t\tthis.c = new Arc2d({\n\t\t\t\tstart: new Vec(w - r, 0),\n\t\t\t\tend: new Vec(w - r, h),\n\t\t\t\tcenter: new Vec(w - r, r),\n\t\t\t\tsweepFlag: 1,\n\t\t\t\tlargeArcFlag: 1,\n\t\t\t})\n\t\t\tthis.d = new Edge2d({ start: new Vec(w - r, h), end: new Vec(r, h) })\n\t\t}\n\t}\n\n\tnearestPoint(A: VecLike): Vec {\n\t\tlet nearest: Vec | undefined\n\t\tlet dist = Infinity\n\t\tlet _d: number\n\t\tlet p: Vec\n\n\t\tconst { a, b, c, d } = this\n\t\tfor (const part of [a, b, c, d]) {\n\t\t\tp = part.nearestPoint(A)\n\t\t\t_d = Vec.Dist2(p, A)\n\t\t\tif (_d < dist) {\n\t\t\t\tnearest = p\n\t\t\t\tdist = _d\n\t\t\t}\n\t\t}\n\t\tif (!nearest) throw Error('nearest point not found')\n\t\treturn nearest\n\t}\n\n\thitTestLineSegment(A: VecLike, B: VecLike): boolean {\n\t\tconst { a, b, c, d } = this\n\t\treturn [a, b, c, d].some((edge) => edge.hitTestLineSegment(A, B))\n\t}\n\n\tgetVertices() {\n\t\tconst { a, b, c, d } = this\n\t\treturn [a, b, c, d].reduce<Vec[]>((a, p) => {\n\t\t\ta.push(...p.vertices)\n\t\t\treturn a\n\t\t}, [])\n\t}\n\n\tgetBounds() {\n\t\treturn new Box(0, 0, this.w, this.h)\n\t}\n\n\tgetLength() {\n\t\tconst { h, w } = this\n\t\tif (h > w) return (PI * (w / 2) + (h - w)) * 2\n\t\telse return (PI * (h / 2) + (w - h)) * 2\n\t}\n\n\tgetSvgPathData() {\n\t\tconst { a, b, c, d } = this\n\t\treturn [a, b, c, d].map((p, i) => p.getSvgPathData(i === 0)).join(' ') + ' Z'\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Box } from '../Box'\nimport { Vec, VecLike } from '../Vec'\nimport { PI } from '../utils'\nimport { Arc2d } from './Arc2d'\nimport { Edge2d } from './Edge2d'\nimport { Geometry2d, Geometry2dOptions } from './Geometry2d'\n\n/** @public */\nexport class Stadium2d extends Geometry2d {\n\tprivate _w: number\n\tprivate _h: number\n\tprivate _a: Arc2d\n\tprivate _b: Edge2d\n\tprivate _c: Arc2d\n\tprivate _d: Edge2d\n\n\tconstructor(\n\t\tpublic config: Omit<Geometry2dOptions, 'isClosed'> & {\n\t\t\twidth: number\n\t\t\theight: number\n\t\t}\n\t) {\n\t\tsuper({ ...config, isClosed: true })\n\t\tconst { width: w, height: h } = config\n\t\tthis._w = w\n\t\tthis._h = h\n\n\t\tif (h > w) {\n\t\t\tconst r = w / 2\n\t\t\tthis._a = new Arc2d({\n\t\t\t\tstart: new Vec(0, r),\n\t\t\t\tend: new Vec(w, r),\n\t\t\t\tcenter: new Vec(w / 2, r),\n\t\t\t\tsweepFlag: 1,\n\t\t\t\tlargeArcFlag: 1,\n\t\t\t})\n\t\t\tthis._b = new Edge2d({ start: new Vec(w, r), end: new Vec(w, h - r) })\n\t\t\tthis._c = new Arc2d({\n\t\t\t\tstart: new Vec(w, h - r),\n\t\t\t\tend: new Vec(0, h - r),\n\t\t\t\tcenter: new Vec(w / 2, h - r),\n\t\t\t\tsweepFlag: 1,\n\t\t\t\tlargeArcFlag: 1,\n\t\t\t})\n\t\t\tthis._d = new Edge2d({ start: new Vec(0, h - r), end: new Vec(0, r) })\n\t\t} else {\n\t\t\tconst r = h / 2\n\t\t\tthis._a = new Arc2d({\n\t\t\t\tstart: new Vec(r, h),\n\t\t\t\tend: new Vec(r, 0),\n\t\t\t\tcenter: new Vec(r, r),\n\t\t\t\tsweepFlag: 1,\n\t\t\t\tlargeArcFlag: 1,\n\t\t\t})\n\t\t\tthis._b = new Edge2d({ start: new Vec(r, 0), end: new Vec(w - r, 0) })\n\t\t\tthis._c = new Arc2d({\n\t\t\t\tstart: new Vec(w - r, 0),\n\t\t\t\tend: new Vec(w - r, h),\n\t\t\t\tcenter: new Vec(w - r, r),\n\t\t\t\tsweepFlag: 1,\n\t\t\t\tlargeArcFlag: 1,\n\t\t\t})\n\t\t\tthis._d = new Edge2d({ start: new Vec(w - r, h), end: new Vec(r, h) })\n\t\t}\n\t}\n\n\tnearestPoint(A: VecLike): Vec {\n\t\tlet nearest: Vec | undefined\n\t\tlet dist = Infinity\n\t\tlet _d: number\n\t\tlet p: Vec\n\n\t\tconst { _a: a, _b: b, _c: c, _d: d } = this\n\t\tfor (const part of [a, b, c, d]) {\n\t\t\tp = part.nearestPoint(A)\n\t\t\t_d = Vec.Dist2(p, A)\n\t\t\tif (_d < dist) {\n\t\t\t\tnearest = p\n\t\t\t\tdist = _d\n\t\t\t}\n\t\t}\n\t\tif (!nearest) throw Error('nearest point not found')\n\t\treturn nearest\n\t}\n\n\thitTestLineSegment(A: VecLike, B: VecLike): boolean {\n\t\tconst { _a: a, _b: b, _c: c, _d: d } = this\n\t\treturn [a, b, c, d].some((edge) => edge.hitTestLineSegment(A, B))\n\t}\n\n\tgetVertices() {\n\t\tconst { _a: a, _b: b, _c: c, _d: d } = this\n\t\treturn [a, b, c, d].reduce<Vec[]>((a, p) => {\n\t\t\ta.push(...p.vertices)\n\t\t\treturn a\n\t\t}, [])\n\t}\n\n\tgetBounds() {\n\t\treturn new Box(0, 0, this._w, this._h)\n\t}\n\n\tgetLength() {\n\t\tconst { _h: h, _w: w } = this\n\t\tif (h > w) return (PI * (w / 2) + (h - w)) * 2\n\t\telse return (PI * (h / 2) + (w - h)) * 2\n\t}\n\n\tgetSvgPathData() {\n\t\tconst { _a: a, _b: b, _c: c, _d: d } = this\n\t\treturn [a, b, c, d].map((p, i) => p.getSvgPathData(i === 0)).join(' ') + ' Z'\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["a"]
7
7
  }
@@ -1,4 +1,4 @@
1
- const areShapesContentEqual = (a, b) => a.props === b.props && a.meta === b.meta;
1
+ const areShapesContentEqual = (a, b) => a.parentId === b.parentId && a.props === b.props && a.meta === b.meta;
2
2
  export {
3
3
  areShapesContentEqual
4
4
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/utils/areShapesContentEqual.ts"],
4
- "sourcesContent": ["import { TLShape } from '@tldraw/tlschema'\n\nexport const areShapesContentEqual = (a: TLShape, b: TLShape) =>\n\ta.props === b.props && a.meta === b.meta\n"],
5
- "mappings": "AAEO,MAAM,wBAAwB,CAAC,GAAY,MACjD,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE;",
4
+ "sourcesContent": ["import { TLShape } from '@tldraw/tlschema'\n\n/** @public */\nexport const areShapesContentEqual = (a: TLShape, b: TLShape) =>\n\ta.parentId === b.parentId && a.props === b.props && a.meta === b.meta\n"],
5
+ "mappings": "AAGO,MAAM,wBAAwB,CAAC,GAAY,MACjD,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE;",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,4 @@
1
1
  import { compact, getIndicesBetween, sortByIndex } from "@tldraw/utils";
2
- import { polygonsIntersect } from "../primitives/intersect.mjs";
3
2
  function getReorderingShapesChanges(editor, operation, ids, opts) {
4
3
  if (ids.length === 0) return [];
5
4
  const parents = /* @__PURE__ */ new Map();
@@ -89,16 +88,18 @@ function reorderToFront(moving, children, changes) {
89
88
  }
90
89
  }
91
90
  function getOverlapChecker(editor, moving) {
92
- const movingVertices = Array.from(moving).map((shape) => {
93
- const vertices = editor.getShapePageGeometry(shape).vertices;
94
- if (!vertices) return null;
95
- return { shape, vertices };
96
- }).filter(Boolean);
91
+ const movingBounds = compact(
92
+ Array.from(moving).map((shape) => {
93
+ const bounds = editor.getShapePageBounds(shape);
94
+ if (!bounds) return null;
95
+ return { shape, bounds };
96
+ })
97
+ );
97
98
  const isOverlapping = (child) => {
98
- const vertices = editor.getShapePageGeometry(child).vertices;
99
- if (!vertices) return false;
100
- return movingVertices.some((other) => {
101
- return polygonsIntersect(other.vertices, vertices);
99
+ const bounds = editor.getShapePageBounds(child);
100
+ if (!bounds) return false;
101
+ return movingBounds.some((other) => {
102
+ return other.bounds.includes(bounds);
102
103
  });
103
104
  };
104
105
  return isOverlapping;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/utils/reorderShapes.ts"],
4
- "sourcesContent": ["import { TLParentId, TLShape, TLShapeId, TLShapePartial } from '@tldraw/tlschema'\nimport { IndexKey, compact, getIndicesBetween, sortByIndex } from '@tldraw/utils'\nimport { Editor } from '../editor/Editor'\nimport { Vec } from '../primitives/Vec'\nimport { polygonsIntersect } from '../primitives/intersect'\n\nexport function getReorderingShapesChanges(\n\teditor: Editor,\n\toperation: 'toBack' | 'toFront' | 'forward' | 'backward',\n\tids: TLShapeId[],\n\topts?: { considerAllShapes?: boolean }\n) {\n\tif (ids.length === 0) return []\n\n\t// From the ids that are moving, collect the parents, their children, and which of those children are moving\n\tconst parents = new Map<TLParentId, { moving: Set<TLShape>; children: TLShape[] }>()\n\n\tfor (const shape of compact(ids.map((id) => editor.getShape(id)))) {\n\t\tconst { parentId } = shape\n\t\tif (!parents.has(parentId)) {\n\t\t\tparents.set(parentId, {\n\t\t\t\tchildren: compact(\n\t\t\t\t\teditor.getSortedChildIdsForParent(parentId).map((id) => editor.getShape(id))\n\t\t\t\t),\n\t\t\t\tmoving: new Set(),\n\t\t\t})\n\t\t}\n\t\tparents.get(parentId)!.moving.add(shape)\n\t}\n\n\tconst changes: TLShapePartial[] = []\n\n\tswitch (operation) {\n\t\tcase 'toBack': {\n\t\t\tparents.forEach(({ moving, children }) => reorderToBack(moving, children, changes))\n\t\t\tbreak\n\t\t}\n\t\tcase 'toFront': {\n\t\t\tparents.forEach(({ moving, children }) => reorderToFront(moving, children, changes))\n\t\t\tbreak\n\t\t}\n\t\tcase 'forward': {\n\t\t\tparents.forEach(({ moving, children }) =>\n\t\t\t\treorderForward(editor, moving, children, changes, opts)\n\t\t\t)\n\t\t\tbreak\n\t\t}\n\t\tcase 'backward': {\n\t\t\tparents.forEach(({ moving, children }) =>\n\t\t\t\treorderBackward(editor, moving, children, changes, opts)\n\t\t\t)\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn changes\n}\n\n/**\n * Reorders the moving shapes to the back of the parent's children.\n *\n * @param moving The set of shapes that are moving\n * @param children The parent's children\n * @param changes The changes array to push changes to\n */\nfunction reorderToBack(moving: Set<TLShape>, children: TLShape[], changes: TLShapePartial[]) {\n\tconst len = children.length\n\n\t// If all of the children are moving, there's nothing to do\n\tif (moving.size === len) return\n\n\tlet below: IndexKey | undefined\n\tlet above: IndexKey | undefined\n\n\t// Starting at the bottom of this parent's children...\n\tfor (let i = 0; i < len; i++) {\n\t\tconst shape = children[i]\n\n\t\tif (moving.has(shape)) {\n\t\t\t// If we've found a moving shape before we've found a non-moving shape,\n\t\t\t// then that shape is already at the back; we can remove it from the\n\t\t\t// moving set and mark it as the shape that will be below the moved shapes.\n\t\t\tbelow = shape.index\n\t\t\tmoving.delete(shape)\n\t\t} else {\n\t\t\t// The first non-moving shape we find will be above our moved shapes; we'll\n\t\t\t// put our moving shapes between it and the shape marked as below (if any).\n\t\t\tabove = shape.index\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif (moving.size === 0) {\n\t\t// If our moving set is empty, there's nothing to do; all of our shapes were\n\t\t// already at the back of the parent's children.\n\t\treturn\n\t} else {\n\t\t// Sort the moving shapes by their current index, then apply the new indices\n\t\tconst indices = getIndicesBetween(below, above, moving.size)\n\t\tchanges.push(\n\t\t\t...Array.from(moving.values())\n\t\t\t\t.sort(sortByIndex)\n\t\t\t\t.map((shape, i) => ({ ...shape, index: indices[i] }))\n\t\t)\n\t}\n}\n\n/**\n * Reorders the moving shapes to the front of the parent's children.\n *\n * @param moving The set of shapes that are moving\n * @param children The parent's children\n * @param changes The changes array to push changes to\n */\nfunction reorderToFront(moving: Set<TLShape>, children: TLShape[], changes: TLShapePartial[]) {\n\tconst len = children.length\n\n\t// If all of the children are moving, there's nothing to do\n\tif (moving.size === len) return\n\n\tlet below: IndexKey | undefined\n\tlet above: IndexKey | undefined\n\n\t// Starting at the top of this parent's children...\n\tfor (let i = len - 1; i > -1; i--) {\n\t\tconst shape = children[i]\n\n\t\tif (moving.has(shape)) {\n\t\t\t// If we've found a moving shape before we've found a non-moving shape,\n\t\t\t// then that shape is already at the front; we can remove it from the\n\t\t\t// moving set and mark it as the shape that will be above the moved shapes.\n\t\t\tabove = shape.index\n\t\t\tmoving.delete(shape)\n\t\t} else {\n\t\t\t// The first non-moving shape we find will be below our moved shapes; we'll\n\t\t\t// put our moving shapes between it and the shape marked as above (if any).\n\t\t\tbelow = shape.index\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif (moving.size === 0) {\n\t\t// If our moving set is empty, there's nothing to do; all of our shapes were\n\t\t// already at the front of the parent's children.\n\t\treturn\n\t} else {\n\t\t// Sort the moving shapes by their current index, then apply the new indices\n\t\tconst indices = getIndicesBetween(below, above, moving.size)\n\t\tchanges.push(\n\t\t\t...Array.from(moving.values())\n\t\t\t\t.sort(sortByIndex)\n\t\t\t\t.map((shape, i) => ({ ...shape, index: indices[i] }))\n\t\t)\n\t}\n}\n\nfunction getOverlapChecker(editor: Editor, moving: Set<TLShape>) {\n\tconst movingVertices = Array.from(moving)\n\t\t.map((shape) => {\n\t\t\tconst vertices = editor.getShapePageGeometry(shape).vertices\n\t\t\tif (!vertices) return null\n\t\t\treturn { shape, vertices }\n\t\t})\n\t\t.filter(Boolean) as { shape: TLShape; vertices: Vec[] }[]\n\n\tconst isOverlapping = (child: TLShape) => {\n\t\tconst vertices = editor.getShapePageGeometry(child).vertices\n\t\tif (!vertices) return false\n\t\treturn movingVertices.some((other) => {\n\t\t\treturn polygonsIntersect(other.vertices, vertices)\n\t\t})\n\t}\n\n\treturn isOverlapping\n}\n\n/**\n * Reorders the moving shapes forward in the parent's children.\n *\n * @param editor The editor\n * @param moving The set of shapes that are moving\n * @param children The parent's children\n * @param changes The changes array to push changes to\n * @param opts The options\n */\nfunction reorderForward(\n\teditor: Editor,\n\tmoving: Set<TLShape>,\n\tchildren: TLShape[],\n\tchanges: TLShapePartial[],\n\topts?: { considerAllShapes?: boolean }\n) {\n\tconst isOverlapping = getOverlapChecker(editor, moving)\n\n\tconst len = children.length\n\n\t// If all of the children are moving, there's nothing to do\n\tif (moving.size === len) return\n\n\tlet state = { name: 'skipping' } as\n\t\t| { name: 'skipping' }\n\t\t| { name: 'selecting'; selectIndex: number }\n\n\t// Starting at the bottom of this parent's children...\n\tfor (let i = 0; i < len; i++) {\n\t\tconst isMoving = moving.has(children[i])\n\n\t\tswitch (state.name) {\n\t\t\tcase 'skipping': {\n\t\t\t\tif (!isMoving) continue\n\t\t\t\t// If we find a moving shape while skipping, start selecting\n\t\t\t\tstate = { name: 'selecting', selectIndex: i }\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'selecting': {\n\t\t\t\tif (isMoving) continue\n\t\t\t\tif (!opts?.considerAllShapes && !isOverlapping(children[i])) continue\n\t\t\t\t// if we find a non-moving and overlapping shape while selecting, move all selected\n\t\t\t\t// shapes in front of the not moving shape; and start skipping\n\t\t\t\tconst { selectIndex } = state\n\t\t\t\tgetIndicesBetween(children[i].index, children[i + 1]?.index, i - selectIndex).forEach(\n\t\t\t\t\t(index, k) => {\n\t\t\t\t\t\tconst child = children[selectIndex + k]\n\t\t\t\t\t\t// If the shape is not moving (therefore also not overlapping), skip it\n\t\t\t\t\t\tif (!moving.has(child)) return\n\t\t\t\t\t\tchanges.push({ ...child, index })\n\t\t\t\t\t}\n\t\t\t\t)\n\t\t\t\tstate = { name: 'skipping' }\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Reorders the moving shapes backward in the parent's children.\n *\n * @param editor The editor\n * @param moving The set of shapes that are moving\n * @param children The parent's children\n * @param changes The changes array to push changes to\n * @param opts The options\n */\nfunction reorderBackward(\n\teditor: Editor,\n\tmoving: Set<TLShape>,\n\tchildren: TLShape[],\n\tchanges: TLShapePartial[],\n\topts?: { considerAllShapes?: boolean }\n) {\n\tconst isOverlapping = getOverlapChecker(editor, moving)\n\n\tconst len = children.length\n\n\tif (moving.size === len) return\n\n\tlet state = { name: 'skipping' } as\n\t\t| { name: 'skipping' }\n\t\t| { name: 'selecting'; selectIndex: number }\n\n\t// Starting at the top of this parent's children...\n\tfor (let i = len - 1; i > -1; i--) {\n\t\tconst isMoving = moving.has(children[i])\n\n\t\tswitch (state.name) {\n\t\t\tcase 'skipping': {\n\t\t\t\tif (!isMoving) continue\n\t\t\t\t// If we find a moving shape while skipping, start selecting\n\t\t\t\tstate = { name: 'selecting', selectIndex: i }\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'selecting': {\n\t\t\t\tif (isMoving) continue\n\t\t\t\tif (!opts?.considerAllShapes && !isOverlapping(children[i])) continue\n\t\t\t\t// if we find a non-moving and overlapping shape while selecting, move all selected\n\t\t\t\t// shapes in behind of the not moving shape; and start skipping\n\t\t\t\tgetIndicesBetween(children[i - 1]?.index, children[i].index, state.selectIndex - i).forEach(\n\t\t\t\t\t(index, k) => {\n\t\t\t\t\t\tconst child = children[i + k + 1]\n\t\t\t\t\t\t// If the shape is not moving (therefore also not overlapping), skip it\n\t\t\t\t\t\tif (!moving.has(child)) return\n\t\t\t\t\t\tchanges.push({ ...child, index })\n\t\t\t\t\t}\n\t\t\t\t)\n\t\t\t\tstate = { name: 'skipping' }\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { TLParentId, TLShape, TLShapeId, TLShapePartial } from '@tldraw/tlschema'\nimport { IndexKey, compact, getIndicesBetween, sortByIndex } from '@tldraw/utils'\nimport { Editor } from '../editor/Editor'\n\nexport function getReorderingShapesChanges(\n\teditor: Editor,\n\toperation: 'toBack' | 'toFront' | 'forward' | 'backward',\n\tids: TLShapeId[],\n\topts?: { considerAllShapes?: boolean }\n) {\n\tif (ids.length === 0) return []\n\n\t// From the ids that are moving, collect the parents, their children, and which of those children are moving\n\tconst parents = new Map<TLParentId, { moving: Set<TLShape>; children: TLShape[] }>()\n\n\tfor (const shape of compact(ids.map((id) => editor.getShape(id)))) {\n\t\tconst { parentId } = shape\n\t\tif (!parents.has(parentId)) {\n\t\t\tparents.set(parentId, {\n\t\t\t\tchildren: compact(\n\t\t\t\t\teditor.getSortedChildIdsForParent(parentId).map((id) => editor.getShape(id))\n\t\t\t\t),\n\t\t\t\tmoving: new Set(),\n\t\t\t})\n\t\t}\n\t\tparents.get(parentId)!.moving.add(shape)\n\t}\n\n\tconst changes: TLShapePartial[] = []\n\n\tswitch (operation) {\n\t\tcase 'toBack': {\n\t\t\tparents.forEach(({ moving, children }) => reorderToBack(moving, children, changes))\n\t\t\tbreak\n\t\t}\n\t\tcase 'toFront': {\n\t\t\tparents.forEach(({ moving, children }) => reorderToFront(moving, children, changes))\n\t\t\tbreak\n\t\t}\n\t\tcase 'forward': {\n\t\t\tparents.forEach(({ moving, children }) =>\n\t\t\t\treorderForward(editor, moving, children, changes, opts)\n\t\t\t)\n\t\t\tbreak\n\t\t}\n\t\tcase 'backward': {\n\t\t\tparents.forEach(({ moving, children }) =>\n\t\t\t\treorderBackward(editor, moving, children, changes, opts)\n\t\t\t)\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn changes\n}\n\n/**\n * Reorders the moving shapes to the back of the parent's children.\n *\n * @param moving The set of shapes that are moving\n * @param children The parent's children\n * @param changes The changes array to push changes to\n */\nfunction reorderToBack(moving: Set<TLShape>, children: TLShape[], changes: TLShapePartial[]) {\n\tconst len = children.length\n\n\t// If all of the children are moving, there's nothing to do\n\tif (moving.size === len) return\n\n\tlet below: IndexKey | undefined\n\tlet above: IndexKey | undefined\n\n\t// Starting at the bottom of this parent's children...\n\tfor (let i = 0; i < len; i++) {\n\t\tconst shape = children[i]\n\n\t\tif (moving.has(shape)) {\n\t\t\t// If we've found a moving shape before we've found a non-moving shape,\n\t\t\t// then that shape is already at the back; we can remove it from the\n\t\t\t// moving set and mark it as the shape that will be below the moved shapes.\n\t\t\tbelow = shape.index\n\t\t\tmoving.delete(shape)\n\t\t} else {\n\t\t\t// The first non-moving shape we find will be above our moved shapes; we'll\n\t\t\t// put our moving shapes between it and the shape marked as below (if any).\n\t\t\tabove = shape.index\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif (moving.size === 0) {\n\t\t// If our moving set is empty, there's nothing to do; all of our shapes were\n\t\t// already at the back of the parent's children.\n\t\treturn\n\t} else {\n\t\t// Sort the moving shapes by their current index, then apply the new indices\n\t\tconst indices = getIndicesBetween(below, above, moving.size)\n\t\tchanges.push(\n\t\t\t...Array.from(moving.values())\n\t\t\t\t.sort(sortByIndex)\n\t\t\t\t.map((shape, i) => ({ ...shape, index: indices[i] }))\n\t\t)\n\t}\n}\n\n/**\n * Reorders the moving shapes to the front of the parent's children.\n *\n * @param moving The set of shapes that are moving\n * @param children The parent's children\n * @param changes The changes array to push changes to\n */\nfunction reorderToFront(moving: Set<TLShape>, children: TLShape[], changes: TLShapePartial[]) {\n\tconst len = children.length\n\n\t// If all of the children are moving, there's nothing to do\n\tif (moving.size === len) return\n\n\tlet below: IndexKey | undefined\n\tlet above: IndexKey | undefined\n\n\t// Starting at the top of this parent's children...\n\tfor (let i = len - 1; i > -1; i--) {\n\t\tconst shape = children[i]\n\n\t\tif (moving.has(shape)) {\n\t\t\t// If we've found a moving shape before we've found a non-moving shape,\n\t\t\t// then that shape is already at the front; we can remove it from the\n\t\t\t// moving set and mark it as the shape that will be above the moved shapes.\n\t\t\tabove = shape.index\n\t\t\tmoving.delete(shape)\n\t\t} else {\n\t\t\t// The first non-moving shape we find will be below our moved shapes; we'll\n\t\t\t// put our moving shapes between it and the shape marked as above (if any).\n\t\t\tbelow = shape.index\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif (moving.size === 0) {\n\t\t// If our moving set is empty, there's nothing to do; all of our shapes were\n\t\t// already at the front of the parent's children.\n\t\treturn\n\t} else {\n\t\t// Sort the moving shapes by their current index, then apply the new indices\n\t\tconst indices = getIndicesBetween(below, above, moving.size)\n\t\tchanges.push(\n\t\t\t...Array.from(moving.values())\n\t\t\t\t.sort(sortByIndex)\n\t\t\t\t.map((shape, i) => ({ ...shape, index: indices[i] }))\n\t\t)\n\t}\n}\n\nfunction getOverlapChecker(editor: Editor, moving: Set<TLShape>) {\n\tconst movingBounds = compact(\n\t\tArray.from(moving).map((shape) => {\n\t\t\tconst bounds = editor.getShapePageBounds(shape)\n\t\t\tif (!bounds) return null\n\t\t\treturn { shape, bounds }\n\t\t})\n\t)\n\tconst isOverlapping = (child: TLShape) => {\n\t\tconst bounds = editor.getShapePageBounds(child)\n\t\tif (!bounds) return false\n\t\treturn movingBounds.some((other) => {\n\t\t\treturn other.bounds.includes(bounds)\n\t\t})\n\t}\n\n\treturn isOverlapping\n}\n\n/**\n * Reorders the moving shapes forward in the parent's children.\n *\n * @param editor The editor\n * @param moving The set of shapes that are moving\n * @param children The parent's children\n * @param changes The changes array to push changes to\n * @param opts The options\n */\nfunction reorderForward(\n\teditor: Editor,\n\tmoving: Set<TLShape>,\n\tchildren: TLShape[],\n\tchanges: TLShapePartial[],\n\topts?: { considerAllShapes?: boolean }\n) {\n\tconst isOverlapping = getOverlapChecker(editor, moving)\n\n\tconst len = children.length\n\n\t// If all of the children are moving, there's nothing to do\n\tif (moving.size === len) return\n\n\tlet state = { name: 'skipping' } as\n\t\t| { name: 'skipping' }\n\t\t| { name: 'selecting'; selectIndex: number }\n\n\t// Starting at the bottom of this parent's children...\n\tfor (let i = 0; i < len; i++) {\n\t\tconst isMoving = moving.has(children[i])\n\n\t\tswitch (state.name) {\n\t\t\tcase 'skipping': {\n\t\t\t\tif (!isMoving) continue\n\t\t\t\t// If we find a moving shape while skipping, start selecting\n\t\t\t\tstate = { name: 'selecting', selectIndex: i }\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'selecting': {\n\t\t\t\tif (isMoving) continue\n\t\t\t\tif (!opts?.considerAllShapes && !isOverlapping(children[i])) continue\n\t\t\t\t// if we find a non-moving and overlapping shape while selecting, move all selected\n\t\t\t\t// shapes in front of the not moving shape; and start skipping\n\t\t\t\tconst { selectIndex } = state\n\t\t\t\tgetIndicesBetween(children[i].index, children[i + 1]?.index, i - selectIndex).forEach(\n\t\t\t\t\t(index, k) => {\n\t\t\t\t\t\tconst child = children[selectIndex + k]\n\t\t\t\t\t\t// If the shape is not moving (therefore also not overlapping), skip it\n\t\t\t\t\t\tif (!moving.has(child)) return\n\t\t\t\t\t\tchanges.push({ ...child, index })\n\t\t\t\t\t}\n\t\t\t\t)\n\t\t\t\tstate = { name: 'skipping' }\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Reorders the moving shapes backward in the parent's children.\n *\n * @param editor The editor\n * @param moving The set of shapes that are moving\n * @param children The parent's children\n * @param changes The changes array to push changes to\n * @param opts The options\n */\nfunction reorderBackward(\n\teditor: Editor,\n\tmoving: Set<TLShape>,\n\tchildren: TLShape[],\n\tchanges: TLShapePartial[],\n\topts?: { considerAllShapes?: boolean }\n) {\n\tconst isOverlapping = getOverlapChecker(editor, moving)\n\n\tconst len = children.length\n\n\tif (moving.size === len) return\n\n\tlet state = { name: 'skipping' } as\n\t\t| { name: 'skipping' }\n\t\t| { name: 'selecting'; selectIndex: number }\n\n\t// Starting at the top of this parent's children...\n\tfor (let i = len - 1; i > -1; i--) {\n\t\tconst isMoving = moving.has(children[i])\n\n\t\tswitch (state.name) {\n\t\t\tcase 'skipping': {\n\t\t\t\tif (!isMoving) continue\n\t\t\t\t// If we find a moving shape while skipping, start selecting\n\t\t\t\tstate = { name: 'selecting', selectIndex: i }\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcase 'selecting': {\n\t\t\t\tif (isMoving) continue\n\t\t\t\tif (!opts?.considerAllShapes && !isOverlapping(children[i])) continue\n\t\t\t\t// if we find a non-moving and overlapping shape while selecting, move all selected\n\t\t\t\t// shapes in behind of the not moving shape; and start skipping\n\t\t\t\tgetIndicesBetween(children[i - 1]?.index, children[i].index, state.selectIndex - i).forEach(\n\t\t\t\t\t(index, k) => {\n\t\t\t\t\t\tconst child = children[i + k + 1]\n\t\t\t\t\t\t// If the shape is not moving (therefore also not overlapping), skip it\n\t\t\t\t\t\tif (!moving.has(child)) return\n\t\t\t\t\t\tchanges.push({ ...child, index })\n\t\t\t\t\t}\n\t\t\t\t)\n\t\t\t\tstate = { name: 'skipping' }\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,8 +1,8 @@
1
- const version = "3.14.0-canary.b8a2a8b543ef";
1
+ const version = "3.14.0-canary.b8c5ab05b06a";
2
2
  const publishDates = {
3
3
  major: "2024-09-13T14:36:29.063Z",
4
- minor: "2025-05-27T15:19:55.206Z",
5
- patch: "2025-05-27T15:19:55.206Z"
4
+ minor: "2025-06-07T15:16:37.651Z",
5
+ patch: "2025-06-07T15:16:37.651Z"
6
6
  };
7
7
  export {
8
8
  publishDates,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/version.ts"],
4
- "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.14.0-canary.b8a2a8b543ef'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-05-27T15:19:55.206Z',\n\tpatch: '2025-05-27T15:19:55.206Z',\n}\n"],
4
+ "sourcesContent": ["// This file is automatically generated by internal/scripts/refresh-assets.ts.\n// Do not edit manually. Or do, I'm a comment, not a cop.\n\nexport const version = '3.14.0-canary.b8c5ab05b06a'\nexport const publishDates = {\n\tmajor: '2024-09-13T14:36:29.063Z',\n\tminor: '2025-06-07T15:16:37.651Z',\n\tpatch: '2025-06-07T15:16:37.651Z',\n}\n"],
5
5
  "mappings": "AAGO,MAAM,UAAU;AAChB,MAAM,eAAe;AAAA,EAC3B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACR;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tldraw/editor",
3
3
  "description": "A tiny little drawing app (editor).",
4
- "version": "3.14.0-canary.b8a2a8b543ef",
4
+ "version": "3.14.0-canary.b8c5ab05b06a",
5
5
  "author": {
6
6
  "name": "tldraw Inc.",
7
7
  "email": "hello@tldraw.com"
@@ -48,12 +48,12 @@
48
48
  "@tiptap/core": "^2.9.1",
49
49
  "@tiptap/pm": "^2.9.1",
50
50
  "@tiptap/react": "^2.9.1",
51
- "@tldraw/state": "3.14.0-canary.b8a2a8b543ef",
52
- "@tldraw/state-react": "3.14.0-canary.b8a2a8b543ef",
53
- "@tldraw/store": "3.14.0-canary.b8a2a8b543ef",
54
- "@tldraw/tlschema": "3.14.0-canary.b8a2a8b543ef",
55
- "@tldraw/utils": "3.14.0-canary.b8a2a8b543ef",
56
- "@tldraw/validate": "3.14.0-canary.b8a2a8b543ef",
51
+ "@tldraw/state": "3.14.0-canary.b8c5ab05b06a",
52
+ "@tldraw/state-react": "3.14.0-canary.b8c5ab05b06a",
53
+ "@tldraw/store": "3.14.0-canary.b8c5ab05b06a",
54
+ "@tldraw/tlschema": "3.14.0-canary.b8c5ab05b06a",
55
+ "@tldraw/utils": "3.14.0-canary.b8c5ab05b06a",
56
+ "@tldraw/validate": "3.14.0-canary.b8c5ab05b06a",
57
57
  "@types/core-js": "^2.5.8",
58
58
  "@use-gesture/react": "^10.3.1",
59
59
  "classnames": "^2.5.1",
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ import 'core-js/stable/array/flat-map.js'
4
4
  import 'core-js/stable/array/flat.js'
5
5
  import 'core-js/stable/string/at.js'
6
6
  import 'core-js/stable/string/replace-all.js'
7
+ export { areShapesContentEqual } from './lib/utils/areShapesContentEqual'
7
8
 
8
9
  // eslint-disable-next-line local/no-export-star
9
10
  export * from '@tldraw/state'
@@ -506,6 +506,8 @@ export class Editor extends EventEmitter<TLEventMap> {
506
506
  shape: {
507
507
  afterChange: (shapeBefore, shapeAfter) => {
508
508
  for (const binding of this.getBindingsInvolvingShape(shapeAfter)) {
509
+ if (areShapesContentEqual(shapeBefore, shapeAfter)) continue
510
+
509
511
  invalidBindingTypes.add(binding.type)
510
512
  if (binding.fromId === shapeAfter.id) {
511
513
  this.getBindingUtil(binding).onAfterChangeFromShape?.({
@@ -3715,10 +3717,7 @@ export class Editor extends EventEmitter<TLEventMap> {
3715
3717
  */
3716
3718
  @computed getViewportScreenCenter() {
3717
3719
  const viewportScreenBounds = this.getViewportScreenBounds()
3718
- return new Vec(
3719
- viewportScreenBounds.midX - viewportScreenBounds.minX,
3720
- viewportScreenBounds.midY - viewportScreenBounds.minY
3721
- )
3720
+ return new Vec(viewportScreenBounds.w / 2, viewportScreenBounds.h / 2)
3722
3721
  }
3723
3722
 
3724
3723
  /**
@@ -4644,44 +4643,6 @@ export class Editor extends EventEmitter<TLEventMap> {
4644
4643
  )! as T
4645
4644
  }
4646
4645
 
4647
- private _shapePageGeometryCaches: Record<string, ComputedCache<Geometry2d, TLShape>> = {}
4648
-
4649
- /**
4650
- * Get the geometry of a shape in page-space.
4651
- *
4652
- * @example
4653
- * ```ts
4654
- * editor.getShapePageGeometry(myShape)
4655
- * editor.getShapePageGeometry(myShapeId)
4656
- * editor.getShapePageGeometry(myShapeId, { context: "arrow" })
4657
- * ```
4658
- *
4659
- * @param shape - The shape (or shape id) to get the geometry for.
4660
- * @param opts - Additional options about the request for geometry. Passed to {@link ShapeUtil.getGeometry}.
4661
- *
4662
- * @public
4663
- */
4664
- getShapePageGeometry<T extends Geometry2d>(shape: TLShape | TLShapeId, opts?: TLGeometryOpts): T {
4665
- const context = opts?.context ?? 'none'
4666
- if (!this._shapePageGeometryCaches[context]) {
4667
- this._shapePageGeometryCaches[context] = this.store.createComputedCache(
4668
- 'bounds',
4669
- (shape) => {
4670
- const geometry = this.getShapeGeometry(shape.id, opts)
4671
- const pageTransform = this.getShapePageTransform(shape.id)
4672
- return geometry.transform(pageTransform)
4673
- },
4674
- {
4675
- // we only depend directly on the shape id, and changing geometry/transform will update us anyway
4676
- areRecordsEqual: () => true,
4677
- }
4678
- )
4679
- }
4680
- return this._shapePageGeometryCaches[context].get(
4681
- typeof shape === 'string' ? shape : shape.id
4682
- )! as T
4683
- }
4684
-
4685
4646
  /** @internal */
4686
4647
  @computed private _getShapeHandlesCache(): ComputedCache<TLHandle[] | undefined, TLShape> {
4687
4648
  return this.store.createComputedCache(
@@ -4794,7 +4755,10 @@ export class Editor extends EventEmitter<TLEventMap> {
4794
4755
  /** @internal */
4795
4756
  @computed private _getShapePageBoundsCache(): ComputedCache<Box, TLShape> {
4796
4757
  return this.store.createComputedCache<Box, TLShape>('pageBoundsCache', (shape) => {
4797
- return this.getShapePageGeometry(shape).bounds
4758
+ const pageTransform = this.getShapePageTransform(shape)
4759
+ if (!pageTransform) return undefined
4760
+ const geometry = this.getShapeGeometry(shape)
4761
+ return Box.FromPoints(pageTransform.applyToPoints(geometry.vertices))
4798
4762
  })
4799
4763
  }
4800
4764
 
@@ -4868,11 +4832,12 @@ export class Editor extends EventEmitter<TLEventMap> {
4868
4832
  if (frameAncestors.length === 0) return undefined
4869
4833
 
4870
4834
  const pageMask = frameAncestors
4871
- .map<Vec[] | undefined>(
4872
- (s) =>
4873
- // Apply the frame transform to the frame outline to get the frame outline in the current page space
4874
- this.getShapePageGeometry(s.id).vertices
4875
- )
4835
+ .map<Vec[] | undefined>((s) => {
4836
+ // Apply the frame transform to the frame outline to get the frame outline in the current page space
4837
+ const geometry = this.getShapeGeometry(s.id)
4838
+ const pageTransform = this.getShapePageTransform(s.id)
4839
+ return pageTransform.applyToPoints(geometry.vertices)
4840
+ })
4876
4841
  .reduce((acc, b) => {
4877
4842
  if (!(b && acc)) return undefined
4878
4843
  const intersection = intersectPolygonPolygon(acc, b)
@@ -94,7 +94,11 @@ export class FontManager {
94
94
  const shapeUtil = this.editor.getShapeUtil(shape)
95
95
  return shapeUtil.getFontFaces(shape)
96
96
  },
97
- { areResultsEqual: areArraysShallowEqual, areRecordsEqual: (a, b) => a.props === b.props }
97
+ {
98
+ areResultsEqual: areArraysShallowEqual,
99
+ // @ts-expect-error
100
+ areRecordsEqual: (a, b) => a.props.richText === b.props.richText,
101
+ }
98
102
  )
99
103
 
100
104
  this.shapeFontLoadStateCache = editor.store.createCache<(FontState | null)[], TLShape>(