@xom11/whiteboard 0.31.0 → 0.33.0

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 (93) hide show
  1. package/dist/ai.d.mts +149 -28
  2. package/dist/ai.d.ts +149 -28
  3. package/dist/ai.js +2652 -430
  4. package/dist/ai.js.map +1 -1
  5. package/dist/ai.mjs +2482 -426
  6. package/dist/ai.mjs.map +1 -1
  7. package/dist/catalog.json +4 -4
  8. package/dist/{chunk-JJ4FPCBE.mjs → chunk-4EXRNZRX.mjs} +641 -75
  9. package/dist/chunk-4EXRNZRX.mjs.map +1 -0
  10. package/dist/{chunk-TMRFSOM7.mjs → chunk-4NJUTCFF.mjs} +4 -4
  11. package/dist/{chunk-TMRFSOM7.mjs.map → chunk-4NJUTCFF.mjs.map} +1 -1
  12. package/dist/{chunk-LTLLQUMN.mjs → chunk-BZZ5BROM.mjs} +6 -7
  13. package/dist/chunk-BZZ5BROM.mjs.map +1 -0
  14. package/dist/{host-KMWP7KBT.mjs → chunk-C3RDFBQ5.mjs} +20 -35
  15. package/dist/chunk-C3RDFBQ5.mjs.map +1 -0
  16. package/dist/chunk-C7C66BAV.mjs +11 -0
  17. package/dist/chunk-C7C66BAV.mjs.map +1 -0
  18. package/dist/{chunk-T3N4BSJV.mjs → chunk-DCFZTKFE.mjs} +13 -5
  19. package/dist/chunk-DCFZTKFE.mjs.map +1 -0
  20. package/dist/{chunk-44JY2AKC.mjs → chunk-F42MZ745.mjs} +3 -3
  21. package/dist/{chunk-44JY2AKC.mjs.map → chunk-F42MZ745.mjs.map} +1 -1
  22. package/dist/{chunk-BMYC2ILT.mjs → chunk-FOPZOVI3.mjs} +5 -4
  23. package/dist/chunk-FOPZOVI3.mjs.map +1 -0
  24. package/dist/{chunk-K5BS2H56.mjs → chunk-GKDQ5IV7.mjs} +4 -4
  25. package/dist/{chunk-K5BS2H56.mjs.map → chunk-GKDQ5IV7.mjs.map} +1 -1
  26. package/dist/{chunk-73Q7ADVL.mjs → chunk-MNHOFOM5.mjs} +2 -2
  27. package/dist/{chunk-73Q7ADVL.mjs.map → chunk-MNHOFOM5.mjs.map} +1 -1
  28. package/dist/{chunk-KOXOC2FI.mjs → chunk-NBMYET5H.mjs} +11 -15
  29. package/dist/chunk-NBMYET5H.mjs.map +1 -0
  30. package/dist/{chunk-DWIEVCGK.mjs → chunk-NTFG7ZRN.mjs} +53 -3
  31. package/dist/chunk-NTFG7ZRN.mjs.map +1 -0
  32. package/dist/{chunk-C76SOFXF.mjs → chunk-QCK3Z5WN.mjs} +3 -3
  33. package/dist/{chunk-C76SOFXF.mjs.map → chunk-QCK3Z5WN.mjs.map} +1 -1
  34. package/dist/chunk-SUXIMLYN.mjs +238 -0
  35. package/dist/chunk-SUXIMLYN.mjs.map +1 -0
  36. package/dist/geometry-2d.d.mts +1 -1
  37. package/dist/geometry-2d.d.ts +1 -1
  38. package/dist/geometry-2d.js +1026 -231
  39. package/dist/geometry-2d.js.map +1 -1
  40. package/dist/geometry-2d.mjs +6 -5
  41. package/dist/geometry-3d.d.mts +1 -1
  42. package/dist/geometry-3d.d.ts +1 -1
  43. package/dist/geometry-3d.js +4001 -475
  44. package/dist/geometry-3d.js.map +1 -1
  45. package/dist/geometry-3d.mjs +5 -5
  46. package/dist/graph-2d.d.mts +1 -1
  47. package/dist/graph-2d.d.ts +1 -1
  48. package/dist/graph-2d.js +712 -115
  49. package/dist/graph-2d.js.map +1 -1
  50. package/dist/graph-2d.mjs +5 -5
  51. package/dist/{handleExtractProblem-BrDY9ifM.d.ts → handleExtractProblem-mtJyhEw3.d.mts} +2 -0
  52. package/dist/{handleExtractProblem-BrDY9ifM.d.mts → handleExtractProblem-mtJyhEw3.d.ts} +2 -0
  53. package/dist/host-6WTLCXUE.mjs +62 -0
  54. package/dist/host-6WTLCXUE.mjs.map +1 -0
  55. package/dist/{host-H2IGOKJU.mjs → host-BWIXT5TR.mjs} +3 -3
  56. package/dist/{host-H2IGOKJU.mjs.map → host-BWIXT5TR.mjs.map} +1 -1
  57. package/dist/{host-4FIUNIDQ.mjs → host-QDVRULJV.mjs} +14 -11
  58. package/dist/host-QDVRULJV.mjs.map +1 -0
  59. package/dist/host-USOIVTQB.mjs +4976 -0
  60. package/dist/host-USOIVTQB.mjs.map +1 -0
  61. package/dist/index.d.mts +14 -4
  62. package/dist/index.d.ts +14 -4
  63. package/dist/index.js +4273 -521
  64. package/dist/index.js.map +1 -1
  65. package/dist/index.mjs +48 -27
  66. package/dist/index.mjs.map +1 -1
  67. package/dist/latex.d.mts +1 -1
  68. package/dist/latex.d.ts +1 -1
  69. package/dist/latex.mjs +1 -1
  70. package/dist/serialize-7UZWQQ4I.mjs +9 -0
  71. package/dist/{serialize-PGHQZEPV.mjs.map → serialize-7UZWQQ4I.mjs.map} +1 -1
  72. package/dist/studio.d.mts +35 -0
  73. package/dist/studio.d.ts +35 -0
  74. package/dist/studio.js +10883 -0
  75. package/dist/studio.js.map +1 -0
  76. package/dist/studio.mjs +28 -0
  77. package/dist/studio.mjs.map +1 -0
  78. package/dist/{types-tePd94vW.d.ts → types-eEEEVsLF.d.mts} +1 -1
  79. package/dist/{types-tePd94vW.d.mts → types-eEEEVsLF.d.ts} +1 -1
  80. package/package.json +12 -2
  81. package/dist/chunk-BMYC2ILT.mjs.map +0 -1
  82. package/dist/chunk-DWIEVCGK.mjs.map +0 -1
  83. package/dist/chunk-JJ4FPCBE.mjs.map +0 -1
  84. package/dist/chunk-KOXOC2FI.mjs.map +0 -1
  85. package/dist/chunk-LTLLQUMN.mjs.map +0 -1
  86. package/dist/chunk-QK6OVDLC.mjs +0 -103
  87. package/dist/chunk-QK6OVDLC.mjs.map +0 -1
  88. package/dist/chunk-T3N4BSJV.mjs.map +0 -1
  89. package/dist/host-4FIUNIDQ.mjs.map +0 -1
  90. package/dist/host-4ZB4XD4S.mjs +0 -2378
  91. package/dist/host-4ZB4XD4S.mjs.map +0 -1
  92. package/dist/host-KMWP7KBT.mjs.map +0 -1
  93. package/dist/serialize-PGHQZEPV.mjs +0 -9
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { labelOpts, compile, validate } from './chunk-CH6SFONH.mjs';
3
- import { createEmptyState } from './chunk-73Q7ADVL.mjs';
3
+ import { createEmptyState } from './chunk-MNHOFOM5.mjs';
4
4
  import { registerKind } from './chunk-B4NJJZFR.mjs';
