@xom11/whiteboard 0.30.0 → 0.32.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 +374 -59
- package/dist/ai.d.ts +374 -59
- package/dist/ai.js +7542 -545
- package/dist/ai.js.map +1 -1
- package/dist/ai.mjs +7243 -569
- package/dist/ai.mjs.map +1 -1
- package/dist/catalog.json +5 -5
- package/dist/{chunk-ZTQBUKLJ.mjs → chunk-4EXRNZRX.mjs} +781 -95
- package/dist/chunk-4EXRNZRX.mjs.map +1 -0
- 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-XVVLT6B3.mjs → chunk-DBG6DZ2V.mjs} +4 -4
- package/dist/{chunk-XVVLT6B3.mjs.map → chunk-DBG6DZ2V.mjs.map} +1 -1
- package/dist/{chunk-BU5KLO3P.mjs → chunk-DCFZTKFE.mjs} +14 -6
- package/dist/chunk-DCFZTKFE.mjs.map +1 -0
- package/dist/{chunk-SF3U7ZF4.mjs → chunk-DJM5EWTT.mjs} +231 -16
- package/dist/chunk-DJM5EWTT.mjs.map +1 -0
- package/dist/{chunk-V3YJ6JFL.mjs → chunk-F42MZ745.mjs} +3 -3
- package/dist/{chunk-V3YJ6JFL.mjs.map → chunk-F42MZ745.mjs.map} +1 -1
- package/dist/{chunk-5JM35CXV.mjs → chunk-FI2MGX5H.mjs} +4 -4
- package/dist/{chunk-5JM35CXV.mjs.map → chunk-FI2MGX5H.mjs.map} +1 -1
- package/dist/{chunk-SZDAS7LK.mjs → chunk-IE2GGHNF.mjs} +131 -81
- package/dist/chunk-IE2GGHNF.mjs.map +1 -0
- 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-KLGPAMJS.mjs} +55 -43
- package/dist/chunk-KLGPAMJS.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-73Q7ADVL.mjs → chunk-MNHOFOM5.mjs} +2 -2
- package/dist/{chunk-73Q7ADVL.mjs.map → chunk-MNHOFOM5.mjs.map} +1 -1
- package/dist/{chunk-QRUAEXLR.mjs → chunk-MOAXHVKI.mjs} +5 -5
- package/dist/{chunk-QRUAEXLR.mjs.map → chunk-MOAXHVKI.mjs.map} +1 -1
- package/dist/{chunk-QGNU34T7.mjs → chunk-QLQ4MJNO.mjs} +10 -4
- package/dist/chunk-QLQ4MJNO.mjs.map +1 -0
- package/dist/chunk-SUXIMLYN.mjs +238 -0
- package/dist/chunk-SUXIMLYN.mjs.map +1 -0
- package/dist/{chunk-OQIQNKPQ.mjs → chunk-XSTGTEGP.mjs} +5 -5
- package/dist/{chunk-OQIQNKPQ.mjs.map → chunk-XSTGTEGP.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 +1329 -288
- package/dist/geometry-2d.js.map +1 -1
- package/dist/geometry-2d.mjs +6 -6
- package/dist/geometry-3d.d.mts +1 -1
- package/dist/geometry-3d.d.ts +1 -1
- package/dist/geometry-3d.js +4136 -462
- package/dist/geometry-3d.js.map +1 -1
- package/dist/geometry-3d.mjs +5 -5
- package/dist/graph-2d.d.mts +1 -1
- package/dist/graph-2d.d.ts +1 -1
- package/dist/graph-2d.js +1010 -208
- package/dist/graph-2d.js.map +1 -1
- package/dist/graph-2d.mjs +8 -8
- package/dist/{handleExtractProblem-BrDY9ifM.d.ts → handleExtractProblem-mtJyhEw3.d.mts} +2 -0
- package/dist/{handleExtractProblem-BrDY9ifM.d.mts → handleExtractProblem-mtJyhEw3.d.ts} +2 -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-3UFGFMJ2.mjs → host-JNLJ5BVR.mjs} +92 -45
- package/dist/host-JNLJ5BVR.mjs.map +1 -0
- package/dist/{host-HOSJHQ5H.mjs → host-VRWLQSMH.mjs} +14 -13
- package/dist/host-VRWLQSMH.mjs.map +1 -0
- package/dist/host-W4U6YCSX.mjs +4973 -0
- package/dist/host-W4U6YCSX.mjs.map +1 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +4577 -600
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +35 -29
- 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-7UZWQQ4I.mjs +9 -0
- package/dist/{serialize-N4G6RFBB.mjs.map → serialize-7UZWQQ4I.mjs.map} +1 -1
- package/dist/{types-C3FjpoUi.d.mts → types-tePd94vW.d.mts} +8 -0
- package/dist/{types-C3FjpoUi.d.ts → types-tePd94vW.d.ts} +8 -0
- package/package.json +4 -1
- package/dist/chunk-BU5KLO3P.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-QK6OVDLC.mjs +0 -103
- package/dist/chunk-QK6OVDLC.mjs.map +0 -1
- package/dist/chunk-SF3U7ZF4.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 +0 -2377
- package/dist/host-2ISGVO7O.mjs.map +0 -1
- package/dist/host-3UFGFMJ2.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
package/dist/graph-2d.js
CHANGED
|
@@ -37,16 +37,16 @@ var __export = (target, all) => {
|
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
// src/core/scene/registry.ts
|
|
40
|
-
function registerKind(
|
|
41
|
-
if (registry.has(
|
|
42
|
-
console.warn(`[scene] kind "${
|
|
40
|
+
function registerKind(def24) {
|
|
41
|
+
if (registry.has(def24.type)) {
|
|
42
|
+
console.warn(`[scene] kind "${def24.type}" \u0111\xE3 \u0111\u01B0\u1EE3c \u0111\u0103ng k\xFD \u2014 ghi \u0111\xE8 \u0111\u1ECBnh ngh\u0129a c\u0169`);
|
|
43
43
|
}
|
|
44
|
-
registry.set(
|
|
44
|
+
registry.set(def24.type, def24);
|
|
45
45
|
}
|
|
46
46
|
function getKind(type) {
|
|
47
|
-
const
|
|
48
|
-
if (!
|
|
49
|
-
return
|
|
47
|
+
const def24 = registry.get(type);
|
|
48
|
+
if (!def24) throw new Error(`[scene] unknown kind: ${type}`);
|
|
49
|
+
return def24;
|
|
50
50
|
}
|
|
51
51
|
var registry;
|
|
52
52
|
var init_registry = __esm({
|
|
@@ -132,8 +132,8 @@ function reduce(draft, action) {
|
|
|
132
132
|
return;
|
|
133
133
|
}
|
|
134
134
|
case "TRANSACTION": {
|
|
135
|
-
for (const
|
|
136
|
-
reduce(draft,
|
|
135
|
+
for (const sub2 of action.payload.actions) {
|
|
136
|
+
reduce(draft, sub2);
|
|
137
137
|
}
|
|
138
138
|
return;
|
|
139
139
|
}
|
|
@@ -392,6 +392,22 @@ var init_parser = __esm({
|
|
|
392
392
|
}
|
|
393
393
|
});
|
|
394
394
|
|
|
395
|
+
// src/core/scene/kinds/_label.ts
|
|
396
|
+
function labelOpts(labelOffset, dflt) {
|
|
397
|
+
const offset = labelOffset ?? dflt;
|
|
398
|
+
return { label: { fixed: false, ...offset ? { offset } : {} } };
|
|
399
|
+
}
|
|
400
|
+
function readLabelOffset(label) {
|
|
401
|
+
const off = label.evalVisProp?.("offset") ?? label.visProp?.offset;
|
|
402
|
+
const rel = label.relativeCoords?.scrCoords;
|
|
403
|
+
if (!off || !rel || off.length < 2 || rel.length < 3) return null;
|
|
404
|
+
return [Math.round(off[0] + rel[1]), Math.round(off[1] - rel[2])];
|
|
405
|
+
}
|
|
406
|
+
var init_label = __esm({
|
|
407
|
+
"src/core/scene/kinds/_label.ts"() {
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
|
|
395
411
|
// src/core/scene/render/JxgRenderer.ts
|
|
396
412
|
var JxgRenderer;
|
|
397
413
|
var init_JxgRenderer = __esm({
|
|
@@ -399,6 +415,7 @@ var init_JxgRenderer = __esm({
|
|
|
399
415
|
init_registry();
|
|
400
416
|
init_types2d();
|
|
401
417
|
init_parser();
|
|
418
|
+
init_label();
|
|
402
419
|
JxgRenderer = class {
|
|
403
420
|
constructor(store, board, options = {}) {
|
|
404
421
|
this.elements = /* @__PURE__ */ new Map();
|
|
@@ -462,11 +479,12 @@ var init_JxgRenderer = __esm({
|
|
|
462
479
|
}
|
|
463
480
|
create(obj) {
|
|
464
481
|
try {
|
|
465
|
-
const
|
|
466
|
-
const el =
|
|
482
|
+
const def24 = getKind(obj.kind);
|
|
483
|
+
const el = def24.render(obj, this.ctx());
|
|
467
484
|
this.elements.set(obj.id, el);
|
|
468
485
|
this.attachFreePointDragSync(obj, el);
|
|
469
486
|
this.attachGliderDragSync(obj, el);
|
|
487
|
+
this.attachLabelDragSync(obj, el);
|
|
470
488
|
} catch (err) {
|
|
471
489
|
console.warn(`[scene/render/2d] kh\xF4ng render \u0111\u01B0\u1EE3c ${obj.kind} id="${obj.id}":`, err);
|
|
472
490
|
}
|
|
@@ -581,6 +599,54 @@ var init_JxgRenderer = __esm({
|
|
|
581
599
|
});
|
|
582
600
|
});
|
|
583
601
|
}
|
|
602
|
+
/**
|
|
603
|
+
* Cho phép kéo NHÃN của bất kỳ object nào có label (point/line/segment/
|
|
604
|
+
* circle...). Khi user kéo nhãn, JSXGraph cộng dồn vào label.relativeCoords
|
|
605
|
+
* (drag-delta screen px) nhưng KHÔNG đổi attribute offset → vị trí cuối =
|
|
606
|
+
* offset + relativeCoords. Ta gộp lại thành offset thuần (readLabelOffset),
|
|
607
|
+
* zero relativeCoords để khỏi double-count khi update-hook/recreate áp lại
|
|
608
|
+
* offset, rồi dispatch UPDATE_ATTRS { labelOffset }. Right-click → reset.
|
|
609
|
+
*/
|
|
610
|
+
attachLabelDragSync(obj, el) {
|
|
611
|
+
const label = el?.label;
|
|
612
|
+
if (!label || typeof label.on !== "function") return;
|
|
613
|
+
const sceneId = obj.id;
|
|
614
|
+
label.on("up", () => {
|
|
615
|
+
if (this.disposed) return;
|
|
616
|
+
const off = readLabelOffset(label);
|
|
617
|
+
if (!off) return;
|
|
618
|
+
const cur = this.store.getState().objects[sceneId];
|
|
619
|
+
if (!cur) return;
|
|
620
|
+
const prev = cur.attrs.labelOffset;
|
|
621
|
+
if (prev && prev[0] === off[0] && prev[1] === off[1]) return;
|
|
622
|
+
try {
|
|
623
|
+
label.setAttribute({ offset: off });
|
|
624
|
+
if (label.relativeCoords?.scrCoords) {
|
|
625
|
+
label.relativeCoords.scrCoords[1] = 0;
|
|
626
|
+
label.relativeCoords.scrCoords[2] = 0;
|
|
627
|
+
}
|
|
628
|
+
} catch {
|
|
629
|
+
}
|
|
630
|
+
this.store.dispatch({
|
|
631
|
+
type: "UPDATE_ATTRS",
|
|
632
|
+
payload: { id: sceneId, patch: { labelOffset: off } }
|
|
633
|
+
});
|
|
634
|
+
});
|
|
635
|
+
const node = label.rendNode;
|
|
636
|
+
if (node?.addEventListener) {
|
|
637
|
+
node.addEventListener("contextmenu", (ev) => {
|
|
638
|
+
if (this.disposed) return;
|
|
639
|
+
ev.preventDefault();
|
|
640
|
+
const c = this.store.getState().objects[sceneId];
|
|
641
|
+
if (!c) return;
|
|
642
|
+
if (c.attrs.labelOffset === void 0) return;
|
|
643
|
+
this.store.dispatch({
|
|
644
|
+
type: "UPDATE_ATTRS",
|
|
645
|
+
payload: { id: sceneId, patch: { labelOffset: void 0 } }
|
|
646
|
+
});
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
}
|
|
584
650
|
remove(id) {
|
|
585
651
|
this.removeHalo(id);
|
|
586
652
|
this.selectedIds.delete(id);
|
|
@@ -618,11 +684,11 @@ var init_JxgRenderer = __esm({
|
|
|
618
684
|
continue;
|
|
619
685
|
}
|
|
620
686
|
if (Object.is(old, cur)) continue;
|
|
621
|
-
const
|
|
687
|
+
const def24 = getKind(cur.kind);
|
|
622
688
|
const existing = this.elements.get(id);
|
|
623
|
-
if (
|
|
689
|
+
if (def24.update && existing) {
|
|
624
690
|
try {
|
|
625
|
-
|
|
691
|
+
def24.update(cur, old, this.ctx(), existing);
|
|
626
692
|
continue;
|
|
627
693
|
} catch (err) {
|
|
628
694
|
console.warn(`[scene/render/2d] update fail, recreate:`, err);
|
|
@@ -717,88 +783,89 @@ var init_JxgRenderer = __esm({
|
|
|
717
783
|
layer: 4,
|
|
718
784
|
needsRegularUpdate: true
|
|
719
785
|
};
|
|
786
|
+
const elementClass = el.elementClass;
|
|
787
|
+
const elType = el.elType;
|
|
788
|
+
const isPointLike = elementClass === 1 || elType === "point" || elType === "glider" || elType === "intersection";
|
|
789
|
+
const isPolygonLike = Array.isArray(el.vertices) && el.vertices.length >= 3;
|
|
790
|
+
const isCircleLike = elementClass === 3 || elType === "circle" || elType === "circumcircle" || elType === "incircle";
|
|
791
|
+
const isSegment = elType === "segment";
|
|
792
|
+
const isLineLike = elementClass === 2 || elType === "line" || elType === "arrow" || elType === "ray" || elType === "vector" || elType === "tangent" || elType === "normal" || elType === "parallel" || elType === "perpendicular" || elType === "bisector";
|
|
720
793
|
const halos = [];
|
|
721
794
|
try {
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
});
|
|
749
|
-
halos.push(halo);
|
|
750
|
-
}
|
|
751
|
-
break;
|
|
752
|
-
}
|
|
753
|
-
case "line":
|
|
754
|
-
case "arrow":
|
|
755
|
-
case "ray":
|
|
756
|
-
case "vector":
|
|
757
|
-
case "tangent":
|
|
758
|
-
case "normal":
|
|
759
|
-
case "parallel":
|
|
760
|
-
case "perpendicular":
|
|
761
|
-
case "bisector": {
|
|
762
|
-
if (el.point1 && el.point2) {
|
|
763
|
-
const halo = board.create("line", [el.point1, el.point2], {
|
|
764
|
-
...haloBase,
|
|
765
|
-
strokeWidth: 9
|
|
766
|
-
});
|
|
767
|
-
halos.push(halo);
|
|
768
|
-
}
|
|
769
|
-
break;
|
|
770
|
-
}
|
|
771
|
-
case "circle": {
|
|
772
|
-
if (el.center && typeof el.Radius === "function") {
|
|
773
|
-
const halo = board.create("circle", [el.center, () => el.Radius?.() ?? 0], {
|
|
774
|
-
...haloBase,
|
|
775
|
-
strokeWidth: 9,
|
|
776
|
-
fillOpacity: 0
|
|
777
|
-
});
|
|
778
|
-
halos.push(halo);
|
|
779
|
-
}
|
|
780
|
-
break;
|
|
781
|
-
}
|
|
782
|
-
case "polygon": {
|
|
783
|
-
if (Array.isArray(el.vertices) && el.vertices.length >= 3) {
|
|
784
|
-
const last = el.vertices.length - 1;
|
|
785
|
-
const verts = el.vertices[last] === el.vertices[0] ? el.vertices.slice(0, last) : el.vertices.slice();
|
|
786
|
-
const halo = board.create("polygon", verts, {
|
|
787
|
-
...haloBase,
|
|
788
|
-
fillOpacity: 0.2,
|
|
789
|
-
borders: {
|
|
790
|
-
strokeColor: SEL_STROKE,
|
|
791
|
-
strokeWidth: 7,
|
|
792
|
-
strokeOpacity: 0.55,
|
|
793
|
-
highlight: false
|
|
794
|
-
}
|
|
795
|
-
});
|
|
796
|
-
halos.push(halo);
|
|
795
|
+
if (isPointLike) {
|
|
796
|
+
const baseSize = el.getAttribute?.("size") ?? 4;
|
|
797
|
+
const halo = board.create("point", [
|
|
798
|
+
() => el.X?.() ?? 0,
|
|
799
|
+
() => el.Y?.() ?? 0
|
|
800
|
+
], {
|
|
801
|
+
...haloBase,
|
|
802
|
+
size: baseSize + 6,
|
|
803
|
+
face: "o",
|
|
804
|
+
strokeWidth: 2,
|
|
805
|
+
strokeOpacity: 0.75,
|
|
806
|
+
fillOpacity: 0.25
|
|
807
|
+
});
|
|
808
|
+
halos.push(halo);
|
|
809
|
+
} else if (isPolygonLike) {
|
|
810
|
+
const verts = el.vertices;
|
|
811
|
+
const last = verts.length - 1;
|
|
812
|
+
const trimmed = verts[last] === verts[0] ? verts.slice(0, last) : verts.slice();
|
|
813
|
+
const halo = board.create("polygon", trimmed, {
|
|
814
|
+
...haloBase,
|
|
815
|
+
fillOpacity: 0.2,
|
|
816
|
+
borders: {
|
|
817
|
+
strokeColor: SEL_STROKE,
|
|
818
|
+
strokeWidth: 7,
|
|
819
|
+
strokeOpacity: 0.55,
|
|
820
|
+
highlight: false
|
|
797
821
|
}
|
|
798
|
-
|
|
822
|
+
});
|
|
823
|
+
halos.push(halo);
|
|
824
|
+
} else if (isCircleLike && el.center && typeof el.Radius === "function") {
|
|
825
|
+
const halo = board.create("circle", [el.center, () => el.Radius?.() ?? 0], {
|
|
826
|
+
...haloBase,
|
|
827
|
+
strokeWidth: 9,
|
|
828
|
+
fillOpacity: 0
|
|
829
|
+
});
|
|
830
|
+
halos.push(halo);
|
|
831
|
+
} else if (isSegment && el.point1 && el.point2) {
|
|
832
|
+
const halo = board.create("segment", [el.point1, el.point2], {
|
|
833
|
+
...haloBase,
|
|
834
|
+
strokeWidth: 9,
|
|
835
|
+
straightFirst: false,
|
|
836
|
+
straightLast: false
|
|
837
|
+
});
|
|
838
|
+
halos.push(halo);
|
|
839
|
+
} else if (isLineLike && el.point1 && el.point2) {
|
|
840
|
+
const halo = board.create("line", [el.point1, el.point2], {
|
|
841
|
+
...haloBase,
|
|
842
|
+
strokeWidth: 9
|
|
843
|
+
});
|
|
844
|
+
halos.push(halo);
|
|
845
|
+
} else if (el.center && (el.radiuspoint ?? el.radiusPoint) && (el.anglepoint ?? el.anglePoint) && (elType === "arc" || elType === "semicircle" || elType === "circumcirclearc" || elType === "sector" || elType === "angle")) {
|
|
846
|
+
const rp = el.radiuspoint ?? el.radiusPoint;
|
|
847
|
+
const ap = el.anglepoint ?? el.anglePoint;
|
|
848
|
+
if (elType === "sector") {
|
|
849
|
+
halos.push(board.create("sector", [el.center, rp, ap], {
|
|
850
|
+
...haloBase,
|
|
851
|
+
strokeWidth: 7,
|
|
852
|
+
fillOpacity: 0.2
|
|
853
|
+
}));
|
|
854
|
+
} else if (elType === "angle") {
|
|
855
|
+
halos.push(board.create("angle", [rp, el.center, ap], {
|
|
856
|
+
...haloBase,
|
|
857
|
+
strokeWidth: 7,
|
|
858
|
+
fillOpacity: 0.2,
|
|
859
|
+
radius: el.getAttribute?.("radius") ?? 1
|
|
860
|
+
}));
|
|
861
|
+
} else {
|
|
862
|
+
halos.push(board.create("arc", [el.center, rp, ap], {
|
|
863
|
+
...haloBase,
|
|
864
|
+
strokeWidth: 9,
|
|
865
|
+
fillColor: "none",
|
|
866
|
+
fillOpacity: 0
|
|
867
|
+
}));
|
|
799
868
|
}
|
|
800
|
-
default:
|
|
801
|
-
break;
|
|
802
869
|
}
|
|
803
870
|
} catch (err) {
|
|
804
871
|
console.warn("[scene/render/2d] halo create fail:", err);
|
|
@@ -1090,8 +1157,36 @@ function constraintRefs(c) {
|
|
|
1090
1157
|
return [c.polygonId];
|
|
1091
1158
|
case "onSphere":
|
|
1092
1159
|
return [c.sphereId];
|
|
1093
|
-
|
|
1160
|
+
case "midpoint":
|
|
1161
|
+
return [c.p1, c.p2];
|
|
1162
|
+
case "centroid":
|
|
1163
|
+
return [...c.vertices];
|
|
1164
|
+
case "intersectionLines":
|
|
1165
|
+
return [c.a1, c.b1, c.a2, c.b2];
|
|
1166
|
+
case "intersectionLinePlane":
|
|
1167
|
+
return [c.a, c.b, c.plane];
|
|
1168
|
+
case "perpFootLine":
|
|
1169
|
+
return [c.from, c.a, c.b];
|
|
1170
|
+
case "perpFootPlane":
|
|
1171
|
+
return [c.from, c.plane];
|
|
1172
|
+
case "circumsphereCenter":
|
|
1173
|
+
return [...c.vertices];
|
|
1174
|
+
case "pyramidInsphereCenter":
|
|
1175
|
+
return [c.apex, ...c.vertices];
|
|
1176
|
+
case "faceCircumcenter":
|
|
1177
|
+
return [...c.vertices];
|
|
1178
|
+
case "pointAboveFace":
|
|
1179
|
+
return [c.base, c.apex, ...c.vertices];
|
|
1180
|
+
// Kind KHÔNG có ref scene-object (toạ độ literal): liệt kê TƯỜNG MINH để
|
|
1181
|
+
// exhaustive never-guard bên dưới buộc khai báo khi thêm constraint kind mới
|
|
1182
|
+
// (mô phỏng constraintRefs2D — quên case = cascade-delete/deps sai âm thầm).
|
|
1183
|
+
case "free":
|
|
1184
|
+
case "onGround":
|
|
1185
|
+
case "onAxis":
|
|
1094
1186
|
return [];
|
|
1187
|
+
default: {
|
|
1188
|
+
return [];
|
|
1189
|
+
}
|
|
1095
1190
|
}
|
|
1096
1191
|
}
|
|
1097
1192
|
var init_d_constraint = __esm({
|
|
@@ -1099,12 +1194,359 @@ var init_d_constraint = __esm({
|
|
|
1099
1194
|
}
|
|
1100
1195
|
});
|
|
1101
1196
|
|
|
1197
|
+
// src/core/scene/kinds/constraint3d-math.ts
|
|
1198
|
+
function sub(a, b) {
|
|
1199
|
+
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
1200
|
+
}
|
|
1201
|
+
function add(a, b) {
|
|
1202
|
+
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
|
|
1203
|
+
}
|
|
1204
|
+
function scale(a, k) {
|
|
1205
|
+
return [a[0] * k, a[1] * k, a[2] * k];
|
|
1206
|
+
}
|
|
1207
|
+
function dot(a, b) {
|
|
1208
|
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
1209
|
+
}
|
|
1210
|
+
function cross(a, b) {
|
|
1211
|
+
return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
|
|
1212
|
+
}
|
|
1213
|
+
function norm(a) {
|
|
1214
|
+
return Math.sqrt(dot(a, a));
|
|
1215
|
+
}
|
|
1216
|
+
function normalize(a) {
|
|
1217
|
+
const n = norm(a);
|
|
1218
|
+
return n === 0 ? a : scale(a, 1 / n);
|
|
1219
|
+
}
|
|
1220
|
+
function getPointWorld(id, state) {
|
|
1221
|
+
const obj = state.objects[id];
|
|
1222
|
+
if (!obj || obj.kind !== "point3d") {
|
|
1223
|
+
throw new Error(`constraintMath: point ${id} not found`);
|
|
1224
|
+
}
|
|
1225
|
+
const attrs = obj.attrs;
|
|
1226
|
+
return constraintToWorld(attrs.constraint, state);
|
|
1227
|
+
}
|
|
1228
|
+
function getPlaneBasis(planeObj, state) {
|
|
1229
|
+
const a = planeObj.attrs;
|
|
1230
|
+
let p1, p2, p3;
|
|
1231
|
+
if (a.construction) {
|
|
1232
|
+
const r = planeConstructionWorld(a.construction, state);
|
|
1233
|
+
p1 = r.p1;
|
|
1234
|
+
p2 = r.p2;
|
|
1235
|
+
p3 = r.p3;
|
|
1236
|
+
} else {
|
|
1237
|
+
p1 = getPointWorld(a.p1, state);
|
|
1238
|
+
p2 = getPointWorld(a.p2, state);
|
|
1239
|
+
p3 = getPointWorld(a.p3, state);
|
|
1240
|
+
}
|
|
1241
|
+
const basis1 = sub(p2, p1);
|
|
1242
|
+
const tmp = sub(p3, p1);
|
|
1243
|
+
const normal = normalize(cross(basis1, tmp));
|
|
1244
|
+
const basis2 = cross(normal, basis1);
|
|
1245
|
+
return { origin: p1, basis1, basis2, normal };
|
|
1246
|
+
}
|
|
1247
|
+
function orthoBasisFromNormal(n) {
|
|
1248
|
+
const seed = Math.abs(n[0]) < 0.9 ? [1, 0, 0] : [0, 1, 0];
|
|
1249
|
+
const basis1 = normalize(cross(n, seed));
|
|
1250
|
+
const basis2 = cross(n, basis1);
|
|
1251
|
+
return { basis1, basis2 };
|
|
1252
|
+
}
|
|
1253
|
+
function planeConstructionWorld(c, state) {
|
|
1254
|
+
if (_recDepth >= MAX_REC_DEPTH) throw new Error("planeConstructionWorld: \u0111\u1EC7 quy qu\xE1 s\xE2u \u2014 chu tr\xECnh tham chi\u1EBFu m\u1EB7t?");
|
|
1255
|
+
_recDepth++;
|
|
1256
|
+
try {
|
|
1257
|
+
return planeConstructionWorldInner(c, state);
|
|
1258
|
+
} finally {
|
|
1259
|
+
_recDepth--;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
function planeConstructionWorldInner(c, state) {
|
|
1263
|
+
switch (c.kind) {
|
|
1264
|
+
case "planeParallelThrough": {
|
|
1265
|
+
const P = getPointWorld(c.point, state);
|
|
1266
|
+
const ref = state.objects[c.refPlane];
|
|
1267
|
+
if (!ref || ref.kind !== "plane3d") throw new Error("planeParallelThrough: m\u1EB7t tham chi\u1EBFu thi\u1EBFu");
|
|
1268
|
+
const { basis1, basis2 } = getPlaneBasis(ref, state);
|
|
1269
|
+
if (norm(basis1) < 1e-12 || norm(basis2) < 1e-12) {
|
|
1270
|
+
return { p1: P, p2: add(P, [1, 0, 0]), p3: add(P, [0, 1, 0]) };
|
|
1271
|
+
}
|
|
1272
|
+
return { p1: P, p2: add(P, basis1), p3: add(P, basis2) };
|
|
1273
|
+
}
|
|
1274
|
+
case "planePerpToLine": {
|
|
1275
|
+
const P = getPointWorld(c.point, state);
|
|
1276
|
+
const dir = sub(getPointWorld(c.lineB, state), getPointWorld(c.lineA, state));
|
|
1277
|
+
const dn = norm(dir);
|
|
1278
|
+
if (dn < 1e-12) return { p1: P, p2: add(P, [1, 0, 0]), p3: add(P, [0, 1, 0]) };
|
|
1279
|
+
const { basis1, basis2 } = orthoBasisFromNormal(scale(dir, 1 / dn));
|
|
1280
|
+
return { p1: P, p2: add(P, basis1), p3: add(P, basis2) };
|
|
1281
|
+
}
|
|
1282
|
+
default: {
|
|
1283
|
+
throw new Error("planeConstructionWorld: kind kh\xF4ng h\u1ED7 tr\u1EE3");
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
function lineDefiningPointIds(line) {
|
|
1288
|
+
if (line.kind === "line3d" || line.kind === "segment3d") {
|
|
1289
|
+
const a = line.attrs;
|
|
1290
|
+
return a.p1 && a.p2 ? [a.p1, a.p2] : null;
|
|
1291
|
+
}
|
|
1292
|
+
if (line.kind === "ray3d") {
|
|
1293
|
+
const a = line.attrs;
|
|
1294
|
+
return [a.origin, a.through];
|
|
1295
|
+
}
|
|
1296
|
+
if (line.kind === "vector3d") {
|
|
1297
|
+
const a = line.attrs;
|
|
1298
|
+
return [a.from, a.to];
|
|
1299
|
+
}
|
|
1300
|
+
return null;
|
|
1301
|
+
}
|
|
1302
|
+
function lineEndpointsWorld(lineId, state, ctx) {
|
|
1303
|
+
const line = state.objects[lineId];
|
|
1304
|
+
if (!line) throw new Error(`${ctx}: \u0111\u01B0\u1EDDng ${lineId} kh\xF4ng t\u1ED3n t\u1EA1i`);
|
|
1305
|
+
const ids = lineDefiningPointIds(line);
|
|
1306
|
+
if (!ids) throw new Error(`${ctx}: kind ${line.kind} kh\xF4ng ph\u1EA3i \u0111\u01B0\u1EDDng`);
|
|
1307
|
+
return { a: getPointWorld(ids[0], state), b: getPointWorld(ids[1], state) };
|
|
1308
|
+
}
|
|
1309
|
+
function lineLineClosestMidpoint(A, B, C, D) {
|
|
1310
|
+
const u = sub(B, A), v = sub(D, C), w0 = sub(A, C);
|
|
1311
|
+
const a = dot(u, u), b = dot(u, v), cc = dot(v, v), d = dot(u, w0), e = dot(v, w0);
|
|
1312
|
+
const denom = a * cc - b * b;
|
|
1313
|
+
if (Math.abs(denom) < 1e-12) return scale(add(A, C), 0.5);
|
|
1314
|
+
const sc = (b * e - cc * d) / denom;
|
|
1315
|
+
const tc = (a * e - b * d) / denom;
|
|
1316
|
+
return scale(add(add(A, scale(u, sc)), add(C, scale(v, tc))), 0.5);
|
|
1317
|
+
}
|
|
1318
|
+
function planeOriginNormal(planeId, state, ctx) {
|
|
1319
|
+
const plane = state.objects[planeId];
|
|
1320
|
+
if (!plane || plane.kind !== "plane3d") throw new Error(`${ctx}: m\u1EB7t ph\u1EB3ng ${planeId} thi\u1EBFu`);
|
|
1321
|
+
const { origin, normal } = getPlaneBasis(plane, state);
|
|
1322
|
+
return { origin, normal };
|
|
1323
|
+
}
|
|
1324
|
+
function lineConstructionWorld(c, state) {
|
|
1325
|
+
switch (c.kind) {
|
|
1326
|
+
case "planePlaneIntersection": {
|
|
1327
|
+
const P1 = planeOriginNormal(c.plane1, state, "planePlaneIntersection");
|
|
1328
|
+
const P2 = planeOriginNormal(c.plane2, state, "planePlaneIntersection");
|
|
1329
|
+
const d = cross(P1.normal, P2.normal);
|
|
1330
|
+
const dd = dot(d, d);
|
|
1331
|
+
if (dd < 1e-12) {
|
|
1332
|
+
return { a: P1.origin, b: add(P1.origin, P2.normal) };
|
|
1333
|
+
}
|
|
1334
|
+
const c1 = dot(P1.normal, P1.origin);
|
|
1335
|
+
const c2 = dot(P2.normal, P2.origin);
|
|
1336
|
+
const p0 = scale(add(scale(cross(P2.normal, d), c1), scale(cross(d, P1.normal), c2)), 1 / dd);
|
|
1337
|
+
return { a: p0, b: add(p0, d) };
|
|
1338
|
+
}
|
|
1339
|
+
case "lineParallelThrough": {
|
|
1340
|
+
const P = getPointWorld(c.point, state);
|
|
1341
|
+
const dir = sub(getPointWorld(c.dirB, state), getPointWorld(c.dirA, state));
|
|
1342
|
+
if (norm(dir) < 1e-12) return { a: P, b: add(P, [1, 0, 0]) };
|
|
1343
|
+
return { a: P, b: add(P, dir) };
|
|
1344
|
+
}
|
|
1345
|
+
case "linePerpToPlane": {
|
|
1346
|
+
const P = getPointWorld(c.point, state);
|
|
1347
|
+
const { normal } = planeOriginNormal(c.plane, state, "linePerpToPlane");
|
|
1348
|
+
return { a: P, b: add(P, normal) };
|
|
1349
|
+
}
|
|
1350
|
+
default: {
|
|
1351
|
+
throw new Error("lineConstructionWorld: kind kh\xF4ng h\u1ED7 tr\u1EE3");
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
function solve3(M, b) {
|
|
1356
|
+
const det3 = (m) => m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
|
|
1357
|
+
const det = det3(M);
|
|
1358
|
+
if (Math.abs(det) < 1e-9) return null;
|
|
1359
|
+
const colDet = (j) => {
|
|
1360
|
+
const A = M.map((r) => r.slice());
|
|
1361
|
+
for (let i = 0; i < 3; i++) A[i][j] = b[i];
|
|
1362
|
+
return det3(A);
|
|
1363
|
+
};
|
|
1364
|
+
return [colDet(0) / det, colDet(1) / det, colDet(2) / det];
|
|
1365
|
+
}
|
|
1366
|
+
function constraintToWorld(c, state) {
|
|
1367
|
+
if (_recDepth >= MAX_REC_DEPTH) throw new Error("constraintToWorld: \u0111\u1EC7 quy qu\xE1 s\xE2u \u2014 chu tr\xECnh tham chi\u1EBFu?");
|
|
1368
|
+
_recDepth++;
|
|
1369
|
+
try {
|
|
1370
|
+
return constraintToWorldInner(c, state);
|
|
1371
|
+
} finally {
|
|
1372
|
+
_recDepth--;
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
function constraintToWorldInner(c, state) {
|
|
1376
|
+
switch (c.kind) {
|
|
1377
|
+
case "free":
|
|
1378
|
+
return [c.x, c.y, c.z];
|
|
1379
|
+
case "onGround":
|
|
1380
|
+
return [c.x, c.y, 0];
|
|
1381
|
+
case "onAxis": {
|
|
1382
|
+
if (c.axis === "x") return [c.t, 0, 0];
|
|
1383
|
+
if (c.axis === "y") return [0, c.t, 0];
|
|
1384
|
+
return [0, 0, c.t];
|
|
1385
|
+
}
|
|
1386
|
+
case "onPlane": {
|
|
1387
|
+
const plane = state.objects[c.planeId];
|
|
1388
|
+
if (!plane || plane.kind !== "plane3d") throw new Error("onPlane: plane missing");
|
|
1389
|
+
const { origin, basis1, basis2 } = getPlaneBasis(plane, state);
|
|
1390
|
+
return add(add(origin, scale(basis1, c.u)), scale(basis2, c.v));
|
|
1391
|
+
}
|
|
1392
|
+
case "onLine": {
|
|
1393
|
+
const { a, b } = lineEndpointsWorld(c.lineId, state, "onLine");
|
|
1394
|
+
return add(a, scale(sub(b, a), c.t));
|
|
1395
|
+
}
|
|
1396
|
+
case "onPolygon": {
|
|
1397
|
+
const pg = state.objects[c.polygonId];
|
|
1398
|
+
if (!pg || pg.kind !== "polygon3d") throw new Error("onPolygon: parent missing");
|
|
1399
|
+
const v = pg.attrs.vertices;
|
|
1400
|
+
if (v.length < 3) throw new Error("onPolygon: < 3 vertices");
|
|
1401
|
+
const p1 = getPointWorld(v[0], state);
|
|
1402
|
+
const p2 = getPointWorld(v[1], state);
|
|
1403
|
+
const p3 = getPointWorld(v[2], state);
|
|
1404
|
+
const basis1 = sub(p2, p1);
|
|
1405
|
+
const tmp = sub(p3, p1);
|
|
1406
|
+
const normal = normalize(cross(basis1, tmp));
|
|
1407
|
+
const basis2 = cross(normal, basis1);
|
|
1408
|
+
return add(add(p1, scale(basis1, c.u)), scale(basis2, c.v));
|
|
1409
|
+
}
|
|
1410
|
+
case "onSphere": {
|
|
1411
|
+
const sph = state.objects[c.sphereId];
|
|
1412
|
+
if (!sph || sph.kind !== "sphere3d") throw new Error("onSphere: parent missing");
|
|
1413
|
+
const a = sph.attrs;
|
|
1414
|
+
const center = getPointWorld(a.center, state);
|
|
1415
|
+
const surface = getPointWorld(a.surfacePoint, state);
|
|
1416
|
+
const radius = norm(sub(surface, center));
|
|
1417
|
+
const x = center[0] + radius * Math.sin(c.phi) * Math.cos(c.theta);
|
|
1418
|
+
const y = center[1] + radius * Math.sin(c.phi) * Math.sin(c.theta);
|
|
1419
|
+
const z = center[2] + radius * Math.cos(c.phi);
|
|
1420
|
+
return [x, y, z];
|
|
1421
|
+
}
|
|
1422
|
+
// ───── Điểm phái sinh (v1) ─────
|
|
1423
|
+
case "midpoint": {
|
|
1424
|
+
const p1 = getPointWorld(c.p1, state);
|
|
1425
|
+
const p2 = getPointWorld(c.p2, state);
|
|
1426
|
+
return scale(add(p1, p2), 0.5);
|
|
1427
|
+
}
|
|
1428
|
+
case "centroid": {
|
|
1429
|
+
const n = c.vertices.length;
|
|
1430
|
+
if (n === 0) return [0, 0, 0];
|
|
1431
|
+
let acc = [0, 0, 0];
|
|
1432
|
+
for (const id of c.vertices) acc = add(acc, getPointWorld(id, state));
|
|
1433
|
+
return scale(acc, 1 / n);
|
|
1434
|
+
}
|
|
1435
|
+
case "circumsphereCenter": {
|
|
1436
|
+
const P = c.vertices.map((id) => getPointWorld(id, state));
|
|
1437
|
+
if (P.length < 4) return P.length ? P[0] : [0, 0, 0];
|
|
1438
|
+
const p0 = P[0];
|
|
1439
|
+
const M = [[0, 0, 0], [0, 0, 0], [0, 0, 0]];
|
|
1440
|
+
const rhs = [0, 0, 0];
|
|
1441
|
+
for (let i = 1; i < P.length; i++) {
|
|
1442
|
+
const r = [2 * (P[i][0] - p0[0]), 2 * (P[i][1] - p0[1]), 2 * (P[i][2] - p0[2])];
|
|
1443
|
+
const bi = dot(P[i], P[i]) - dot(p0, p0);
|
|
1444
|
+
for (let a = 0; a < 3; a++) {
|
|
1445
|
+
for (let bcol = 0; bcol < 3; bcol++) M[a][bcol] += r[a] * r[bcol];
|
|
1446
|
+
rhs[a] += r[a] * bi;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
const sol = solve3(M, rhs);
|
|
1450
|
+
if (sol) return sol;
|
|
1451
|
+
let acc = [0, 0, 0];
|
|
1452
|
+
for (const p of P) acc = add(acc, p);
|
|
1453
|
+
return scale(acc, 1 / P.length);
|
|
1454
|
+
}
|
|
1455
|
+
case "pyramidInsphereCenter": {
|
|
1456
|
+
const S = getPointWorld(c.apex, state);
|
|
1457
|
+
const base = c.vertices.map((id) => getPointWorld(id, state));
|
|
1458
|
+
const n = base.length;
|
|
1459
|
+
if (n < 3) return S;
|
|
1460
|
+
let G = [0, 0, 0];
|
|
1461
|
+
for (const p of base) G = add(G, p);
|
|
1462
|
+
G = scale(G, 1 / n);
|
|
1463
|
+
let nb = normalize(cross(sub(base[1], base[0]), sub(base[2], base[0])));
|
|
1464
|
+
if (dot(nb, sub(S, G)) < 0) nb = scale(nb, -1);
|
|
1465
|
+
let m = normalize(cross(sub(base[0], S), sub(base[1], S)));
|
|
1466
|
+
if (dot(m, sub(G, base[0])) < 0) m = scale(m, -1);
|
|
1467
|
+
const denom = 1 - dot(m, nb);
|
|
1468
|
+
const s = Math.abs(denom) < 1e-9 ? NaN : dot(m, sub(G, base[0])) / denom;
|
|
1469
|
+
if (!Number.isFinite(s) || s <= 0) {
|
|
1470
|
+
return scale(add(S, scale(G, n)), 1 / (n + 1));
|
|
1471
|
+
}
|
|
1472
|
+
return add(G, scale(nb, s));
|
|
1473
|
+
}
|
|
1474
|
+
case "faceCircumcenter": {
|
|
1475
|
+
const P = c.vertices.map((id) => getPointWorld(id, state));
|
|
1476
|
+
if (P.length < 3) return P.length ? P[0] : [0, 0, 0];
|
|
1477
|
+
const [p0, p1, p2] = P;
|
|
1478
|
+
const e1 = sub(p1, p0), e2 = sub(p2, p0);
|
|
1479
|
+
const nrm = cross(e1, e2);
|
|
1480
|
+
const M = [
|
|
1481
|
+
[2 * e1[0], 2 * e1[1], 2 * e1[2]],
|
|
1482
|
+
[2 * e2[0], 2 * e2[1], 2 * e2[2]],
|
|
1483
|
+
[nrm[0], nrm[1], nrm[2]]
|
|
1484
|
+
];
|
|
1485
|
+
const rhs = [dot(p1, p1) - dot(p0, p0), dot(p2, p2) - dot(p0, p0), dot(nrm, p0)];
|
|
1486
|
+
const sol = solve3(M, rhs);
|
|
1487
|
+
if (sol) return sol;
|
|
1488
|
+
return scale(add(add(p0, p1), p2), 1 / 3);
|
|
1489
|
+
}
|
|
1490
|
+
case "pointAboveFace": {
|
|
1491
|
+
const G = getPointWorld(c.base, state);
|
|
1492
|
+
const P = c.vertices.map((id) => getPointWorld(id, state));
|
|
1493
|
+
if (P.length < 3) return G;
|
|
1494
|
+
let n = normalize(cross(sub(P[1], P[0]), sub(P[2], P[0])));
|
|
1495
|
+
const S = getPointWorld(c.apex, state);
|
|
1496
|
+
if (dot(n, sub(S, P[0])) < 0) n = scale(n, -1);
|
|
1497
|
+
const h = dot(sub(S, P[0]), n);
|
|
1498
|
+
if (!Number.isFinite(h) || h <= 1e-9) return G;
|
|
1499
|
+
return add(G, scale(n, h));
|
|
1500
|
+
}
|
|
1501
|
+
case "intersectionLines": {
|
|
1502
|
+
const A = getPointWorld(c.a1, state), B = getPointWorld(c.b1, state);
|
|
1503
|
+
const C = getPointWorld(c.a2, state), D = getPointWorld(c.b2, state);
|
|
1504
|
+
return lineLineClosestMidpoint(A, B, C, D);
|
|
1505
|
+
}
|
|
1506
|
+
case "intersectionLinePlane": {
|
|
1507
|
+
const A = getPointWorld(c.a, state), B = getPointWorld(c.b, state);
|
|
1508
|
+
const plane = state.objects[c.plane];
|
|
1509
|
+
if (!plane || plane.kind !== "plane3d") throw new Error("intersectionLinePlane: m\u1EB7t ph\u1EB3ng thi\u1EBFu");
|
|
1510
|
+
const { origin, normal } = getPlaneBasis(plane, state);
|
|
1511
|
+
const dir = sub(B, A);
|
|
1512
|
+
const dn = dot(dir, normal);
|
|
1513
|
+
if (Math.abs(dn) < 1e-12) return A;
|
|
1514
|
+
const t = dot(sub(origin, A), normal) / dn;
|
|
1515
|
+
return add(A, scale(dir, t));
|
|
1516
|
+
}
|
|
1517
|
+
case "perpFootLine": {
|
|
1518
|
+
const P = getPointWorld(c.from, state);
|
|
1519
|
+
const A = getPointWorld(c.a, state), B = getPointWorld(c.b, state);
|
|
1520
|
+
const u = sub(B, A);
|
|
1521
|
+
const uu = dot(u, u);
|
|
1522
|
+
const t = uu === 0 ? 0 : dot(sub(P, A), u) / uu;
|
|
1523
|
+
return add(A, scale(u, t));
|
|
1524
|
+
}
|
|
1525
|
+
case "perpFootPlane": {
|
|
1526
|
+
const P = getPointWorld(c.from, state);
|
|
1527
|
+
const plane = state.objects[c.plane];
|
|
1528
|
+
if (!plane || plane.kind !== "plane3d") throw new Error("perpFootPlane: m\u1EB7t ph\u1EB3ng thi\u1EBFu");
|
|
1529
|
+
const { origin, normal } = getPlaneBasis(plane, state);
|
|
1530
|
+
const dist2 = dot(sub(P, origin), normal);
|
|
1531
|
+
return sub(P, scale(normal, dist2));
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
var MAX_REC_DEPTH, _recDepth;
|
|
1536
|
+
var init_constraint3d_math = __esm({
|
|
1537
|
+
"src/core/scene/kinds/constraint3d-math.ts"() {
|
|
1538
|
+
MAX_REC_DEPTH = 512;
|
|
1539
|
+
_recDepth = 0;
|
|
1540
|
+
}
|
|
1541
|
+
});
|
|
1542
|
+
|
|
1102
1543
|
// src/core/scene/kinds/point3d.ts
|
|
1103
1544
|
var def;
|
|
1104
1545
|
var init_point3d = __esm({
|
|
1105
1546
|
"src/core/scene/kinds/point3d.ts"() {
|
|
1106
1547
|
init_registry();
|
|
1107
1548
|
init_d_constraint();
|
|
1549
|
+
init_constraint3d_math();
|
|
1108
1550
|
def = {
|
|
1109
1551
|
type: "point3d",
|
|
1110
1552
|
schemaVersion: 1,
|
|
@@ -1142,6 +1584,15 @@ var init_point3d = __esm({
|
|
|
1142
1584
|
if (c.kind === "onLine") return `${obj.label} tr\xEAn \u0111\u01B0\u1EDDng ${c.lineId}`;
|
|
1143
1585
|
if (c.kind === "onPolygon") return `${obj.label} tr\xEAn \u0111a gi\xE1c ${c.polygonId}`;
|
|
1144
1586
|
if (c.kind === "onSphere") return `${obj.label} tr\xEAn m\u1EB7t c\u1EA7u ${c.sphereId}`;
|
|
1587
|
+
if (c.kind === "midpoint") return `${obj.label} = trung \u0111i\u1EC3m ${c.p1}${c.p2}`;
|
|
1588
|
+
if (c.kind === "centroid") return `${obj.label} = tr\u1ECDng t\xE2m ${c.vertices.join("")}`;
|
|
1589
|
+
if (c.kind === "intersectionLines") return `${obj.label} = giao 2 \u0111\u01B0\u1EDDng (${c.a1}${c.b1}, ${c.a2}${c.b2})`;
|
|
1590
|
+
if (c.kind === "intersectionLinePlane") return `${obj.label} = giao ${c.a}${c.b} \u2229 ${c.plane}`;
|
|
1591
|
+
if (c.kind === "perpFootLine") return `${obj.label} = ch\xE2n \u22A5 t\u1EEB ${c.from} xu\u1ED1ng ${c.a}${c.b}`;
|
|
1592
|
+
if (c.kind === "perpFootPlane") return `${obj.label} = ch\xE2n \u22A5 t\u1EEB ${c.from} xu\u1ED1ng ${c.plane}`;
|
|
1593
|
+
if (c.kind === "pyramidInsphereCenter") return `${obj.label} = t\xE2m c\u1EA7u n\u1ED9i ti\u1EBFp ch\xF3p ${c.apex}.${c.vertices.join("")}`;
|
|
1594
|
+
if (c.kind === "faceCircumcenter") return `${obj.label} = t\xE2m ngo\u1EA1i ti\u1EBFp ${c.vertices.join("")}`;
|
|
1595
|
+
if (c.kind === "pointAboveFace") return `${obj.label} = \u0111\u1EC9nh tr\u1EE5c tr\u1EE5 \u22A5 m\u1EB7t ${c.vertices.join("")}`;
|
|
1145
1596
|
return obj.label;
|
|
1146
1597
|
},
|
|
1147
1598
|
render: (obj, ctx) => {
|
|
@@ -1191,6 +1642,15 @@ var init_point3d = __esm({
|
|
|
1191
1642
|
() => sph.F(c.theta, c.phi)[2]
|
|
1192
1643
|
], opts);
|
|
1193
1644
|
}
|
|
1645
|
+
const getState = ctx.getState;
|
|
1646
|
+
if (getState) {
|
|
1647
|
+
const cw = () => constraintToWorld(c, getState());
|
|
1648
|
+
return view.create("point3d", [
|
|
1649
|
+
() => cw()[0],
|
|
1650
|
+
() => cw()[1],
|
|
1651
|
+
() => cw()[2]
|
|
1652
|
+
], { ...opts, needsRegularUpdate: true });
|
|
1653
|
+
}
|
|
1194
1654
|
return view.create("point3d", [0, 0, 0], opts);
|
|
1195
1655
|
}
|
|
1196
1656
|
};
|
|
@@ -1252,32 +1712,77 @@ var init_segment3d = __esm({
|
|
|
1252
1712
|
});
|
|
1253
1713
|
|
|
1254
1714
|
// src/core/scene/kinds/line3d.ts
|
|
1715
|
+
function line3dConstructionRefs(c) {
|
|
1716
|
+
switch (c.kind) {
|
|
1717
|
+
case "planePlaneIntersection":
|
|
1718
|
+
return [c.plane1, c.plane2];
|
|
1719
|
+
case "lineParallelThrough":
|
|
1720
|
+
return [c.point, c.dirA, c.dirB];
|
|
1721
|
+
case "linePerpToPlane":
|
|
1722
|
+
return [c.point, c.plane];
|
|
1723
|
+
default: {
|
|
1724
|
+
return [];
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
var def3;
|
|
1255
1729
|
var init_line3d = __esm({
|
|
1256
1730
|
"src/core/scene/kinds/line3d.ts"() {
|
|
1257
1731
|
init_registry();
|
|
1258
1732
|
init_labelOf();
|
|
1259
|
-
|
|
1733
|
+
init_constraint3d_math();
|
|
1734
|
+
def3 = {
|
|
1260
1735
|
type: "line3d",
|
|
1261
1736
|
schemaVersion: 1,
|
|
1262
1737
|
migrate: {},
|
|
1263
1738
|
validate: (a) => {
|
|
1264
|
-
if (
|
|
1739
|
+
if (a?.construction) return;
|
|
1740
|
+
if (!a?.p1 || !a?.p2) throw new Error("line3d: p1/p2 required (ho\u1EB7c construction)");
|
|
1741
|
+
},
|
|
1742
|
+
dependsOn: (a) => a.construction ? line3dConstructionRefs(a.construction) : [a.p1, a.p2],
|
|
1743
|
+
describe: (obj, state) => {
|
|
1744
|
+
const c = obj.attrs.construction;
|
|
1745
|
+
if (!c) return `\u0110\u01B0\u1EDDng ${obj.label} qua ${labelOf(obj.attrs.p1, state)}, ${labelOf(obj.attrs.p2, state)}`;
|
|
1746
|
+
switch (c.kind) {
|
|
1747
|
+
case "planePlaneIntersection":
|
|
1748
|
+
return `Giao tuy\u1EBFn ${obj.label} = ${labelOf(c.plane1, state)} \u2229 ${labelOf(c.plane2, state)}`;
|
|
1749
|
+
case "lineParallelThrough":
|
|
1750
|
+
return `${obj.label} \u2225 ${labelOf(c.dirA, state)}${labelOf(c.dirB, state)} qua ${labelOf(c.point, state)}`;
|
|
1751
|
+
case "linePerpToPlane":
|
|
1752
|
+
return `${obj.label} \u22A5 ${labelOf(c.plane, state)} qua ${labelOf(c.point, state)}`;
|
|
1753
|
+
default:
|
|
1754
|
+
return obj.label;
|
|
1755
|
+
}
|
|
1265
1756
|
},
|
|
1266
|
-
dependsOn: (a) => [a.p1, a.p2],
|
|
1267
|
-
describe: (obj, state) => `\u0110\u01B0\u1EDDng ${obj.label} qua ${labelOf(obj.attrs.p1, state)}, ${labelOf(obj.attrs.p2, state)}`,
|
|
1268
1757
|
render: (obj, ctx) => {
|
|
1269
1758
|
const view = ctx.jxg;
|
|
1759
|
+
const baseOpts = {
|
|
1760
|
+
strokeColor: obj.attrs.color ?? "#0f172a",
|
|
1761
|
+
strokeWidth: 2,
|
|
1762
|
+
visible: obj.visible
|
|
1763
|
+
};
|
|
1764
|
+
const c = obj.attrs.construction;
|
|
1765
|
+
if (c) {
|
|
1766
|
+
const getState = ctx.getState;
|
|
1767
|
+
if (!getState) throw new Error("line3d construction: c\u1EA7n ctx.getState");
|
|
1768
|
+
const lc = () => lineConstructionWorld(c, getState());
|
|
1769
|
+
return view.create("line3d", [() => lc().a, () => lc().b], {
|
|
1770
|
+
...baseOpts,
|
|
1771
|
+
straightFirst: true,
|
|
1772
|
+
straightLast: true,
|
|
1773
|
+
needsRegularUpdate: true
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1270
1776
|
const pA = ctx.resolveRef(obj.attrs.p1);
|
|
1271
1777
|
const pB = ctx.resolveRef(obj.attrs.p2);
|
|
1272
1778
|
return view.create("line3d", [pA, pB], {
|
|
1779
|
+
...baseOpts,
|
|
1273
1780
|
straightFirst: true,
|
|
1274
|
-
straightLast: true
|
|
1275
|
-
strokeColor: obj.attrs.color ?? "#0f172a",
|
|
1276
|
-
strokeWidth: 2,
|
|
1277
|
-
visible: obj.visible
|
|
1781
|
+
straightLast: true
|
|
1278
1782
|
});
|
|
1279
1783
|
}
|
|
1280
|
-
}
|
|
1784
|
+
};
|
|
1785
|
+
registerKind(def3);
|
|
1281
1786
|
}
|
|
1282
1787
|
});
|
|
1283
1788
|
|
|
@@ -1343,33 +1848,77 @@ var init_vector3d = __esm({
|
|
|
1343
1848
|
});
|
|
1344
1849
|
|
|
1345
1850
|
// src/core/scene/kinds/plane3d.ts
|
|
1851
|
+
function plane3dConstructionRefs(c) {
|
|
1852
|
+
switch (c.kind) {
|
|
1853
|
+
case "planeParallelThrough":
|
|
1854
|
+
return [c.point, c.refPlane];
|
|
1855
|
+
case "planePerpToLine":
|
|
1856
|
+
return [c.point, c.lineA, c.lineB];
|
|
1857
|
+
default: {
|
|
1858
|
+
return [];
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
var def4;
|
|
1346
1863
|
var init_plane3d = __esm({
|
|
1347
1864
|
"src/core/scene/kinds/plane3d.ts"() {
|
|
1348
1865
|
init_registry();
|
|
1349
1866
|
init_labelOf();
|
|
1350
|
-
|
|
1867
|
+
init_constraint3d_math();
|
|
1868
|
+
def4 = {
|
|
1351
1869
|
type: "plane3d",
|
|
1352
1870
|
schemaVersion: 1,
|
|
1353
1871
|
migrate: {},
|
|
1354
1872
|
validate: (a) => {
|
|
1355
|
-
if (
|
|
1873
|
+
if (a?.construction) return;
|
|
1874
|
+
if (!a?.p1 || !a?.p2 || !a?.p3) throw new Error("plane3d: c\u1EA7n 3 \u0111i\u1EC3m (ho\u1EB7c construction)");
|
|
1875
|
+
},
|
|
1876
|
+
dependsOn: (a) => a.construction ? plane3dConstructionRefs(a.construction) : [a.p1, a.p2, a.p3],
|
|
1877
|
+
describe: (obj, state) => {
|
|
1878
|
+
const c = obj.attrs.construction;
|
|
1879
|
+
if (!c) {
|
|
1880
|
+
return `M\u1EB7t ${obj.label} qua ${labelOf(obj.attrs.p1, state)}, ${labelOf(obj.attrs.p2, state)}, ${labelOf(obj.attrs.p3, state)}`;
|
|
1881
|
+
}
|
|
1882
|
+
switch (c.kind) {
|
|
1883
|
+
case "planeParallelThrough":
|
|
1884
|
+
return `${obj.label} \u2225 ${labelOf(c.refPlane, state)} qua ${labelOf(c.point, state)}`;
|
|
1885
|
+
case "planePerpToLine":
|
|
1886
|
+
return `${obj.label} \u22A5 ${labelOf(c.lineA, state)}${labelOf(c.lineB, state)} qua ${labelOf(c.point, state)}`;
|
|
1887
|
+
default:
|
|
1888
|
+
return obj.label;
|
|
1889
|
+
}
|
|
1356
1890
|
},
|
|
1357
|
-
dependsOn: (a) => [a.p1, a.p2, a.p3],
|
|
1358
|
-
describe: (obj, state) => `M\u1EB7t ${obj.label} qua ${labelOf(obj.attrs.p1, state)}, ${labelOf(obj.attrs.p2, state)}, ${labelOf(obj.attrs.p3, state)}`,
|
|
1359
1891
|
render: (obj, ctx) => {
|
|
1360
1892
|
const view = ctx.jxg;
|
|
1361
|
-
|
|
1362
|
-
ctx.resolveRef(obj.attrs.p1),
|
|
1363
|
-
ctx.resolveRef(obj.attrs.p2),
|
|
1364
|
-
ctx.resolveRef(obj.attrs.p3)
|
|
1365
|
-
], {
|
|
1893
|
+
const opts = {
|
|
1366
1894
|
fillOpacity: 0.15,
|
|
1367
1895
|
fillColor: obj.attrs.color ?? "#60a5fa",
|
|
1368
1896
|
strokeColor: obj.attrs.color ?? "#60a5fa",
|
|
1369
1897
|
visible: obj.visible
|
|
1370
|
-
}
|
|
1898
|
+
};
|
|
1899
|
+
const c = obj.attrs.construction;
|
|
1900
|
+
if (c) {
|
|
1901
|
+
const getState = ctx.getState;
|
|
1902
|
+
if (!getState) throw new Error("plane3d construction: c\u1EA7n ctx.getState");
|
|
1903
|
+
const pc = () => planeConstructionWorld(c, getState());
|
|
1904
|
+
const diff = (which) => {
|
|
1905
|
+
const r = pc();
|
|
1906
|
+
const q = r[which];
|
|
1907
|
+
return [q[0] - r.p1[0], q[1] - r.p1[1], q[2] - r.p1[2]];
|
|
1908
|
+
};
|
|
1909
|
+
return view.create("plane3d", [() => pc().p1, () => diff("p2"), () => diff("p3")], {
|
|
1910
|
+
...opts,
|
|
1911
|
+
needsRegularUpdate: true
|
|
1912
|
+
});
|
|
1913
|
+
}
|
|
1914
|
+
return view.create("plane3d", [
|
|
1915
|
+
ctx.resolveRef(obj.attrs.p1),
|
|
1916
|
+
ctx.resolveRef(obj.attrs.p2),
|
|
1917
|
+
ctx.resolveRef(obj.attrs.p3)
|
|
1918
|
+
], opts);
|
|
1371
1919
|
}
|
|
1372
|
-
}
|
|
1920
|
+
};
|
|
1921
|
+
registerKind(def4);
|
|
1373
1922
|
}
|
|
1374
1923
|
});
|
|
1375
1924
|
|
|
@@ -1468,11 +2017,37 @@ var init_polyhedron3d = __esm({
|
|
|
1468
2017
|
}
|
|
1469
2018
|
});
|
|
1470
2019
|
|
|
2020
|
+
// src/core/scene/kinds/_ringBasis.ts
|
|
2021
|
+
function perpBasis(axis) {
|
|
2022
|
+
const n = Math.hypot(axis[0], axis[1], axis[2]);
|
|
2023
|
+
if (n < 1e-12) return [[1, 0, 0], [0, 1, 0]];
|
|
2024
|
+
const a = [axis[0] / n, axis[1] / n, axis[2] / n];
|
|
2025
|
+
const seed = Math.abs(a[2]) < 0.9 ? [0, 0, 1] : [1, 0, 0];
|
|
2026
|
+
let u = [
|
|
2027
|
+
seed[1] * a[2] - seed[2] * a[1],
|
|
2028
|
+
seed[2] * a[0] - seed[0] * a[2],
|
|
2029
|
+
seed[0] * a[1] - seed[1] * a[0]
|
|
2030
|
+
];
|
|
2031
|
+
const un = Math.hypot(u[0], u[1], u[2]) || 1;
|
|
2032
|
+
u = [u[0] / un, u[1] / un, u[2] / un];
|
|
2033
|
+
const v = [
|
|
2034
|
+
a[1] * u[2] - a[2] * u[1],
|
|
2035
|
+
a[2] * u[0] - a[0] * u[2],
|
|
2036
|
+
a[0] * u[1] - a[1] * u[0]
|
|
2037
|
+
];
|
|
2038
|
+
return [u, v];
|
|
2039
|
+
}
|
|
2040
|
+
var init_ringBasis = __esm({
|
|
2041
|
+
"src/core/scene/kinds/_ringBasis.ts"() {
|
|
2042
|
+
}
|
|
2043
|
+
});
|
|
2044
|
+
|
|
1471
2045
|
// src/core/scene/kinds/cylinder3d.ts
|
|
1472
2046
|
var CURVED_SEGMENTS;
|
|
1473
2047
|
var init_cylinder3d = __esm({
|
|
1474
2048
|
"src/core/scene/kinds/cylinder3d.ts"() {
|
|
1475
2049
|
init_registry();
|
|
2050
|
+
init_ringBasis();
|
|
1476
2051
|
CURVED_SEGMENTS = 16;
|
|
1477
2052
|
registerKind({
|
|
1478
2053
|
type: "cylinder3d",
|
|
@@ -1491,14 +2066,17 @@ var init_cylinder3d = __esm({
|
|
|
1491
2066
|
const r = obj.attrs.radius;
|
|
1492
2067
|
const ax = a.X?.() ?? 0, ay = a.Y?.() ?? 0, az = a.Z?.() ?? 0;
|
|
1493
2068
|
const bx = b.X?.() ?? 0, by = b.Y?.() ?? 0, bz = b.Z?.() ?? 0;
|
|
2069
|
+
const [u, v] = perpBasis([bx - ax, by - ay, bz - az]);
|
|
1494
2070
|
const baseRing = [];
|
|
1495
2071
|
const topRing = [];
|
|
1496
2072
|
for (let i = 0; i < CURVED_SEGMENTS; i++) {
|
|
1497
2073
|
const theta = i / CURVED_SEGMENTS * Math.PI * 2;
|
|
1498
|
-
const
|
|
1499
|
-
const
|
|
1500
|
-
|
|
1501
|
-
|
|
2074
|
+
const c = Math.cos(theta), s = Math.sin(theta);
|
|
2075
|
+
const ox = r * (c * u[0] + s * v[0]);
|
|
2076
|
+
const oy = r * (c * u[1] + s * v[1]);
|
|
2077
|
+
const oz = r * (c * u[2] + s * v[2]);
|
|
2078
|
+
baseRing.push([ax + ox, ay + oy, az + oz]);
|
|
2079
|
+
topRing.push([bx + ox, by + oy, bz + oz]);
|
|
1502
2080
|
}
|
|
1503
2081
|
const vertices = [...baseRing, ...topRing];
|
|
1504
2082
|
const faces = [];
|
|
@@ -1509,7 +2087,7 @@ var init_cylinder3d = __esm({
|
|
|
1509
2087
|
faces.push([i, next, CURVED_SEGMENTS + next, CURVED_SEGMENTS + i]);
|
|
1510
2088
|
}
|
|
1511
2089
|
const vertJxgs = vertices.map(
|
|
1512
|
-
(
|
|
2090
|
+
(v2, i) => view.create("point3d", v2, {
|
|
1513
2091
|
id: `${obj.id}.v${i}`,
|
|
1514
2092
|
visible: false,
|
|
1515
2093
|
fixed: true,
|
|
@@ -1537,6 +2115,7 @@ var CURVED_SEGMENTS2;
|
|
|
1537
2115
|
var init_cone3d = __esm({
|
|
1538
2116
|
"src/core/scene/kinds/cone3d.ts"() {
|
|
1539
2117
|
init_registry();
|
|
2118
|
+
init_ringBasis();
|
|
1540
2119
|
CURVED_SEGMENTS2 = 16;
|
|
1541
2120
|
registerKind({
|
|
1542
2121
|
type: "cone3d",
|
|
@@ -1559,10 +2138,16 @@ var init_cone3d = __esm({
|
|
|
1559
2138
|
apexPt.Y?.() ?? 0,
|
|
1560
2139
|
apexPt.Z?.() ?? 0
|
|
1561
2140
|
];
|
|
2141
|
+
const [u, v] = perpBasis([apexCoords[0] - bx, apexCoords[1] - by, apexCoords[2] - bz]);
|
|
1562
2142
|
const baseRing = [];
|
|
1563
2143
|
for (let i = 0; i < CURVED_SEGMENTS2; i++) {
|
|
1564
2144
|
const theta = i / CURVED_SEGMENTS2 * Math.PI * 2;
|
|
1565
|
-
|
|
2145
|
+
const c = Math.cos(theta), s = Math.sin(theta);
|
|
2146
|
+
baseRing.push([
|
|
2147
|
+
bx + r * (c * u[0] + s * v[0]),
|
|
2148
|
+
by + r * (c * u[1] + s * v[1]),
|
|
2149
|
+
bz + r * (c * u[2] + s * v[2])
|
|
2150
|
+
]);
|
|
1566
2151
|
}
|
|
1567
2152
|
const apexIdx = baseRing.length;
|
|
1568
2153
|
const vertices = [...baseRing, apexCoords];
|
|
@@ -1571,7 +2156,7 @@ var init_cone3d = __esm({
|
|
|
1571
2156
|
faces.push([i, (i + 1) % CURVED_SEGMENTS2, apexIdx]);
|
|
1572
2157
|
}
|
|
1573
2158
|
const vertJxgs = vertices.map(
|
|
1574
|
-
(
|
|
2159
|
+
(v2, i) => view.create("point3d", v2, {
|
|
1575
2160
|
id: `${obj.id}.v${i}`,
|
|
1576
2161
|
visible: false,
|
|
1577
2162
|
fixed: true,
|
|
@@ -1647,8 +2232,12 @@ function constraintRefs2D(c) {
|
|
|
1647
2232
|
return [c.line, c.circle, c.other];
|
|
1648
2233
|
case "tangencyPoint":
|
|
1649
2234
|
return [c.circle, c.onLine];
|
|
1650
|
-
case "arcMidpoint":
|
|
1651
|
-
|
|
2235
|
+
case "arcMidpoint": {
|
|
2236
|
+
const containment = c.notContaining ?? c.containing;
|
|
2237
|
+
return containment ? [c.circle, c.a, c.b, containment] : [c.circle, c.a, c.b];
|
|
2238
|
+
}
|
|
2239
|
+
case "mixtilinearPoint":
|
|
2240
|
+
return [c.vertices[0], c.vertices[1], c.vertices[2]];
|
|
1652
2241
|
case "pointAtDistance": {
|
|
1653
2242
|
const d = c.distance;
|
|
1654
2243
|
const extra = d.kind === "circleRadius" ? [d.circle] : d.kind === "segmentLength" ? [d.p1, d.p2] : [];
|
|
@@ -1656,8 +2245,16 @@ function constraintRefs2D(c) {
|
|
|
1656
2245
|
}
|
|
1657
2246
|
case "excenter":
|
|
1658
2247
|
return [c.vertices[0], c.vertices[1], c.vertices[2]];
|
|
1659
|
-
|
|
2248
|
+
case "commonTangentPoint":
|
|
2249
|
+
return [c.circles[0], c.circles[1]];
|
|
2250
|
+
// Các kind KHÔNG có ref scene-object (toạ độ literal): liệt kê TƯỜNG MINH để
|
|
2251
|
+
// exhaustive never-guard bên dưới buộc khai báo khi thêm constraint kind mới.
|
|
2252
|
+
case "free":
|
|
2253
|
+
case "onAxis":
|
|
2254
|
+
return [];
|
|
2255
|
+
default: {
|
|
1660
2256
|
return [];
|
|
2257
|
+
}
|
|
1661
2258
|
}
|
|
1662
2259
|
}
|
|
1663
2260
|
var init_d_constraint2 = __esm({
|
|
@@ -1935,7 +2532,7 @@ function dist(p, q) {
|
|
|
1935
2532
|
function sideOf(a, b, p) {
|
|
1936
2533
|
return (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]);
|
|
1937
2534
|
}
|
|
1938
|
-
function arcMidpoint(center, radius, a, b,
|
|
2535
|
+
function arcMidpoint(center, radius, a, b, reference, sameSide = false) {
|
|
1939
2536
|
const mcx = (a[0] + b[0]) / 2;
|
|
1940
2537
|
const mcy = (a[1] + b[1]) / 2;
|
|
1941
2538
|
let ux = mcx - center[0];
|
|
@@ -1950,12 +2547,18 @@ function arcMidpoint(center, radius, a, b, notContaining) {
|
|
|
1950
2547
|
uy /= len;
|
|
1951
2548
|
const cand1 = [center[0] + radius * ux, center[1] + radius * uy];
|
|
1952
2549
|
const cand2 = [center[0] - radius * ux, center[1] - radius * uy];
|
|
2550
|
+
if (!reference) return cand1[1] >= cand2[1] ? cand1 : cand2;
|
|
2551
|
+
const notContaining = reference;
|
|
1953
2552
|
const sN = sideOf(a, b, notContaining);
|
|
1954
2553
|
if (Math.abs(sN) < 1e-9) {
|
|
1955
|
-
|
|
2554
|
+
const far = dist(cand1, notContaining) >= dist(cand2, notContaining) ? cand1 : cand2;
|
|
2555
|
+
const near = far === cand1 ? cand2 : cand1;
|
|
2556
|
+
return sameSide ? near : far;
|
|
1956
2557
|
}
|
|
1957
2558
|
const s1 = sideOf(a, b, cand1);
|
|
1958
|
-
|
|
2559
|
+
const opp = s1 * sN < 0 ? cand1 : cand2;
|
|
2560
|
+
const same = opp === cand1 ? cand2 : cand1;
|
|
2561
|
+
return sameSide ? same : opp;
|
|
1959
2562
|
}
|
|
1960
2563
|
function excenter(vertices, oppositeIndex) {
|
|
1961
2564
|
const [A, B, C] = vertices;
|
|
@@ -1971,6 +2574,35 @@ function excenter(vertices, oppositeIndex) {
|
|
|
1971
2574
|
(w[0] * A[1] + w[1] * B[1] + w[2] * C[1]) / sum
|
|
1972
2575
|
];
|
|
1973
2576
|
}
|
|
2577
|
+
function circumcenterXY(a, b, c) {
|
|
2578
|
+
const ax = a[0], ay = a[1], bx = b[0], by = b[1], cx = c[0], cy = c[1];
|
|
2579
|
+
const d = 2 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
|
|
2580
|
+
if (Math.abs(d) < 1e-12) return [(ax + bx + cx) / 3, (ay + by + cy) / 3];
|
|
2581
|
+
const ux = ((ax * ax + ay * ay) * (by - cy) + (bx * bx + by * by) * (cy - ay) + (cx * cx + cy * cy) * (ay - by)) / d;
|
|
2582
|
+
const uy = ((ax * ax + ay * ay) * (cx - bx) + (bx * bx + by * by) * (ax - cx) + (cx * cx + cy * cy) * (bx - ax)) / d;
|
|
2583
|
+
return [ux, uy];
|
|
2584
|
+
}
|
|
2585
|
+
function mixtilinearPoint(a, b, c, which) {
|
|
2586
|
+
const O = circumcenterXY(a, b, c);
|
|
2587
|
+
const R = Math.hypot(O[0] - a[0], O[1] - a[1]);
|
|
2588
|
+
const ux1 = (b[0] - a[0]) / (Math.hypot(b[0] - a[0], b[1] - a[1]) || 1);
|
|
2589
|
+
const uy1 = (b[1] - a[1]) / (Math.hypot(b[0] - a[0], b[1] - a[1]) || 1);
|
|
2590
|
+
const ux2 = (c[0] - a[0]) / (Math.hypot(c[0] - a[0], c[1] - a[1]) || 1);
|
|
2591
|
+
const uy2 = (c[1] - a[1]) / (Math.hypot(c[0] - a[0], c[1] - a[1]) || 1);
|
|
2592
|
+
let bx = ux1 + ux2, by = uy1 + uy2;
|
|
2593
|
+
const bl = Math.hypot(bx, by) || 1;
|
|
2594
|
+
bx /= bl;
|
|
2595
|
+
by /= bl;
|
|
2596
|
+
const cosA = ux1 * ux2 + uy1 * uy2;
|
|
2597
|
+
const sinHalf = Math.sqrt(Math.max(0, (1 - cosA) / 2));
|
|
2598
|
+
const cos2Half = Math.max(1e-9, (1 + cosA) / 2);
|
|
2599
|
+
const dotAO = bx * (O[0] - a[0]) + by * (O[1] - a[1]);
|
|
2600
|
+
const d = 2 * (dotAO - R * sinHalf) / cos2Half;
|
|
2601
|
+
const K = [a[0] + d * bx, a[1] + d * by];
|
|
2602
|
+
if (which === "center") return K;
|
|
2603
|
+
const kl = Math.hypot(K[0] - O[0], K[1] - O[1]) || 1;
|
|
2604
|
+
return [O[0] + R * (K[0] - O[0]) / kl, O[1] + R * (K[1] - O[1]) / kl];
|
|
2605
|
+
}
|
|
1974
2606
|
function pointAtDistanceCoord(from, through, d) {
|
|
1975
2607
|
const dx = through[0] - from[0];
|
|
1976
2608
|
const dy = through[1] - from[1];
|
|
@@ -1998,29 +2630,38 @@ var init_arcMidpoint = __esm({
|
|
|
1998
2630
|
arcMidpointConstraint = definePointConstraint({
|
|
1999
2631
|
kind: "arcMidpoint",
|
|
2000
2632
|
validate: (c) => {
|
|
2001
|
-
if (!c.circle || !c.a || !c.b
|
|
2002
|
-
throw new Error("point.arcMidpoint: circle, a, b
|
|
2633
|
+
if (!c.circle || !c.a || !c.b) {
|
|
2634
|
+
throw new Error("point.arcMidpoint: circle, a, b b\u1EAFt bu\u1ED9c");
|
|
2635
|
+
}
|
|
2636
|
+
if (c.notContaining && c.containing) {
|
|
2637
|
+
throw new Error("point.arcMidpoint: kh\xF4ng th\u1EC3 v\u1EEBa notContaining v\u1EEBa containing");
|
|
2003
2638
|
}
|
|
2004
2639
|
},
|
|
2005
2640
|
describe: (obj, state, c) => {
|
|
2006
2641
|
const al = state?.objects[c.a]?.label ?? c.a;
|
|
2007
2642
|
const bl = state?.objects[c.b]?.label ?? c.b;
|
|
2008
|
-
const
|
|
2009
|
-
return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl}
|
|
2643
|
+
const ref = c.containing ?? c.notContaining;
|
|
2644
|
+
if (!ref) return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl}`;
|
|
2645
|
+
const rl = state?.objects[ref]?.label ?? ref;
|
|
2646
|
+
const rel = c.containing ? "ch\u1EE9a" : "kh\xF4ng ch\u1EE9a";
|
|
2647
|
+
return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl} (${rel} ${rl})`;
|
|
2010
2648
|
},
|
|
2011
2649
|
render: (obj, ctx, c, opts) => {
|
|
2012
2650
|
const board = ctx.jxg;
|
|
2013
2651
|
const circle = ctx.resolveRef(c.circle);
|
|
2014
2652
|
const A = ctx.resolveRef(c.a);
|
|
2015
2653
|
const B = ctx.resolveRef(c.b);
|
|
2016
|
-
const
|
|
2654
|
+
const refName = c.containing ?? c.notContaining;
|
|
2655
|
+
const ref = refName ? ctx.resolveRef(refName) : void 0;
|
|
2656
|
+
const sameSide = !!c.containing;
|
|
2017
2657
|
const O = circle?.center ?? circle?.midpoint ?? circle;
|
|
2018
2658
|
const am = () => arcMidpoint(
|
|
2019
2659
|
[O.X(), O.Y()],
|
|
2020
2660
|
circle.Radius(),
|
|
2021
2661
|
[A.X(), A.Y()],
|
|
2022
2662
|
[B.X(), B.Y()],
|
|
2023
|
-
[
|
|
2663
|
+
ref ? [ref.X(), ref.Y()] : void 0,
|
|
2664
|
+
sameSide
|
|
2024
2665
|
);
|
|
2025
2666
|
return board.create("point", [() => am()[0], () => am()[1]], opts);
|
|
2026
2667
|
}
|
|
@@ -2070,6 +2711,43 @@ var init_excenter = __esm({
|
|
|
2070
2711
|
}
|
|
2071
2712
|
});
|
|
2072
2713
|
|
|
2714
|
+
// src/core/scene/kinds/point-constraints/mixtilinearPoint.ts
|
|
2715
|
+
var mixtilinearPointConstraint;
|
|
2716
|
+
var init_mixtilinearPoint = __esm({
|
|
2717
|
+
"src/core/scene/kinds/point-constraints/mixtilinearPoint.ts"() {
|
|
2718
|
+
init_pointConstructions();
|
|
2719
|
+
init_types3();
|
|
2720
|
+
mixtilinearPointConstraint = definePointConstraint({
|
|
2721
|
+
kind: "mixtilinearPoint",
|
|
2722
|
+
validate: (c) => {
|
|
2723
|
+
if (!Array.isArray(c.vertices) || c.vertices.length !== 3 || !c.vertices.every(Boolean)) {
|
|
2724
|
+
throw new Error("point.mixtilinearPoint: vertices ph\u1EA3i l\xE0 tuple 3 id non-empty");
|
|
2725
|
+
}
|
|
2726
|
+
if (c.which !== "center" && c.which !== "touch") {
|
|
2727
|
+
throw new Error("point.mixtilinearPoint: which ph\u1EA3i 'center' | 'touch'");
|
|
2728
|
+
}
|
|
2729
|
+
},
|
|
2730
|
+
describe: (obj, state, c) => {
|
|
2731
|
+
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
2732
|
+
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)`;
|
|
2733
|
+
},
|
|
2734
|
+
render: (obj, ctx, c, opts) => {
|
|
2735
|
+
const board = ctx.jxg;
|
|
2736
|
+
const a = ctx.resolveRef(c.vertices[0]);
|
|
2737
|
+
const b = ctx.resolveRef(c.vertices[1]);
|
|
2738
|
+
const d = ctx.resolveRef(c.vertices[2]);
|
|
2739
|
+
const mp = () => mixtilinearPoint(
|
|
2740
|
+
[a.X(), a.Y()],
|
|
2741
|
+
[b.X(), b.Y()],
|
|
2742
|
+
[d.X(), d.Y()],
|
|
2743
|
+
c.which
|
|
2744
|
+
);
|
|
2745
|
+
return board.create("point", [() => mp()[0], () => mp()[1]], opts);
|
|
2746
|
+
}
|
|
2747
|
+
});
|
|
2748
|
+
}
|
|
2749
|
+
});
|
|
2750
|
+
|
|
2073
2751
|
// src/core/scene/kinds/point-constraints/shared.ts
|
|
2074
2752
|
function buildJxgTransforms(board, ctx, t) {
|
|
2075
2753
|
switch (t.kind) {
|
|
@@ -2098,19 +2776,19 @@ function buildJxgTransforms(board, ctx, t) {
|
|
|
2098
2776
|
}
|
|
2099
2777
|
}
|
|
2100
2778
|
function makeDistanceFn(ctx, d) {
|
|
2101
|
-
const
|
|
2779
|
+
const scale2 = d.scale ?? 1;
|
|
2102
2780
|
const offset = d.offset ?? 0;
|
|
2103
2781
|
if (d.kind === "literal") {
|
|
2104
2782
|
const v = d.value;
|
|
2105
|
-
return () =>
|
|
2783
|
+
return () => scale2 * v + offset;
|
|
2106
2784
|
}
|
|
2107
2785
|
if (d.kind === "segmentLength") {
|
|
2108
2786
|
const p = ctx.resolveRef(d.p1);
|
|
2109
2787
|
const q = ctx.resolveRef(d.p2);
|
|
2110
|
-
return () =>
|
|
2788
|
+
return () => scale2 * Math.hypot(p.X() - q.X(), p.Y() - q.Y()) + offset;
|
|
2111
2789
|
}
|
|
2112
2790
|
const circle = ctx.resolveRef(d.circle);
|
|
2113
|
-
return () =>
|
|
2791
|
+
return () => scale2 * circle.Radius() + offset;
|
|
2114
2792
|
}
|
|
2115
2793
|
function buildPointOpts(obj) {
|
|
2116
2794
|
return {
|
|
@@ -2121,11 +2799,13 @@ function buildPointOpts(obj) {
|
|
|
2121
2799
|
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
2122
2800
|
fillColor: obj.attrs.color ?? "#1e40af",
|
|
2123
2801
|
face: obj.attrs.face ?? "o",
|
|
2124
|
-
size: obj.attrs.size ?? 4
|
|
2802
|
+
size: obj.attrs.size ?? 4,
|
|
2803
|
+
...labelOpts(obj.attrs.labelOffset, [10, 10])
|
|
2125
2804
|
};
|
|
2126
2805
|
}
|
|
2127
2806
|
var init_shared = __esm({
|
|
2128
2807
|
"src/core/scene/kinds/point-constraints/shared.ts"() {
|
|
2808
|
+
init_label();
|
|
2129
2809
|
}
|
|
2130
2810
|
});
|
|
2131
2811
|
|
|
@@ -2157,6 +2837,73 @@ var init_pointAtDistance = __esm({
|
|
|
2157
2837
|
}
|
|
2158
2838
|
});
|
|
2159
2839
|
|
|
2840
|
+
// src/stamps/geometry-2d/geometry/commonTangent.ts
|
|
2841
|
+
function computeCommonTangentPoint(O1, r1, O2, r2, on, variant, side) {
|
|
2842
|
+
const dx = O2[0] - O1[0];
|
|
2843
|
+
const dy = O2[1] - O1[1];
|
|
2844
|
+
const d = Math.hypot(dx, dy);
|
|
2845
|
+
if (d < EPS) return null;
|
|
2846
|
+
const beta = Math.atan2(dy, dx);
|
|
2847
|
+
const ratio = variant === "external" ? (r1 - r2) / d : (r1 + r2) / d;
|
|
2848
|
+
if (Math.abs(ratio) > 1) return null;
|
|
2849
|
+
const gamma = Math.acos(ratio);
|
|
2850
|
+
const s = side === 0 ? 1 : -1;
|
|
2851
|
+
const angle = beta + s * gamma;
|
|
2852
|
+
const nx = Math.cos(angle);
|
|
2853
|
+
const ny = Math.sin(angle);
|
|
2854
|
+
if (on === 0) {
|
|
2855
|
+
return [O1[0] + r1 * nx, O1[1] + r1 * ny];
|
|
2856
|
+
}
|
|
2857
|
+
if (variant === "external") {
|
|
2858
|
+
return [O2[0] + r2 * nx, O2[1] + r2 * ny];
|
|
2859
|
+
}
|
|
2860
|
+
return [O2[0] - r2 * nx, O2[1] - r2 * ny];
|
|
2861
|
+
}
|
|
2862
|
+
var EPS;
|
|
2863
|
+
var init_commonTangent = __esm({
|
|
2864
|
+
"src/stamps/geometry-2d/geometry/commonTangent.ts"() {
|
|
2865
|
+
EPS = 1e-9;
|
|
2866
|
+
}
|
|
2867
|
+
});
|
|
2868
|
+
|
|
2869
|
+
// src/core/scene/kinds/point-constraints/commonTangentPoint.ts
|
|
2870
|
+
var commonTangentPointConstraint;
|
|
2871
|
+
var init_commonTangentPoint = __esm({
|
|
2872
|
+
"src/core/scene/kinds/point-constraints/commonTangentPoint.ts"() {
|
|
2873
|
+
init_commonTangent();
|
|
2874
|
+
init_types3();
|
|
2875
|
+
commonTangentPointConstraint = definePointConstraint({
|
|
2876
|
+
kind: "commonTangentPoint",
|
|
2877
|
+
describe: (obj, state, c) => {
|
|
2878
|
+
const c1 = state?.objects[c.circles[0]]?.label ?? c.circles[0];
|
|
2879
|
+
const c2 = state?.objects[c.circles[1]]?.label ?? c.circles[1];
|
|
2880
|
+
const v = c.variant === "internal" ? "trong" : "ngo\xE0i";
|
|
2881
|
+
return `${obj.label} = ti\u1EBFp \u0111i\u1EC3m ti\u1EBFp tuy\u1EBFn chung ${v} ${c1},${c2} (tr\xEAn ${c.on === 0 ? c1 : c2})`;
|
|
2882
|
+
},
|
|
2883
|
+
render: (obj, ctx, c, opts) => {
|
|
2884
|
+
const board = ctx.jxg;
|
|
2885
|
+
const k1 = ctx.resolveRef(c.circles[0]);
|
|
2886
|
+
const k2 = ctx.resolveRef(c.circles[1]);
|
|
2887
|
+
const O1 = k1?.center ?? k1?.midpoint ?? k1;
|
|
2888
|
+
const O2 = k2?.center ?? k2?.midpoint ?? k2;
|
|
2889
|
+
const compute = () => {
|
|
2890
|
+
const t = computeCommonTangentPoint(
|
|
2891
|
+
[O1.X(), O1.Y()],
|
|
2892
|
+
k1.Radius(),
|
|
2893
|
+
[O2.X(), O2.Y()],
|
|
2894
|
+
k2.Radius(),
|
|
2895
|
+
c.on,
|
|
2896
|
+
c.variant,
|
|
2897
|
+
c.side
|
|
2898
|
+
);
|
|
2899
|
+
return t ?? [0, 0];
|
|
2900
|
+
};
|
|
2901
|
+
return board.create("point", [() => compute()[0], () => compute()[1]], opts);
|
|
2902
|
+
}
|
|
2903
|
+
});
|
|
2904
|
+
}
|
|
2905
|
+
});
|
|
2906
|
+
|
|
2160
2907
|
// src/core/scene/kinds/point-constraints/circleIntersection.ts
|
|
2161
2908
|
var circleIntersectionConstraint;
|
|
2162
2909
|
var init_circleIntersection = __esm({
|
|
@@ -2434,7 +3181,9 @@ var init_registry2 = __esm({
|
|
|
2434
3181
|
init_centroid();
|
|
2435
3182
|
init_arcMidpoint();
|
|
2436
3183
|
init_excenter();
|
|
3184
|
+
init_mixtilinearPoint();
|
|
2437
3185
|
init_pointAtDistance();
|
|
3186
|
+
init_commonTangentPoint();
|
|
2438
3187
|
init_circleIntersection();
|
|
2439
3188
|
init_circleSecondIntersection();
|
|
2440
3189
|
init_secondIntersection();
|
|
@@ -2459,7 +3208,9 @@ var init_registry2 = __esm({
|
|
|
2459
3208
|
centroidConstraint,
|
|
2460
3209
|
arcMidpointConstraint,
|
|
2461
3210
|
excenterConstraint,
|
|
3211
|
+
mixtilinearPointConstraint,
|
|
2462
3212
|
pointAtDistanceConstraint,
|
|
3213
|
+
commonTangentPointConstraint,
|
|
2463
3214
|
circleIntersectionConstraint,
|
|
2464
3215
|
circleSecondIntersectionConstraint,
|
|
2465
3216
|
secondIntersectionConstraint,
|
|
@@ -2476,14 +3227,15 @@ var init_registry2 = __esm({
|
|
|
2476
3227
|
});
|
|
2477
3228
|
|
|
2478
3229
|
// src/core/scene/kinds/point.ts
|
|
2479
|
-
var
|
|
3230
|
+
var def5;
|
|
2480
3231
|
var init_point = __esm({
|
|
2481
3232
|
"src/core/scene/kinds/point.ts"() {
|
|
2482
3233
|
init_registry();
|
|
2483
3234
|
init_d_constraint2();
|
|
2484
3235
|
init_registry2();
|
|
2485
3236
|
init_shared();
|
|
2486
|
-
|
|
3237
|
+
init_label();
|
|
3238
|
+
def5 = {
|
|
2487
3239
|
type: "point",
|
|
2488
3240
|
schemaVersion: 1,
|
|
2489
3241
|
migrate: {},
|
|
@@ -2550,7 +3302,8 @@ var init_point = __esm({
|
|
|
2550
3302
|
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
2551
3303
|
fillColor: obj.attrs.color ?? "#1e40af",
|
|
2552
3304
|
face: obj.attrs.face ?? "o",
|
|
2553
|
-
size: obj.attrs.size ?? 4
|
|
3305
|
+
size: obj.attrs.size ?? 4,
|
|
3306
|
+
...labelOpts(obj.attrs.labelOffset, [10, 10])
|
|
2554
3307
|
});
|
|
2555
3308
|
} catch {
|
|
2556
3309
|
}
|
|
@@ -2560,17 +3313,18 @@ var init_point = __esm({
|
|
|
2560
3313
|
throw new Error("point: constraint kind changed \u2014 recreate");
|
|
2561
3314
|
}
|
|
2562
3315
|
};
|
|
2563
|
-
registerKind(
|
|
3316
|
+
registerKind(def5);
|
|
2564
3317
|
}
|
|
2565
3318
|
});
|
|
2566
3319
|
|
|
2567
3320
|
// src/core/scene/kinds/segment.ts
|
|
2568
|
-
var
|
|
3321
|
+
var def6;
|
|
2569
3322
|
var init_segment = __esm({
|
|
2570
3323
|
"src/core/scene/kinds/segment.ts"() {
|
|
2571
3324
|
init_registry();
|
|
2572
3325
|
init_labelOf();
|
|
2573
|
-
|
|
3326
|
+
init_label();
|
|
3327
|
+
def6 = {
|
|
2574
3328
|
type: "segment",
|
|
2575
3329
|
schemaVersion: 1,
|
|
2576
3330
|
migrate: {},
|
|
@@ -2601,11 +3355,12 @@ var init_segment = __esm({
|
|
|
2601
3355
|
strokeWidth: obj.attrs.width ?? 2,
|
|
2602
3356
|
dash: obj.attrs.dash ?? 0,
|
|
2603
3357
|
visible: obj.visible,
|
|
2604
|
-
fixed: obj.locked
|
|
3358
|
+
fixed: obj.locked,
|
|
3359
|
+
...labelOpts(obj.attrs.labelOffset)
|
|
2605
3360
|
});
|
|
2606
3361
|
}
|
|
2607
3362
|
};
|
|
2608
|
-
registerKind(
|
|
3363
|
+
registerKind(def6);
|
|
2609
3364
|
}
|
|
2610
3365
|
});
|
|
2611
3366
|
|
|
@@ -2633,13 +3388,14 @@ function constructionRefs(c) {
|
|
|
2633
3388
|
return [c.throughPoint, c.toCircle];
|
|
2634
3389
|
}
|
|
2635
3390
|
}
|
|
2636
|
-
var
|
|
3391
|
+
var def7;
|
|
2637
3392
|
var init_line = __esm({
|
|
2638
3393
|
"src/core/scene/kinds/line.ts"() {
|
|
2639
3394
|
init_registry();
|
|
2640
3395
|
init_labelOf();
|
|
3396
|
+
init_label();
|
|
2641
3397
|
init_pointConstructions();
|
|
2642
|
-
|
|
3398
|
+
def7 = {
|
|
2643
3399
|
type: "line",
|
|
2644
3400
|
schemaVersion: 1,
|
|
2645
3401
|
migrate: {},
|
|
@@ -2680,7 +3436,8 @@ var init_line = __esm({
|
|
|
2680
3436
|
strokeWidth: obj.attrs.width ?? 2,
|
|
2681
3437
|
dash: obj.attrs.dash ?? 0,
|
|
2682
3438
|
visible: obj.visible,
|
|
2683
|
-
fixed: obj.locked
|
|
3439
|
+
fixed: obj.locked,
|
|
3440
|
+
...labelOpts(obj.attrs.labelOffset)
|
|
2684
3441
|
};
|
|
2685
3442
|
const c = obj.attrs.construction;
|
|
2686
3443
|
if (!c) {
|
|
@@ -2834,17 +3591,17 @@ var init_line = __esm({
|
|
|
2834
3591
|
}
|
|
2835
3592
|
}
|
|
2836
3593
|
};
|
|
2837
|
-
registerKind(
|
|
3594
|
+
registerKind(def7);
|
|
2838
3595
|
}
|
|
2839
3596
|
});
|
|
2840
3597
|
|
|
2841
3598
|
// src/core/scene/kinds/ray.ts
|
|
2842
|
-
var
|
|
3599
|
+
var def8;
|
|
2843
3600
|
var init_ray = __esm({
|
|
2844
3601
|
"src/core/scene/kinds/ray.ts"() {
|
|
2845
3602
|
init_registry();
|
|
2846
3603
|
init_labelOf();
|
|
2847
|
-
|
|
3604
|
+
def8 = {
|
|
2848
3605
|
type: "ray",
|
|
2849
3606
|
schemaVersion: 1,
|
|
2850
3607
|
migrate: {},
|
|
@@ -2869,17 +3626,17 @@ var init_ray = __esm({
|
|
|
2869
3626
|
});
|
|
2870
3627
|
}
|
|
2871
3628
|
};
|
|
2872
|
-
registerKind(
|
|
3629
|
+
registerKind(def8);
|
|
2873
3630
|
}
|
|
2874
3631
|
});
|
|
2875
3632
|
|
|
2876
3633
|
// src/core/scene/kinds/vector.ts
|
|
2877
|
-
var
|
|
3634
|
+
var def9;
|
|
2878
3635
|
var init_vector = __esm({
|
|
2879
3636
|
"src/core/scene/kinds/vector.ts"() {
|
|
2880
3637
|
init_registry();
|
|
2881
3638
|
init_labelOf();
|
|
2882
|
-
|
|
3639
|
+
def9 = {
|
|
2883
3640
|
type: "vector",
|
|
2884
3641
|
schemaVersion: 1,
|
|
2885
3642
|
migrate: {},
|
|
@@ -2901,7 +3658,7 @@ var init_vector = __esm({
|
|
|
2901
3658
|
});
|
|
2902
3659
|
}
|
|
2903
3660
|
};
|
|
2904
|
-
registerKind(
|
|
3661
|
+
registerKind(def9);
|
|
2905
3662
|
}
|
|
2906
3663
|
});
|
|
2907
3664
|
|
|
@@ -2931,13 +3688,14 @@ function constructionRefs2(c) {
|
|
|
2931
3688
|
return [c.p1, c.p2];
|
|
2932
3689
|
}
|
|
2933
3690
|
}
|
|
2934
|
-
var
|
|
3691
|
+
var def10;
|
|
2935
3692
|
var init_circle = __esm({
|
|
2936
3693
|
"src/core/scene/kinds/circle.ts"() {
|
|
2937
3694
|
init_registry();
|
|
2938
3695
|
init_labelOf();
|
|
3696
|
+
init_label();
|
|
2939
3697
|
init_pointConstructions();
|
|
2940
|
-
|
|
3698
|
+
def10 = {
|
|
2941
3699
|
type: "circle",
|
|
2942
3700
|
schemaVersion: 1,
|
|
2943
3701
|
migrate: {},
|
|
@@ -2997,15 +3755,17 @@ var init_circle = __esm({
|
|
|
2997
3755
|
const board = ctx.jxg;
|
|
2998
3756
|
const isCenterLabel = (l) => /^[A-Z]['′]?\d*$/u.test(l);
|
|
2999
3757
|
const isCenter = isCenterLabel(obj.label);
|
|
3758
|
+
const isRenamedCircle = /_c$/.test(obj.label);
|
|
3000
3759
|
const baseOpts = {
|
|
3001
3760
|
name: obj.label,
|
|
3002
|
-
withLabel: isCenter ? obj.attrs.showLabel ?? false : true,
|
|
3761
|
+
withLabel: isCenter ? obj.attrs.showLabel ?? false : isRenamedCircle ? false : true,
|
|
3003
3762
|
strokeColor: obj.attrs.color ?? "#0f172a",
|
|
3004
3763
|
strokeWidth: obj.attrs.width ?? 2,
|
|
3005
3764
|
dash: obj.attrs.dash ?? 0,
|
|
3006
3765
|
fillColor: "none",
|
|
3007
3766
|
visible: obj.visible,
|
|
3008
|
-
fixed: obj.locked
|
|
3767
|
+
fixed: obj.locked,
|
|
3768
|
+
...labelOpts(obj.attrs.labelOffset)
|
|
3009
3769
|
};
|
|
3010
3770
|
const c = asConstruction(obj.attrs);
|
|
3011
3771
|
if (c?.kind === "circumscribed") {
|
|
@@ -3087,7 +3847,7 @@ var init_circle = __esm({
|
|
|
3087
3847
|
return board.create("circle", [center, surface], baseOpts);
|
|
3088
3848
|
}
|
|
3089
3849
|
};
|
|
3090
|
-
registerKind(
|
|
3850
|
+
registerKind(def10);
|
|
3091
3851
|
}
|
|
3092
3852
|
});
|
|
3093
3853
|
|
|
@@ -3102,12 +3862,12 @@ function constructionRefs3(c) {
|
|
|
3102
3862
|
return [c.p1, c.p2, c.p3];
|
|
3103
3863
|
}
|
|
3104
3864
|
}
|
|
3105
|
-
var
|
|
3865
|
+
var def11;
|
|
3106
3866
|
var init_arc = __esm({
|
|
3107
3867
|
"src/core/scene/kinds/arc.ts"() {
|
|
3108
3868
|
init_registry();
|
|
3109
3869
|
init_labelOf();
|
|
3110
|
-
|
|
3870
|
+
def11 = {
|
|
3111
3871
|
type: "arc",
|
|
3112
3872
|
schemaVersion: 1,
|
|
3113
3873
|
migrate: {},
|
|
@@ -3165,17 +3925,17 @@ var init_arc = __esm({
|
|
|
3165
3925
|
return board.create("circumcirclearc", [A, B, C], baseOpts);
|
|
3166
3926
|
}
|
|
3167
3927
|
};
|
|
3168
|
-
registerKind(
|
|
3928
|
+
registerKind(def11);
|
|
3169
3929
|
}
|
|
3170
3930
|
});
|
|
3171
3931
|
|
|
3172
3932
|
// src/core/scene/kinds/sector.ts
|
|
3173
|
-
var
|
|
3933
|
+
var def12;
|
|
3174
3934
|
var init_sector = __esm({
|
|
3175
3935
|
"src/core/scene/kinds/sector.ts"() {
|
|
3176
3936
|
init_registry();
|
|
3177
3937
|
init_labelOf();
|
|
3178
|
-
|
|
3938
|
+
def12 = {
|
|
3179
3939
|
type: "sector",
|
|
3180
3940
|
schemaVersion: 1,
|
|
3181
3941
|
migrate: {},
|
|
@@ -3215,7 +3975,7 @@ var init_sector = __esm({
|
|
|
3215
3975
|
});
|
|
3216
3976
|
}
|
|
3217
3977
|
};
|
|
3218
|
-
registerKind(
|
|
3978
|
+
registerKind(def12);
|
|
3219
3979
|
}
|
|
3220
3980
|
});
|
|
3221
3981
|
|
|
@@ -3261,12 +4021,12 @@ function specialShapeName(kind) {
|
|
|
3261
4021
|
return "";
|
|
3262
4022
|
}
|
|
3263
4023
|
}
|
|
3264
|
-
var
|
|
4024
|
+
var def13;
|
|
3265
4025
|
var init_polygon = __esm({
|
|
3266
4026
|
"src/core/scene/kinds/polygon.ts"() {
|
|
3267
4027
|
init_registry();
|
|
3268
4028
|
init_labelOf();
|
|
3269
|
-
|
|
4029
|
+
def13 = {
|
|
3270
4030
|
type: "polygon",
|
|
3271
4031
|
schemaVersion: 1,
|
|
3272
4032
|
migrate: {},
|
|
@@ -3453,16 +4213,16 @@ var init_polygon = __esm({
|
|
|
3453
4213
|
return poly;
|
|
3454
4214
|
}
|
|
3455
4215
|
};
|
|
3456
|
-
registerKind(
|
|
4216
|
+
registerKind(def13);
|
|
3457
4217
|
}
|
|
3458
4218
|
});
|
|
3459
4219
|
|
|
3460
4220
|
// src/core/scene/kinds/intersection.ts
|
|
3461
|
-
var
|
|
4221
|
+
var def14;
|
|
3462
4222
|
var init_intersection = __esm({
|
|
3463
4223
|
"src/core/scene/kinds/intersection.ts"() {
|
|
3464
4224
|
init_registry();
|
|
3465
|
-
|
|
4225
|
+
def14 = {
|
|
3466
4226
|
type: "intersection",
|
|
3467
4227
|
schemaVersion: 1,
|
|
3468
4228
|
migrate: {},
|
|
@@ -3490,8 +4250,11 @@ var init_intersection = __esm({
|
|
|
3490
4250
|
const opts = {
|
|
3491
4251
|
name: obj.label,
|
|
3492
4252
|
withLabel: true,
|
|
3493
|
-
|
|
3494
|
-
|
|
4253
|
+
// Cùng màu xanh với mọi điểm khác (buildPointOpts dùng '#1e40af').
|
|
4254
|
+
// Trước đây điểm giao tô đỏ '#dc2626' → tách biệt thị giác nhưng gây
|
|
4255
|
+
// khó hiểu ("vì sao điểm này khác màu?"). Giữ chung một màu.
|
|
4256
|
+
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
4257
|
+
fillColor: obj.attrs.color ?? "#1e40af",
|
|
3495
4258
|
visible: obj.visible,
|
|
3496
4259
|
fixed: obj.locked
|
|
3497
4260
|
};
|
|
@@ -3500,19 +4263,51 @@ var init_intersection = __esm({
|
|
|
3500
4263
|
}
|
|
3501
4264
|
const branch = obj.attrs.branch ?? 0;
|
|
3502
4265
|
return board.create("intersection", [a, b, branch], opts);
|
|
4266
|
+
},
|
|
4267
|
+
/**
|
|
4268
|
+
* Cập nhật TẠI CHỖ các thuộc tính "trang trí" (tên/màu/ẩn-hiện/khoá) qua
|
|
4269
|
+
* setAttribute — giữ nguyên JxgObj identity nên các object phụ thuộc điểm
|
|
4270
|
+
* giao (đường thẳng qua nó, …) KHÔNG bị stale parent ref. Mô phỏng update
|
|
4271
|
+
* hook của point.ts.
|
|
4272
|
+
*
|
|
4273
|
+
* Nếu định nghĩa hình học đổi (kind/ref1/ref2/branch) thì throw → renderer
|
|
4274
|
+
* fallback remove + create để JSXGraph dựng lại phép giao đúng.
|
|
4275
|
+
*/
|
|
4276
|
+
update: (obj, prev, ctx, existing) => {
|
|
4277
|
+
const a = obj.attrs;
|
|
4278
|
+
const p = prev.attrs;
|
|
4279
|
+
const branchA = a.branch;
|
|
4280
|
+
const branchP = p.branch;
|
|
4281
|
+
if (a.kind !== p.kind || a.ref1 !== p.ref1 || a.ref2 !== p.ref2 || branchA !== branchP) {
|
|
4282
|
+
throw new Error("intersection: \u0111\u1ECBnh ngh\u0129a h\xECnh h\u1ECDc \u0111\u1ED5i \u2014 recreate");
|
|
4283
|
+
}
|
|
4284
|
+
const el = existing;
|
|
4285
|
+
if (typeof el.setAttribute === "function") {
|
|
4286
|
+
try {
|
|
4287
|
+
el.setAttribute({
|
|
4288
|
+
name: obj.label,
|
|
4289
|
+
withLabel: true,
|
|
4290
|
+
strokeColor: a.color ?? "#1e40af",
|
|
4291
|
+
fillColor: a.color ?? "#1e40af",
|
|
4292
|
+
visible: obj.visible,
|
|
4293
|
+
fixed: obj.locked
|
|
4294
|
+
});
|
|
4295
|
+
} catch {
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
3503
4298
|
}
|
|
3504
4299
|
};
|
|
3505
|
-
registerKind(
|
|
4300
|
+
registerKind(def14);
|
|
3506
4301
|
}
|
|
3507
4302
|
});
|
|
3508
4303
|
|
|
3509
4304
|
// src/core/scene/kinds/angle.ts
|
|
3510
|
-
var
|
|
4305
|
+
var def15;
|
|
3511
4306
|
var init_angle = __esm({
|
|
3512
4307
|
"src/core/scene/kinds/angle.ts"() {
|
|
3513
4308
|
init_registry();
|
|
3514
4309
|
init_labelOf();
|
|
3515
|
-
|
|
4310
|
+
def15 = {
|
|
3516
4311
|
type: "angle",
|
|
3517
4312
|
schemaVersion: 1,
|
|
3518
4313
|
migrate: {},
|
|
@@ -3548,17 +4343,17 @@ var init_angle = __esm({
|
|
|
3548
4343
|
});
|
|
3549
4344
|
}
|
|
3550
4345
|
};
|
|
3551
|
-
registerKind(
|
|
4346
|
+
registerKind(def15);
|
|
3552
4347
|
}
|
|
3553
4348
|
});
|
|
3554
4349
|
|
|
3555
4350
|
// src/core/scene/kinds/distance.ts
|
|
3556
|
-
var
|
|
4351
|
+
var def16;
|
|
3557
4352
|
var init_distance = __esm({
|
|
3558
4353
|
"src/core/scene/kinds/distance.ts"() {
|
|
3559
4354
|
init_registry();
|
|
3560
4355
|
init_labelOf();
|
|
3561
|
-
|
|
4356
|
+
def16 = {
|
|
3562
4357
|
type: "distance",
|
|
3563
4358
|
schemaVersion: 1,
|
|
3564
4359
|
migrate: {},
|
|
@@ -3587,17 +4382,17 @@ var init_distance = __esm({
|
|
|
3587
4382
|
});
|
|
3588
4383
|
}
|
|
3589
4384
|
};
|
|
3590
|
-
registerKind(
|
|
4385
|
+
registerKind(def16);
|
|
3591
4386
|
}
|
|
3592
4387
|
});
|
|
3593
4388
|
|
|
3594
4389
|
// src/core/scene/kinds/function2d.ts
|
|
3595
|
-
var
|
|
4390
|
+
var def17;
|
|
3596
4391
|
var init_function2d = __esm({
|
|
3597
4392
|
"src/core/scene/kinds/function2d.ts"() {
|
|
3598
4393
|
init_registry();
|
|
3599
4394
|
init_parser();
|
|
3600
|
-
|
|
4395
|
+
def17 = {
|
|
3601
4396
|
type: "function2d",
|
|
3602
4397
|
schemaVersion: 1,
|
|
3603
4398
|
migrate: {},
|
|
@@ -3636,16 +4431,16 @@ var init_function2d = __esm({
|
|
|
3636
4431
|
});
|
|
3637
4432
|
}
|
|
3638
4433
|
};
|
|
3639
|
-
registerKind(
|
|
4434
|
+
registerKind(def17);
|
|
3640
4435
|
}
|
|
3641
4436
|
});
|
|
3642
4437
|
|
|
3643
4438
|
// src/core/scene/kinds/parameter.ts
|
|
3644
|
-
var
|
|
4439
|
+
var def18;
|
|
3645
4440
|
var init_parameter = __esm({
|
|
3646
4441
|
"src/core/scene/kinds/parameter.ts"() {
|
|
3647
4442
|
init_registry();
|
|
3648
|
-
|
|
4443
|
+
def18 = {
|
|
3649
4444
|
type: "parameter",
|
|
3650
4445
|
schemaVersion: 1,
|
|
3651
4446
|
migrate: {},
|
|
@@ -3663,16 +4458,16 @@ var init_parameter = __esm({
|
|
|
3663
4458
|
render: () => null
|
|
3664
4459
|
// Không render lên board
|
|
3665
4460
|
};
|
|
3666
|
-
registerKind(
|
|
4461
|
+
registerKind(def18);
|
|
3667
4462
|
}
|
|
3668
4463
|
});
|
|
3669
4464
|
|
|
3670
4465
|
// src/core/scene/kinds/pointOnCurve.ts
|
|
3671
|
-
var
|
|
4466
|
+
var def19;
|
|
3672
4467
|
var init_pointOnCurve = __esm({
|
|
3673
4468
|
"src/core/scene/kinds/pointOnCurve.ts"() {
|
|
3674
4469
|
init_registry();
|
|
3675
|
-
|
|
4470
|
+
def19 = {
|
|
3676
4471
|
type: "pointOnCurve",
|
|
3677
4472
|
schemaVersion: 1,
|
|
3678
4473
|
migrate: {},
|
|
@@ -3699,16 +4494,16 @@ var init_pointOnCurve = __esm({
|
|
|
3699
4494
|
});
|
|
3700
4495
|
}
|
|
3701
4496
|
};
|
|
3702
|
-
registerKind(
|
|
4497
|
+
registerKind(def19);
|
|
3703
4498
|
}
|
|
3704
4499
|
});
|
|
3705
4500
|
|
|
3706
4501
|
// src/core/scene/kinds/tangent2d.ts
|
|
3707
|
-
var
|
|
4502
|
+
var def20;
|
|
3708
4503
|
var init_tangent2d = __esm({
|
|
3709
4504
|
"src/core/scene/kinds/tangent2d.ts"() {
|
|
3710
4505
|
init_registry();
|
|
3711
|
-
|
|
4506
|
+
def20 = {
|
|
3712
4507
|
type: "tangent2d",
|
|
3713
4508
|
schemaVersion: 1,
|
|
3714
4509
|
migrate: {},
|
|
@@ -3731,7 +4526,7 @@ var init_tangent2d = __esm({
|
|
|
3731
4526
|
});
|
|
3732
4527
|
}
|
|
3733
4528
|
};
|
|
3734
|
-
registerKind(
|
|
4529
|
+
registerKind(def20);
|
|
3735
4530
|
}
|
|
3736
4531
|
});
|
|
3737
4532
|
|
|
@@ -3807,13 +4602,13 @@ var init_evaluator = __esm({
|
|
|
3807
4602
|
});
|
|
3808
4603
|
|
|
3809
4604
|
// src/core/scene/kinds/extremum2d.ts
|
|
3810
|
-
var
|
|
4605
|
+
var def21;
|
|
3811
4606
|
var init_extremum2d = __esm({
|
|
3812
4607
|
"src/core/scene/kinds/extremum2d.ts"() {
|
|
3813
4608
|
init_registry();
|
|
3814
4609
|
init_evaluator();
|
|
3815
4610
|
init_parser();
|
|
3816
|
-
|
|
4611
|
+
def21 = {
|
|
3817
4612
|
type: "extremum2d",
|
|
3818
4613
|
schemaVersion: 1,
|
|
3819
4614
|
migrate: {},
|
|
@@ -3846,18 +4641,18 @@ var init_extremum2d = __esm({
|
|
|
3846
4641
|
}));
|
|
3847
4642
|
}
|
|
3848
4643
|
};
|
|
3849
|
-
registerKind(
|
|
4644
|
+
registerKind(def21);
|
|
3850
4645
|
}
|
|
3851
4646
|
});
|
|
3852
4647
|
|
|
3853
4648
|
// src/core/scene/kinds/root2d.ts
|
|
3854
|
-
var
|
|
4649
|
+
var def22;
|
|
3855
4650
|
var init_root2d = __esm({
|
|
3856
4651
|
"src/core/scene/kinds/root2d.ts"() {
|
|
3857
4652
|
init_registry();
|
|
3858
4653
|
init_evaluator();
|
|
3859
4654
|
init_parser();
|
|
3860
|
-
|
|
4655
|
+
def22 = {
|
|
3861
4656
|
type: "root2d",
|
|
3862
4657
|
schemaVersion: 1,
|
|
3863
4658
|
migrate: {},
|
|
@@ -3887,16 +4682,16 @@ var init_root2d = __esm({
|
|
|
3887
4682
|
}));
|
|
3888
4683
|
}
|
|
3889
4684
|
};
|
|
3890
|
-
registerKind(
|
|
4685
|
+
registerKind(def22);
|
|
3891
4686
|
}
|
|
3892
4687
|
});
|
|
3893
4688
|
|
|
3894
4689
|
// src/core/scene/kinds/slope2d.ts
|
|
3895
|
-
var
|
|
4690
|
+
var def23;
|
|
3896
4691
|
var init_slope2d = __esm({
|
|
3897
4692
|
"src/core/scene/kinds/slope2d.ts"() {
|
|
3898
4693
|
init_registry();
|
|
3899
|
-
|
|
4694
|
+
def23 = {
|
|
3900
4695
|
type: "slope2d",
|
|
3901
4696
|
schemaVersion: 1,
|
|
3902
4697
|
migrate: {},
|
|
@@ -3920,7 +4715,7 @@ var init_slope2d = __esm({
|
|
|
3920
4715
|
});
|
|
3921
4716
|
}
|
|
3922
4717
|
};
|
|
3923
|
-
registerKind(
|
|
4718
|
+
registerKind(def23);
|
|
3924
4719
|
}
|
|
3925
4720
|
});
|
|
3926
4721
|
|
|
@@ -5619,7 +6414,7 @@ var init_useToolHoverTooltip = __esm({
|
|
|
5619
6414
|
init_types4();
|
|
5620
6415
|
}
|
|
5621
6416
|
});
|
|
5622
|
-
function
|
|
6417
|
+
function normalize2(s) {
|
|
5623
6418
|
return s.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/đ/g, "d").replace(/Đ/g, "d");
|
|
5624
6419
|
}
|
|
5625
6420
|
function SearchIcon() {
|
|
@@ -5666,12 +6461,12 @@ function ToolGrid(props) {
|
|
|
5666
6461
|
const { tools, groupOrder, groupLabels, activeTool, onToolChange, chord } = props;
|
|
5667
6462
|
const { hover, portalReady, showHover, hideHover } = useToolHoverTooltip();
|
|
5668
6463
|
const [query, setQuery] = React5.useState("");
|
|
5669
|
-
const normalizedQuery = React5.useMemo(() =>
|
|
6464
|
+
const normalizedQuery = React5.useMemo(() => normalize2(query.trim()), [query]);
|
|
5670
6465
|
const filteredTools = React5.useMemo(() => {
|
|
5671
6466
|
if (!normalizedQuery) return tools;
|
|
5672
6467
|
return tools.filter((t) => {
|
|
5673
|
-
if (
|
|
5674
|
-
if (t.hint &&
|
|
6468
|
+
if (normalize2(t.label).includes(normalizedQuery)) return true;
|
|
6469
|
+
if (t.hint && normalize2(t.hint).includes(normalizedQuery)) return true;
|
|
5675
6470
|
return false;
|
|
5676
6471
|
});
|
|
5677
6472
|
}, [tools, normalizedQuery]);
|
|
@@ -6258,11 +7053,17 @@ async function insertStampImage(api, opts) {
|
|
|
6258
7053
|
const elements = api.getSceneElements();
|
|
6259
7054
|
const editingId = opts.editingElementId ?? null;
|
|
6260
7055
|
if (editingId) {
|
|
7056
|
+
const old = elements.find((e) => e.id === editingId) ;
|
|
7057
|
+
const oldLongest = old ? Math.max(old.width ?? 0, old.height ?? 0) : 0;
|
|
7058
|
+
const newLongest = Math.max(width, height);
|
|
7059
|
+
const scale2 = oldLongest > 0 && newLongest > 0 ? oldLongest / newLongest : 1;
|
|
7060
|
+
const w = width * scale2;
|
|
7061
|
+
const h = height * scale2;
|
|
6261
7062
|
const updated = elements.map(
|
|
6262
|
-
(e) => e.id === editingId ? { ...e, fileId, customData, width, height } : e
|
|
7063
|
+
(e) => e.id === editingId ? { ...e, fileId, customData, width: w, height: h } : e
|
|
6263
7064
|
);
|
|
6264
7065
|
api.updateScene({ elements: updated, appState: clearAppStateAfterInsert() });
|
|
6265
|
-
return { fileId, width, height, elementId: editingId };
|
|
7066
|
+
return { fileId, width: w, height: h, elementId: editingId };
|
|
6266
7067
|
}
|
|
6267
7068
|
const newElement = buildStampImageElement(
|
|
6268
7069
|
api,
|
|
@@ -6736,7 +7537,8 @@ var init_host = __esm({
|
|
|
6736
7537
|
version: 2,
|
|
6737
7538
|
jsonState
|
|
6738
7539
|
}),
|
|
6739
|
-
editingElementId: editingElement?.id ?? null
|
|
7540
|
+
editingElementId: editingElement?.id ?? null,
|
|
7541
|
+
preserveExistingSize: true
|
|
6740
7542
|
});
|
|
6741
7543
|
} catch (err) {
|
|
6742
7544
|
console.error("Graph2D insert failed:", err);
|