@xom11/whiteboard 0.24.2 → 0.27.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 (103) hide show
  1. package/README.md +84 -11
  2. package/dist/{ExcalidrawWithMenus-WENZRYYE.mjs → ExcalidrawWithMenus-2QPPTXJM.mjs} +3 -2
  3. package/dist/ExcalidrawWithMenus-2QPPTXJM.mjs.map +1 -0
  4. package/dist/ai.d.mts +3217 -434
  5. package/dist/ai.d.ts +3217 -434
  6. package/dist/ai.js +7679 -598
  7. package/dist/ai.js.map +1 -1
  8. package/dist/ai.mjs +5707 -679
  9. package/dist/ai.mjs.map +1 -1
  10. package/dist/catalog.json +5 -5
  11. package/dist/{chunk-7WQXXEVR.mjs → chunk-4ETJ4CDY.mjs} +5 -5
  12. package/dist/{chunk-7WQXXEVR.mjs.map → chunk-4ETJ4CDY.mjs.map} +1 -1
  13. package/dist/chunk-AJAHD35N.mjs +1708 -0
  14. package/dist/chunk-AJAHD35N.mjs.map +1 -0
  15. package/dist/chunk-AYJPOHCI.mjs +265 -0
  16. package/dist/chunk-AYJPOHCI.mjs.map +1 -0
  17. package/dist/chunk-B4NJJZFR.mjs +18 -0
  18. package/dist/chunk-B4NJJZFR.mjs.map +1 -0
  19. package/dist/{chunk-AZIARTGX.mjs → chunk-BNBOIDO5.mjs} +3 -3
  20. package/dist/{chunk-AZIARTGX.mjs.map → chunk-BNBOIDO5.mjs.map} +1 -1
  21. package/dist/{chunk-LVNCYP4J.mjs → chunk-CXHNVYMD.mjs} +5 -5
  22. package/dist/{chunk-LVNCYP4J.mjs.map → chunk-CXHNVYMD.mjs.map} +1 -1
  23. package/dist/{chunk-45CGKJ7S.mjs → chunk-D5JLJ3PT.mjs} +4 -4
  24. package/dist/{chunk-45CGKJ7S.mjs.map → chunk-D5JLJ3PT.mjs.map} +1 -1
  25. package/dist/{chunk-WM2VDYQA.mjs → chunk-D5LWSN2Y.mjs} +944 -196
  26. package/dist/chunk-D5LWSN2Y.mjs.map +1 -0
  27. package/dist/{chunk-KRC2XOIG.mjs → chunk-HLAOGXEK.mjs} +3 -3
  28. package/dist/{chunk-KRC2XOIG.mjs.map → chunk-HLAOGXEK.mjs.map} +1 -1
  29. package/dist/{chunk-2WF6KIGF.mjs → chunk-I3L56GVH.mjs} +212 -71
  30. package/dist/chunk-I3L56GVH.mjs.map +1 -0
  31. package/dist/{chunk-ZBJBQKJ2.mjs → chunk-IHUFOV7L.mjs} +4 -19
  32. package/dist/chunk-IHUFOV7L.mjs.map +1 -0
  33. package/dist/chunk-J5LGTIGS.mjs +10 -0
  34. package/dist/chunk-J5LGTIGS.mjs.map +1 -0
  35. package/dist/{chunk-BEZSQKPY.mjs → chunk-KYMBUTPO.mjs} +5 -4
  36. package/dist/chunk-KYMBUTPO.mjs.map +1 -0
  37. package/dist/{chunk-4DS3MKID.mjs → chunk-KZGPSTZI.mjs} +4 -4
  38. package/dist/{chunk-4DS3MKID.mjs.map → chunk-KZGPSTZI.mjs.map} +1 -1
  39. package/dist/{chunk-SGFJLHHG.mjs → chunk-PPKHCRRE.mjs} +3 -3
  40. package/dist/{chunk-SGFJLHHG.mjs.map → chunk-PPKHCRRE.mjs.map} +1 -1
  41. package/dist/{chunk-BKSXPNPQ.mjs → chunk-SZDAS7LK.mjs} +81 -3
  42. package/dist/chunk-SZDAS7LK.mjs.map +1 -0
  43. package/dist/chunk-T3SOHYB2.mjs +851 -0
  44. package/dist/chunk-T3SOHYB2.mjs.map +1 -0
  45. package/dist/geometry-2d.d.mts +2 -2
  46. package/dist/geometry-2d.d.ts +2 -2
  47. package/dist/geometry-2d.js +6288 -901
  48. package/dist/geometry-2d.js.map +1 -1
  49. package/dist/geometry-2d.mjs +7 -5
  50. package/dist/geometry-3d.d.mts +2 -2
  51. package/dist/geometry-3d.d.ts +2 -2
  52. package/dist/geometry-3d.js +1335 -253
  53. package/dist/geometry-3d.js.map +1 -1
  54. package/dist/geometry-3d.mjs +6 -4
  55. package/dist/graph-2d.d.mts +2 -2
  56. package/dist/graph-2d.d.ts +2 -2
  57. package/dist/graph-2d.js +1501 -342
  58. package/dist/graph-2d.js.map +1 -1
  59. package/dist/graph-2d.mjs +9 -7
  60. package/dist/handleExtractProblem-C-U5KluK.d.mts +158 -0
  61. package/dist/handleExtractProblem-C-U5KluK.d.ts +158 -0
  62. package/dist/{host-EPZCNFLH.mjs → host-HAYCJJ2T.mjs} +1390 -376
  63. package/dist/host-HAYCJJ2T.mjs.map +1 -0
  64. package/dist/{host-LKCMYEAV.mjs → host-LTJHAY5A.mjs} +12 -10
  65. package/dist/host-LTJHAY5A.mjs.map +1 -0
  66. package/dist/{host-ZIQ77W33.mjs → host-M26FS244.mjs} +8 -6
  67. package/dist/host-M26FS244.mjs.map +1 -0
  68. package/dist/{host-QS2EOTRJ.mjs → host-ZQCDAT6O.mjs} +3 -2
  69. package/dist/host-ZQCDAT6O.mjs.map +1 -0
  70. package/dist/index.d.mts +4 -3
  71. package/dist/index.d.ts +4 -3
  72. package/dist/index.js +6493 -1102
  73. package/dist/index.js.map +1 -1
  74. package/dist/index.mjs +24 -21
  75. package/dist/index.mjs.map +1 -1
  76. package/dist/latex.d.mts +2 -2
  77. package/dist/latex.d.ts +2 -2
  78. package/dist/latex.mjs +2 -1
  79. package/dist/render-ZX2O2IK7.mjs +10 -0
  80. package/dist/{render-SA4JTOW3.mjs.map → render-ZX2O2IK7.mjs.map} +1 -1
  81. package/dist/serialize-C3LSUMSA.mjs +9 -0
  82. package/dist/{serialize-JAVOU22E.mjs.map → serialize-C3LSUMSA.mjs.map} +1 -1
  83. package/dist/types-zc_Pa0mp.d.mts +418 -0
  84. package/dist/types-zc_Pa0mp.d.ts +418 -0
  85. package/package.json +10 -1
  86. package/dist/ExcalidrawWithMenus-WENZRYYE.mjs.map +0 -1
  87. package/dist/chunk-2WF6KIGF.mjs.map +0 -1
  88. package/dist/chunk-BEZSQKPY.mjs.map +0 -1
  89. package/dist/chunk-BKSXPNPQ.mjs.map +0 -1
  90. package/dist/chunk-CGZZO4BX.mjs +0 -96
  91. package/dist/chunk-CGZZO4BX.mjs.map +0 -1
  92. package/dist/chunk-WM2VDYQA.mjs.map +0 -1
  93. package/dist/chunk-ZBJBQKJ2.mjs.map +0 -1
  94. package/dist/host-EPZCNFLH.mjs.map +0 -1
  95. package/dist/host-LKCMYEAV.mjs.map +0 -1
  96. package/dist/host-QS2EOTRJ.mjs.map +0 -1
  97. package/dist/host-ZIQ77W33.mjs.map +0 -1
  98. package/dist/render-SA4JTOW3.mjs +0 -8
  99. package/dist/serialize-JAVOU22E.mjs +0 -7
  100. package/dist/types-Crbefnfe.d.ts +0 -128
  101. package/dist/types-DxlMPh-6.d.mts +0 -49
  102. package/dist/types-DxlMPh-6.d.ts +0 -49
  103. package/dist/types-vtvyKGAA.d.mts +0 -128