5
5
  import * as React from 'react';
6
6
 
@@ -81,8 +81,377 @@ function constraintRefs(c) {
81
81
  return [c.polygonId];
82
82
  case "onSphere":
83
83
  return [c.sphereId];
84
- default:
84
+ case "midpoint":
85
+ return [c.p1, c.p2];
86
+ case "centroid":
87
+ return [...c.vertices];
88
+ case "intersectionLines":
89
+ return [c.a1, c.b1, c.a2, c.b2];
90
+ case "intersectionLinePlane":
91
+ return [c.a, c.b, c.plane];
92
+ case "perpFootLine":
93
+ return [c.from, c.a, c.b];
94
+ case "perpFootPlane":
95
+ return [c.from, c.plane];
96
+ case "circumsphereCenter":
97
+ return [...c.vertices];
98
+ case "pyramidInsphereCenter":
99
+ return [c.apex, ...c.vertices];
100
+ case "faceCircumcenter":
101
+ return [...c.vertices];
102
+ case "pointAboveFace":
103
+ return [c.base, c.apex, ...c.vertices];
104
+ // Kind KHÔNG có ref scene-object (toạ độ literal): liệt kê TƯỜNG MINH để
105
+ // exhaustive never-guard bên dưới buộc khai báo khi thêm constraint kind mới
106
+ // (mô phỏng constraintRefs2D — quên case = cascade-delete/deps sai âm thầm).
107
+ case "free":
108
+ case "onGround":
109
+ case "onAxis":
110
+ return [];
111
+ default: {
85
112
  return [];
113
+ }
114
+ }
115
+ }
116
+
117
+ // src/core/scene/kinds/constraint3d-math.ts
118
+ function sub(a, b) {
119
+ return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
120
+ }
121
+ function add(a, b) {
122
+ return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
123
+ }
124
+ function scale(a, k) {
125
+ return [a[0] * k, a[1] * k, a[2] * k];
126
+ }
127
+ function dot(a, b) {
128
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
129
+ }
130
+ function cross(a, b) {
131
+ return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
132
+ }
133
+ function norm(a) {
134
+ return Math.sqrt(dot(a, a));
135
+ }
136
+ function normalize(a) {
137
+ const n = norm(a);
138
+ return n === 0 ? a : scale(a, 1 / n);
139
+ }
140
+ var MAX_REC_DEPTH = 512;
141
+ var _recDepth = 0;
142
+ function getPointWorld(id, state) {
143
+ const obj = state.objects[id];
144
+ if (!obj || obj.kind !== "point3d") {
145
+ throw new Error(`constraintMath: point ${id} not found`);
146
+ }
147
+ const attrs = obj.attrs;
148
+ return constraintToWorld(attrs.constraint, state);
149
+ }
150
+ function getPlaneBasis(planeObj, state) {
151
+ const a = planeObj.attrs;
152
+ let p1, p2, p3;
153
+ if (a.construction) {
154
+ const r = planeConstructionWorld(a.construction, state);
155
+ p1 = r.p1;
156
+ p2 = r.p2;
157
+ p3 = r.p3;
158
+ } else {
159
+ p1 = getPointWorld(a.p1, state);
160
+ p2 = getPointWorld(a.p2, state);
161
+ p3 = getPointWorld(a.p3, state);
162
+ }
163
+ const basis1 = sub(p2, p1);
164
+ const tmp = sub(p3, p1);
165
+ const normal = normalize(cross(basis1, tmp));
166
+ const basis2 = cross(normal, basis1);
167
+ return { origin: p1, basis1, basis2, normal };
168
+ }
169
+ function orthoBasisFromNormal(n) {
170
+ const seed = Math.abs(n[0]) < 0.9 ? [1, 0, 0] : [0, 1, 0];
171
+ const basis1 = normalize(cross(n, seed));
172
+ const basis2 = cross(n, basis1);
173
+ return { basis1, basis2 };
174
+ }
175
+ function planeConstructionWorld(c, state) {
176
+ if (_recDepth >= MAX_REC_DEPTH) throw new Error("planeConstructionWorld: \u0111\u1EC7 quy qu\xE1 s\xE2u \u2014 chu tr\xECnh tham chi\u1EBFu m\u1EB7t?");
177
+ _recDepth++;
178
+ try {
179
+ return planeConstructionWorldInner(c, state);
180
+ } finally {
181
+ _recDepth--;
182
+ }
183
+ }
184
+ function planeConstructionWorldInner(c, state) {
185
+ switch (c.kind) {
186
+ case "planeParallelThrough": {
187
+ const P = getPointWorld(c.point, state);
188
+ const ref = state.objects[c.refPlane];
189
+ if (!ref || ref.kind !== "plane3d") throw new Error("planeParallelThrough: m\u1EB7t tham chi\u1EBFu thi\u1EBFu");
190
+ const { basis1, basis2 } = getPlaneBasis(ref, state);
191
+ if (norm(basis1) < 1e-12 || norm(basis2) < 1e-12) {
192
+ return { p1: P, p2: add(P, [1, 0, 0]), p3: add(P, [0, 1, 0]) };
193
+ }
194
+ return { p1: P, p2: add(P, basis1), p3: add(P, basis2) };
195
+ }
196
+ case "planePerpToLine": {
197
+ const P = getPointWorld(c.point, state);
198
+ const dir = sub(getPointWorld(c.lineB, state), getPointWorld(c.lineA, state));
199
+ const dn = norm(dir);
200
+ if (dn < 1e-12) return { p1: P, p2: add(P, [1, 0, 0]), p3: add(P, [0, 1, 0]) };
201
+ const { basis1, basis2 } = orthoBasisFromNormal(scale(dir, 1 / dn));
202
+ return { p1: P, p2: add(P, basis1), p3: add(P, basis2) };
203
+ }
204
+ default: {
205
+ throw new Error("planeConstructionWorld: kind kh\xF4ng h\u1ED7 tr\u1EE3");
206
+ }
207
+ }
208
+ }
209
+ function lineDefiningPointIds(line) {
210
+ if (line.kind === "line3d" || line.kind === "segment3d") {
211
+ const a = line.attrs;
212
+ return a.p1 && a.p2 ? [a.p1, a.p2] : null;
213
+ }
214
+ if (line.kind === "ray3d") {
215
+ const a = line.attrs;
216
+ return [a.origin, a.through];
217
+ }
218
+ if (line.kind === "vector3d") {
219
+ const a = line.attrs;
220
+ return [a.from, a.to];
221
+ }
222
+ return null;
223
+ }
224
+ function lineEndpointsWorld(lineId, state, ctx) {
225
+ const line = state.objects[lineId];
226
+ if (!line) throw new Error(`${ctx}: \u0111\u01B0\u1EDDng ${lineId} kh\xF4ng t\u1ED3n t\u1EA1i`);
227
+ const ids = lineDefiningPointIds(line);
228
+ if (!ids) throw new Error(`${ctx}: kind ${line.kind} kh\xF4ng ph\u1EA3i \u0111\u01B0\u1EDDng`);
229
+ return { a: getPointWorld(ids[0], state), b: getPointWorld(ids[1], state) };
230
+ }
231
+ function lineLineClosestMidpoint(A, B, C, D) {
232
+ const u = sub(B, A), v = sub(D, C), w0 = sub(A, C);
233
+ const a = dot(u, u), b = dot(u, v), cc = dot(v, v), d = dot(u, w0), e = dot(v, w0);
234
+ const denom = a * cc - b * b;
235
+ if (Math.abs(denom) < 1e-12) return scale(add(A, C), 0.5);
236
+ const sc = (b * e - cc * d) / denom;
237
+ const tc = (a * e - b * d) / denom;
238
+ return scale(add(add(A, scale(u, sc)), add(C, scale(v, tc))), 0.5);
239
+ }
240
+ function planeOriginNormal(planeId, state, ctx) {
241
+ const plane = state.objects[planeId];
242
+ if (!plane || plane.kind !== "plane3d") throw new Error(`${ctx}: m\u1EB7t ph\u1EB3ng ${planeId} thi\u1EBFu`);
243
+ const { origin, normal } = getPlaneBasis(plane, state);
244
+ return { origin, normal };
245
+ }
246
+ function lineConstructionWorld(c, state) {
247
+ switch (c.kind) {
248
+ case "planePlaneIntersection": {
249
+ const P1 = planeOriginNormal(c.plane1, state, "planePlaneIntersection");
250
+ const P2 = planeOriginNormal(c.plane2, state, "planePlaneIntersection");
251
+ const d = cross(P1.normal, P2.normal);
252
+ const dd = dot(d, d);
253
+ if (dd < 1e-12) {
254
+ return { a: P1.origin, b: add(P1.origin, P2.normal) };
255
+ }
256
+ const c1 = dot(P1.normal, P1.origin);
257
+ const c2 = dot(P2.normal, P2.origin);
258
+ const p0 = scale(add(scale(cross(P2.normal, d), c1), scale(cross(d, P1.normal), c2)), 1 / dd);
259
+ return { a: p0, b: add(p0, d) };
260
+ }
261
+ case "lineParallelThrough": {
262
+ const P = getPointWorld(c.point, state);
263
+ const dir = sub(getPointWorld(c.dirB, state), getPointWorld(c.dirA, state));
264
+ if (norm(dir) < 1e-12) return { a: P, b: add(P, [1, 0, 0]) };
265
+ return { a: P, b: add(P, dir) };
266
+ }
267
+ case "linePerpToPlane": {
268
+ const P = getPointWorld(c.point, state);
269
+ const { normal } = planeOriginNormal(c.plane, state, "linePerpToPlane");
270
+ return { a: P, b: add(P, normal) };
271
+ }
272
+ default: {
273
+ throw new Error("lineConstructionWorld: kind kh\xF4ng h\u1ED7 tr\u1EE3");
274
+ }
275
+ }
276
+ }
277
+ function solve3(M, b) {
278
+ const det3 = (m) => m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
279
+ const det = det3(M);
280
+ if (Math.abs(det) < 1e-9) return null;
281
+ const colDet = (j) => {
282
+ const A = M.map((r) => r.slice());
283
+ for (let i = 0; i < 3; i++) A[i][j] = b[i];
284
+ return det3(A);
285
+ };
286
+ return [colDet(0) / det, colDet(1) / det, colDet(2) / det];
287
+ }
288
+ function constraintToWorld(c, state) {
289
+ if (_recDepth >= MAX_REC_DEPTH) throw new Error("constraintToWorld: \u0111\u1EC7 quy qu\xE1 s\xE2u \u2014 chu tr\xECnh tham chi\u1EBFu?");
290
+ _recDepth++;
291
+ try {
292
+ return constraintToWorldInner(c, state);
293
+ } finally {
294
+ _recDepth--;
295
+ }
296
+ }
297
+ function constraintToWorldInner(c, state) {
298
+ switch (c.kind) {
299
+ case "free":
300
+ return [c.x, c.y, c.z];
301
+ case "onGround":
302
+ return [c.x, c.y, 0];
303
+ case "onAxis": {
304
+ if (c.axis === "x") return [c.t, 0, 0];
305
+ if (c.axis === "y") return [0, c.t, 0];
306
+ return [0, 0, c.t];
307
+ }
308
+ case "onPlane": {
309
+ const plane = state.objects[c.planeId];
310
+ if (!plane || plane.kind !== "plane3d") throw new Error("onPlane: plane missing");
311
+ const { origin, basis1, basis2 } = getPlaneBasis(plane, state);
312
+ return add(add(origin, scale(basis1, c.u)), scale(basis2, c.v));
313
+ }
314
+ case "onLine": {
315
+ const { a, b } = lineEndpointsWorld(c.lineId, state, "onLine");
316
+ return add(a, scale(sub(b, a), c.t));
317
+ }
318
+ case "onPolygon": {
319
+ const pg = state.objects[c.polygonId];
320
+ if (!pg || pg.kind !== "polygon3d") throw new Error("onPolygon: parent missing");
321
+ const v = pg.attrs.vertices;
322
+ if (v.length < 3) throw new Error("onPolygon: < 3 vertices");
323
+ const p1 = getPointWorld(v[0], state);
324
+ const p2 = getPointWorld(v[1], state);
325
+ const p3 = getPointWorld(v[2], state);
326
+ const basis1 = sub(p2, p1);
327
+ const tmp = sub(p3, p1);
328
+ const normal = normalize(cross(basis1, tmp));
329
+ const basis2 = cross(normal, basis1);
330
+ return add(add(p1, scale(basis1, c.u)), scale(basis2, c.v));
331
+ }
332
+ case "onSphere": {
333
+ const sph = state.objects[c.sphereId];
334
+ if (!sph || sph.kind !== "sphere3d") throw new Error("onSphere: parent missing");
335
+ const a = sph.attrs;
336
+ const center = getPointWorld(a.center, state);
337
+ const surface = getPointWorld(a.surfacePoint, state);
338
+ const radius = norm(sub(surface, center));
339
+ const x = center[0] + radius * Math.sin(c.phi) * Math.cos(c.theta);
340
+ const y = center[1] + radius * Math.sin(c.phi) * Math.sin(c.theta);
341
+ const z = center[2] + radius * Math.cos(c.phi);
342
+ return [x, y, z];
343
+ }
344
+ // ───── Điểm phái sinh (v1) ─────
345
+ case "midpoint": {
346
+ const p1 = getPointWorld(c.p1, state);
347
+ const p2 = getPointWorld(c.p2, state);
348
+ return scale(add(p1, p2), 0.5);
349
+ }
350
+ case "centroid": {
351
+ const n = c.vertices.length;
352
+ if (n === 0) return [0, 0, 0];
353
+ let acc = [0, 0, 0];
354
+ for (const id of c.vertices) acc = add(acc, getPointWorld(id, state));
355
+ return scale(acc, 1 / n);
356
+ }
357
+ case "circumsphereCenter": {
358
+ const P = c.vertices.map((id) => getPointWorld(id, state));
359
+ if (P.length < 4) return P.length ? P[0] : [0, 0, 0];
360
+ const p0 = P[0];
361
+ const M = [[0, 0, 0], [0, 0, 0], [0, 0, 0]];
362
+ const rhs = [0, 0, 0];
363
+ for (let i = 1; i < P.length; i++) {
364
+ const r = [2 * (P[i][0] - p0[0]), 2 * (P[i][1] - p0[1]), 2 * (P[i][2] - p0[2])];
365
+ const bi = dot(P[i], P[i]) - dot(p0, p0);
366
+ for (let a = 0; a < 3; a++) {
367
+ for (let bcol = 0; bcol < 3; bcol++) M[a][bcol] += r[a] * r[bcol];
368
+ rhs[a] += r[a] * bi;
369
+ }
370
+ }
371
+ const sol = solve3(M, rhs);
372
+ if (sol) return sol;
373
+ let acc = [0, 0, 0];
374
+ for (const p of P) acc = add(acc, p);
375
+ return scale(acc, 1 / P.length);
376
+ }
377
+ case "pyramidInsphereCenter": {
378
+ const S = getPointWorld(c.apex, state);
379
+ const base = c.vertices.map((id) => getPointWorld(id, state));
380
+ const n = base.length;
381
+ if (n < 3) return S;
382
+ let G = [0, 0, 0];
383
+ for (const p of base) G = add(G, p);
384
+ G = scale(G, 1 / n);
385
+ let nb = normalize(cross(sub(base[1], base[0]), sub(base[2], base[0])));
386
+ if (dot(nb, sub(S, G)) < 0) nb = scale(nb, -1);
387
+ let m = normalize(cross(sub(base[0], S), sub(base[1], S)));
388
+ if (dot(m, sub(G, base[0])) < 0) m = scale(m, -1);
389
+ const denom = 1 - dot(m, nb);
390
+ const s = Math.abs(denom) < 1e-9 ? NaN : dot(m, sub(G, base[0])) / denom;
391
+ if (!Number.isFinite(s) || s <= 0) {
392
+ return scale(add(S, scale(G, n)), 1 / (n + 1));
393
+ }
394
+ return add(G, scale(nb, s));
395
+ }
396
+ case "faceCircumcenter": {
397
+ const P = c.vertices.map((id) => getPointWorld(id, state));
398
+ if (P.length < 3) return P.length ? P[0] : [0, 0, 0];
399
+ const [p0, p1, p2] = P;
400
+ const e1 = sub(p1, p0), e2 = sub(p2, p0);
401
+ const nrm = cross(e1, e2);
402
+ const M = [
403
+ [2 * e1[0], 2 * e1[1], 2 * e1[2]],
404
+ [2 * e2[0], 2 * e2[1], 2 * e2[2]],
405
+ [nrm[0], nrm[1], nrm[2]]
406
+ ];
407
+ const rhs = [dot(p1, p1) - dot(p0, p0), dot(p2, p2) - dot(p0, p0), dot(nrm, p0)];
408
+ const sol = solve3(M, rhs);
409
+ if (sol) return sol;
410
+ return scale(add(add(p0, p1), p2), 1 / 3);
411
+ }
412
+ case "pointAboveFace": {
413
+ const G = getPointWorld(c.base, state);
414
+ const P = c.vertices.map((id) => getPointWorld(id, state));
415
+ if (P.length < 3) return G;
416
+ let n = normalize(cross(sub(P[1], P[0]), sub(P[2], P[0])));
417
+ const S = getPointWorld(c.apex, state);
418
+ if (dot(n, sub(S, P[0])) < 0) n = scale(n, -1);
419
+ const h = dot(sub(S, P[0]), n);
420
+ if (!Number.isFinite(h) || h <= 1e-9) return G;
421
+ return add(G, scale(n, h));
422
+ }
423
+ case "intersectionLines": {
424
+ const A = getPointWorld(c.a1, state), B = getPointWorld(c.b1, state);
425
+ const C = getPointWorld(c.a2, state), D = getPointWorld(c.b2, state);
426
+ return lineLineClosestMidpoint(A, B, C, D);
427
+ }
428
+ case "intersectionLinePlane": {
429
+ const A = getPointWorld(c.a, state), B = getPointWorld(c.b, state);
430
+ const plane = state.objects[c.plane];
431
+ if (!plane || plane.kind !== "plane3d") throw new Error("intersectionLinePlane: m\u1EB7t ph\u1EB3ng thi\u1EBFu");
432
+ const { origin, normal } = getPlaneBasis(plane, state);
433
+ const dir = sub(B, A);
434
+ const dn = dot(dir, normal);
435
+ if (Math.abs(dn) < 1e-12) return A;
436
+ const t = dot(sub(origin, A), normal) / dn;
437
+ return add(A, scale(dir, t));
438
+ }
439
+ case "perpFootLine": {
440
+ const P = getPointWorld(c.from, state);
441
+ const A = getPointWorld(c.a, state), B = getPointWorld(c.b, state);
442
+ const u = sub(B, A);
443
+ const uu = dot(u, u);
444
+ const t = uu === 0 ? 0 : dot(sub(P, A), u) / uu;
445
+ return add(A, scale(u, t));
446
+ }
447
+ case "perpFootPlane": {
448
+ const P = getPointWorld(c.from, state);
449
+ const plane = state.objects[c.plane];
450
+ if (!plane || plane.kind !== "plane3d") throw new Error("perpFootPlane: m\u1EB7t ph\u1EB3ng thi\u1EBFu");
451
+ const { origin, normal } = getPlaneBasis(plane, state);
452
+ const dist2 = dot(sub(P, origin), normal);
453
+ return sub(P, scale(normal, dist2));
454
+ }
86
455
  }
87
456
  }
