@xom11/whiteboard 0.29.0 → 0.31.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.
- package/dist/ai.d.mts +259 -33
- package/dist/ai.d.ts +259 -33
- package/dist/ai.js +5424 -470
- package/dist/ai.js.map +1 -1
- package/dist/ai.mjs +4971 -351
- package/dist/ai.mjs.map +1 -1
- package/dist/catalog.json +5 -5
- package/dist/{chunk-V3YJ6JFL.mjs → chunk-44JY2AKC.mjs} +3 -3
- package/dist/{chunk-V3YJ6JFL.mjs.map → chunk-44JY2AKC.mjs.map} +1 -1
- package/dist/{chunk-GEC2D2EQ.mjs → chunk-BMYC2ILT.mjs} +4 -4
- package/dist/{chunk-GEC2D2EQ.mjs.map → chunk-BMYC2ILT.mjs.map} +1 -1
- package/dist/{chunk-PPKHCRRE.mjs → chunk-C76SOFXF.mjs} +3 -3
- package/dist/{chunk-PPKHCRRE.mjs.map → chunk-C76SOFXF.mjs.map} +1 -1
- package/dist/{chunk-IHUFOV7L.mjs → chunk-CH6SFONH.mjs} +15 -3
- package/dist/chunk-CH6SFONH.mjs.map +1 -0
- package/dist/{chunk-E6EDOPGT.mjs → chunk-DWIEVCGK.mjs} +254 -16
- package/dist/chunk-DWIEVCGK.mjs.map +1 -0
- package/dist/{chunk-SZDAS7LK.mjs → chunk-IE2GGHNF.mjs} +131 -81
- package/dist/chunk-IE2GGHNF.mjs.map +1 -0
- package/dist/{chunk-ZTQBUKLJ.mjs → chunk-JJ4FPCBE.mjs} +142 -22
- package/dist/chunk-JJ4FPCBE.mjs.map +1 -0
- package/dist/{chunk-QRUAEXLR.mjs → chunk-K5BS2H56.mjs} +5 -5
- package/dist/{chunk-QRUAEXLR.mjs.map → chunk-K5BS2H56.mjs.map} +1 -1
- package/dist/{chunk-BNBOIDO5.mjs → chunk-K7VJU7LQ.mjs} +3 -3
- package/dist/{chunk-BNBOIDO5.mjs.map → chunk-K7VJU7LQ.mjs.map} +1 -1
- package/dist/{chunk-H22OZYTW.mjs → chunk-KOXOC2FI.mjs} +48 -39
- package/dist/chunk-KOXOC2FI.mjs.map +1 -0
- package/dist/{chunk-CXHNVYMD.mjs → chunk-KWDBVLST.mjs} +5 -5
- package/dist/{chunk-CXHNVYMD.mjs.map → chunk-KWDBVLST.mjs.map} +1 -1
- package/dist/{chunk-OQIQNKPQ.mjs → chunk-LTLLQUMN.mjs} +4 -4
- package/dist/{chunk-OQIQNKPQ.mjs.map → chunk-LTLLQUMN.mjs.map} +1 -1
- package/dist/chunk-QK6OVDLC.mjs +103 -0
- package/dist/chunk-QK6OVDLC.mjs.map +1 -0
- package/dist/{chunk-QGNU34T7.mjs → chunk-QLQ4MJNO.mjs} +10 -4
- package/dist/chunk-QLQ4MJNO.mjs.map +1 -0
- package/dist/{chunk-BU5KLO3P.mjs → chunk-T3N4BSJV.mjs} +4 -4
- package/dist/{chunk-BU5KLO3P.mjs.map → chunk-T3N4BSJV.mjs.map} +1 -1
- package/dist/{chunk-5JM35CXV.mjs → chunk-TMRFSOM7.mjs} +4 -4
- package/dist/{chunk-5JM35CXV.mjs.map → chunk-TMRFSOM7.mjs.map} +1 -1
- package/dist/geometry-2d.d.mts +1 -1
- package/dist/geometry-2d.d.ts +1 -1
- package/dist/geometry-2d.js +841 -204
- package/dist/geometry-2d.js.map +1 -1
- package/dist/geometry-2d.mjs +5 -5
- package/dist/geometry-3d.d.mts +1 -1
- package/dist/geometry-3d.d.ts +1 -1
- package/dist/geometry-3d.js +172 -22
- package/dist/geometry-3d.js.map +1 -1
- package/dist/geometry-3d.mjs +4 -4
- package/dist/graph-2d.d.mts +1 -1
- package/dist/graph-2d.d.ts +1 -1
- package/dist/graph-2d.js +307 -100
- package/dist/graph-2d.js.map +1 -1
- package/dist/graph-2d.mjs +7 -7
- package/dist/handleExtractProblem-BrDY9ifM.d.mts +58 -0
- package/dist/handleExtractProblem-BrDY9ifM.d.ts +58 -0
- package/dist/{host-HOSJHQ5H.mjs → host-4FIUNIDQ.mjs} +13 -12
- package/dist/host-4FIUNIDQ.mjs.map +1 -0
- package/dist/{host-2ISGVO7O.mjs → host-4ZB4XD4S.mjs} +9 -8
- package/dist/host-4ZB4XD4S.mjs.map +1 -0
- package/dist/{host-ZQCDAT6O.mjs → host-H2IGOKJU.mjs} +3 -3
- package/dist/{host-ZQCDAT6O.mjs.map → host-H2IGOKJU.mjs.map} +1 -1
- package/dist/{host-HKMZSCIT.mjs → host-KMWP7KBT.mjs} +286 -74
- package/dist/host-KMWP7KBT.mjs.map +1 -0
- package/dist/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +849 -206
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +22 -21
- package/dist/index.mjs.map +1 -1
- package/dist/latex.d.mts +1 -1
- package/dist/latex.d.ts +1 -1
- package/dist/latex.js +8 -2
- package/dist/latex.js.map +1 -1
- package/dist/latex.mjs +1 -1
- package/dist/render-NMS7OAV6.mjs +10 -0
- package/dist/{render-ZX2O2IK7.mjs.map → render-NMS7OAV6.mjs.map} +1 -1
- package/dist/serialize-PGHQZEPV.mjs +9 -0
- package/dist/{serialize-N4G6RFBB.mjs.map → serialize-PGHQZEPV.mjs.map} +1 -1
- package/dist/{types-C3FjpoUi.d.ts → types-tePd94vW.d.mts} +8 -0
- package/dist/{types-C3FjpoUi.d.mts → types-tePd94vW.d.ts} +8 -0
- package/package.json +2 -1
- package/dist/chunk-E6EDOPGT.mjs.map +0 -1
- package/dist/chunk-H22OZYTW.mjs.map +0 -1
- package/dist/chunk-IHUFOV7L.mjs.map +0 -1
- package/dist/chunk-QGNU34T7.mjs.map +0 -1
- package/dist/chunk-SZDAS7LK.mjs.map +0 -1
- package/dist/chunk-ZTQBUKLJ.mjs.map +0 -1
- package/dist/host-2ISGVO7O.mjs.map +0 -1
- package/dist/host-HKMZSCIT.mjs.map +0 -1
- package/dist/host-HOSJHQ5H.mjs.map +0 -1
- package/dist/render-ZX2O2IK7.mjs +0 -10
- package/dist/serialize-N4G6RFBB.mjs +0 -9
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { collectFreeVars } from './chunk-
|
|
2
|
+
import { readLabelOffset, collectFreeVars } from './chunk-CH6SFONH.mjs';
|
|
3
3
|
import { getKind } from './chunk-B4NJJZFR.mjs';
|
|
4
4
|
|
|
5
5
|
// src/core/scene/render/types2d.ts
|
|
@@ -81,6 +81,7 @@ var JxgRenderer = class {
|
|
|
81
81
|
this.elements.set(obj.id, el);
|
|
82
82
|
this.attachFreePointDragSync(obj, el);
|
|
83
83
|
this.attachGliderDragSync(obj, el);
|
|
84
|
+
this.attachLabelDragSync(obj, el);
|
|
84
85
|
} catch (err) {
|
|
85
86
|
console.warn(`[scene/render/2d] kh\xF4ng render \u0111\u01B0\u1EE3c ${obj.kind} id="${obj.id}":`, err);
|
|
86
87
|
}
|
|
@@ -195,6 +196,54 @@ var JxgRenderer = class {
|
|
|
195
196
|
});
|
|
196
197
|
});
|
|
197
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Cho phép kéo NHÃN của bất kỳ object nào có label (point/line/segment/
|
|
201
|
+
* circle...). Khi user kéo nhãn, JSXGraph cộng dồn vào label.relativeCoords
|
|
202
|
+
* (drag-delta screen px) nhưng KHÔNG đổi attribute offset → vị trí cuối =
|
|
203
|
+
* offset + relativeCoords. Ta gộp lại thành offset thuần (readLabelOffset),
|
|
204
|
+
* zero relativeCoords để khỏi double-count khi update-hook/recreate áp lại
|
|
205
|
+
* offset, rồi dispatch UPDATE_ATTRS { labelOffset }. Right-click → reset.
|
|
206
|
+
*/
|
|
207
|
+
attachLabelDragSync(obj, el) {
|
|
208
|
+
const label = el?.label;
|
|
209
|
+
if (!label || typeof label.on !== "function") return;
|
|
210
|
+
const sceneId = obj.id;
|
|
211
|
+
label.on("up", () => {
|
|
212
|
+
if (this.disposed) return;
|
|
213
|
+
const off = readLabelOffset(label);
|
|
214
|
+
if (!off) return;
|
|
215
|
+
const cur = this.store.getState().objects[sceneId];
|
|
216
|
+
if (!cur) return;
|
|
217
|
+
const prev = cur.attrs.labelOffset;
|
|
218
|
+
if (prev && prev[0] === off[0] && prev[1] === off[1]) return;
|
|
219
|
+
try {
|
|
220
|
+
label.setAttribute({ offset: off });
|
|
221
|
+
if (label.relativeCoords?.scrCoords) {
|
|
222
|
+
label.relativeCoords.scrCoords[1] = 0;
|
|
223
|
+
label.relativeCoords.scrCoords[2] = 0;
|
|
224
|
+
}
|
|
225
|
+
} catch {
|
|
226
|
+
}
|
|
227
|
+
this.store.dispatch({
|
|
228
|
+
type: "UPDATE_ATTRS",
|
|
229
|
+
payload: { id: sceneId, patch: { labelOffset: off } }
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
const node = label.rendNode;
|
|
233
|
+
if (node?.addEventListener) {
|
|
234
|
+
node.addEventListener("contextmenu", (ev) => {
|
|
235
|
+
if (this.disposed) return;
|
|
236
|
+
ev.preventDefault();
|
|
237
|
+
const c = this.store.getState().objects[sceneId];
|
|
238
|
+
if (!c) return;
|
|
239
|
+
if (c.attrs.labelOffset === void 0) return;
|
|
240
|
+
this.store.dispatch({
|
|
241
|
+
type: "UPDATE_ATTRS",
|
|
242
|
+
payload: { id: sceneId, patch: { labelOffset: void 0 } }
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
}
|
|
198
247
|
remove(id) {
|
|
199
248
|
this.removeHalo(id);
|
|
200
249
|
this.selectedIds.delete(id);
|
|
@@ -331,88 +380,89 @@ var JxgRenderer = class {
|
|
|
331
380
|
layer: 4,
|
|
332
381
|
needsRegularUpdate: true
|
|
333
382
|
};
|
|
383
|
+
const elementClass = el.elementClass;
|
|
384
|
+
const elType = el.elType;
|
|
385
|
+
const isPointLike = elementClass === 1 || elType === "point" || elType === "glider" || elType === "intersection";
|
|
386
|
+
const isPolygonLike = Array.isArray(el.vertices) && el.vertices.length >= 3;
|
|
387
|
+
const isCircleLike = elementClass === 3 || elType === "circle" || elType === "circumcircle" || elType === "incircle";
|
|
388
|
+
const isSegment = elType === "segment";
|
|
389
|
+
const isLineLike = elementClass === 2 || elType === "line" || elType === "arrow" || elType === "ray" || elType === "vector" || elType === "tangent" || elType === "normal" || elType === "parallel" || elType === "perpendicular" || elType === "bisector";
|
|
334
390
|
const halos = [];
|
|
335
391
|
try {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
});
|
|
363
|
-
halos.push(halo);
|
|
364
|
-
}
|
|
365
|
-
break;
|
|
366
|
-
}
|
|
367
|
-
case "line":
|
|
368
|
-
case "arrow":
|
|
369
|
-
case "ray":
|
|
370
|
-
case "vector":
|
|
371
|
-
case "tangent":
|
|
372
|
-
case "normal":
|
|
373
|
-
case "parallel":
|
|
374
|
-
case "perpendicular":
|
|
375
|
-
case "bisector": {
|
|
376
|
-
if (el.point1 && el.point2) {
|
|
377
|
-
const halo = board.create("line", [el.point1, el.point2], {
|
|
378
|
-
...haloBase,
|
|
379
|
-
strokeWidth: 9
|
|
380
|
-
});
|
|
381
|
-
halos.push(halo);
|
|
382
|
-
}
|
|
383
|
-
break;
|
|
384
|
-
}
|
|
385
|
-
case "circle": {
|
|
386
|
-
if (el.center && typeof el.Radius === "function") {
|
|
387
|
-
const halo = board.create("circle", [el.center, () => el.Radius?.() ?? 0], {
|
|
388
|
-
...haloBase,
|
|
389
|
-
strokeWidth: 9,
|
|
390
|
-
fillOpacity: 0
|
|
391
|
-
});
|
|
392
|
-
halos.push(halo);
|
|
393
|
-
}
|
|
394
|
-
break;
|
|
395
|
-
}
|
|
396
|
-
case "polygon": {
|
|
397
|
-
if (Array.isArray(el.vertices) && el.vertices.length >= 3) {
|
|
398
|
-
const last = el.vertices.length - 1;
|
|
399
|
-
const verts = el.vertices[last] === el.vertices[0] ? el.vertices.slice(0, last) : el.vertices.slice();
|
|
400
|
-
const halo = board.create("polygon", verts, {
|
|
401
|
-
...haloBase,
|
|
402
|
-
fillOpacity: 0.2,
|
|
403
|
-
borders: {
|
|
404
|
-
strokeColor: SEL_STROKE,
|
|
405
|
-
strokeWidth: 7,
|
|
406
|
-
strokeOpacity: 0.55,
|
|
407
|
-
highlight: false
|
|
408
|
-
}
|
|
409
|
-
});
|
|
410
|
-
halos.push(halo);
|
|
392
|
+
if (isPointLike) {
|
|
393
|
+
const baseSize = el.getAttribute?.("size") ?? 4;
|
|
394
|
+
const halo = board.create("point", [
|
|
395
|
+
() => el.X?.() ?? 0,
|
|
396
|
+
() => el.Y?.() ?? 0
|
|
397
|
+
], {
|
|
398
|
+
...haloBase,
|
|
399
|
+
size: baseSize + 6,
|
|
400
|
+
face: "o",
|
|
401
|
+
strokeWidth: 2,
|
|
402
|
+
strokeOpacity: 0.75,
|
|
403
|
+
fillOpacity: 0.25
|
|
404
|
+
});
|
|
405
|
+
halos.push(halo);
|
|
406
|
+
} else if (isPolygonLike) {
|
|
407
|
+
const verts = el.vertices;
|
|
408
|
+
const last = verts.length - 1;
|
|
409
|
+
const trimmed = verts[last] === verts[0] ? verts.slice(0, last) : verts.slice();
|
|
410
|
+
const halo = board.create("polygon", trimmed, {
|
|
411
|
+
...haloBase,
|
|
412
|
+
fillOpacity: 0.2,
|
|
413
|
+
borders: {
|
|
414
|
+
strokeColor: SEL_STROKE,
|
|
415
|
+
strokeWidth: 7,
|
|
416
|
+
strokeOpacity: 0.55,
|
|
417
|
+
highlight: false
|
|
411
418
|
}
|
|
412
|
-
|
|
419
|
+
});
|
|
420
|
+
halos.push(halo);
|
|
421
|
+
} else if (isCircleLike && el.center && typeof el.Radius === "function") {
|
|
422
|
+
const halo = board.create("circle", [el.center, () => el.Radius?.() ?? 0], {
|
|
423
|
+
...haloBase,
|
|
424
|
+
strokeWidth: 9,
|
|
425
|
+
fillOpacity: 0
|
|
426
|
+
});
|
|
427
|
+
halos.push(halo);
|
|
428
|
+
} else if (isSegment && el.point1 && el.point2) {
|
|
429
|
+
const halo = board.create("segment", [el.point1, el.point2], {
|
|
430
|
+
...haloBase,
|
|
431
|
+
strokeWidth: 9,
|
|
432
|
+
straightFirst: false,
|
|
433
|
+
straightLast: false
|
|
434
|
+
});
|
|
435
|
+
halos.push(halo);
|
|
436
|
+
} else if (isLineLike && el.point1 && el.point2) {
|
|
437
|
+
const halo = board.create("line", [el.point1, el.point2], {
|
|
438
|
+
...haloBase,
|
|
439
|
+
strokeWidth: 9
|
|
440
|
+
});
|
|
441
|
+
halos.push(halo);
|
|
442
|
+
} else if (el.center && (el.radiuspoint ?? el.radiusPoint) && (el.anglepoint ?? el.anglePoint) && (elType === "arc" || elType === "semicircle" || elType === "circumcirclearc" || elType === "sector" || elType === "angle")) {
|
|
443
|
+
const rp = el.radiuspoint ?? el.radiusPoint;
|
|
444
|
+
const ap = el.anglepoint ?? el.anglePoint;
|
|
445
|
+
if (elType === "sector") {
|
|
446
|
+
halos.push(board.create("sector", [el.center, rp, ap], {
|
|
447
|
+
...haloBase,
|
|
448
|
+
strokeWidth: 7,
|
|
449
|
+
fillOpacity: 0.2
|
|
450
|
+
}));
|
|
451
|
+
} else if (elType === "angle") {
|
|
452
|
+
halos.push(board.create("angle", [rp, el.center, ap], {
|
|
453
|
+
...haloBase,
|
|
454
|
+
strokeWidth: 7,
|
|
455
|
+
fillOpacity: 0.2,
|
|
456
|
+
radius: el.getAttribute?.("radius") ?? 1
|
|
457
|
+
}));
|
|
458
|
+
} else {
|
|
459
|
+
halos.push(board.create("arc", [el.center, rp, ap], {
|
|
460
|
+
...haloBase,
|
|
461
|
+
strokeWidth: 9,
|
|
462
|
+
fillColor: "none",
|
|
463
|
+
fillOpacity: 0
|
|
464
|
+
}));
|
|
413
465
|
}
|
|
414
|
-
default:
|
|
415
|
-
break;
|
|
416
466
|
}
|
|
417
467
|
} catch (err) {
|
|
418
468
|
console.warn("[scene/render/2d] halo create fail:", err);
|
|
@@ -422,5 +472,5 @@ var JxgRenderer = class {
|
|
|
422
472
|
};
|
|
423
473
|
|
|
424
474
|
export { DEFAULT_THEME_2D, JxgRenderer };
|
|
425
|
-
//# sourceMappingURL=chunk-
|
|
426
|
-
//# sourceMappingURL=chunk-
|
|
475
|
+
//# sourceMappingURL=chunk-IE2GGHNF.mjs.map
|
|
476
|
+
//# sourceMappingURL=chunk-IE2GGHNF.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/scene/render/types2d.ts","../src/core/scene/render/JxgRenderer.ts"],"names":[],"mappings":"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","file":"chunk-IE2GGHNF.mjs","sourcesContent":["// src/core/scene/render/types2d.ts\nimport type { RenderCtx } from '../types';\n\nexport type Theme2D = {\n stroke: string;\n fill: string;\n label: string;\n axis: string;\n grid: string;\n pointFill: string;\n};\n\nexport const DEFAULT_THEME_2D: Theme2D = {\n stroke: '#0f172a',\n fill: '#60a5fa',\n label: '#0f172a',\n axis: '#94a3b8',\n grid: '#e2e8f0',\n pointFill: '#1e40af',\n};\n\nexport type RenderCtx2D = RenderCtx & {\n theme: Theme2D;\n};\n","// src/core/scene/render/JxgRenderer.ts\nimport type { Store } from '../store';\nimport type { State, SceneObject, RenderCtx } from '../types';\nimport { getKind } from '../registry';\nimport { DEFAULT_THEME_2D, type Theme2D } from './types2d';\nimport { collectFreeVars } from '../expressions/parser';\nimport { readLabelOffset } from '../kinds/_label';\n\nexport type JxgRendererOptions = { theme?: Theme2D };\n\nexport class JxgRenderer {\n private board: unknown;\n private store: Store;\n private theme: Theme2D;\n private elements = new Map<string, unknown>();\n private unsubscribe: () => void;\n private disposed = false;\n\n /** Chỉ dùng cho domain='graph2d': parameter.label → parameter.value */\n private paramMap: Record<string, number> = {};\n /** Chỉ dùng cho domain='graph2d': function2d.id → expression string */\n private functionExpr: Record<string, string> = {};\n\n constructor(store: Store, board: unknown, options: JxgRendererOptions = {}) {\n this.store = store;\n this.board = board;\n this.theme = options.theme ?? DEFAULT_THEME_2D;\n this.unsubscribe = store.subscribe((next, prev) => this.applyDiff(prev, next));\n // Render state hiện tại (vd LOAD chạy trước khi subscribe).\n this.applyDiff(undefined, store.getState());\n }\n\n private ctx(): RenderCtx {\n return {\n jxg: this.board,\n resolveRef: (id: string) => {\n // Synthetic \"<polyId>:border:<N>\" → polygon.borders[N]. Polygon edges\n // là sub-segment do JSXGraph auto-tạo bên trong polygon; chúng không\n // có scene id riêng. Synthetic id cho phép construct tools (vd\n // perpendicular qua một cạnh đa giác) tham chiếu cạnh như một line.\n const m = /^(.+):border:(\\d+)$/.exec(id);\n if (m) {\n const poly = this.elements.get(m[1]) as { borders?: unknown[] } | undefined;\n if (!poly) throw new Error(`[scene/2d] resolveRef: chưa render polygon id=\"${m[1]}\"`);\n const borders = poly.borders;\n const idx = parseInt(m[2], 10);\n if (!Array.isArray(borders) || !borders[idx]) {\n throw new Error(`[scene/2d] resolveRef: polygon \"${m[1]}\" không có border[${idx}]`);\n }\n return borders[idx];\n }\n const el = this.elements.get(id);\n if (!el) throw new Error(`[scene/2d] resolveRef: chưa render id=\"${id}\"`);\n return el;\n },\n defaults: { theme: this.theme, _functionExpr: this.functionExpr },\n paramMap: this.paramMap,\n };\n }\n\n /**\n * Rebuild `paramMap` và `functionExpr` từ state hiện tại.\n * Chỉ chạy khi domain='graph2d'. Chi phí thấp vì parameters thường ≤ 8.\n */\n private rebuildGraphMaps(state: State): void {\n if (state.meta.domain !== 'graph2d') return;\n const params: Record<string, number> = {};\n const fns: Record<string, string> = {};\n for (const id of state.order) {\n const obj = state.objects[id];\n if (obj.kind === 'parameter') {\n params[obj.label] = (obj.attrs as { value: number }).value;\n } else if (obj.kind === 'function2d') {\n fns[obj.id] = (obj.attrs as { expression: string }).expression;\n }\n }\n this.paramMap = params;\n this.functionExpr = fns;\n }\n\n private create(obj: SceneObject): void {\n try {\n const def = getKind(obj.kind);\n const el = def.render(obj, this.ctx());\n this.elements.set(obj.id, el);\n this.attachFreePointDragSync(obj, el);\n this.attachGliderDragSync(obj, el);\n this.attachLabelDragSync(obj, el);\n } catch (err) {\n console.warn(`[scene/render/2d] không render được ${obj.kind} id=\"${obj.id}\":`, err);\n }\n }\n\n /**\n * Sync vị trí glider của 3 constraint mới (onPerpendicular / onPerpBisector /\n * onCircleAroundPoint) — kéo → recompute t/theta dựa trên ref points + vị trí\n * glider hiện tại → UPDATE_ATTRS. Không đụng onLine/onSegment/onCircle hiện\n * tại (giữ behavior cũ).\n */\n private attachGliderDragSync(obj: SceneObject, el: unknown): void {\n if (obj.kind !== 'point') return;\n const c = (obj.attrs as { constraint?: { kind?: string } }).constraint as\n | { kind: string; [k: string]: unknown }\n | undefined;\n if (!c) return;\n if (\n c.kind !== 'onPerpendicular' &&\n c.kind !== 'onPerpBisector' &&\n c.kind !== 'onCircleAroundPoint'\n ) {\n return;\n }\n\n const point = el as { on?: (ev: string, cb: () => void) => void; X?: () => number; Y?: () => number };\n if (typeof point.on !== 'function') return;\n const sceneId = obj.id;\n\n point.on('up', () => {\n if (this.disposed) return;\n const cur = this.store.getState().objects[sceneId];\n if (!cur) return;\n const curC = (cur.attrs as { constraint?: { kind?: string; [k: string]: unknown } }).constraint as\n | { kind: string; [k: string]: unknown }\n | undefined;\n if (!curC) return;\n if (typeof point.X !== 'function' || typeof point.Y !== 'function') return;\n const x = point.X();\n const y = point.Y();\n if (!Number.isFinite(x) || !Number.isFinite(y)) return;\n\n if (curC.kind === 'onPerpendicular') {\n const T = this.elements.get(curC.through as string) as { X: () => number; Y: () => number } | null;\n const A = this.elements.get(curC.perpToA as string) as { X: () => number; Y: () => number } | null;\n const B = this.elements.get(curC.perpToB as string) as { X: () => number; Y: () => number } | null;\n if (!T || !A || !B) return;\n const dx = B.X() - A.X();\n const dy = B.Y() - A.Y();\n const len = Math.hypot(dx, dy) || 1;\n const ux = -dy / len;\n const uy = dx / len;\n const newT = (x - T.X()) * ux + (y - T.Y()) * uy;\n if (Math.abs(newT - (curC.t as number)) < 1e-9) return;\n this.store.dispatch({\n type: 'UPDATE_ATTRS',\n payload: { id: sceneId, patch: { constraint: { ...curC, t: newT } } },\n });\n return;\n }\n\n if (curC.kind === 'onPerpBisector') {\n const A = this.elements.get(curC.p1 as string) as { X: () => number; Y: () => number } | null;\n const B = this.elements.get(curC.p2 as string) as { X: () => number; Y: () => number } | null;\n if (!A || !B) return;\n const Mx = (A.X() + B.X()) / 2;\n const My = (A.Y() + B.Y()) / 2;\n const dx = B.X() - A.X();\n const dy = B.Y() - A.Y();\n const len = Math.hypot(dx, dy) || 1;\n const ux = -dy / len;\n const uy = dx / len;\n const newT = (x - Mx) * ux + (y - My) * uy;\n if (Math.abs(newT - (curC.t as number)) < 1e-9) return;\n this.store.dispatch({\n type: 'UPDATE_ATTRS',\n payload: { id: sceneId, patch: { constraint: { ...curC, t: newT } } },\n });\n return;\n }\n\n if (curC.kind === 'onCircleAroundPoint') {\n const C = this.elements.get(curC.center as string) as { X: () => number; Y: () => number } | null;\n if (!C) return;\n const newTheta = Math.atan2(y - C.Y(), x - C.X());\n if (Math.abs(newTheta - (curC.theta as number)) < 1e-9) return;\n this.store.dispatch({\n type: 'UPDATE_ATTRS',\n payload: { id: sceneId, patch: { constraint: { ...curC, theta: newTheta } } },\n });\n return;\n }\n });\n }\n\n /**\n * Đồng bộ toạ độ live của free point về scene.constraint khi user kéo bằng\n * tay (Move tool / mobile drag). JSXGraph mutate obj.X()/Y() ngay nhưng\n * constraint vẫn giữ giá trị lúc tạo → serialize sẽ ra SVG y hệt cũ →\n * fileId SHA-256 trùng → Excalidraw bỏ qua refresh. (Regression từ\n * commit f41f366 sau scene v2 port.)\n *\n * Chỉ áp dụng cho free point — glider/intersection/midpoint không drag được\n * trực tiếp (toạ độ derived từ ref khác).\n */\n private attachFreePointDragSync(obj: SceneObject, el: unknown): void {\n if (obj.kind !== 'point') return;\n const c = (obj.attrs as { constraint?: { kind?: string } }).constraint;\n if (!c || c.kind !== 'free') return;\n \n const point = el as any;\n if (typeof point.on !== 'function') return;\n const sceneId = obj.id;\n point.on('up', () => {\n if (this.disposed) return;\n if (typeof point.X !== 'function' || typeof point.Y !== 'function') return;\n const x = point.X();\n const y = point.Y();\n if (!Number.isFinite(x) || !Number.isFinite(y)) return;\n const cur = this.store.getState().objects[sceneId];\n if (!cur) return;\n const curC = (cur.attrs as { constraint?: { kind?: string; x?: number; y?: number } }).constraint;\n if (!curC || curC.kind !== 'free') return;\n if (curC.x === x && curC.y === y) return;\n this.store.dispatch({\n type: 'UPDATE_ATTRS',\n payload: { id: sceneId, patch: { constraint: { kind: 'free', x, y } } },\n });\n });\n }\n\n /**\n * Cho phép kéo NHÃN của bất kỳ object nào có label (point/line/segment/\n * circle...). Khi user kéo nhãn, JSXGraph cộng dồn vào label.relativeCoords\n * (drag-delta screen px) nhưng KHÔNG đổi attribute offset → vị trí cuối =\n * offset + relativeCoords. Ta gộp lại thành offset thuần (readLabelOffset),\n * zero relativeCoords để khỏi double-count khi update-hook/recreate áp lại\n * offset, rồi dispatch UPDATE_ATTRS { labelOffset }. Right-click → reset.\n */\n private attachLabelDragSync(obj: SceneObject, el: unknown): void {\n\n const label = (el as { label?: any })?.label;\n if (!label || typeof label.on !== 'function') return;\n const sceneId = obj.id;\n\n label.on('up', () => {\n if (this.disposed) return;\n const off = readLabelOffset(label);\n if (!off) return;\n const cur = this.store.getState().objects[sceneId];\n if (!cur) return;\n const prev = (cur.attrs as { labelOffset?: [number, number] }).labelOffset;\n if (prev && prev[0] === off[0] && prev[1] === off[1]) return;\n // Gộp drag-delta vào offset thuần + zero relativeCoords → vị trí không đổi,\n // tránh double-count khi update-hook/recreate áp lại offset.\n try {\n label.setAttribute({ offset: off });\n if (label.relativeCoords?.scrCoords) {\n label.relativeCoords.scrCoords[1] = 0;\n label.relativeCoords.scrCoords[2] = 0;\n }\n } catch { /* ignore */ }\n this.store.dispatch({\n type: 'UPDATE_ATTRS',\n payload: { id: sceneId, patch: { labelOffset: off } },\n });\n });\n\n // Reset bằng chuột phải trên nhãn.\n const node = (label as { rendNode?: { addEventListener?: (e: string, cb: (ev: Event) => void) => void } }).rendNode;\n if (node?.addEventListener) {\n node.addEventListener('contextmenu', (ev: Event) => {\n if (this.disposed) return;\n ev.preventDefault();\n const c = this.store.getState().objects[sceneId];\n if (!c) return;\n if ((c.attrs as { labelOffset?: unknown }).labelOffset === undefined) return;\n this.store.dispatch({\n type: 'UPDATE_ATTRS',\n payload: { id: sceneId, patch: { labelOffset: undefined } },\n });\n });\n }\n }\n\n private remove(id: string): void {\n // Selection halo (nếu có) phải bị xoá TRƯỚC element gốc — halo tham chiếu\n // tới point1/point2/center/vertices của element gốc qua lambda; xoá element\n // gốc trước sẽ làm halo dangling.\n this.removeHalo(id);\n this.selectedIds.delete(id);\n const el = this.elements.get(id);\n if (!el) return;\n try {\n const helpers = (el as Record<string, unknown>)._helpers;\n // Element chính bị remove trước, sau đó helpers (glider phụ trợ cho\n // tangent ...). Helpers thường là parent của element chính — nếu xoá\n // parent trước, JSXGraph có thể phàn nàn dangling reference.\n (this.board as { removeObject?: (e: unknown) => void }).removeObject?.(el);\n if (Array.isArray(helpers)) {\n for (const h of helpers) {\n try {\n (this.board as { removeObject?: (e: unknown) => void }).removeObject?.(h);\n } catch { /* ignore */ }\n }\n }\n } catch (err) {\n console.warn(`[scene/render/2d] không remove được id=\"${id}\":`, err);\n }\n this.elements.delete(id);\n }\n\n private applyDiff(prev: State | undefined, next: State): void {\n if (this.disposed) return;\n\n // Rebuild paramMap + functionExpr TRƯỚC khi diff, để ctx() có đúng\n // paramMap khi render lần đầu tiên.\n this.rebuildGraphMaps(next);\n\n const prevObjs = prev?.objects ?? {};\n const nextObjs = next.objects;\n\n // Xoá ids biến mất.\n for (const id of Object.keys(prevObjs)) {\n if (!(id in nextObjs)) this.remove(id);\n }\n\n // Thêm/cập nhật theo state.order — đảm bảo refs có trước.\n for (const id of next.order) {\n const cur = nextObjs[id];\n const old = prevObjs[id] as SceneObject | undefined;\n if (!old) {\n this.create(cur);\n continue;\n }\n if (Object.is(old, cur)) continue;\n const def = getKind(cur.kind);\n const existing = this.elements.get(id);\n if (def.update && existing) {\n try { def.update(cur, old, this.ctx(), existing); continue; }\n catch (err) { console.warn(`[scene/render/2d] update fail, recreate:`, err); }\n }\n this.remove(id);\n this.create(cur);\n }\n\n // Sau diff bình thường: nếu domain='graph2d', detect parameter value changes\n // và force re-render các function2d phụ thuộc.\n if (next.meta.domain === 'graph2d' && prev) {\n const changedParams = new Set<string>();\n for (const id of next.order) {\n const cur = next.objects[id];\n const old = prev.objects[id] as SceneObject | undefined;\n if (cur.kind !== 'parameter' || old?.kind !== 'parameter') continue;\n if ((cur.attrs as { value: number }).value !== (old.attrs as { value: number }).value) {\n changedParams.add(cur.label);\n }\n }\n if (changedParams.size > 0) {\n for (const id of next.order) {\n const obj = next.objects[id];\n if (obj.kind !== 'function2d') continue;\n const expr = (obj.attrs as { expression: string }).expression;\n const refs = collectFreeVars(expr);\n if (refs.some((r) => changedParams.has(r))) {\n this.remove(id);\n this.create(obj);\n }\n }\n }\n }\n }\n\n dispose(): void {\n if (this.disposed) return;\n this.unsubscribe();\n for (const id of Array.from(this.elements.keys())) this.remove(id);\n this.disposed = true;\n }\n\n /** Return the rendered JSXGraph element for a scene id, or null if not found. */\n getElement(id: string): unknown {\n return this.elements.get(id) ?? null;\n }\n\n /** Return a read-only view of the scene id → JSXGraph element map (for hit-test). */\n listElements(): Map<string, unknown> {\n return this.elements;\n }\n\n // Selection halo overlay model: thay vì đổi màu element gốc thành đỏ, tạo\n // một halo element gray phía sau (lower layer) → giữ nguyên màu gốc. Hỗ\n // trợ multi-select cho cả canvas click-selection lẫn ObjectListPanel.\n private selectedIds: Set<string> = new Set();\n private haloMap: Map<string, unknown[]> = new Map();\n\n highlight(ids: string | string[] | null): void {\n if (this.disposed) return;\n const newIds = new Set<string>(\n ids == null ? [] : Array.isArray(ids) ? ids : [ids],\n );\n // Remove halos cho ids đã bị bỏ chọn.\n for (const id of this.selectedIds) {\n if (!newIds.has(id)) this.removeHalo(id);\n }\n // Add halos cho ids mới chọn.\n for (const id of newIds) {\n if (!this.selectedIds.has(id) && this.elements.has(id)) this.addHalo(id);\n }\n this.selectedIds = newIds;\n try {\n (this.board as { update?: () => void }).update?.();\n } catch { /* ignore */ }\n }\n\n private removeHalo(id: string): void {\n const halos = this.haloMap.get(id);\n if (!halos) return;\n const board = this.board as { removeObject?: (e: unknown) => void };\n for (const h of halos) {\n try { board.removeObject?.(h); } catch { /* ignore */ }\n }\n this.haloMap.delete(id);\n }\n\n private addHalo(id: string): void {\n const el = this.elements.get(id) as\n | {\n elType?: string;\n getAttribute?: (k: string) => unknown;\n X?: () => number;\n Y?: () => number;\n point1?: unknown;\n point2?: unknown;\n center?: unknown;\n Radius?: () => number;\n vertices?: unknown[];\n // Arc: radiuspoint/anglepoint (chữ thường). Sector/Angle: radiusPoint/\n // anglePoint (chữ P HOA). center dùng chung.\n radiuspoint?: unknown;\n anglepoint?: unknown;\n radiusPoint?: unknown;\n anglePoint?: unknown;\n }\n | undefined;\n if (!el) return;\n const board = this.board as {\n create?: (kind: string, parents: unknown[], attrs?: unknown) => unknown;\n };\n if (!board.create) return;\n\n // Selection palette — gray fill + darker gray border (xem\n // tham chiếu /tmp/ss.png).\n const SEL_STROKE = '#475569'; // slate-600\n const SEL_FILL = '#cbd5e1'; // slate-300\n const haloBase = {\n strokeColor: SEL_STROKE,\n strokeOpacity: 0.55,\n fillColor: SEL_FILL,\n fillOpacity: 0.3,\n fixed: true,\n withLabel: false,\n name: '',\n highlight: false,\n layer: 4,\n needsRegularUpdate: true,\n };\n // Phân loại element bằng `elementClass` (JSXGraph constant: 1=POINT, 2=LINE,\n // 3=CIRCLE) — set ĐÚNG cho MỌI cách dựng phái sinh (circumcenter,\n // otherintersection, perpendicularpoint, circumcircle, incircle, perpendicular,\n // parallel, tangent...). elType string KHÔNG đủ vì mỗi construction 1 tên riêng\n // → trước đây O/M/N/D/E/F (điểm), O_c (circumcircle), square (regularpolygon)\n // bị bỏ halo. Đồng bộ với objKind (tools.tsx). Polygon nhận qua `vertices`\n // (elementClass polygon ≠ 1/2/3). Fallback elType cho test mock không set class.\n const elementClass = (el as { elementClass?: number }).elementClass;\n const elType = el.elType;\n const isPointLike =\n elementClass === 1 || elType === 'point' || elType === 'glider' || elType === 'intersection';\n const isPolygonLike = Array.isArray(el.vertices) && el.vertices.length >= 3;\n const isCircleLike =\n elementClass === 3 || elType === 'circle' || elType === 'circumcircle' || elType === 'incircle';\n const isSegment = elType === 'segment';\n const isLineLike =\n elementClass === 2 ||\n elType === 'line' || elType === 'arrow' || elType === 'ray' || elType === 'vector' ||\n elType === 'tangent' || elType === 'normal' || elType === 'parallel' ||\n elType === 'perpendicular' || elType === 'bisector';\n\n const halos: unknown[] = [];\n try {\n if (isPointLike) {\n const baseSize = (el.getAttribute?.('size') as number | undefined) ?? 4;\n const halo = board.create('point', [\n () => el.X?.() ?? 0,\n () => el.Y?.() ?? 0,\n ], {\n ...haloBase,\n size: baseSize + 6,\n face: 'o',\n strokeWidth: 2,\n strokeOpacity: 0.75,\n fillOpacity: 0.25,\n });\n halos.push(halo);\n } else if (isPolygonLike) {\n // JSXGraph polygon.vertices có thể append vertex đầu lặp lại ở cuối để\n // đóng path — trim cho an toàn. Bắt cả 'polygon' lẫn 'regularpolygon'.\n const verts = el.vertices!;\n const last = verts.length - 1;\n const trimmed = verts[last] === verts[0] ? verts.slice(0, last) : verts.slice();\n const halo = board.create('polygon', trimmed, {\n ...haloBase,\n fillOpacity: 0.2,\n borders: {\n strokeColor: SEL_STROKE,\n strokeWidth: 7,\n strokeOpacity: 0.55,\n highlight: false,\n },\n });\n halos.push(halo);\n } else if (isCircleLike && el.center && typeof el.Radius === 'function') {\n // Bắt cả 'circle', 'circumcircle', 'incircle' (elementClass 3).\n const halo = board.create('circle', [el.center, () => el.Radius?.() ?? 0], {\n ...haloBase,\n strokeWidth: 9,\n fillOpacity: 0,\n });\n halos.push(halo);\n } else if (isSegment && el.point1 && el.point2) {\n const halo = board.create('segment', [el.point1, el.point2], {\n ...haloBase,\n strokeWidth: 9,\n straightFirst: false,\n straightLast: false,\n });\n halos.push(halo);\n } else if (isLineLike && el.point1 && el.point2) {\n // Gồm cả slopetriangle (extends Line, elementClass 2) → halo line dọc\n // đường dốc.\n const halo = board.create('line', [el.point1, el.point2], {\n ...haloBase,\n strokeWidth: 9,\n });\n halos.push(halo);\n } else if (\n el.center && (el.radiuspoint ?? el.radiusPoint) && (el.anglepoint ?? el.anglePoint) &&\n (elType === 'arc' || elType === 'semicircle' || elType === 'circumcirclearc' ||\n elType === 'sector' || elType === 'angle')\n ) {\n // Arc/Sector/Angle (CURVE class) — dựng lại element cùng loại, dày xám.\n // ⚠️ Runtime JSXGraph dùng `radiuspoint`/`anglepoint` (chữ THƯỜNG) cho cả\n // Arc LẪN Sector/Angle — .d.ts ghi `radiusPoint`/`anglePoint` (P HOA) cho\n // Sector/Angle là SAI (đã verify bằng Playwright). Đọc cả hai cho an toàn.\n const rp = el.radiuspoint ?? el.radiusPoint;\n const ap = el.anglepoint ?? el.anglePoint;\n if (elType === 'sector') {\n halos.push(board.create('sector', [el.center, rp, ap], {\n ...haloBase, strokeWidth: 7, fillOpacity: 0.2,\n }));\n } else if (elType === 'angle') {\n // create('angle', [p1, vertex, p2]); vertex = center.\n halos.push(board.create('angle', [rp, el.center, ap], {\n ...haloBase, strokeWidth: 7, fillOpacity: 0.2,\n radius: (el.getAttribute?.('radius') as number | undefined) ?? 1,\n }));\n } else {\n halos.push(board.create('arc', [el.center, rp, ap], {\n ...haloBase, strokeWidth: 9, fillColor: 'none', fillOpacity: 0,\n }));\n }\n }\n // Còn lại (functiongraph/curve, text...) — chưa hỗ trợ halo; hiếm khi chọn.\n } catch (err) {\n console.warn('[scene/render/2d] halo create fail:', err);\n }\n if (halos.length) this.haloMap.set(id, halos);\n }\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { compile, validate } from './chunk-
|
|
2
|
+
import { labelOpts, compile, validate } from './chunk-CH6SFONH.mjs';
|
|
3
3
|
import { createEmptyState } from './chunk-73Q7ADVL.mjs';
|
|
4
4
|
import { registerKind } from './chunk-B4NJJZFR.mjs';
|
|
5
5
|
import * as React from 'react';
|
|
@@ -563,8 +563,12 @@ function constraintRefs2D(c) {
|
|
|
563
563
|
return [c.line, c.circle, c.other];
|
|
564
564
|
case "tangencyPoint":
|
|
565
565
|
return [c.circle, c.onLine];
|
|
566
|
-
case "arcMidpoint":
|
|
567
|
-
|
|
566
|
+
case "arcMidpoint": {
|
|
567
|
+
const containment = c.notContaining ?? c.containing;
|
|
568
|
+
return containment ? [c.circle, c.a, c.b, containment] : [c.circle, c.a, c.b];
|
|
569
|
+
}
|
|
570
|
+
case "mixtilinearPoint":
|
|
571
|
+
return [c.vertices[0], c.vertices[1], c.vertices[2]];
|
|
568
572
|
case "pointAtDistance": {
|
|
569
573
|
const d = c.distance;
|
|
570
574
|
const extra = d.kind === "circleRadius" ? [d.circle] : d.kind === "segmentLength" ? [d.p1, d.p2] : [];
|
|
@@ -777,7 +781,7 @@ function dist(p, q) {
|
|
|
777
781
|
function sideOf(a, b, p) {
|
|
778
782
|
return (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]);
|
|
779
783
|
}
|
|
780
|
-
function arcMidpoint(center, radius, a, b,
|
|
784
|
+
function arcMidpoint(center, radius, a, b, reference, sameSide = false) {
|
|
781
785
|
const mcx = (a[0] + b[0]) / 2;
|
|
782
786
|
const mcy = (a[1] + b[1]) / 2;
|
|
783
787
|
let ux = mcx - center[0];
|
|
@@ -792,12 +796,18 @@ function arcMidpoint(center, radius, a, b, notContaining) {
|
|
|
792
796
|
uy /= len;
|
|
793
797
|
const cand1 = [center[0] + radius * ux, center[1] + radius * uy];
|
|
794
798
|
const cand2 = [center[0] - radius * ux, center[1] - radius * uy];
|
|
799
|
+
if (!reference) return cand1[1] >= cand2[1] ? cand1 : cand2;
|
|
800
|
+
const notContaining = reference;
|
|
795
801
|
const sN = sideOf(a, b, notContaining);
|
|
796
802
|
if (Math.abs(sN) < 1e-9) {
|
|
797
|
-
|
|
803
|
+
const far = dist(cand1, notContaining) >= dist(cand2, notContaining) ? cand1 : cand2;
|
|
804
|
+
const near = far === cand1 ? cand2 : cand1;
|
|
805
|
+
return sameSide ? near : far;
|
|
798
806
|
}
|
|
799
807
|
const s1 = sideOf(a, b, cand1);
|
|
800
|
-
|
|
808
|
+
const opp = s1 * sN < 0 ? cand1 : cand2;
|
|
809
|
+
const same = opp === cand1 ? cand2 : cand1;
|
|
810
|
+
return sameSide ? same : opp;
|
|
801
811
|
}
|
|
802
812
|
function excenter(vertices, oppositeIndex) {
|
|
803
813
|
const [A, B, C] = vertices;
|
|
@@ -813,6 +823,35 @@ function excenter(vertices, oppositeIndex) {
|
|
|
813
823
|
(w[0] * A[1] + w[1] * B[1] + w[2] * C[1]) / sum
|
|
814
824
|
];
|
|
815
825
|
}
|
|
826
|
+
function circumcenterXY(a, b, c) {
|
|
827
|
+
const ax = a[0], ay = a[1], bx = b[0], by = b[1], cx = c[0], cy = c[1];
|
|
828
|
+
const d = 2 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
|
|
829
|
+
if (Math.abs(d) < 1e-12) return [(ax + bx + cx) / 3, (ay + by + cy) / 3];
|
|
830
|
+
const ux = ((ax * ax + ay * ay) * (by - cy) + (bx * bx + by * by) * (cy - ay) + (cx * cx + cy * cy) * (ay - by)) / d;
|
|
831
|
+
const uy = ((ax * ax + ay * ay) * (cx - bx) + (bx * bx + by * by) * (ax - cx) + (cx * cx + cy * cy) * (bx - ax)) / d;
|
|
832
|
+
return [ux, uy];
|
|
833
|
+
}
|
|
834
|
+
function mixtilinearPoint(a, b, c, which) {
|
|
835
|
+
const O = circumcenterXY(a, b, c);
|
|
836
|
+
const R = Math.hypot(O[0] - a[0], O[1] - a[1]);
|
|
837
|
+
const ux1 = (b[0] - a[0]) / (Math.hypot(b[0] - a[0], b[1] - a[1]) || 1);
|
|
838
|
+
const uy1 = (b[1] - a[1]) / (Math.hypot(b[0] - a[0], b[1] - a[1]) || 1);
|
|
839
|
+
const ux2 = (c[0] - a[0]) / (Math.hypot(c[0] - a[0], c[1] - a[1]) || 1);
|
|
840
|
+
const uy2 = (c[1] - a[1]) / (Math.hypot(c[0] - a[0], c[1] - a[1]) || 1);
|
|
841
|
+
let bx = ux1 + ux2, by = uy1 + uy2;
|
|
842
|
+
const bl = Math.hypot(bx, by) || 1;
|
|
843
|
+
bx /= bl;
|
|
844
|
+
by /= bl;
|
|
845
|
+
const cosA = ux1 * ux2 + uy1 * uy2;
|
|
846
|
+
const sinHalf = Math.sqrt(Math.max(0, (1 - cosA) / 2));
|
|
847
|
+
const cos2Half = Math.max(1e-9, (1 + cosA) / 2);
|
|
848
|
+
const dotAO = bx * (O[0] - a[0]) + by * (O[1] - a[1]);
|
|
849
|
+
const d = 2 * (dotAO - R * sinHalf) / cos2Half;
|
|
850
|
+
const K = [a[0] + d * bx, a[1] + d * by];
|
|
851
|
+
if (which === "center") return K;
|
|
852
|
+
const kl = Math.hypot(K[0] - O[0], K[1] - O[1]) || 1;
|
|
853
|
+
return [O[0] + R * (K[0] - O[0]) / kl, O[1] + R * (K[1] - O[1]) / kl];
|
|
854
|
+
}
|
|
816
855
|
function pointAtDistanceCoord(from, through, d) {
|
|
817
856
|
const dx = through[0] - from[0];
|
|
818
857
|
const dy = through[1] - from[1];
|
|
@@ -831,29 +870,38 @@ function radicalAxisFoot(o1, r1, o2, r2) {
|
|
|
831
870
|
var arcMidpointConstraint = definePointConstraint({
|
|
832
871
|
kind: "arcMidpoint",
|
|
833
872
|
validate: (c) => {
|
|
834
|
-
if (!c.circle || !c.a || !c.b
|
|
835
|
-
throw new Error("point.arcMidpoint: circle, a, b
|
|
873
|
+
if (!c.circle || !c.a || !c.b) {
|
|
874
|
+
throw new Error("point.arcMidpoint: circle, a, b b\u1EAFt bu\u1ED9c");
|
|
875
|
+
}
|
|
876
|
+
if (c.notContaining && c.containing) {
|
|
877
|
+
throw new Error("point.arcMidpoint: kh\xF4ng th\u1EC3 v\u1EEBa notContaining v\u1EEBa containing");
|
|
836
878
|
}
|
|
837
879
|
},
|
|
838
880
|
describe: (obj, state, c) => {
|
|
839
881
|
const al = state?.objects[c.a]?.label ?? c.a;
|
|
840
882
|
const bl = state?.objects[c.b]?.label ?? c.b;
|
|
841
|
-
const
|
|
842
|
-
return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl}
|
|
883
|
+
const ref = c.containing ?? c.notContaining;
|
|
884
|
+
if (!ref) return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl}`;
|
|
885
|
+
const rl = state?.objects[ref]?.label ?? ref;
|
|
886
|
+
const rel = c.containing ? "ch\u1EE9a" : "kh\xF4ng ch\u1EE9a";
|
|
887
|
+
return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl} (${rel} ${rl})`;
|
|
843
888
|
},
|
|
844
889
|
render: (obj, ctx, c, opts) => {
|
|
845
890
|
const board = ctx.jxg;
|
|
846
891
|
const circle = ctx.resolveRef(c.circle);
|
|
847
892
|
const A = ctx.resolveRef(c.a);
|
|
848
893
|
const B = ctx.resolveRef(c.b);
|
|
849
|
-
const
|
|
894
|
+
const refName = c.containing ?? c.notContaining;
|
|
895
|
+
const ref = refName ? ctx.resolveRef(refName) : void 0;
|
|
896
|
+
const sameSide = !!c.containing;
|
|
850
897
|
const O = circle?.center ?? circle?.midpoint ?? circle;
|
|
851
898
|
const am = () => arcMidpoint(
|
|
852
899
|
[O.X(), O.Y()],
|
|
853
900
|
circle.Radius(),
|
|
854
901
|
[A.X(), A.Y()],
|
|
855
902
|
[B.X(), B.Y()],
|
|
856
|
-
[
|
|
903
|
+
ref ? [ref.X(), ref.Y()] : void 0,
|
|
904
|
+
sameSide
|
|
857
905
|
);
|
|
858
906
|
return board.create("point", [() => am()[0], () => am()[1]], opts);
|
|
859
907
|
}
|
|
@@ -894,6 +942,36 @@ var excenterConstraint = definePointConstraint({
|
|
|
894
942
|
}
|
|
895
943
|
});
|
|
896
944
|
|
|
945
|
+
// src/core/scene/kinds/point-constraints/mixtilinearPoint.ts
|
|
946
|
+
var mixtilinearPointConstraint = definePointConstraint({
|
|
947
|
+
kind: "mixtilinearPoint",
|
|
948
|
+
validate: (c) => {
|
|
949
|
+
if (!Array.isArray(c.vertices) || c.vertices.length !== 3 || !c.vertices.every(Boolean)) {
|
|
950
|
+
throw new Error("point.mixtilinearPoint: vertices ph\u1EA3i l\xE0 tuple 3 id non-empty");
|
|
951
|
+
}
|
|
952
|
+
if (c.which !== "center" && c.which !== "touch") {
|
|
953
|
+
throw new Error("point.mixtilinearPoint: which ph\u1EA3i 'center' | 'touch'");
|
|
954
|
+
}
|
|
955
|
+
},
|
|
956
|
+
describe: (obj, state, c) => {
|
|
957
|
+
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
958
|
+
return c.which === "center" ? `${obj.label} = t\xE2m \u0111\u01B0\u1EDDng tr\xF2n mixtilinear \u0394${labels}` : `${obj.label} = ti\u1EBFp \u0111i\u1EC3m mixtilinear \u0394${labels} v\u1EDBi (O)`;
|
|
959
|
+
},
|
|
960
|
+
render: (obj, ctx, c, opts) => {
|
|
961
|
+
const board = ctx.jxg;
|
|
962
|
+
const a = ctx.resolveRef(c.vertices[0]);
|
|
963
|
+
const b = ctx.resolveRef(c.vertices[1]);
|
|
964
|
+
const d = ctx.resolveRef(c.vertices[2]);
|
|
965
|
+
const mp = () => mixtilinearPoint(
|
|
966
|
+
[a.X(), a.Y()],
|
|
967
|
+
[b.X(), b.Y()],
|
|
968
|
+
[d.X(), d.Y()],
|
|
969
|
+
c.which
|
|
970
|
+
);
|
|
971
|
+
return board.create("point", [() => mp()[0], () => mp()[1]], opts);
|
|
972
|
+
}
|
|
973
|
+
});
|
|
974
|
+
|
|
897
975
|
// src/core/scene/kinds/point-constraints/shared.ts
|
|
898
976
|
function buildJxgTransforms(board, ctx, t) {
|
|
899
977
|
switch (t.kind) {
|
|
@@ -945,7 +1023,8 @@ function buildPointOpts(obj) {
|
|
|
945
1023
|
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
946
1024
|
fillColor: obj.attrs.color ?? "#1e40af",
|
|
947
1025
|
face: obj.attrs.face ?? "o",
|
|
948
|
-
size: obj.attrs.size ?? 4
|
|
1026
|
+
size: obj.attrs.size ?? 4,
|
|
1027
|
+
...labelOpts(obj.attrs.labelOffset, [10, 10])
|
|
949
1028
|
};
|
|
950
1029
|
}
|
|
951
1030
|
|
|
@@ -1183,6 +1262,7 @@ var ALL = [
|
|
|
1183
1262
|
centroidConstraint,
|
|
1184
1263
|
arcMidpointConstraint,
|
|
1185
1264
|
excenterConstraint,
|
|
1265
|
+
mixtilinearPointConstraint,
|
|
1186
1266
|
pointAtDistanceConstraint,
|
|
1187
1267
|
circleIntersectionConstraint,
|
|
1188
1268
|
circleSecondIntersectionConstraint,
|
|
@@ -1265,7 +1345,8 @@ var def3 = {
|
|
|
1265
1345
|
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
1266
1346
|
fillColor: obj.attrs.color ?? "#1e40af",
|
|
1267
1347
|
face: obj.attrs.face ?? "o",
|
|
1268
|
-
size: obj.attrs.size ?? 4
|
|
1348
|
+
size: obj.attrs.size ?? 4,
|
|
1349
|
+
...labelOpts(obj.attrs.labelOffset, [10, 10])
|
|
1269
1350
|
});
|
|
1270
1351
|
} catch {
|
|
1271
1352
|
}
|
|
@@ -1309,7 +1390,8 @@ var def4 = {
|
|
|
1309
1390
|
strokeWidth: obj.attrs.width ?? 2,
|
|
1310
1391
|
dash: obj.attrs.dash ?? 0,
|
|
1311
1392
|
visible: obj.visible,
|
|
1312
|
-
fixed: obj.locked
|
|
1393
|
+
fixed: obj.locked,
|
|
1394
|
+
...labelOpts(obj.attrs.labelOffset)
|
|
1313
1395
|
});
|
|
1314
1396
|
}
|
|
1315
1397
|
};
|
|
@@ -1380,7 +1462,8 @@ var def5 = {
|
|
|
1380
1462
|
strokeWidth: obj.attrs.width ?? 2,
|
|
1381
1463
|
dash: obj.attrs.dash ?? 0,
|
|
1382
1464
|
visible: obj.visible,
|
|
1383
|
-
fixed: obj.locked
|
|
1465
|
+
fixed: obj.locked,
|
|
1466
|
+
...labelOpts(obj.attrs.labelOffset)
|
|
1384
1467
|
};
|
|
1385
1468
|
const c = obj.attrs.construction;
|
|
1386
1469
|
if (!c) {
|
|
@@ -1675,15 +1758,17 @@ var def8 = {
|
|
|
1675
1758
|
const board = ctx.jxg;
|
|
1676
1759
|
const isCenterLabel = (l) => /^[A-Z]['′]?\d*$/u.test(l);
|
|
1677
1760
|
const isCenter = isCenterLabel(obj.label);
|
|
1761
|
+
const isRenamedCircle = /_c$/.test(obj.label);
|
|
1678
1762
|
const baseOpts = {
|
|
1679
1763
|
name: obj.label,
|
|
1680
|
-
withLabel: isCenter ? obj.attrs.showLabel ?? false : true,
|
|
1764
|
+
withLabel: isCenter ? obj.attrs.showLabel ?? false : isRenamedCircle ? false : true,
|
|
1681
1765
|
strokeColor: obj.attrs.color ?? "#0f172a",
|
|
1682
1766
|
strokeWidth: obj.attrs.width ?? 2,
|
|
1683
1767
|
dash: obj.attrs.dash ?? 0,
|
|
1684
1768
|
fillColor: "none",
|
|
1685
1769
|
visible: obj.visible,
|
|
1686
|
-
fixed: obj.locked
|
|
1770
|
+
fixed: obj.locked,
|
|
1771
|
+
...labelOpts(obj.attrs.labelOffset)
|
|
1687
1772
|
};
|
|
1688
1773
|
const c = asConstruction(obj.attrs);
|
|
1689
1774
|
if (c?.kind === "circumscribed") {
|
|
@@ -2141,8 +2226,11 @@ var def12 = {
|
|
|
2141
2226
|
const opts = {
|
|
2142
2227
|
name: obj.label,
|
|
2143
2228
|
withLabel: true,
|
|
2144
|
-
|
|
2145
|
-
|
|
2229
|
+
// Cùng màu xanh với mọi điểm khác (buildPointOpts dùng '#1e40af').
|
|
2230
|
+
// Trước đây điểm giao tô đỏ '#dc2626' → tách biệt thị giác nhưng gây
|
|
2231
|
+
// khó hiểu ("vì sao điểm này khác màu?"). Giữ chung một màu.
|
|
2232
|
+
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
2233
|
+
fillColor: obj.attrs.color ?? "#1e40af",
|
|
2146
2234
|
visible: obj.visible,
|
|
2147
2235
|
fixed: obj.locked
|
|
2148
2236
|
};
|
|
@@ -2151,6 +2239,38 @@ var def12 = {
|
|
|
2151
2239
|
}
|
|
2152
2240
|
const branch = obj.attrs.branch ?? 0;
|
|
2153
2241
|
return board.create("intersection", [a, b, branch], opts);
|
|
2242
|
+
},
|
|
2243
|
+
/**
|
|
2244
|
+
* Cập nhật TẠI CHỖ các thuộc tính "trang trí" (tên/màu/ẩn-hiện/khoá) qua
|
|
2245
|
+
* setAttribute — giữ nguyên JxgObj identity nên các object phụ thuộc điểm
|
|
2246
|
+
* giao (đường thẳng qua nó, …) KHÔNG bị stale parent ref. Mô phỏng update
|
|
2247
|
+
* hook của point.ts.
|
|
2248
|
+
*
|
|
2249
|
+
* Nếu định nghĩa hình học đổi (kind/ref1/ref2/branch) thì throw → renderer
|
|
2250
|
+
* fallback remove + create để JSXGraph dựng lại phép giao đúng.
|
|
2251
|
+
*/
|
|
2252
|
+
update: (obj, prev, ctx, existing) => {
|
|
2253
|
+
const a = obj.attrs;
|
|
2254
|
+
const p = prev.attrs;
|
|
2255
|
+
const branchA = a.branch;
|
|
2256
|
+
const branchP = p.branch;
|
|
2257
|
+
if (a.kind !== p.kind || a.ref1 !== p.ref1 || a.ref2 !== p.ref2 || branchA !== branchP) {
|
|
2258
|
+
throw new Error("intersection: \u0111\u1ECBnh ngh\u0129a h\xECnh h\u1ECDc \u0111\u1ED5i \u2014 recreate");
|
|
2259
|
+
}
|
|
2260
|
+
const el = existing;
|
|
2261
|
+
if (typeof el.setAttribute === "function") {
|
|
2262
|
+
try {
|
|
2263
|
+
el.setAttribute({
|
|
2264
|
+
name: obj.label,
|
|
2265
|
+
withLabel: true,
|
|
2266
|
+
strokeColor: a.color ?? "#1e40af",
|
|
2267
|
+
fillColor: a.color ?? "#1e40af",
|
|
2268
|
+
visible: obj.visible,
|
|
2269
|
+
fixed: obj.locked
|
|
2270
|
+
});
|
|
2271
|
+
} catch {
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2154
2274
|
}
|
|
2155
2275
|
};
|
|
2156
2276
|
registerKind(def12);
|
|
@@ -2532,5 +2652,5 @@ function deserializeScene(domain, raw) {
|
|
|
2532
2652
|
}
|
|
2533
2653
|
|
|
2534
2654
|
export { deserializeScene, listObjects, nextLabel, serializeScene, useEditorState };
|
|
2535
|
-
//# sourceMappingURL=chunk-
|
|
2536
|
-
//# sourceMappingURL=chunk-
|
|
2655
|
+
//# sourceMappingURL=chunk-JJ4FPCBE.mjs.map
|
|
2656
|
+
//# sourceMappingURL=chunk-JJ4FPCBE.mjs.map
|