@@ -0,0 +1,1708 @@
1
+ "use client";
2
+ import { getKind } from './chunk-B4NJJZFR.mjs';
3
+ import { z } from 'zod';
4
+
5
+ // src/stamps/geometry-2d/ai/vision/preprocess.ts
6
+ var MAX_EDGE_PX = 2048;
7
+ var MAX_RAW_BYTES = 10 * 1024 * 1024;
8
+ var MAX_ENCODED_BYTES = 3 * 1024 * 1024;
9
+ var ALLOWED_TYPES = ["image/png", "image/jpeg", "image/webp"];
10
+ function inferMediaType(file) {
11
+ const t = file.type.toLowerCase();
12
+ if (ALLOWED_TYPES.includes(t)) return t;
13
+ return null;
14
+ }
15
+ function validateFile(file) {
16
+ const mt = inferMediaType(file);
17
+ if (mt == null) {
18
+ return {
19
+ ok: false,
20
+ code: "invalid-format",
21
+ message: "Ch\u1EC9 h\u1ED7 tr\u1EE3 PNG, JPEG, WEBP."
22
+ };
23
+ }
24
+ if (file.size > MAX_RAW_BYTES) {
25
+ return {
26
+ ok: false,
27
+ code: "too-large",
28
+ message: `\u1EA2nh qu\xE1 l\u1EDBn (> ${Math.round(MAX_RAW_BYTES / 1024 / 1024)} MB). Crop ho\u1EB7c resize tr\u01B0\u1EDBc.`
29
+ };
30
+ }
31
+ return { ok: true, mediaType: mt };
32
+ }
33
+ async function fileToImagePart(file) {
34
+ const v = validateFile(file);
35
+ if (!v.ok) throw new Error(v.message);
36
+ const bitmap = await createImageBitmap(file);
37
+ const { width, height } = bitmap;
38
+ const maxEdge = Math.max(width, height);
39
+ const scale = maxEdge > MAX_EDGE_PX ? MAX_EDGE_PX / maxEdge : 1;
40
+ const targetW = Math.round(width * scale);
41
+ const targetH = Math.round(height * scale);
42
+ const canvas = typeof OffscreenCanvas !== "undefined" ? new OffscreenCanvas(targetW, targetH) : Object.assign(document.createElement("canvas"), { width: targetW, height: targetH });
43
+ const ctx = canvas.getContext("2d");
44
+ if (!ctx) throw new Error("Kh\xF4ng t\u1EA1o \u0111\u01B0\u1EE3c canvas 2D context");
45
+ ctx.drawImage(bitmap, 0, 0, targetW, targetH);
46
+ bitmap.close();
47
+ let outputType = v.mediaType === "image/png" ? "image/png" : "image/jpeg";
48
+ let finalBlob = await canvasToBlob(canvas, outputType, 0.92);
49
+ if (finalBlob.size > MAX_ENCODED_BYTES) {
50
+ outputType = "image/jpeg";
51
+ finalBlob = await canvasToBlob(canvas, "image/jpeg", 0.7);
52
+ }
53
+ const base64 = await blobToBase64(finalBlob);
54
+ return { mediaType: outputType, base64 };
55
+ }
56
+ async function canvasToBlob(canvas, type, quality) {
57
+ if ("convertToBlob" in canvas) {
58
+ return canvas.convertToBlob({ type, quality });
59
+ }
60
+ return new Promise((resolve, reject) => {
61
+ canvas.toBlob(
62
+ (b) => b ? resolve(b) : reject(new Error("Canvas encode fail")),
63
+ type,
64
+ quality
65
+ );
66
+ });
67
+ }
68
+ async function blobToBase64(blob) {
69
+ const buf = await blob.arrayBuffer();
70
+ let binary = "";
71
+ const bytes = new Uint8Array(buf);
72
+ const chunk = 32768;
73
+ for (let i = 0; i < bytes.length; i += chunk) {
74
+ binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
75
+ }
76
+ return typeof btoa === "function" ? btoa(binary) : Buffer.from(binary, "binary").toString("base64");
77
+ }
78
+ var NameZ = z.string().regex(/^[A-Za-z][A-Za-z0-9_'₀-₉]{0,11}$/);
79
+
80
+ // src/stamps/geometry-2d/dsl/kinds/_types.ts
81
+ function defineModule(m) {
82
+ return m;
83
+ }
84
+
85
+ // src/stamps/geometry-2d/dsl/kinds/_shared.ts
86
+ var POINT_BASE_FIELDS = {
87
+ visible: true,
88
+ locked: false,
89
+ layer: "default",
90
+ schemaVersion: 1
91
+ };
92
+ function emitPointObject(id, name, constraint, visible = true) {
93
+ return {
94
+ id,
95
+ kind: "point",
96
+ label: name,
97
+ ...POINT_BASE_FIELDS,
98
+ visible,
99
+ attrs: { constraint }
100
+ };
101
+ }
102
+ function resolveTriangleVertices(ctx, vertices) {
103
+ return [ctx.resolveId(vertices[0]), ctx.resolveId(vertices[1]), ctx.resolveId(vertices[2])];
104
+ }
105
+ var SHAPE_BASE_FIELDS = {
106
+ visible: true,
107
+ locked: false,
108
+ layer: "default",
109
+ schemaVersion: 1
110
+ };
111
+
112
+ // src/stamps/geometry-2d/dsl/kinds/points/free.ts
113
+ var freeModule = defineModule({
114
+ kind: "free",
115
+ role: "point",
116
+ category: "points",
117
+ prefix: "p",
118
+ schema: z.object({
119
+ name: NameZ,
120
+ kind: z.literal("free"),
121
+ x: z.number().finite(),
122
+ y: z.number().finite()
123
+ }),
124
+ collectRefs: () => [],
125
+ emit: (e, ctx) => [{
126
+ role: "primary",
127
+ object: emitPointObject(ctx.resolveId(e.name), e.name, { kind: "free", x: e.x, y: e.y })
128
+ }]
129
+ });
130
+ var midpointModule = defineModule({
131
+ kind: "midpoint",
132
+ role: "point",
133
+ category: "points",
134
+ prefix: "p",
135
+ schema: z.object({
136
+ name: NameZ,
137
+ kind: z.literal("midpoint"),
138
+ p1: NameZ,
139
+ p2: NameZ,
140
+ visible: z.boolean().optional()
141
+ }),
142
+ collectRefs: (e) => [e.p1, e.p2],
143
+ emit: (e, ctx) => [{
144
+ role: "primary",
145
+ object: emitPointObject(
146
+ ctx.resolveId(e.name),
147
+ e.name,
148
+ { kind: "midpoint", p1: ctx.resolveId(e.p1), p2: ctx.resolveId(e.p2) },
149
+ e.visible ?? true
150
+ )
151
+ }]
152
+ });
153
+ var onSegmentModule = defineModule({
154
+ kind: "onSegment",
155
+ role: "point",
156
+ category: "points",
157
+ prefix: "p",
158
+ schema: z.object({
159
+ name: NameZ,
160
+ kind: z.literal("onSegment"),
161
+ segmentId: NameZ,
162
+ t: z.number().min(0).max(1)
163
+ }),
164
+ collectRefs: (e) => [e.segmentId],
165
+ emit: (e, ctx) => [{
166
+ role: "primary",
167
+ object: emitPointObject(
168
+ ctx.resolveId(e.name),
169
+ e.name,
170
+ { kind: "onSegment", segmentId: ctx.resolveId(e.segmentId), t: e.t }
171
+ )
172
+ }]
173
+ });
174
+ var onLineModule = defineModule({
175
+ kind: "onLine",
176
+ role: "point",
177
+ category: "points",
178
+ prefix: "p",
179
+ schema: z.object({
180
+ name: NameZ,
181
+ kind: z.literal("onLine"),
182
+ lineId: NameZ,
183
+ t: z.number().finite()
184
+ }),
185
+ collectRefs: (e) => [e.lineId],
186
+ emit: (e, ctx) => [{
187
+ role: "primary",
188
+ object: emitPointObject(
189
+ ctx.resolveId(e.name),
190
+ e.name,
191
+ { kind: "onLine", lineId: ctx.resolveId(e.lineId), t: e.t }
192
+ )
193
+ }]
194
+ });
195
+ var onCircleModule = defineModule({
196
+ kind: "onCircle",
197
+ role: "point",
198
+ category: "points",
199
+ prefix: "p",
200
+ schema: z.object({
201
+ name: NameZ,
202
+ kind: z.literal("onCircle"),
203
+ circleId: NameZ,
204
+ theta: z.number().finite()
205
+ }),
206
+ collectRefs: (e) => [e.circleId],
207
+ emit: (e, ctx) => [{
208
+ role: "primary",
209
+ object: emitPointObject(
210
+ ctx.resolveId(e.name),
211
+ e.name,
212
+ { kind: "onCircle", circleId: ctx.resolveId(e.circleId), theta: e.theta }
213
+ )
214
+ }]
215
+ });
216
+ var perpFootModule = defineModule({
217
+ kind: "perpFoot",
218
+ role: "point",
219
+ category: "points",
220
+ prefix: "p",
221
+ schema: z.object({
222
+ name: NameZ,
223
+ kind: z.literal("perpFoot"),
224
+ from: NameZ,
225
+ onLine: NameZ
226
+ }),
227
+ collectRefs: (e) => [e.from, e.onLine],
228
+ emit: (e, ctx) => [{
229
+ role: "primary",
230
+ object: emitPointObject(
231
+ ctx.resolveId(e.name),
232
+ e.name,
233
+ { kind: "perpFoot", from: ctx.resolveId(e.from), onLine: ctx.resolveId(e.onLine) }
234
+ )
235
+ }]
236
+ });
237
+ var circumcenterModule = defineModule({
238
+ kind: "circumcenter",
239
+ role: "point",
240
+ category: "points",
241
+ prefix: "p",
242
+ schema: z.object({
243
+ name: NameZ,
244
+ kind: z.literal("circumcenter"),
245
+ vertices: z.tuple([NameZ, NameZ, NameZ])
246
+ }),
247
+ collectRefs: (e) => [...e.vertices],
248
+ emit: (e, ctx) => [{
249
+ role: "primary",
250
+ object: emitPointObject(
251
+ ctx.resolveId(e.name),
252
+ e.name,
253
+ { kind: "circumcenter", vertices: resolveTriangleVertices(ctx, e.vertices) }
254
+ )
255
+ }]
256
+ });
257
+ var incenterModule = defineModule({
258
+ kind: "incenter",
259
+ role: "point",
260
+ category: "points",
261
+ prefix: "p",
262
+ schema: z.object({
263
+ name: NameZ,
264
+ kind: z.literal("incenter"),
265
+ vertices: z.tuple([NameZ, NameZ, NameZ])
266
+ }),
267
+ collectRefs: (e) => [...e.vertices],
268
+ emit: (e, ctx) => [{
269
+ role: "primary",
270
+ object: emitPointObject(
271
+ ctx.resolveId(e.name),
272
+ e.name,
273
+ { kind: "incenter", vertices: resolveTriangleVertices(ctx, e.vertices) }
274
+ )
275
+ }]
276
+ });
277
+ var centroidModule = defineModule({
278
+ kind: "centroid",
279
+ role: "point",
280
+ category: "points",
281
+ prefix: "p",
282
+ schema: z.object({
283
+ name: NameZ,
284
+ kind: z.literal("centroid"),
285
+ vertices: z.tuple([NameZ, NameZ, NameZ])
286
+ }),
287
+ collectRefs: (e) => [...e.vertices],
288
+ emit: (e, ctx) => [{
289
+ role: "primary",
290
+ object: emitPointObject(
291
+ ctx.resolveId(e.name),
292
+ e.name,
293
+ { kind: "centroid", vertices: resolveTriangleVertices(ctx, e.vertices) }
294
+ )
295
+ }]
296
+ });
297
+ var orthocenterModule = defineModule({
298
+ kind: "orthocenter",
299
+ role: "point",
300
+ category: "points",
301
+ prefix: "p",
302
+ schema: z.object({
303
+ name: NameZ,
304
+ kind: z.literal("orthocenter"),
305
+ vertices: z.tuple([NameZ, NameZ, NameZ])
306
+ }),
307
+ collectRefs: (e) => [...e.vertices],
308
+ emit: (e, ctx) => [{
309
+ role: "primary",
310
+ object: emitPointObject(
311
+ ctx.resolveId(e.name),
312
+ e.name,
313
+ { kind: "orthocenter", vertices: resolveTriangleVertices(ctx, e.vertices) }
314
+ )
315
+ }]
316
+ });
317
+ var intersectionModule = defineModule({
318
+ kind: "intersection",
319
+ role: "point",
320
+ category: "points",
321
+ prefix: "i",
322
+ schema: z.object({
323
+ name: NameZ,
324
+ kind: z.literal("intersection"),
325
+ ref1: NameZ,
326
+ ref2: NameZ,
327
+ branch: z.union([z.literal(0), z.literal(1)]).optional()
328
+ }),
329
+ collectRefs: (e) => [e.ref1, e.ref2],
330
+ emit: (e, ctx) => {
331
+ const r1IsCircle = ctx.hintOf(e.ref1) === "circle";
332
+ const r2IsCircle = ctx.hintOf(e.ref2) === "circle";
333
+ let intersectKind;
334
+ if (r1IsCircle && r2IsCircle) intersectKind = "circleCircle";
335
+ else if (r1IsCircle || r2IsCircle) intersectKind = "lineCircle";
336
+ else intersectKind = "lineLine";
337
+ const attrs = {
338
+ kind: intersectKind,
339
+ ref1: ctx.resolveId(e.ref1),
340
+ ref2: ctx.resolveId(e.ref2)
341
+ };
342
+ if (intersectKind !== "lineLine") {
343
+ attrs.branch = e.branch ?? 0;
344
+ }
345
+ return [{
346
+ role: "primary",
347
+ object: {
348
+ id: ctx.resolveId(e.name),
349
+ kind: "intersection",
350
+ label: e.name,
351
+ ...POINT_BASE_FIELDS,
352
+ attrs
353
+ }
354
+ }];
355
+ }
356
+ });
357
+ var segmentModule = defineModule({
358
+ kind: "segment",
359
+ role: "segment",
360
+ category: "lines",
361
+ prefix: "s",
362
+ schema: z.object({
363
+ name: NameZ,
364
+ kind: z.literal("segment"),
365
+ p1: NameZ,
366
+ p2: NameZ
367
+ }),
368
+ collectRefs: (e) => [e.p1, e.p2],
369
+ emit: (e, ctx) => [{
370
+ role: "primary",
371
+ object: {
372
+ id: ctx.resolveId(e.name),
373
+ kind: "segment",
374
+ label: e.name,
375
+ ...SHAPE_BASE_FIELDS,
376
+ attrs: { p1: ctx.resolveId(e.p1), p2: ctx.resolveId(e.p2) }
377
+ }
378
+ }]
379
+ });
380
+ var lineModule = defineModule({
381
+ kind: "line",
382
+ role: "line",
383
+ category: "lines",
384
+ prefix: "l",
385
+ schema: z.object({
386
+ name: NameZ,
387
+ kind: z.literal("line"),
388
+ p1: NameZ,
389
+ p2: NameZ
390
+ }),
391
+ collectRefs: (e) => [e.p1, e.p2],
392
+ emit: (e, ctx) => [{
393
+ role: "primary",
394
+ object: {
395
+ id: ctx.resolveId(e.name),
396
+ kind: "line",
397
+ label: e.name,
398
+ ...SHAPE_BASE_FIELDS,
399
+ attrs: { p1: ctx.resolveId(e.p1), p2: ctx.resolveId(e.p2) }
400
+ }
401
+ }]
402
+ });
403
+ var rayModule = defineModule({
404
+ kind: "ray",
405
+ role: "ray",
406
+ category: "lines",
407
+ prefix: "r",
408
+ schema: z.object({
409
+ name: NameZ,
410
+ kind: z.literal("ray"),
411
+ origin: NameZ,
412
+ through: NameZ
413
+ }),
414
+ collectRefs: (e) => [e.origin, e.through],
415
+ emit: (e, ctx) => [{
416
+ role: "primary",
417
+ object: {
418
+ id: ctx.resolveId(e.name),
419
+ kind: "ray",
420
+ label: e.name,
421
+ ...SHAPE_BASE_FIELDS,
422
+ attrs: { origin: ctx.resolveId(e.origin), through: ctx.resolveId(e.through) }
423
+ }
424
+ }]
425
+ });
426
+ var perpendicularModule = defineModule({
427
+ kind: "perpendicular",
428
+ role: "lineConstruction",
429
+ category: "lines",
430
+ prefix: "l",
431
+ schema: z.object({
432
+ name: NameZ,
433
+ kind: z.literal("perpendicular"),
434
+ throughPoint: NameZ,
435
+ toLine: NameZ
436
+ }),
437
+ collectRefs: (e) => [e.throughPoint, e.toLine],
438
+ emit: (e, ctx) => [{
439
+ role: "primary",
440
+ object: {
441
+ id: ctx.resolveId(e.name),
442
+ kind: "line",
443
+ label: e.name,
444
+ ...SHAPE_BASE_FIELDS,
445
+ attrs: {
446
+ construction: {
447
+ kind: "perpendicular",
448
+ throughPoint: ctx.resolveId(e.throughPoint),
449
+ toLine: ctx.resolveId(e.toLine)
450
+ }
451
+ }
452
+ }
453
+ }]
454
+ });
455
+ var parallelModule = defineModule({
456
+ kind: "parallel",
457
+ role: "lineConstruction",
458
+ category: "lines",
459
+ prefix: "l",
460
+ schema: z.object({
461
+ name: NameZ,
462
+ kind: z.literal("parallel"),
463
+ throughPoint: NameZ,
464
+ toLine: NameZ
465
+ }),
466
+ collectRefs: (e) => [e.throughPoint, e.toLine],
467
+ emit: (e, ctx) => [{
468
+ role: "primary",
469
+ object: {
470
+ id: ctx.resolveId(e.name),
471
+ kind: "line",
472
+ label: e.name,
473
+ ...SHAPE_BASE_FIELDS,
474
+ attrs: {
475
+ construction: {
476
+ kind: "parallel",
477
+ throughPoint: ctx.resolveId(e.throughPoint),
478
+ toLine: ctx.resolveId(e.toLine)
479
+ }
480
+ }
481
+ }
482
+ }]
483
+ });
484
+ var perpBisectorModule = defineModule({
485
+ kind: "perpBisector",
486
+ role: "lineConstruction",
487
+ category: "lines",
488
+ prefix: "l",
489
+ schema: z.object({
490
+ name: NameZ,
491
+ kind: z.literal("perpBisector"),
492
+ p1: NameZ,
493
+ p2: NameZ
494
+ }),
495
+ collectRefs: (e) => [e.p1, e.p2],
496
+ emit: (e, ctx) => [{
497
+ role: "primary",
498
+ object: {
499
+ id: ctx.resolveId(e.name),
500
+ kind: "line",
501
+ label: e.name,
502
+ ...SHAPE_BASE_FIELDS,
503
+ attrs: {
504
+ construction: {
505
+ kind: "perpBisector",
506
+ p1: ctx.resolveId(e.p1),
507
+ p2: ctx.resolveId(e.p2)
508
+ }
509
+ }
510
+ }
511
+ }]
512
+ });
513
+ var angleBisectorModule = defineModule({
514
+ kind: "angleBisector",
515
+ role: "lineConstruction",
516
+ category: "lines",
517
+ prefix: "l",
518
+ schema: z.object({
519
+ name: NameZ,
520
+ kind: z.literal("angleBisector"),
521
+ p1: NameZ,
522
+ vertex: NameZ,
523
+ p2: NameZ
524
+ }),
525
+ collectRefs: (e) => [e.p1, e.vertex, e.p2],
526
+ emit: (e, ctx) => [{
527
+ role: "primary",
528
+ object: {
529
+ id: ctx.resolveId(e.name),
530
+ kind: "line",
531
+ label: e.name,
532
+ ...SHAPE_BASE_FIELDS,
533
+ attrs: {
534
+ construction: {
535
+ kind: "angleBisector",
536
+ p1: ctx.resolveId(e.p1),
537
+ vertex: ctx.resolveId(e.vertex),
538
+ p2: ctx.resolveId(e.p2)
539
+ }
540
+ }
541
+ }
542
+ }]
543
+ });
544
+ var lineThroughModule = defineModule({
545
+ kind: "lineThrough",
546
+ role: "lineConstruction",
547
+ category: "lines",
548
+ prefix: "l",
549
+ schema: z.object({
550
+ name: NameZ,
551
+ kind: z.literal("lineThrough"),
552
+ points: z.array(NameZ).min(2)
553
+ }),
554
+ collectRefs: (e) => [...e.points],
555
+ refSpecs: [{ field: "points", role: "point", many: true }],
556
+ emit: (e, ctx) => [{
557
+ role: "primary",
558
+ object: {
559
+ id: ctx.resolveId(e.name),
560
+ kind: "line",
561
+ label: e.name,
562
+ ...SHAPE_BASE_FIELDS,
563
+ attrs: {
564
+ construction: {
565
+ kind: "lineThrough",
566
+ points: e.points.map((p) => ctx.resolveId(p))
567
+ }
568
+ }
569
+ }
570
+ }]
571
+ });
572
+ var radicalAxisModule = defineModule({
573
+ kind: "radicalAxis",
574
+ role: "lineConstruction",
575
+ category: "lines",
576
+ prefix: "l",
577
+ schema: z.object({
578
+ name: NameZ,
579
+ kind: z.literal("radicalAxis"),
580
+ circle1: NameZ,
581
+ circle2: NameZ
582
+ }),
583
+ collectRefs: (e) => [e.circle1, e.circle2],
584
+ refSpecs: [
585
+ { field: "circle1", role: "circle" },
586
+ { field: "circle2", role: "circle" }
587
+ ],
588
+ emit: (e, ctx) => [{
589
+ role: "primary",
590
+ object: {
591
+ id: ctx.resolveId(e.name),
592
+ kind: "line",
593
+ label: e.name,
594
+ ...SHAPE_BASE_FIELDS,
595
+ attrs: {
596
+ construction: {
597
+ kind: "radicalAxis",
598
+ circle1: ctx.resolveId(e.circle1),
599
+ circle2: ctx.resolveId(e.circle2)
600
+ }
601
+ }
602
+ }
603
+ }]
604
+ });
605
+ var tangentModule = defineModule({
606
+ kind: "tangent",
607
+ role: "lineConstruction",
608
+ category: "lines",
609
+ prefix: "l",
610
+ schema: z.object({
611
+ name: NameZ,
612
+ kind: z.literal("tangent"),
613
+ throughPoint: NameZ,
614
+ toCircle: NameZ,
615
+ branch: z.union([z.literal(0), z.literal(1), z.literal("on")]).optional()
616
+ }),
617
+ collectRefs: (e) => [e.throughPoint, e.toCircle],
618
+ emit: (e, ctx) => {
619
+ const construction = {
620
+ kind: "tangent",
621
+ throughPoint: ctx.resolveId(e.throughPoint),
622
+ toCircle: ctx.resolveId(e.toCircle)
623
+ };
624
+ if (e.branch !== void 0) construction.branch = e.branch;
625
+ return [{
626
+ role: "primary",
627
+ object: {
628
+ id: ctx.resolveId(e.name),
629
+ kind: "line",
630
+ label: e.name,
631
+ ...SHAPE_BASE_FIELDS,
632
+ attrs: { construction }
633
+ }
634
+ }];
635
+ }
636
+ });
637
+ var polygonModule = defineModule({
638
+ kind: "polygon",
639
+ role: "polygon",
640
+ category: "polygons",
641
+ prefix: "poly",
642
+ schema: z.object({
643
+ name: NameZ,
644
+ kind: z.literal("polygon"),
645
+ vertices: z.array(NameZ).min(3)
646
+ }),
647
+ collectRefs: (e) => [...e.vertices],
648
+ emit: (e, ctx) => [{
649
+ role: "primary",
650
+ object: {
651
+ id: ctx.resolveId(e.name),
652
+ kind: "polygon",
653
+ label: e.name,
654
+ ...SHAPE_BASE_FIELDS,
655
+ attrs: { vertices: e.vertices.map((v) => ctx.resolveId(v)) }
656
+ }
657
+ }]
658
+ });
659
+ var circleCPModule = defineModule({
660
+ kind: "circleCP",
661
+ role: "circle",
662
+ category: "circles",
663
+ prefix: "c",
664
+ schema: z.object({
665
+ name: NameZ,
666
+ kind: z.literal("circleCP"),
667
+ center: NameZ,
668
+ surfacePoint: NameZ,
669
+ visible: z.boolean().optional()
670
+ }),
671
+ collectRefs: (e) => [e.center, e.surfacePoint],
672
+ emit: (e, ctx) => [{
673
+ role: "primary",
674
+ object: {
675
+ id: ctx.resolveId(e.name),
676
+ kind: "circle",
677
+ label: e.name,
678
+ ...SHAPE_BASE_FIELDS,
679
+ visible: e.visible ?? true,
680
+ attrs: { center: ctx.resolveId(e.center), surfacePoint: ctx.resolveId(e.surfacePoint) }
681
+ }
682
+ }]
683
+ });
684
+ var circle3Module = defineModule({
685
+ kind: "circle3",
686
+ role: "circle",
687
+ category: "circles",
688
+ prefix: "c",
689
+ schema: z.object({
690
+ name: NameZ,
691
+ kind: z.literal("circle3"),
692
+ p1: NameZ,
693
+ p2: NameZ,
694
+ p3: NameZ
695
+ }),
696
+ collectRefs: (e) => [e.p1, e.p2, e.p3],
697
+ emit: (e, ctx) => [{
698
+ role: "primary",
699
+ object: {
700
+ id: ctx.resolveId(e.name),
701
+ kind: "circle",
702
+ label: e.name,
703
+ ...SHAPE_BASE_FIELDS,
704
+ attrs: {
705
+ construction: {
706
+ kind: "circumscribed",
707
+ p1: ctx.resolveId(e.p1),
708
+ p2: ctx.resolveId(e.p2),
709
+ p3: ctx.resolveId(e.p3)
710
+ }
711
+ }
712
+ }
713
+ }]
714
+ });
715
+ var circleDiameterModule = defineModule({
716
+ kind: "circleDiameter",
717
+ role: "circle",
718
+ category: "circles",
719
+ prefix: "c",
720
+ schema: z.object({
721
+ name: NameZ,
722
+ kind: z.literal("circleDiameter"),
723
+ p1: NameZ,
724
+ p2: NameZ,
725
+ visible: z.boolean().optional()
726
+ }),
727
+ collectRefs: (e) => [e.p1, e.p2],
728
+ emit: (e, ctx) => [{
729
+ role: "primary",
730
+ object: {
731
+ id: ctx.resolveId(e.name),
732
+ kind: "circle",
733
+ label: e.name,
734
+ ...SHAPE_BASE_FIELDS,
735
+ visible: e.visible ?? true,
736
+ attrs: {
737
+ construction: {
738
+ kind: "diameter",
739
+ p1: ctx.resolveId(e.p1),
740
+ p2: ctx.resolveId(e.p2)
741
+ }
742
+ }
743
+ }
744
+ }]
745
+ });
746
+ var secondIntersectionModule = defineModule({
747
+ kind: "secondIntersection",
748
+ role: "point",
749
+ category: "points",
750
+ prefix: "p",
751
+ schema: z.object({
752
+ name: NameZ,
753
+ kind: z.literal("secondIntersection"),
754
+ line: NameZ,
755
+ circle: NameZ,
756
+ other: NameZ
757
+ }),
758
+ collectRefs: (e) => [e.line, e.circle, e.other],
759
+ refSpecs: [
760
+ { field: "line", role: "line-like" },
761
+ { field: "circle", role: "circle" },
762
+ { field: "other", role: "point" }
763
+ ],
764
+ emit: (e, ctx) => [{
765
+ role: "primary",
766
+ object: emitPointObject(
767
+ ctx.resolveId(e.name),
768
+ e.name,
769
+ {
770
+ kind: "secondIntersection",
771
+ line: ctx.resolveId(e.line),
772
+ circle: ctx.resolveId(e.circle),
773
+ other: ctx.resolveId(e.other)
774
+ }
775
+ )
776
+ }]
777
+ });
778
+ var circleIntersectionModule = defineModule({
779
+ kind: "circleIntersection",
780
+ role: "point",
781
+ category: "points",
782
+ prefix: "p",
783
+ schema: z.object({
784
+ name: NameZ,
785
+ kind: z.literal("circleIntersection"),
786
+ c1: NameZ,
787
+ c2: NameZ,
788
+ which: z.union([z.literal(0), z.literal(1)])
789
+ }),
790
+ collectRefs: (e) => [e.c1, e.c2],
791
+ refSpecs: [
792
+ { field: "c1", role: "circle" },
793
+ { field: "c2", role: "circle" }
794
+ ],
795
+ emit: (e, ctx) => [{
796
+ role: "primary",
797
+ object: emitPointObject(
798
+ ctx.resolveId(e.name),
799
+ e.name,
800
+ {
801
+ kind: "circleIntersection",
802
+ c1: ctx.resolveId(e.c1),
803
+ c2: ctx.resolveId(e.c2),
804
+ which: e.which
805
+ }
806
+ )
807
+ }]
808
+ });
809
+ var circleSecondIntersectionModule = defineModule({
810
+ kind: "circleSecondIntersection",
811
+ role: "point",
812
+ category: "points",
813
+ prefix: "p",
814
+ schema: z.object({
815
+ name: NameZ,
816
+ kind: z.literal("circleSecondIntersection"),
817
+ c1: NameZ,
818
+ c2: NameZ,
819
+ exclude: NameZ
820
+ }),
821
+ collectRefs: (e) => [e.c1, e.c2, e.exclude],
822
+ refSpecs: [
823
+ { field: "c1", role: "circle" },
824
+ { field: "c2", role: "circle" },
825
+ { field: "exclude", role: "point" }
826
+ ],
827
+ emit: (e, ctx) => [{
828
+ role: "primary",
829
+ object: emitPointObject(
830
+ ctx.resolveId(e.name),
831
+ e.name,
832
+ {
833
+ kind: "circleSecondIntersection",
834
+ c1: ctx.resolveId(e.c1),
835
+ c2: ctx.resolveId(e.c2),
836
+ exclude: ctx.resolveId(e.exclude)
837
+ }
838
+ )
839
+ }]
840
+ });
841
+ var tangencyPointModule = defineModule({
842
+ kind: "tangencyPoint",
843
+ role: "point",
844
+ category: "points",
845
+ prefix: "p",
846
+ schema: z.object({
847
+ name: NameZ,
848
+ kind: z.literal("tangencyPoint"),
849
+ circle: NameZ,
850
+ onLine: NameZ
851
+ }),
852
+ collectRefs: (e) => [e.circle, e.onLine],
853
+ refSpecs: [
854
+ { field: "circle", role: "circle" },
855
+ { field: "onLine", role: "line-like" }
856
+ ],
857
+ emit: (e, ctx) => [{
858
+ role: "primary",
859
+ object: emitPointObject(
860
+ ctx.resolveId(e.name),
861
+ e.name,
862
+ {
863
+ kind: "tangencyPoint",
864
+ circle: ctx.resolveId(e.circle),
865
+ onLine: ctx.resolveId(e.onLine)
866
+ }
867
+ )
868
+ }]
869
+ });
870
+ var tangentPointExtModule = defineModule({
871
+ kind: "tangentPointExt",
872
+ role: "point",
873
+ category: "points",
874
+ prefix: "p",
875
+ schema: z.object({
876
+ name: NameZ,
877
+ kind: z.literal("tangentPointExt"),
878
+ from: NameZ,
879
+ circle: NameZ,
880
+ which: z.union([z.literal(0), z.literal(1)])
881
+ }),
882
+ collectRefs: (e) => [e.from, e.circle],
883
+ refSpecs: [
884
+ { field: "from", role: "point" },
885
+ { field: "circle", role: "circle" }
886
+ ],
887
+ emit: (e, ctx) => [{
888
+ role: "primary",
889
+ object: emitPointObject(
890
+ ctx.resolveId(e.name),
891
+ e.name,
892
+ {
893
+ kind: "tangentPointExt",
894
+ from: ctx.resolveId(e.from),
895
+ circle: ctx.resolveId(e.circle),
896
+ which: e.which
897
+ }
898
+ )
899
+ }]
900
+ });
901
+ var circleCRModule = defineModule({
902
+ kind: "circleCR",
903
+ role: "circle",
904
+ category: "circles",
905
+ prefix: "c",
906
+ schema: z.object({
907
+ name: NameZ,
908
+ kind: z.literal("circleCR"),
909
+ center: NameZ,
910
+ radius: z.number().positive()
911
+ }),
912
+ collectRefs: (e) => [e.center],
913
+ refSpecs: [{ field: "center", role: "point" }],
914
+ emit: (e, ctx) => [{
915
+ role: "primary",
916
+ object: {
917
+ id: ctx.resolveId(e.name),
918
+ kind: "circle",
919
+ label: e.name,
920
+ ...SHAPE_BASE_FIELDS,
921
+ attrs: { center: ctx.resolveId(e.center), radius: e.radius }
922
+ }
923
+ }]
924
+ });
925
+ var incircleModule = defineModule({
926
+ kind: "incircle",
927
+ role: "circle",
928
+ category: "circles",
929
+ prefix: "c",
930
+ schema: z.object({
931
+ name: NameZ,
932
+ kind: z.literal("incircle"),
933
+ vertices: z.tuple([NameZ, NameZ, NameZ])
934
+ }),
935
+ collectRefs: (e) => [...e.vertices],
936
+ refSpecs: [{ field: "vertices", role: "point", many: true }],
937
+ emit: (e, ctx) => [{
938
+ role: "primary",
939
+ object: {
940
+ id: ctx.resolveId(e.name),
941
+ kind: "circle",
942
+ label: e.name,
943
+ ...SHAPE_BASE_FIELDS,
944
+ attrs: {
945
+ kind: "incircle",
946
+ vertices: [
947
+ ctx.resolveId(e.vertices[0]),
948
+ ctx.resolveId(e.vertices[1]),
949
+ ctx.resolveId(e.vertices[2])
950
+ ]
951
+ }
952
+ }
953
+ }]
954
+ });
955
+ var excircleModule = defineModule({
956
+ kind: "excircle",
957
+ role: "circle",
958
+ category: "circles",
959
+ prefix: "c",
960
+ schema: z.object({
961
+ name: NameZ,
962
+ kind: z.literal("excircle"),
963
+ vertices: z.tuple([NameZ, NameZ, NameZ]),
964
+ opposite: NameZ
965
+ }),
966
+ collectRefs: (e) => [...e.vertices],
967
+ refSpecs: [
968
+ { field: "vertices", role: "point", many: true },
969
+ { field: "opposite", role: "point" }
970
+ ],
971
+ emit: (e, ctx) => [{
972
+ role: "primary",
973
+ object: {
974
+ id: ctx.resolveId(e.name),
975
+ kind: "circle",
976
+ label: e.name,
977
+ ...SHAPE_BASE_FIELDS,
978
+ attrs: {
979
+ construction: {
980
+ kind: "excircle",
981
+ p1: ctx.resolveId(e.vertices[0]),
982
+ p2: ctx.resolveId(e.vertices[1]),
983
+ p3: ctx.resolveId(e.vertices[2]),
984
+ opposite: ctx.resolveId(e.opposite)
985
+ }
986
+ }
987
+ }
988
+ }]
989
+ });
990
+ var arcMidpointModule = defineModule({
991
+ kind: "arcMidpoint",
992
+ role: "point",
993
+ category: "points",
994
+ prefix: "p",
995
+ schema: z.object({
996
+ name: NameZ,
997
+ kind: z.literal("arcMidpoint"),
998
+ circle: NameZ,
999
+ a: NameZ,
1000
+ b: NameZ,
1001
+ notContaining: NameZ
1002
+ }),
1003
+ collectRefs: (e) => [e.circle, e.a, e.b, e.notContaining],
1004
+ emit: (e, ctx) => [{
1005
+ role: "primary",
1006
+ object: emitPointObject(ctx.resolveId(e.name), e.name, {
1007
+ kind: "arcMidpoint",
1008
+ circle: ctx.resolveId(e.circle),
1009
+ a: ctx.resolveId(e.a),
1010
+ b: ctx.resolveId(e.b),
1011
+ notContaining: ctx.resolveId(e.notContaining)
1012
+ })
1013
+ }]
1014
+ });
1015
+ var excenterModule = defineModule({
1016
+ kind: "excenter",
1017
+ role: "point",
1018
+ category: "points",
1019
+ prefix: "p",
1020
+ schema: z.object({
1021
+ name: NameZ,
1022
+ kind: z.literal("excenter"),
1023
+ vertices: z.tuple([NameZ, NameZ, NameZ]),
1024
+ opposite: NameZ
1025
+ }),
1026
+ collectRefs: (e) => [...e.vertices],
1027
+ emit: (e, ctx) => [{
1028
+ role: "primary",
1029
+ object: emitPointObject(ctx.resolveId(e.name), e.name, {
1030
+ kind: "excenter",
1031
+ vertices: resolveTriangleVertices(ctx, e.vertices),
1032
+ opposite: ctx.resolveId(e.opposite)
1033
+ })
1034
+ }]
1035
+ });
1036
+ var reflectPointModule = defineModule({
1037
+ kind: "reflectPoint",
1038
+ role: "point",
1039
+ category: "points",
1040
+ prefix: "p",
1041
+ schema: z.object({ name: NameZ, kind: z.literal("reflectPoint"), of: NameZ, through: NameZ }),
1042
+ collectRefs: (e) => [e.of, e.through],
1043
+ emit: (e, ctx) => [{
1044
+ role: "primary",
1045
+ object: emitPointObject(ctx.resolveId(e.name), e.name, {
1046
+ kind: "transformed",
1047
+ source: ctx.resolveId(e.of),
1048
+ transform: { kind: "reflectPoint", center: ctx.resolveId(e.through) }
1049
+ })
1050
+ }]
1051
+ });
1052
+ var reflectLineModule = defineModule({
1053
+ kind: "reflectLine",
1054
+ role: "point",
1055
+ category: "points",
1056
+ prefix: "p",
1057
+ schema: z.object({ name: NameZ, kind: z.literal("reflectLine"), of: NameZ, through: NameZ }),
1058
+ collectRefs: (e) => [e.of, e.through],
1059
+ emit: (e, ctx) => [{
1060
+ role: "primary",
1061
+ object: emitPointObject(ctx.resolveId(e.name), e.name, {
1062
+ kind: "transformed",
1063
+ source: ctx.resolveId(e.of),
1064
+ transform: { kind: "reflectLine", line: ctx.resolveId(e.through) }
1065
+ })
1066
+ }]
1067
+ });
1068
+ var ScaleOffsetZ = {
1069
+ scale: z.number().positive().optional(),
1070
+ offset: z.number().optional()
1071
+ };
1072
+ var DistanceZ = z.discriminatedUnion("kind", [
1073
+ z.object({ kind: z.literal("circleRadius"), circle: NameZ, ...ScaleOffsetZ }),
1074
+ z.object({ kind: z.literal("segmentLength"), p1: NameZ, p2: NameZ, ...ScaleOffsetZ }),
1075
+ z.object({ kind: z.literal("literal"), value: z.number().positive(), ...ScaleOffsetZ })
1076
+ ]);
1077
+ function withScaleOffset(base, d) {
1078
+ const out = { ...base };
1079
+ if (d.scale !== void 0) out.scale = d.scale;
1080
+ if (d.offset !== void 0) out.offset = d.offset;
1081
+ return out;
1082
+ }
1083
+ var pointAtDistanceModule = defineModule({
1084
+ kind: "pointAtDistance",
1085
+ role: "point",
1086
+ category: "points",
1087
+ prefix: "p",
1088
+ schema: z.object({
1089
+ name: NameZ,
1090
+ kind: z.literal("pointAtDistance"),
1091
+ from: NameZ,
1092
+ through: NameZ,
1093
+ distance: DistanceZ
1094
+ }),
1095
+ collectRefs: (e) => {
1096
+ const d = e.distance;
1097
+ const extra = d.kind === "circleRadius" ? [d.circle] : d.kind === "segmentLength" ? [d.p1, d.p2] : [];
1098
+ return [e.from, e.through, ...extra];
1099
+ },
1100
+ // TODO(Mức 1 defer): distance.{circle,p1,p2} là nested trong `distance` — refSpec
1101
+ // phẳng đọc top-level không với tới, validate riêng nếu cần. Hiện validate from/through.
1102
+ refSpecs: [
1103
+ { field: "from", role: "point" },
1104
+ { field: "through", role: "point" }
1105
+ ],
1106
+ emit: (e, ctx) => {
1107
+ const d = e.distance;
1108
+ const distance = d.kind === "circleRadius" ? withScaleOffset({ kind: "circleRadius", circle: ctx.resolveId(d.circle) }, d) : d.kind === "segmentLength" ? withScaleOffset({ kind: "segmentLength", p1: ctx.resolveId(d.p1), p2: ctx.resolveId(d.p2) }, d) : withScaleOffset({ kind: "literal", value: d.value }, d);
1109
+ return [{
1110
+ role: "primary",
1111
+ object: emitPointObject(ctx.resolveId(e.name), e.name, {
1112
+ kind: "pointAtDistance",
1113
+ from: ctx.resolveId(e.from),
1114
+ through: ctx.resolveId(e.through),
1115
+ distance
1116
+ })
1117
+ }];
1118
+ }
1119
+ });
1120
+
1121
+ // src/stamps/geometry-2d/dsl/registry.ts
1122
+ var ALL_MODULES = [
1123
+ freeModule,
1124
+ midpointModule,
1125
+ onSegmentModule,
1126
+ onLineModule,
1127
+ onCircleModule,
1128
+ perpFootModule,
1129
+ circumcenterModule,
1130
+ incenterModule,
1131
+ centroidModule,
1132
+ orthocenterModule,
1133
+ intersectionModule,
1134
+ // NEW Tier 4+5 points
1135
+ secondIntersectionModule,
1136
+ circleIntersectionModule,
1137
+ circleSecondIntersectionModule,
1138
+ tangencyPointModule,
1139
+ tangentPointExtModule,
1140
+ segmentModule,
1141
+ lineModule,
1142
+ rayModule,
1143
+ perpendicularModule,
1144
+ parallelModule,
1145
+ perpBisectorModule,
1146
+ angleBisectorModule,
1147
+ lineThroughModule,
1148
+ radicalAxisModule,
1149
+ tangentModule,
1150
+ polygonModule,
1151
+ circleCPModule,
1152
+ circle3Module,
1153
+ circleDiameterModule,
1154
+ // NEW Tier 4+5 circles
1155
+ circleCRModule,
1156
+ incircleModule,
1157
+ excircleModule,
1158
+ // Cụm A points
1159
+ arcMidpointModule,
1160
+ excenterModule,
1161
+ reflectPointModule,
1162
+ reflectLineModule,
1163
+ // Cụm B points
1164
+ pointAtDistanceModule
1165
+ ];
1166
+ var KIND_REGISTRY = new Map(ALL_MODULES.map((m) => [m.kind, m]));
1167
+ var POINT_KINDS = new Set(
1168
+ ALL_MODULES.filter((m) => m.role === "point").map((m) => m.kind)
1169
+ );
1170
+ var LINE_LIKE_SHAPE_KINDS = new Set(
1171
+ ALL_MODULES.filter(
1172
+ (m) => m.role === "segment" || m.role === "line" || m.role === "ray" || m.role === "lineConstruction"
1173
+ ).map((m) => m.kind)
1174
+ );
1175
+ var CIRCLE_KINDS = new Set(
1176
+ ALL_MODULES.filter((m) => m.role === "circle").map((m) => m.kind)
1177
+ );
1178
+ z.discriminatedUnion(
1179
+ "kind",
1180
+ ALL_MODULES.map((m) => m.schema)
1181
+ );
1182
+
1183
+ // src/stamps/geometry-2d/dsl/serialize.ts
1184
+ var NAME_REGEX = /^[A-Za-z][A-Za-z0-9_'₀-₉]{0,11}$/;
1185
+ function isValidName(s) {
1186
+ return NAME_REGEX.test(s);
1187
+ }
1188
+ function labelOf(id, state) {
1189
+ const obj = state.objects[id];
1190
+ return obj ? obj.label : null;
1191
+ }
1192
+ function resolveRefs(ids, state) {
1193
+ const out = [];
1194
+ for (const id of ids) {
1195
+ const lab = labelOf(id, state);
1196
+ if (lab == null || !isValidName(lab)) return null;
1197
+ out.push(lab);
1198
+ }
1199
+ return out;
1200
+ }
1201
+ function fail(reason, detail) {
1202
+ return { ok: false, reason, detail };
1203
+ }
1204
+ function serializePoint(obj, state) {
1205
+ const c = obj.attrs?.constraint;
1206
+ if (!c) return fail("unsupported-constraint", "missing");
1207
+ switch (c.kind) {
1208
+ case "free":
1209
+ return { ok: true, entity: { name: obj.label, kind: "free", x: c.x, y: c.y } };
1210
+ case "midpoint": {
1211
+ const refs = resolveRefs([c.p1, c.p2], state);
1212
+ if (!refs) return fail("unresolved-ref", `${c.p1},${c.p2}`);
1213
+ return { ok: true, entity: { name: obj.label, kind: "midpoint", p1: refs[0], p2: refs[1] } };
1214
+ }
1215
+ case "onSegment": {
1216
+ const refs = resolveRefs([c.segmentId], state);
1217
+ if (!refs) return fail("unresolved-ref", c.segmentId);
1218
+ return { ok: true, entity: { name: obj.label, kind: "onSegment", segmentId: refs[0], t: c.t } };
1219
+ }
1220
+ case "onLine": {
1221
+ const refs = resolveRefs([c.lineId], state);
1222
+ if (!refs) return fail("unresolved-ref", c.lineId);
1223
+ return { ok: true, entity: { name: obj.label, kind: "onLine", lineId: refs[0], t: c.t } };
1224
+ }
1225
+ case "onCircle": {
1226
+ const refs = resolveRefs([c.circleId], state);
1227
+ if (!refs) return fail("unresolved-ref", c.circleId);
1228
+ return { ok: true, entity: { name: obj.label, kind: "onCircle", circleId: refs[0], theta: c.theta } };
1229
+ }
1230
+ case "perpFoot": {
1231
+ const refs = resolveRefs([c.from, c.onLine], state);
1232
+ if (!refs) return fail("unresolved-ref", `${c.from},${c.onLine}`);
1233
+ return { ok: true, entity: { name: obj.label, kind: "perpFoot", from: refs[0], onLine: refs[1] } };
1234
+ }
1235
+ case "circumcenter":
1236
+ case "incenter":
1237
+ case "centroid":
1238
+ case "orthocenter": {
1239
+ const refs = resolveRefs(c.vertices, state);
1240
+ if (!refs || refs.length !== 3) return fail("unresolved-ref", c.vertices.join(","));
1241
+ return {
1242
+ ok: true,
1243
+ entity: {
1244
+ name: obj.label,
1245
+ kind: c.kind,
1246
+ vertices: [refs[0], refs[1], refs[2]]
1247
+ }
1248
+ };
1249
+ }
1250
+ case "tangentPointExt": {
1251
+ const refs = resolveRefs([c.from, c.circle], state);
1252
+ if (!refs) return fail("unresolved-ref", `${c.from},${c.circle}`);
1253
+ return {
1254
+ ok: true,
1255
+ entity: {
1256
+ name: obj.label,
1257
+ kind: "tangentPointExt",
1258
+ from: refs[0],
1259
+ circle: refs[1],
1260
+ which: c.which
1261
+ }
1262
+ };
1263
+ }
1264
+ case "circleIntersection": {
1265
+ const refs = resolveRefs([c.c1, c.c2], state);
1266
+ if (!refs) return fail("unresolved-ref", `${c.c1},${c.c2}`);
1267
+ return {
1268
+ ok: true,
1269
+ entity: { name: obj.label, kind: "circleIntersection", c1: refs[0], c2: refs[1], which: c.which }
1270
+ };
1271
+ }
1272
+ case "circleSecondIntersection": {
1273
+ const refs = resolveRefs([c.c1, c.c2, c.exclude], state);
1274
+ if (!refs) return fail("unresolved-ref", `${c.c1},${c.c2},${c.exclude}`);
1275
+ return {
1276
+ ok: true,
1277
+ entity: { name: obj.label, kind: "circleSecondIntersection", c1: refs[0], c2: refs[1], exclude: refs[2] }
1278
+ };
1279
+ }
1280
+ case "secondIntersection": {
1281
+ const refs = resolveRefs([c.line, c.circle, c.other], state);
1282
+ if (!refs) return fail("unresolved-ref", `${c.line},${c.circle},${c.other}`);
1283
+ return {
1284
+ ok: true,
1285
+ entity: { name: obj.label, kind: "secondIntersection", line: refs[0], circle: refs[1], other: refs[2] }
1286
+ };
1287
+ }
1288
+ case "tangencyPoint": {
1289
+ const refs = resolveRefs([c.circle, c.onLine], state);
1290
+ if (!refs) return fail("unresolved-ref", `${c.circle},${c.onLine}`);
1291
+ return {
1292
+ ok: true,
1293
+ entity: { name: obj.label, kind: "tangencyPoint", circle: refs[0], onLine: refs[1] }
1294
+ };
1295
+ }
1296
+ case "arcMidpoint": {
1297
+ const refs = resolveRefs([c.circle, c.a, c.b, c.notContaining], state);
1298
+ if (!refs) return fail("unresolved-ref", `${c.circle},${c.a},${c.b},${c.notContaining}`);
1299
+ return {
1300
+ ok: true,
1301
+ entity: {
1302
+ name: obj.label,
1303
+ kind: "arcMidpoint",
1304
+ circle: refs[0],
1305
+ a: refs[1],
1306
+ b: refs[2],
1307
+ notContaining: refs[3]
1308
+ }
1309
+ };
1310
+ }
1311
+ case "excenter": {
1312
+ const refs = resolveRefs([c.vertices[0], c.vertices[1], c.vertices[2], c.opposite], state);
1313
+ if (!refs) return fail("unresolved-ref", `${c.vertices.join(",")},${c.opposite}`);
1314
+ return {
1315
+ ok: true,
1316
+ entity: {
1317
+ name: obj.label,
1318
+ kind: "excenter",
1319
+ vertices: [refs[0], refs[1], refs[2]],
1320
+ opposite: refs[3]
1321
+ }
1322
+ };
1323
+ }
1324
+ case "pointAtDistance": {
1325
+ const d = c.distance;
1326
+ const distRefIds = d.kind === "circleRadius" ? [d.circle] : d.kind === "segmentLength" ? [d.p1, d.p2] : [];
1327
+ const refs = resolveRefs([c.from, c.through, ...distRefIds], state);
1328
+ if (!refs) return fail("unresolved-ref", [c.from, c.through, ...distRefIds].join(","));
1329
+ let distance;
1330
+ if (d.kind === "circleRadius") {
1331
+ distance = { kind: "circleRadius", circle: refs[2] };
1332
+ } else if (d.kind === "segmentLength") {
1333
+ distance = { kind: "segmentLength", p1: refs[2], p2: refs[3] };
1334
+ } else {
1335
+ distance = { kind: "literal", value: d.value };
1336
+ }
1337
+ if (d.scale !== void 0) distance.scale = d.scale;
1338
+ if (d.offset !== void 0) distance.offset = d.offset;
1339
+ return {
1340
+ ok: true,
1341
+ entity: { name: obj.label, kind: "pointAtDistance", from: refs[0], through: refs[1], distance }
1342
+ };
1343
+ }
1344
+ // Out of DSL v1:
1345
+ case "onAxis":
1346
+ case "onPolygon":
1347
+ case "transformed":
1348
+ case "onPerpendicular":
1349
+ case "onPerpBisector":
1350
+ case "onCircleAroundPoint":
1351
+ return fail("unsupported-constraint", c.kind);
1352
+ default: {
1353
+ return fail("unsupported-constraint");
1354
+ }
1355
+ }
1356
+ }
1357
+ function serializeIntersection(obj, state) {
1358
+ const a = obj.attrs;
1359
+ const refs = resolveRefs([a.ref1, a.ref2], state);
1360
+ if (!refs) return fail("unresolved-ref", `${a.ref1},${a.ref2}`);
1361
+ if (a.kind === "lineLine") {
1362
+ return { ok: true, entity: { name: obj.label, kind: "intersection", ref1: refs[0], ref2: refs[1] } };
1363
+ }
1364
+ return {
1365
+ ok: true,
1366
+ entity: {
1367
+ name: obj.label,
1368
+ kind: "intersection",
1369
+ ref1: refs[0],
1370
+ ref2: refs[1],
1371
+ branch: a.branch
1372
+ }
1373
+ };
1374
+ }
1375
+ function serializeSegment(obj, state) {
1376
+ const refs = resolveRefs([obj.attrs.p1, obj.attrs.p2], state);
1377
+ if (!refs) return fail("unresolved-ref", `${obj.attrs.p1},${obj.attrs.p2}`);
1378
+ return { ok: true, entity: { name: obj.label, kind: "segment", p1: refs[0], p2: refs[1] } };
1379
+ }
1380
+ function serializeRay(obj, state) {
1381
+ const refs = resolveRefs([obj.attrs.origin, obj.attrs.through], state);
1382
+ if (!refs) return fail("unresolved-ref", `${obj.attrs.origin},${obj.attrs.through}`);
1383
+ return { ok: true, entity: { name: obj.label, kind: "ray", origin: refs[0], through: refs[1] } };
1384
+ }
1385
+ function serializeLine(obj, state) {
1386
+ const a = obj.attrs;
1387
+ const c = a.construction;
1388
+ if (!c) {
1389
+ if (!a.p1 || !a.p2) return fail("unsupported-construction", "missing p1/p2");
1390
+ const refs = resolveRefs([a.p1, a.p2], state);
1391
+ if (!refs) return fail("unresolved-ref", `${a.p1},${a.p2}`);
1392
+ return { ok: true, entity: { name: obj.label, kind: "line", p1: refs[0], p2: refs[1] } };
1393
+ }
1394
+ switch (c.kind) {
1395
+ case "perpendicular":
1396
+ case "parallel": {
1397
+ const refs = resolveRefs([c.throughPoint, c.toLine], state);
1398
+ if (!refs) return fail("unresolved-ref", `${c.throughPoint},${c.toLine}`);
1399
+ return {
1400
+ ok: true,
1401
+ entity: { name: obj.label, kind: c.kind, throughPoint: refs[0], toLine: refs[1] }
1402
+ };
1403
+ }
1404
+ case "perpBisector": {
1405
+ const refs = resolveRefs([c.p1, c.p2], state);
1406
+ if (!refs) return fail("unresolved-ref", `${c.p1},${c.p2}`);
1407
+ return { ok: true, entity: { name: obj.label, kind: "perpBisector", p1: refs[0], p2: refs[1] } };
1408
+ }
1409
+ case "angleBisector": {
1410
+ const refs = resolveRefs([c.p1, c.vertex, c.p2], state);
1411
+ if (!refs) return fail("unresolved-ref", `${c.p1},${c.vertex},${c.p2}`);
1412
+ return {
1413
+ ok: true,
1414
+ entity: {
1415
+ name: obj.label,
1416
+ kind: "angleBisector",
1417
+ p1: refs[0],
1418
+ vertex: refs[1],
1419
+ p2: refs[2]
1420
+ }
1421
+ };
1422
+ }
1423
+ case "tangent": {
1424
+ const refs = resolveRefs([c.throughPoint, c.toCircle], state);
1425
+ if (!refs) return fail("unresolved-ref", `${c.throughPoint},${c.toCircle}`);
1426
+ const entity = c.branch !== void 0 ? {
1427
+ name: obj.label,
1428
+ kind: "tangent",
1429
+ throughPoint: refs[0],
1430
+ toCircle: refs[1],
1431
+ branch: c.branch
1432
+ } : { name: obj.label, kind: "tangent", throughPoint: refs[0], toCircle: refs[1] };
1433
+ return { ok: true, entity };
1434
+ }
1435
+ case "lineThrough": {
1436
+ const refs = resolveRefs(c.points, state);
1437
+ if (!refs) return fail("unresolved-ref", c.points.join(","));
1438
+ return { ok: true, entity: { name: obj.label, kind: "lineThrough", points: refs } };
1439
+ }
1440
+ case "radicalAxis": {
1441
+ const refs = resolveRefs([c.circle1, c.circle2], state);
1442
+ if (!refs) return fail("unresolved-ref", `${c.circle1},${c.circle2}`);
1443
+ return { ok: true, entity: { name: obj.label, kind: "radicalAxis", circle1: refs[0], circle2: refs[1] } };
1444
+ }
1445
+ case "angleBisectorLines":
1446
+ return fail("unsupported-construction", "angleBisectorLines");
1447
+ default: {
1448
+ return fail("unsupported-construction");
1449
+ }
1450
+ }
1451
+ }
1452
+ function serializePolygon(obj, state) {
1453
+ const a = obj.attrs;
1454
+ if (a.construction) return fail("unsupported-construction", a.construction.kind);
1455
+ if (!Array.isArray(a.vertices) || a.vertices.length < 3) {
1456
+ return fail("unsupported-construction", "missing vertices");
1457
+ }
1458
+ const refs = resolveRefs(a.vertices, state);
1459
+ if (!refs) return fail("unresolved-ref", a.vertices.join(","));
1460
+ return { ok: true, entity: { name: obj.label, kind: "polygon", vertices: refs } };
1461
+ }
1462
+ function serializeCircle(obj, state) {
1463
+ const a = obj.attrs;
1464
+ const c = (() => {
1465
+ if (a.construction) return a.construction;
1466
+ const raw = a;
1467
+ if (raw.kind === "incircle" && raw.vertices) {
1468
+ return {
1469
+ kind: "incircle",
1470
+ p1: raw.vertices[0],
1471
+ p2: raw.vertices[1],
1472
+ p3: raw.vertices[2]
1473
+ };
1474
+ }
1475
+ if (raw.kind === "excircle" && raw.vertices && raw.opposite) {
1476
+ return {
1477
+ kind: "excircle",
1478
+ p1: raw.vertices[0],
1479
+ p2: raw.vertices[1],
1480
+ p3: raw.vertices[2],
1481
+ opposite: raw.opposite
1482
+ };
1483
+ }
1484
+ if (raw.kind === "circleDiameter" && raw.p1 && raw.p2) {
1485
+ return {
1486
+ kind: "diameter",
1487
+ p1: raw.p1,
1488
+ p2: raw.p2
1489
+ };
1490
+ }
1491
+ return void 0;
1492
+ })();
1493
+ if (!c) {
1494
+ if (typeof a.radius === "number") {
1495
+ if (!a.center) return fail("unsupported-construction", "missing center");
1496
+ const refs2 = resolveRefs([a.center], state);
1497
+ if (!refs2) return fail("unresolved-ref", `${a.center}`);
1498
+ return {
1499
+ ok: true,
1500
+ entity: { name: obj.label, kind: "circleCR", center: refs2[0], radius: a.radius }
1501
+ };
1502
+ }
1503
+ if (!a.center || !a.surfacePoint) {
1504
+ return fail("unsupported-construction", "missing center/surfacePoint");
1505
+ }
1506
+ const refs = resolveRefs([a.center, a.surfacePoint], state);
1507
+ if (!refs) return fail("unresolved-ref", `${a.center},${a.surfacePoint}`);
1508
+ return {
1509
+ ok: true,
1510
+ entity: { name: obj.label, kind: "circleCP", center: refs[0], surfacePoint: refs[1] }
1511
+ };
1512
+ }
1513
+ if (c.kind === "circumscribed") {
1514
+ const refs = resolveRefs([c.p1, c.p2, c.p3], state);
1515
+ if (!refs) return fail("unresolved-ref", `${c.p1},${c.p2},${c.p3}`);
1516
+ return {
1517
+ ok: true,
1518
+ entity: { name: obj.label, kind: "circle3", p1: refs[0], p2: refs[1], p3: refs[2] }
1519
+ };
1520
+ }
1521
+ if (c.kind === "incircle") {
1522
+ const refs = resolveRefs([c.p1, c.p2, c.p3], state);
1523
+ if (!refs) return fail("unresolved-ref", `${c.p1},${c.p2},${c.p3}`);
1524
+ return {
1525
+ ok: true,
1526
+ entity: { name: obj.label, kind: "incircle", vertices: [refs[0], refs[1], refs[2]] }
1527
+ };
1528
+ }
1529
+ if (c.kind === "excircle") {
1530
+ const refs = resolveRefs([c.p1, c.p2, c.p3, c.opposite], state);
1531
+ if (!refs) return fail("unresolved-ref", `${c.p1},${c.p2},${c.p3},${c.opposite}`);
1532
+ return {
1533
+ ok: true,
1534
+ entity: { name: obj.label, kind: "excircle", vertices: [refs[0], refs[1], refs[2]], opposite: refs[3] }
1535
+ };
1536
+ }
1537
+ if (c.kind === "diameter") {
1538
+ const refs = resolveRefs([c.p1, c.p2], state);
1539
+ if (!refs) return fail("unresolved-ref", `${c.p1},${c.p2}`);
1540
+ return {
1541
+ ok: true,
1542
+ entity: { name: obj.label, kind: "circleDiameter", p1: refs[0], p2: refs[1] }
1543
+ };
1544
+ }
1545
+ return fail("unsupported-construction");
1546
+ }
1547
+ function serializeObject(obj, state) {
1548
+ if (!isValidName(obj.label)) {
1549
+ return fail("invalid-label", obj.label);
1550
+ }
1551
+ switch (obj.kind) {
1552
+ case "point":
1553
+ return serializePoint(obj, state);
1554
+ case "segment":
1555
+ return serializeSegment(obj, state);
1556
+ case "ray":
1557
+ return serializeRay(obj, state);
1558
+ case "line":
1559
+ return serializeLine(obj, state);
1560
+ case "polygon":
1561
+ return serializePolygon(obj, state);
1562
+ case "circle":
1563
+ return serializeCircle(obj, state);
1564
+ case "intersection":
1565
+ return serializeIntersection(obj, state);
1566
+ default:
1567
+ return fail("unsupported-kind", obj.kind);
1568
+ }
1569
+ }
1570
+ function serializeState(state) {
1571
+ const points = [];
1572
+ const shapes = [];
1573
+ const unsupported = [];
1574
+ for (const id of state.order) {
1575
+ const obj = state.objects[id];
1576
+ if (!obj) continue;
1577
+ const r = serializeObject(obj, state);
1578
+ if (!r.ok) {
1579
+ unsupported.push({
1580
+ id: obj.id,
1581
+ label: obj.label,
1582
+ kind: obj.kind,
1583
+ reason: r.reason,
1584
+ detail: r.detail
1585
+ });
1586
+ continue;
1587
+ }
1588
+ if (POINT_KINDS.has(r.entity.kind)) {
1589
+ points.push(r.entity);
1590
+ } else {
1591
+ shapes.push(r.entity);
1592
+ }
1593
+ }
1594
+ return {
1595
+ dsl: { version: 1, points, shapes },
1596
+ unsupported
1597
+ };
1598
+ }
1599
+
1600
+ // src/stamps/geometry-2d/dsl/describeDsl.ts
1601
+ function describeEntity(e) {
1602
+ switch (e.kind) {
1603
+ case "free":
1604
+ return `${e.name} = (${e.x}, ${e.y})`;
1605
+ case "midpoint":
1606
+ return `${e.name} = trung \u0111i\u1EC3m ${e.p1}${e.p2}`;
1607
+ case "onSegment":
1608
+ return `${e.name} \u2208 \u0111o\u1EA1n ${e.segmentId} (t = ${e.t})`;
1609
+ case "onLine":
1610
+ return `${e.name} \u2208 \u0111\u01B0\u1EDDng ${e.lineId} (t = ${e.t})`;
1611
+ case "onCircle":
1612
+ return `${e.name} \u2208 \u0111\u01B0\u1EDDng tr\xF2n ${e.circleId} (\u03B8 = ${e.theta})`;
1613
+ case "perpFoot":
1614
+ return `${e.name} = ch\xE2n vu\xF4ng g\xF3c t\u1EEB ${e.from} xu\u1ED1ng ${e.onLine}`;
1615
+ case "circumcenter":
1616
+ return `${e.name} = t\xE2m ngo\u1EA1i ti\u1EBFp ${e.vertices.join("")}`;
1617
+ case "incenter":
1618
+ return `${e.name} = t\xE2m n\u1ED9i ti\u1EBFp ${e.vertices.join("")}`;
1619
+ case "centroid":
1620
+ return `${e.name} = tr\u1ECDng t\xE2m ${e.vertices.join("")}`;
1621
+ case "orthocenter":
1622
+ return `${e.name} = tr\u1EF1c t\xE2m ${e.vertices.join("")}`;
1623
+ case "intersection": {
1624
+ const branch = "branch" in e && e.branch !== void 0 ? ` (nh\xE1nh ${e.branch})` : "";
1625
+ return `${e.name} = ${e.ref1} \u2229 ${e.ref2}${branch}`;
1626
+ }
1627
+ case "segment":
1628
+ return `${e.name} = \u0111o\u1EA1n ${e.p1}${e.p2}`;
1629
+ case "line":
1630
+ return `${e.name} = \u0111\u01B0\u1EDDng th\u1EB3ng ${e.p1}${e.p2}`;
1631
+ case "ray":
1632
+ return `${e.name} = tia ${e.origin}${e.through}`;
1633
+ case "polygon":
1634
+ return `${e.name} = \u0111a gi\xE1c ${e.vertices.join("")}`;
1635
+ case "perpendicular":
1636
+ return `${e.name} \u27C2 ${e.toLine} qua ${e.throughPoint}`;
1637
+ case "parallel":
1638
+ return `${e.name} \u2225 ${e.toLine} qua ${e.throughPoint}`;
1639
+ case "perpBisector":
1640
+ return `${e.name} = trung tr\u1EF1c ${e.p1}${e.p2}`;
1641
+ case "angleBisector":
1642
+ return `${e.name} = ph\xE2n gi\xE1c \u2220${e.p1}${e.vertex}${e.p2}`;
1643
+ case "lineThrough":
1644
+ return `${e.name} = \u0111\u01B0\u1EDDng qua ${e.points.join("")}`;
1645
+ case "radicalAxis":
1646
+ return `${e.name} = tr\u1EE5c \u0111\u1EB3ng ph\u01B0\u01A1ng ${e.circle1} & ${e.circle2}`;
1647
+ case "tangent": {
1648
+ const branch = "branch" in e && e.branch !== void 0 ? ` (nh\xE1nh ${e.branch})` : "";
1649
+ return `${e.name} = ti\u1EBFp tuy\u1EBFn ${e.toCircle} qua ${e.throughPoint}${branch}`;
1650
+ }
1651
+ case "circleCP":
1652
+ return `${e.name} = (${e.center}; ${e.center}${e.surfacePoint})`;
1653
+ case "circle3":
1654
+ return `${e.name} = \u0111\u01B0\u1EDDng tr\xF2n qua ${e.p1}${e.p2}${e.p3}`;
1655
+ case "circleDiameter":
1656
+ return `${e.name} = \u0111\u01B0\u1EDDng tr\xF2n \u0111\u01B0\u1EDDng k\xEDnh ${e.p1}${e.p2}`;
1657
+ // NEW Tier 4+5
1658
+ case "secondIntersection":
1659
+ return `${e.name} = giao th\u1EE9 hai c\u1EE7a ${e.line} v\xE0 ${e.circle} (kh\xE1c ${e.other})`;
1660
+ case "circleIntersection":
1661
+ return `${e.name} = giao ${e.c1} \u2229 ${e.c2} (nh\xE1nh ${e.which})`;
1662
+ case "circleSecondIntersection":
1663
+ return `${e.name} = giao th\u1EE9 hai ${e.c1} \u2229 ${e.c2} (kh\xE1c ${e.exclude})`;
1664
+ case "tangencyPoint":
1665
+ return `${e.name} = ti\u1EBFp \u0111i\u1EC3m c\u1EE7a ${e.circle} tr\xEAn ${e.onLine}`;
1666
+ case "tangentPointExt":
1667
+ return `${e.name} = ti\u1EBFp \u0111i\u1EC3m t\u1EEB ${e.from} l\xEAn ${e.circle} (nh\xE1nh ${e.which})`;
1668
+ case "circleCR":
1669
+ return `${e.name} = \u0111\u01B0\u1EDDng tr\xF2n (${e.center}; r=${e.radius})`;
1670
+ case "incircle":
1671
+ return `${e.name} = \u0111\u01B0\u1EDDng tr\xF2n n\u1ED9i ti\u1EBFp ${e.vertices.join("")}`;
1672
+ case "excircle":
1673
+ return `${e.name} = \u0111\u01B0\u1EDDng tr\xF2n b\xE0ng ti\u1EBFp ${e.vertices.join("")} \u0111\u1ED1i di\u1EC7n ${e.opposite}`;
1674
+ // Cụm A
1675
+ case "arcMidpoint":
1676
+ return `${e.name} = trung \u0111i\u1EC3m cung ${e.a}${e.b} (kh\xF4ng ch\u1EE9a ${e.notContaining}) tr\xEAn ${e.circle}`;
1677
+ case "excenter":
1678
+ return `${e.name} = t\xE2m b\xE0ng ti\u1EBFp ${e.vertices.join("")} \u0111\u1ED1i di\u1EC7n ${e.opposite}`;
1679
+ case "reflectPoint":
1680
+ return `${e.name} = \u0111\u1ED1i x\u1EE9ng ${e.of} qua \u0111i\u1EC3m ${e.through}`;
1681
+ case "reflectLine":
1682
+ return `${e.name} = \u0111\u1ED1i x\u1EE9ng ${e.of} qua \u0111\u01B0\u1EDDng ${e.through}`;
1683
+ // Cụm B
1684
+ case "pointAtDistance": {
1685
+ const d = e.distance;
1686
+ const distStr = d.kind === "circleRadius" ? `r(${d.circle})` : d.kind === "segmentLength" ? `|${d.p1}${d.p2}|` : `${d.value}`;
1687
+ return `${e.name} = \u0111i\u1EC3m tr\xEAn tia ${e.from}${e.through} c\xE1ch ${e.through} m\u1ED9t kho\u1EA3ng ${distStr}`;
1688
+ }
1689
+ default: {
1690
+ return "";
1691
+ }
1692
+ }
1693
+ }
1694
+ function describeDsl(obj, state) {
1695
+ const r = serializeObject(obj, state);
1696
+ if (r.ok) return describeEntity(r.entity);
1697
+ let base;
1698
+ try {
1699
+ base = getKind(obj.kind).describe(obj, state);
1700
+ } catch {
1701
+ base = `${obj.kind} ${obj.label}`;
1702
+ }
1703
+ return `${base} (kh\xF4ng h\u1ED7 tr\u1EE3 DSL)`;
1704
+ }
1705
+
1706
+ export { CIRCLE_KINDS, KIND_REGISTRY, LINE_LIKE_SHAPE_KINDS, MAX_EDGE_PX, MAX_ENCODED_BYTES, MAX_RAW_BYTES, POINT_KINDS, describeDsl, fileToImagePart, inferMediaType, serializeState, validateFile };
1707
+ //# sourceMappingURL=chunk-AJAHD35N.mjs.map
1708
+ //# sourceMappingURL=chunk-AJAHD35N.mjs.map