88
457
 
@@ -124,6 +493,15 @@ var def = {
124
493
  if (c.kind === "onLine") return `${obj.label} tr\xEAn \u0111\u01B0\u1EDDng ${c.lineId}`;
125
494
  if (c.kind === "onPolygon") return `${obj.label} tr\xEAn \u0111a gi\xE1c ${c.polygonId}`;
126
495
  if (c.kind === "onSphere") return `${obj.label} tr\xEAn m\u1EB7t c\u1EA7u ${c.sphereId}`;
496
+ if (c.kind === "midpoint") return `${obj.label} = trung \u0111i\u1EC3m ${c.p1}${c.p2}`;
497
+ if (c.kind === "centroid") return `${obj.label} = tr\u1ECDng t\xE2m ${c.vertices.join("")}`;
498
+ if (c.kind === "intersectionLines") return `${obj.label} = giao 2 \u0111\u01B0\u1EDDng (${c.a1}${c.b1}, ${c.a2}${c.b2})`;
499
+ if (c.kind === "intersectionLinePlane") return `${obj.label} = giao ${c.a}${c.b} \u2229 ${c.plane}`;
500
+ if (c.kind === "perpFootLine") return `${obj.label} = ch\xE2n \u22A5 t\u1EEB ${c.from} xu\u1ED1ng ${c.a}${c.b}`;
501
+ if (c.kind === "perpFootPlane") return `${obj.label} = ch\xE2n \u22A5 t\u1EEB ${c.from} xu\u1ED1ng ${c.plane}`;
502
+ if (c.kind === "pyramidInsphereCenter") return `${obj.label} = t\xE2m c\u1EA7u n\u1ED9i ti\u1EBFp ch\xF3p ${c.apex}.${c.vertices.join("")}`;
503
+ if (c.kind === "faceCircumcenter") return `${obj.label} = t\xE2m ngo\u1EA1i ti\u1EBFp ${c.vertices.join("")}`;
504
+ if (c.kind === "pointAboveFace") return `${obj.label} = \u0111\u1EC9nh tr\u1EE5c tr\u1EE5 \u22A5 m\u1EB7t ${c.vertices.join("")}`;
127
505
  return obj.label;
128
506
  },
129
507
  render: (obj, ctx) => {
@@ -173,6 +551,15 @@ var def = {
173
551
  () => sph.F(c.theta, c.phi)[2]
174
552
  ], opts);
175
553
  }
554
+ const getState = ctx.getState;
555
+ if (getState) {
556
+ const cw = () => constraintToWorld(c, getState());
557
+ return view.create("point3d", [
558
+ () => cw()[0],
559
+ () => cw()[1],
560
+ () => cw()[2]
561
+ ], { ...opts, needsRegularUpdate: true });
562
+ }
176
563
  return view.create("point3d", [0, 0, 0], opts);
177
564
  }
178
565
  };
@@ -221,28 +608,71 @@ var def2 = {
221
608
  registerKind(def2);
222
609
 
223
610
  // src/core/scene/kinds/line3d.ts
224
- registerKind({
611
+ function line3dConstructionRefs(c) {
612
+ switch (c.kind) {
613
+ case "planePlaneIntersection":
614
+ return [c.plane1, c.plane2];
615
+ case "lineParallelThrough":
616
+ return [c.point, c.dirA, c.dirB];
617
+ case "linePerpToPlane":
618
+ return [c.point, c.plane];
619
+ default: {
620
+ return [];
621
+ }
622
+ }
623
+ }
624
+ var def3 = {
225
625
  type: "line3d",
226
626
  schemaVersion: 1,
227
627
  migrate: {},
228
628
  validate: (a) => {
229
- if (!a?.p1 || !a?.p2) throw new Error("line3d: p1/p2 required");
629
+ if (a?.construction) return;
630
+ if (!a?.p1 || !a?.p2) throw new Error("line3d: p1/p2 required (ho\u1EB7c construction)");
631
+ },
632
+ dependsOn: (a) => a.construction ? line3dConstructionRefs(a.construction) : [a.p1, a.p2],
633
+ describe: (obj, state) => {
634
+ const c = obj.attrs.construction;
635
+ if (!c) return `\u0110\u01B0\u1EDDng ${obj.label} qua ${labelOf(obj.attrs.p1, state)}, ${labelOf(obj.attrs.p2, state)}`;
636
+ switch (c.kind) {
637
+ case "planePlaneIntersection":
638
+ return `Giao tuy\u1EBFn ${obj.label} = ${labelOf(c.plane1, state)} \u2229 ${labelOf(c.plane2, state)}`;
639
+ case "lineParallelThrough":
640
+ return `${obj.label} \u2225 ${labelOf(c.dirA, state)}${labelOf(c.dirB, state)} qua ${labelOf(c.point, state)}`;
641
+ case "linePerpToPlane":
642
+ return `${obj.label} \u22A5 ${labelOf(c.plane, state)} qua ${labelOf(c.point, state)}`;
643
+ default:
644
+ return obj.label;
645
+ }
230
646
  },
231
- dependsOn: (a) => [a.p1, a.p2],
232
- describe: (obj, state) => `\u0110\u01B0\u1EDDng ${obj.label} qua ${labelOf(obj.attrs.p1, state)}, ${labelOf(obj.attrs.p2, state)}`,
233
647
  render: (obj, ctx) => {
234
648
  const view = ctx.jxg;
649
+ const baseOpts = {
650
+ strokeColor: obj.attrs.color ?? "#0f172a",
651
+ strokeWidth: 2,
652
+ visible: obj.visible
653
+ };
654
+ const c = obj.attrs.construction;
655
+ if (c) {
656
+ const getState = ctx.getState;
657
+ if (!getState) throw new Error("line3d construction: c\u1EA7n ctx.getState");
658
+ const lc = () => lineConstructionWorld(c, getState());
659
+ return view.create("line3d", [() => lc().a, () => lc().b], {
660
+ ...baseOpts,
661
+ straightFirst: true,
662
+ straightLast: true,
663
+ needsRegularUpdate: true
664
+ });
665
+ }
235
666
  const pA = ctx.resolveRef(obj.attrs.p1);
236
667
  const pB = ctx.resolveRef(obj.attrs.p2);
237
668
  return view.create("line3d", [pA, pB], {
669
+ ...baseOpts,
238
670
  straightFirst: true,
239
- straightLast: true,
240
- strokeColor: obj.attrs.color ?? "#0f172a",
241
- strokeWidth: 2,
242
- visible: obj.visible
671
+ straightLast: true
243
672
  });
244
673
  }
245
- });
674
+ };
675
+ registerKind(def3);
246
676
 
247
677
  // src/core/scene/kinds/ray3d.ts
248
678
  registerKind({
@@ -294,29 +724,71 @@ registerKind({
294
724
  });
295
725
 
296
726
  // src/core/scene/kinds/plane3d.ts
297
- registerKind({
727
+ function plane3dConstructionRefs(c) {
728
+ switch (c.kind) {
729
+ case "planeParallelThrough":
730
+ return [c.point, c.refPlane];
731
+ case "planePerpToLine":
732
+ return [c.point, c.lineA, c.lineB];
733
+ default: {
734
+ return [];
735
+ }
736
+ }
737
+ }
738
+ var def4 = {
298
739
  type: "plane3d",
299
740
  schemaVersion: 1,
300
741
  migrate: {},
301
742
  validate: (a) => {
302
- if (!a?.p1 || !a?.p2 || !a?.p3) throw new Error("plane3d: c\u1EA7n 3 \u0111i\u1EC3m");
743
+ if (a?.construction) return;
744
+ if (!a?.p1 || !a?.p2 || !a?.p3) throw new Error("plane3d: c\u1EA7n 3 \u0111i\u1EC3m (ho\u1EB7c construction)");
745
+ },
746
+ dependsOn: (a) => a.construction ? plane3dConstructionRefs(a.construction) : [a.p1, a.p2, a.p3],
747
+ describe: (obj, state) => {
748
+ const c = obj.attrs.construction;
749
+ if (!c) {
750
+ return `M\u1EB7t ${obj.label} qua ${labelOf(obj.attrs.p1, state)}, ${labelOf(obj.attrs.p2, state)}, ${labelOf(obj.attrs.p3, state)}`;
751
+ }
752
+ switch (c.kind) {
753
+ case "planeParallelThrough":
754
+ return `${obj.label} \u2225 ${labelOf(c.refPlane, state)} qua ${labelOf(c.point, state)}`;
755
+ case "planePerpToLine":
756
+ return `${obj.label} \u22A5 ${labelOf(c.lineA, state)}${labelOf(c.lineB, state)} qua ${labelOf(c.point, state)}`;
757
+ default:
758
+ return obj.label;
759
+ }
303
760
  },
304
- dependsOn: (a) => [a.p1, a.p2, a.p3],
305
- describe: (obj, state) => `M\u1EB7t ${obj.label} qua ${labelOf(obj.attrs.p1, state)}, ${labelOf(obj.attrs.p2, state)}, ${labelOf(obj.attrs.p3, state)}`,
306
761
  render: (obj, ctx) => {
307
762
  const view = ctx.jxg;
308
- return view.create("plane3d", [
309
- ctx.resolveRef(obj.attrs.p1),
310
- ctx.resolveRef(obj.attrs.p2),
311
- ctx.resolveRef(obj.attrs.p3)
312
- ], {
763
+ const opts = {
313
764
  fillOpacity: 0.15,
314
765
  fillColor: obj.attrs.color ?? "#60a5fa",
315
766
  strokeColor: obj.attrs.color ?? "#60a5fa",
316
767
  visible: obj.visible
317
- });
768
+ };
769
+ const c = obj.attrs.construction;
770
+ if (c) {
771
+ const getState = ctx.getState;
772
+ if (!getState) throw new Error("plane3d construction: c\u1EA7n ctx.getState");
773
+ const pc = () => planeConstructionWorld(c, getState());
774
+ const diff = (which) => {
775
+ const r = pc();
776
+ const q = r[which];
777
+ return [q[0] - r.p1[0], q[1] - r.p1[1], q[2] - r.p1[2]];
778
+ };
779
+ return view.create("plane3d", [() => pc().p1, () => diff("p2"), () => diff("p3")], {
780
+ ...opts,
781
+ needsRegularUpdate: true
782
+ });
783
+ }
784
+ return view.create("plane3d", [
785
+ ctx.resolveRef(obj.attrs.p1),
786
+ ctx.resolveRef(obj.attrs.p2),
787
+ ctx.resolveRef(obj.attrs.p3)
788
+ ], opts);
318
789
  }
319
- });
790
+ };
791
+ registerKind(def4);
320
792
 
321
793
  // src/core/scene/kinds/polygon3d.ts
322
794
  registerKind({
@@ -396,6 +868,27 @@ registerKind({
396
868
  }
397
869
  });
398
870
 
871
+ // src/core/scene/kinds/_ringBasis.ts
872
+ function perpBasis(axis) {
873
+ const n = Math.hypot(axis[0], axis[1], axis[2]);
874
+ if (n < 1e-12) return [[1, 0, 0], [0, 1, 0]];
875
+ const a = [axis[0] / n, axis[1] / n, axis[2] / n];
876
+ const seed = Math.abs(a[2]) < 0.9 ? [0, 0, 1] : [1, 0, 0];
877
+ let u = [
878
+ seed[1] * a[2] - seed[2] * a[1],
879
+ seed[2] * a[0] - seed[0] * a[2],
880
+ seed[0] * a[1] - seed[1] * a[0]
881
+ ];
882
+ const un = Math.hypot(u[0], u[1], u[2]) || 1;
883
+ u = [u[0] / un, u[1] / un, u[2] / un];
884
+ const v = [
885
+ a[1] * u[2] - a[2] * u[1],
886
+ a[2] * u[0] - a[0] * u[2],
887
+ a[0] * u[1] - a[1] * u[0]
888
+ ];
889
+ return [u, v];
890
+ }
891
+
399
892
  // src/core/scene/kinds/cylinder3d.ts
400
893
  var CURVED_SEGMENTS = 16;
401
894
  registerKind({
@@ -415,14 +908,17 @@ registerKind({
415
908
  const r = obj.attrs.radius;
416
909
  const ax = a.X?.() ?? 0, ay = a.Y?.() ?? 0, az = a.Z?.() ?? 0;
417
910
  const bx = b.X?.() ?? 0, by = b.Y?.() ?? 0, bz = b.Z?.() ?? 0;
911
+ const [u, v] = perpBasis([bx - ax, by - ay, bz - az]);
418
912
  const baseRing = [];
419
913
  const topRing = [];
420
914
  for (let i = 0; i < CURVED_SEGMENTS; i++) {
421
915
  const theta = i / CURVED_SEGMENTS * Math.PI * 2;
422
- const dx = r * Math.cos(theta);
423
- const dy = r * Math.sin(theta);
424
- baseRing.push([ax + dx, ay + dy, az]);
425
- topRing.push([bx + dx, by + dy, bz]);
916
+ const c = Math.cos(theta), s = Math.sin(theta);
917
+ const ox = r * (c * u[0] + s * v[0]);
918
+ const oy = r * (c * u[1] + s * v[1]);
919
+ const oz = r * (c * u[2] + s * v[2]);
920
+ baseRing.push([ax + ox, ay + oy, az + oz]);
921
+ topRing.push([bx + ox, by + oy, bz + oz]);
426
922
  }
427
923
  const vertices = [...baseRing, ...topRing];
428
924
  const faces = [];
@@ -433,7 +929,7 @@ registerKind({
433
929
  faces.push([i, next, CURVED_SEGMENTS + next, CURVED_SEGMENTS + i]);
434
930
  }
435
931
  const vertJxgs = vertices.map(
436
- (v, i) => view.create("point3d", v, {
932
+ (v2, i) => view.create("point3d", v2, {
437
933
  id: `${obj.id}.v${i}`,
438
934
  visible: false,
439
935
  fixed: true,
@@ -477,10 +973,16 @@ registerKind({
477
973
  apexPt.Y?.() ?? 0,
478
974
  apexPt.Z?.() ?? 0
479
975
  ];
976
+ const [u, v] = perpBasis([apexCoords[0] - bx, apexCoords[1] - by, apexCoords[2] - bz]);
480
977
  const baseRing = [];
481
978
  for (let i = 0; i < CURVED_SEGMENTS2; i++) {
482
979
  const theta = i / CURVED_SEGMENTS2 * Math.PI * 2;
483
- baseRing.push([bx + r * Math.cos(theta), by + r * Math.sin(theta), bz]);
980
+ const c = Math.cos(theta), s = Math.sin(theta);
981
+ baseRing.push([
982
+ bx + r * (c * u[0] + s * v[0]),
983
+ by + r * (c * u[1] + s * v[1]),
984
+ bz + r * (c * u[2] + s * v[2])
985
+ ]);
484
986
  }
485
987
  const apexIdx = baseRing.length;
486
988
  const vertices = [...baseRing, apexCoords];
@@ -489,7 +991,7 @@ registerKind({
489
991
  faces.push([i, (i + 1) % CURVED_SEGMENTS2, apexIdx]);
490
992
  }
491
993
  const vertJxgs = vertices.map(
492
- (v, i) => view.create("point3d", v, {
994
+ (v2, i) => view.create("point3d", v2, {
493
995
  id: `${obj.id}.v${i}`,
494
996
  visible: false,
495
997
  fixed: true,
@@ -576,8 +1078,16 @@ function constraintRefs2D(c) {
576
1078
  }
577
1079
  case "excenter":
578
1080
  return [c.vertices[0], c.vertices[1], c.vertices[2]];
579
- default:
1081
+ case "commonTangentPoint":
1082
+ return [c.circles[0], c.circles[1]];
1083
+ // Các kind KHÔNG có ref scene-object (toạ độ literal): liệt kê TƯỜNG MINH để
1084
+ // exhaustive never-guard bên dưới buộc khai báo khi thêm constraint kind mới.
1085
+ case "free":
1086
+ case "onAxis":
580
1087
  return [];
1088
+ default: {
1089
+ return [];
1090
+ }
581
1091
  }
582
1092
  }
583
1093
 
@@ -1000,19 +1510,19 @@ function buildJxgTransforms(board, ctx, t) {
1000
1510
  }
1001
1511
  }
1002
1512
  function makeDistanceFn(ctx, d) {
1003
- const scale = d.scale ?? 1;
1513
+ const scale2 = d.scale ?? 1;
1004
1514
  const offset = d.offset ?? 0;
1005
1515
  if (d.kind === "literal") {
1006
1516
  const v = d.value;
1007
- return () => scale * v + offset;
1517
+ return () => scale2 * v + offset;
1008
1518
  }
1009
1519
  if (d.kind === "segmentLength") {
1010
1520
  const p = ctx.resolveRef(d.p1);
1011
1521
  const q = ctx.resolveRef(d.p2);
1012
- return () => scale * Math.hypot(p.X() - q.X(), p.Y() - q.Y()) + offset;
1522
+ return () => scale2 * Math.hypot(p.X() - q.X(), p.Y() - q.Y()) + offset;
1013
1523
  }
1014
1524
  const circle = ctx.resolveRef(d.circle);
1015
- return () => scale * circle.Radius() + offset;
1525
+ return () => scale2 * circle.Radius() + offset;
1016
1526
  }
1017
1527
  function buildPointOpts(obj) {
1018
1528
  return {
@@ -1048,6 +1558,61 @@ var pointAtDistanceConstraint = definePointConstraint({
1048
1558
  }
1049
1559
  });
1050
1560
 
1561
+ // src/stamps/geometry-2d/geometry/commonTangent.ts
1562
+ var EPS = 1e-9;
1563
+ function computeCommonTangentPoint(O1, r1, O2, r2, on, variant, side) {
1564
+ const dx = O2[0] - O1[0];
1565
+ const dy = O2[1] - O1[1];
1566
+ const d = Math.hypot(dx, dy);
1567
+ if (d < EPS) return null;
1568
+ const beta = Math.atan2(dy, dx);
1569
+ const ratio = variant === "external" ? (r1 - r2) / d : (r1 + r2) / d;
1570
+ if (Math.abs(ratio) > 1) return null;
1571
+ const gamma = Math.acos(ratio);
1572
+ const s = side === 0 ? 1 : -1;
1573
+ const angle = beta + s * gamma;
1574
+ const nx = Math.cos(angle);
1575
+ const ny = Math.sin(angle);
1576
+ if (on === 0) {
1577
+ return [O1[0] + r1 * nx, O1[1] + r1 * ny];
1578
+ }
1579
+ if (variant === "external") {
1580
+ return [O2[0] + r2 * nx, O2[1] + r2 * ny];
1581
+ }
1582
+ return [O2[0] - r2 * nx, O2[1] - r2 * ny];
1583
+ }
1584
+
1585
+ // src/core/scene/kinds/point-constraints/commonTangentPoint.ts
1586
+ var commonTangentPointConstraint = definePointConstraint({
1587
+ kind: "commonTangentPoint",
1588
+ describe: (obj, state, c) => {
1589
+ const c1 = state?.objects[c.circles[0]]?.label ?? c.circles[0];
1590
+ const c2 = state?.objects[c.circles[1]]?.label ?? c.circles[1];
1591
+ const v = c.variant === "internal" ? "trong" : "ngo\xE0i";
1592
+ return `${obj.label} = ti\u1EBFp \u0111i\u1EC3m ti\u1EBFp tuy\u1EBFn chung ${v} ${c1},${c2} (tr\xEAn ${c.on === 0 ? c1 : c2})`;
1593
+ },
1594
+ render: (obj, ctx, c, opts) => {
1595
+ const board = ctx.jxg;
1596
+ const k1 = ctx.resolveRef(c.circles[0]);
1597
+ const k2 = ctx.resolveRef(c.circles[1]);
1598
+ const O1 = k1?.center ?? k1?.midpoint ?? k1;
1599
+ const O2 = k2?.center ?? k2?.midpoint ?? k2;
1600
+ const compute = () => {
1601
+ const t = computeCommonTangentPoint(
1602
+ [O1.X(), O1.Y()],
1603
+ k1.Radius(),
1604
+ [O2.X(), O2.Y()],
1605
+ k2.Radius(),
1606
+ c.on,
1607
+ c.variant,
1608
+ c.side
1609
+ );
1610
+ return t ?? [0, 0];
1611
+ };
1612
+ return board.create("point", [() => compute()[0], () => compute()[1]], opts);
1613
+ }
1614
+ });
1615
+
1051
1616
  // src/core/scene/kinds/point-constraints/circleIntersection.ts
1052
1617
  var circleIntersectionConstraint = definePointConstraint({
1053
1618
  kind: "circleIntersection",
@@ -1264,6 +1829,7 @@ var ALL = [
1264
1829
  excenterConstraint,
1265
1830
  mixtilinearPointConstraint,
1266
1831
  pointAtDistanceConstraint,
1832
+ commonTangentPointConstraint,
1267
1833
  circleIntersectionConstraint,
1268
1834
  circleSecondIntersectionConstraint,
1269
1835
  secondIntersectionConstraint,
@@ -1278,7 +1844,7 @@ var ALL = [
1278
1844
  var POINT_CONSTRAINTS = new Map(ALL.map((m) => [m.kind, m]));
1279
1845
 
1280
1846
  // src/core/scene/kinds/point.ts
1281
- var def3 = {
1847
+ var def5 = {
1282
1848
  type: "point",
1283
1849
  schemaVersion: 1,
1284
1850
  migrate: {},
@@ -1356,10 +1922,10 @@ var def3 = {
1356
1922
  throw new Error("point: constraint kind changed \u2014 recreate");
1357
1923
  }
1358
1924
  };
1359
- registerKind(def3);
1925
+ registerKind(def5);
1360
1926
 
1361
1927
  // src/core/scene/kinds/segment.ts
1362
- var def4 = {
1928
+ var def6 = {
1363
1929
  type: "segment",
1364
1930
  schemaVersion: 1,
1365
1931
  migrate: {},
@@ -1395,7 +1961,7 @@ var def4 = {
1395
1961
  });
1396
1962
  }
1397
1963
  };
1398
- registerKind(def4);
1964
+ registerKind(def6);
1399
1965
 
1400
1966
  // src/core/scene/kinds/line.ts
1401
1967
  function stripBorderSuffix(id) {
@@ -1421,7 +1987,7 @@ function constructionRefs(c) {
1421
1987
  return [c.throughPoint, c.toCircle];
1422
1988
  }
1423
1989
  }
1424
- var def5 = {
1990
+ var def7 = {
1425
1991
  type: "line",
1426
1992
  schemaVersion: 1,
1427
1993
  migrate: {},
@@ -1617,10 +2183,10 @@ var def5 = {
1617
2183
  }
1618
2184
  }
1619
2185
  };
1620
- registerKind(def5);
2186
+ registerKind(def7);
1621
2187
 
1622
2188
  // src/core/scene/kinds/ray.ts
1623
- var def6 = {
2189
+ var def8 = {
1624
2190
  type: "ray",
1625
2191
  schemaVersion: 1,
1626
2192
  migrate: {},
@@ -1645,10 +2211,10 @@ var def6 = {
1645
2211
  });
1646
2212
  }
1647
2213
  };
1648
- registerKind(def6);
2214
+ registerKind(def8);
1649
2215
 
1650
2216
  // src/core/scene/kinds/vector.ts
1651
- var def7 = {
2217
+ var def9 = {
1652
2218
  type: "vector",
1653
2219
  schemaVersion: 1,
1654
2220
  migrate: {},
@@ -1670,7 +2236,7 @@ var def7 = {
1670
2236
  });
1671
2237
  }
1672
2238
  };
1673
- registerKind(def7);
2239
+ registerKind(def9);
1674
2240
 
1675
2241
  // src/core/scene/kinds/circle.ts
1676
2242
  function asConstruction(a) {
@@ -1698,7 +2264,7 @@ function constructionRefs2(c) {
1698
2264
  return [c.p1, c.p2];
1699
2265
  }
1700
2266
  }
1701
- var def8 = {
2267
+ var def10 = {
1702
2268
  type: "circle",
1703
2269
  schemaVersion: 1,
1704
2270
  migrate: {},
@@ -1850,7 +2416,7 @@ var def8 = {
1850
2416
  return board.create("circle", [center, surface], baseOpts);
1851
2417
  }
1852
2418
  };
1853
- registerKind(def8);
2419
+ registerKind(def10);
1854
2420
 
1855
2421
  // src/core/scene/kinds/arc.ts
1856
2422
  function constructionRefs3(c) {
@@ -1863,7 +2429,7 @@ function constructionRefs3(c) {
1863
2429
  return [c.p1, c.p2, c.p3];
1864
2430
  }
1865
2431
  }
1866
- var def9 = {
2432
+ var def11 = {
1867
2433
  type: "arc",
1868
2434
  schemaVersion: 1,
1869
2435
  migrate: {},
@@ -1921,10 +2487,10 @@ var def9 = {
1921
2487
  return board.create("circumcirclearc", [A, B, C], baseOpts);
1922
2488
  }
1923
2489
  };
1924
- registerKind(def9);
2490
+ registerKind(def11);
1925
2491
 
1926
2492
  // src/core/scene/kinds/sector.ts
1927
- var def10 = {
2493
+ var def12 = {
1928
2494
  type: "sector",
1929
2495
  schemaVersion: 1,
1930
2496
  migrate: {},
@@ -1964,7 +2530,7 @@ var def10 = {
1964
2530
  });
1965
2531
  }
1966
2532
  };
1967
- registerKind(def10);
2533
+ registerKind(def12);
1968
2534
 
1969
2535
  // src/core/scene/kinds/polygon.ts
1970
2536
  function regularPolygonName(n) {
@@ -2008,7 +2574,7 @@ function specialShapeName(kind) {
2008
2574
  return "";
2009
2575
  }
2010
2576
  }
2011
- var def11 = {
2577
+ var def13 = {
2012
2578
  type: "polygon",
2013
2579
  schemaVersion: 1,
2014
2580
  migrate: {},
@@ -2195,10 +2761,10 @@ var def11 = {
2195
2761
  return poly;
2196
2762
  }
2197
2763
  };
2198
- registerKind(def11);
2764
+ registerKind(def13);
2199
2765
 
2200
2766
  // src/core/scene/kinds/intersection.ts
2201
- var def12 = {
2767
+ var def14 = {
2202
2768
  type: "intersection",
2203
2769
  schemaVersion: 1,
2204
2770
  migrate: {},
@@ -2273,10 +2839,10 @@ var def12 = {
2273
2839
  }
2274
2840
  }
2275
2841
  };
2276
- registerKind(def12);
2842
+ registerKind(def14);
2277
2843
 
2278
2844
  // src/core/scene/kinds/angle.ts
2279
- var def13 = {
2845
+ var def15 = {
2280
2846
  type: "angle",
2281
2847
  schemaVersion: 1,
2282
2848
  migrate: {},
@@ -2312,10 +2878,10 @@ var def13 = {
2312
2878
  });
2313
2879
  }
2314
2880
  };
2315
- registerKind(def13);
2881
+ registerKind(def15);
2316
2882
 
2317
2883
  // src/core/scene/kinds/distance.ts
2318
- var def14 = {
2884
+ var def16 = {
2319
2885
  type: "distance",
2320
2886
  schemaVersion: 1,
2321
2887
  migrate: {},
@@ -2344,10 +2910,10 @@ var def14 = {
2344
2910
  });
2345
2911
  }
2346
2912
  };
2347
- registerKind(def14);
2913
+ registerKind(def16);
2348
2914
 
2349
2915
  // src/core/scene/kinds/function2d.ts
2350
- var def15 = {
2916
+ var def17 = {
2351
2917
  type: "function2d",
2352
2918
  schemaVersion: 1,
2353
2919
  migrate: {},
@@ -2386,10 +2952,10 @@ var def15 = {
2386
2952
  });
2387
2953
  }
2388
2954
  };
2389
- registerKind(def15);
2955
+ registerKind(def17);
2390
2956
 
2391
2957
  // src/core/scene/kinds/parameter.ts
2392
- var def16 = {
2958
+ var def18 = {
2393
2959
  type: "parameter",
2394
2960
  schemaVersion: 1,
2395
2961
  migrate: {},
@@ -2407,10 +2973,10 @@ var def16 = {
2407
2973
  render: () => null
2408
2974
  // Không render lên board
2409
2975
  };
2410
- registerKind(def16);
2976
+ registerKind(def18);
2411
2977
 
2412
2978
  // src/core/scene/kinds/pointOnCurve.ts
2413
- var def17 = {
2979
+ var def19 = {
2414
2980
  type: "pointOnCurve",
2415
2981
  schemaVersion: 1,
2416
2982
  migrate: {},
@@ -2437,10 +3003,10 @@ var def17 = {
2437
3003
  });
2438
3004
  }
2439
3005
  };
2440
- registerKind(def17);
3006
+ registerKind(def19);
2441
3007
 
2442
3008
  // src/core/scene/kinds/tangent2d.ts
2443
- var def18 = {
3009
+ var def20 = {
2444
3010
  type: "tangent2d",
2445
3011
  schemaVersion: 1,
2446
3012
  migrate: {},
@@ -2463,7 +3029,7 @@ var def18 = {
2463
3029
  });
2464
3030
  }
2465
3031
  };
2466
- registerKind(def18);
3032
+ registerKind(def20);
2467
3033
 
2468
3034
  // src/core/scene/expressions/evaluator.ts
2469
3035
  var DEFAULT_SAMPLES = 1e3;
@@ -2532,7 +3098,7 @@ function scanExtrema(fn, xMin, xMax, samples = DEFAULT_SAMPLES) {
2532
3098
  }
2533
3099
 
2534
3100
  // src/core/scene/kinds/extremum2d.ts
2535
- var def19 = {
3101
+ var def21 = {
2536
3102
  type: "extremum2d",
2537
3103
  schemaVersion: 1,
2538
3104
  migrate: {},
@@ -2565,10 +3131,10 @@ var def19 = {
2565
3131
  }));
2566
3132
  }
2567
3133
  };
2568
- registerKind(def19);
3134
+ registerKind(def21);
2569
3135
 
2570
3136
  // src/core/scene/kinds/root2d.ts
2571
- var def20 = {
3137
+ var def22 = {
2572
3138
  type: "root2d",
2573
3139
  schemaVersion: 1,
2574
3140
  migrate: {},
@@ -2598,10 +3164,10 @@ var def20 = {
2598
3164
  }));
2599
3165
  }
2600
3166
  };
2601
- registerKind(def20);
3167
+ registerKind(def22);
2602
3168
 
2603
3169
  // src/core/scene/kinds/slope2d.ts
2604
- var def21 = {
3170
+ var def23 = {
2605
3171
  type: "slope2d",
2606
3172
  schemaVersion: 1,
2607
3173
  migrate: {},
@@ -2625,7 +3191,7 @@ var def21 = {
2625
3191
  });
2626
3192
  }
2627
3193
  };
2628
- registerKind(def21);
3194
+ registerKind(def23);
2629
3195
 
2630
3196
  // src/stamps/shared/serializeScene.ts
2631
3197
  function serializeScene(state) {
@@ -2651,6 +3217,6 @@ function deserializeScene(domain, raw) {
2651
3217
  return createEmptyState(domain);
2652
3218
  }
2653
3219
 
2654
- export { deserializeScene, listObjects, nextLabel, serializeScene, useEditorState };
2655
- //# sourceMappingURL=chunk-JJ4FPCBE.mjs.map
2656
- //# sourceMappingURL=chunk-JJ4FPCBE.mjs.map
3220
+ export { constraintToWorld, deserializeScene, listObjects, nextLabel, planeConstructionWorld, serializeScene, useEditorState };
3221
+ //# sourceMappingURL=chunk-4EXRNZRX.mjs.map
3222
+ //# sourceMappingURL=chunk-4EXRNZRX.mjs.map