@xom11/whiteboard 0.30.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 +226 -32
- package/dist/ai.d.ts +226 -32
- package/dist/ai.js +5126 -351
- package/dist/ai.js.map +1 -1
- package/dist/ai.mjs +4970 -352
- 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-XVVLT6B3.mjs → chunk-BMYC2ILT.mjs} +4 -4
- package/dist/{chunk-XVVLT6B3.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-SF3U7ZF4.mjs → chunk-DWIEVCGK.mjs} +180 -15
- 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-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 +449 -172
- 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/{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-3UFGFMJ2.mjs → host-KMWP7KBT.mjs} +90 -43
- package/dist/host-KMWP7KBT.mjs.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +453 -174
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +21 -21
- 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 +1 -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-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.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/geometry-3d.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
export { geometry3dStamp } from './chunk-
|
|
3
|
-
import './chunk-
|
|
2
|
+
export { geometry3dStamp } from './chunk-TMRFSOM7.mjs';
|
|
3
|
+
import './chunk-T3N4BSJV.mjs';
|
|
4
4
|
import './chunk-R5FL6S7L.mjs';
|
|
5
5
|
import './chunk-ICR4CVOE.mjs';
|
|
6
|
-
import './chunk-
|
|
7
|
-
import './chunk-
|
|
6
|
+
import './chunk-JJ4FPCBE.mjs';
|
|
7
|
+
import './chunk-CH6SFONH.mjs';
|
|
8
8
|
import './chunk-73Q7ADVL.mjs';
|
|
9
9
|
import './chunk-B4NJJZFR.mjs';
|
|
10
10
|
import './chunk-5UTGXHLJ.mjs';
|
package/dist/graph-2d.d.mts
CHANGED
package/dist/graph-2d.d.ts
CHANGED
package/dist/graph-2d.js
CHANGED
|
@@ -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();
|
|
@@ -467,6 +484,7 @@ var init_JxgRenderer = __esm({
|
|
|
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);
|
|
@@ -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);
|
|
@@ -1647,8 +1714,12 @@ function constraintRefs2D(c) {
|
|
|
1647
1714
|
return [c.line, c.circle, c.other];
|
|
1648
1715
|
case "tangencyPoint":
|
|
1649
1716
|
return [c.circle, c.onLine];
|
|
1650
|
-
case "arcMidpoint":
|
|
1651
|
-
|
|
1717
|
+
case "arcMidpoint": {
|
|
1718
|
+
const containment = c.notContaining ?? c.containing;
|
|
1719
|
+
return containment ? [c.circle, c.a, c.b, containment] : [c.circle, c.a, c.b];
|
|
1720
|
+
}
|
|
1721
|
+
case "mixtilinearPoint":
|
|
1722
|
+
return [c.vertices[0], c.vertices[1], c.vertices[2]];
|
|
1652
1723
|
case "pointAtDistance": {
|
|
1653
1724
|
const d = c.distance;
|
|
1654
1725
|
const extra = d.kind === "circleRadius" ? [d.circle] : d.kind === "segmentLength" ? [d.p1, d.p2] : [];
|
|
@@ -1935,7 +2006,7 @@ function dist(p, q) {
|
|
|
1935
2006
|
function sideOf(a, b, p) {
|
|
1936
2007
|
return (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]);
|
|
1937
2008
|
}
|
|
1938
|
-
function arcMidpoint(center, radius, a, b,
|
|
2009
|
+
function arcMidpoint(center, radius, a, b, reference, sameSide = false) {
|
|
1939
2010
|
const mcx = (a[0] + b[0]) / 2;
|
|
1940
2011
|
const mcy = (a[1] + b[1]) / 2;
|
|
1941
2012
|
let ux = mcx - center[0];
|
|
@@ -1950,12 +2021,18 @@ function arcMidpoint(center, radius, a, b, notContaining) {
|
|
|
1950
2021
|
uy /= len;
|
|
1951
2022
|
const cand1 = [center[0] + radius * ux, center[1] + radius * uy];
|
|
1952
2023
|
const cand2 = [center[0] - radius * ux, center[1] - radius * uy];
|
|
2024
|
+
if (!reference) return cand1[1] >= cand2[1] ? cand1 : cand2;
|
|
2025
|
+
const notContaining = reference;
|
|
1953
2026
|
const sN = sideOf(a, b, notContaining);
|
|
1954
2027
|
if (Math.abs(sN) < 1e-9) {
|
|
1955
|
-
|
|
2028
|
+
const far = dist(cand1, notContaining) >= dist(cand2, notContaining) ? cand1 : cand2;
|
|
2029
|
+
const near = far === cand1 ? cand2 : cand1;
|
|
2030
|
+
return sameSide ? near : far;
|
|
1956
2031
|
}
|
|
1957
2032
|
const s1 = sideOf(a, b, cand1);
|
|
1958
|
-
|
|
2033
|
+
const opp = s1 * sN < 0 ? cand1 : cand2;
|
|
2034
|
+
const same = opp === cand1 ? cand2 : cand1;
|
|
2035
|
+
return sameSide ? same : opp;
|
|
1959
2036
|
}
|
|
1960
2037
|
function excenter(vertices, oppositeIndex) {
|
|
1961
2038
|
const [A, B, C] = vertices;
|
|
@@ -1971,6 +2048,35 @@ function excenter(vertices, oppositeIndex) {
|
|
|
1971
2048
|
(w[0] * A[1] + w[1] * B[1] + w[2] * C[1]) / sum
|
|
1972
2049
|
];
|
|
1973
2050
|
}
|
|
2051
|
+
function circumcenterXY(a, b, c) {
|
|
2052
|
+
const ax = a[0], ay = a[1], bx = b[0], by = b[1], cx = c[0], cy = c[1];
|
|
2053
|
+
const d = 2 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
|
|
2054
|
+
if (Math.abs(d) < 1e-12) return [(ax + bx + cx) / 3, (ay + by + cy) / 3];
|
|
2055
|
+
const ux = ((ax * ax + ay * ay) * (by - cy) + (bx * bx + by * by) * (cy - ay) + (cx * cx + cy * cy) * (ay - by)) / d;
|
|
2056
|
+
const uy = ((ax * ax + ay * ay) * (cx - bx) + (bx * bx + by * by) * (ax - cx) + (cx * cx + cy * cy) * (bx - ax)) / d;
|
|
2057
|
+
return [ux, uy];
|
|
2058
|
+
}
|
|
2059
|
+
function mixtilinearPoint(a, b, c, which) {
|
|
2060
|
+
const O = circumcenterXY(a, b, c);
|
|
2061
|
+
const R = Math.hypot(O[0] - a[0], O[1] - a[1]);
|
|
2062
|
+
const ux1 = (b[0] - a[0]) / (Math.hypot(b[0] - a[0], b[1] - a[1]) || 1);
|
|
2063
|
+
const uy1 = (b[1] - a[1]) / (Math.hypot(b[0] - a[0], b[1] - a[1]) || 1);
|
|
2064
|
+
const ux2 = (c[0] - a[0]) / (Math.hypot(c[0] - a[0], c[1] - a[1]) || 1);
|
|
2065
|
+
const uy2 = (c[1] - a[1]) / (Math.hypot(c[0] - a[0], c[1] - a[1]) || 1);
|
|
2066
|
+
let bx = ux1 + ux2, by = uy1 + uy2;
|
|
2067
|
+
const bl = Math.hypot(bx, by) || 1;
|
|
2068
|
+
bx /= bl;
|
|
2069
|
+
by /= bl;
|
|
2070
|
+
const cosA = ux1 * ux2 + uy1 * uy2;
|
|
2071
|
+
const sinHalf = Math.sqrt(Math.max(0, (1 - cosA) / 2));
|
|
2072
|
+
const cos2Half = Math.max(1e-9, (1 + cosA) / 2);
|
|
2073
|
+
const dotAO = bx * (O[0] - a[0]) + by * (O[1] - a[1]);
|
|
2074
|
+
const d = 2 * (dotAO - R * sinHalf) / cos2Half;
|
|
2075
|
+
const K = [a[0] + d * bx, a[1] + d * by];
|
|
2076
|
+
if (which === "center") return K;
|
|
2077
|
+
const kl = Math.hypot(K[0] - O[0], K[1] - O[1]) || 1;
|
|
2078
|
+
return [O[0] + R * (K[0] - O[0]) / kl, O[1] + R * (K[1] - O[1]) / kl];
|
|
2079
|
+
}
|
|
1974
2080
|
function pointAtDistanceCoord(from, through, d) {
|
|
1975
2081
|
const dx = through[0] - from[0];
|
|
1976
2082
|
const dy = through[1] - from[1];
|
|
@@ -1998,29 +2104,38 @@ var init_arcMidpoint = __esm({
|
|
|
1998
2104
|
arcMidpointConstraint = definePointConstraint({
|
|
1999
2105
|
kind: "arcMidpoint",
|
|
2000
2106
|
validate: (c) => {
|
|
2001
|
-
if (!c.circle || !c.a || !c.b
|
|
2002
|
-
throw new Error("point.arcMidpoint: circle, a, b
|
|
2107
|
+
if (!c.circle || !c.a || !c.b) {
|
|
2108
|
+
throw new Error("point.arcMidpoint: circle, a, b b\u1EAFt bu\u1ED9c");
|
|
2109
|
+
}
|
|
2110
|
+
if (c.notContaining && c.containing) {
|
|
2111
|
+
throw new Error("point.arcMidpoint: kh\xF4ng th\u1EC3 v\u1EEBa notContaining v\u1EEBa containing");
|
|
2003
2112
|
}
|
|
2004
2113
|
},
|
|
2005
2114
|
describe: (obj, state, c) => {
|
|
2006
2115
|
const al = state?.objects[c.a]?.label ?? c.a;
|
|
2007
2116
|
const bl = state?.objects[c.b]?.label ?? c.b;
|
|
2008
|
-
const
|
|
2009
|
-
return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl}
|
|
2117
|
+
const ref = c.containing ?? c.notContaining;
|
|
2118
|
+
if (!ref) return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl}`;
|
|
2119
|
+
const rl = state?.objects[ref]?.label ?? ref;
|
|
2120
|
+
const rel = c.containing ? "ch\u1EE9a" : "kh\xF4ng ch\u1EE9a";
|
|
2121
|
+
return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl} (${rel} ${rl})`;
|
|
2010
2122
|
},
|
|
2011
2123
|
render: (obj, ctx, c, opts) => {
|
|
2012
2124
|
const board = ctx.jxg;
|
|
2013
2125
|
const circle = ctx.resolveRef(c.circle);
|
|
2014
2126
|
const A = ctx.resolveRef(c.a);
|
|
2015
2127
|
const B = ctx.resolveRef(c.b);
|
|
2016
|
-
const
|
|
2128
|
+
const refName = c.containing ?? c.notContaining;
|
|
2129
|
+
const ref = refName ? ctx.resolveRef(refName) : void 0;
|
|
2130
|
+
const sameSide = !!c.containing;
|
|
2017
2131
|
const O = circle?.center ?? circle?.midpoint ?? circle;
|
|
2018
2132
|
const am = () => arcMidpoint(
|
|
2019
2133
|
[O.X(), O.Y()],
|
|
2020
2134
|
circle.Radius(),
|
|
2021
2135
|
[A.X(), A.Y()],
|
|
2022
2136
|
[B.X(), B.Y()],
|
|
2023
|
-
[
|
|
2137
|
+
ref ? [ref.X(), ref.Y()] : void 0,
|
|
2138
|
+
sameSide
|
|
2024
2139
|
);
|
|
2025
2140
|
return board.create("point", [() => am()[0], () => am()[1]], opts);
|
|
2026
2141
|
}
|
|
@@ -2070,6 +2185,43 @@ var init_excenter = __esm({
|
|
|
2070
2185
|
}
|
|
2071
2186
|
});
|
|
2072
2187
|
|
|
2188
|
+
// src/core/scene/kinds/point-constraints/mixtilinearPoint.ts
|
|
2189
|
+
var mixtilinearPointConstraint;
|
|
2190
|
+
var init_mixtilinearPoint = __esm({
|
|
2191
|
+
"src/core/scene/kinds/point-constraints/mixtilinearPoint.ts"() {
|
|
2192
|
+
init_pointConstructions();
|
|
2193
|
+
init_types3();
|
|
2194
|
+
mixtilinearPointConstraint = definePointConstraint({
|
|
2195
|
+
kind: "mixtilinearPoint",
|
|
2196
|
+
validate: (c) => {
|
|
2197
|
+
if (!Array.isArray(c.vertices) || c.vertices.length !== 3 || !c.vertices.every(Boolean)) {
|
|
2198
|
+
throw new Error("point.mixtilinearPoint: vertices ph\u1EA3i l\xE0 tuple 3 id non-empty");
|
|
2199
|
+
}
|
|
2200
|
+
if (c.which !== "center" && c.which !== "touch") {
|
|
2201
|
+
throw new Error("point.mixtilinearPoint: which ph\u1EA3i 'center' | 'touch'");
|
|
2202
|
+
}
|
|
2203
|
+
},
|
|
2204
|
+
describe: (obj, state, c) => {
|
|
2205
|
+
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
2206
|
+
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)`;
|
|
2207
|
+
},
|
|
2208
|
+
render: (obj, ctx, c, opts) => {
|
|
2209
|
+
const board = ctx.jxg;
|
|
2210
|
+
const a = ctx.resolveRef(c.vertices[0]);
|
|
2211
|
+
const b = ctx.resolveRef(c.vertices[1]);
|
|
2212
|
+
const d = ctx.resolveRef(c.vertices[2]);
|
|
2213
|
+
const mp = () => mixtilinearPoint(
|
|
2214
|
+
[a.X(), a.Y()],
|
|
2215
|
+
[b.X(), b.Y()],
|
|
2216
|
+
[d.X(), d.Y()],
|
|
2217
|
+
c.which
|
|
2218
|
+
);
|
|
2219
|
+
return board.create("point", [() => mp()[0], () => mp()[1]], opts);
|
|
2220
|
+
}
|
|
2221
|
+
});
|
|
2222
|
+
}
|
|
2223
|
+
});
|
|
2224
|
+
|
|
2073
2225
|
// src/core/scene/kinds/point-constraints/shared.ts
|
|
2074
2226
|
function buildJxgTransforms(board, ctx, t) {
|
|
2075
2227
|
switch (t.kind) {
|
|
@@ -2121,11 +2273,13 @@ function buildPointOpts(obj) {
|
|
|
2121
2273
|
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
2122
2274
|
fillColor: obj.attrs.color ?? "#1e40af",
|
|
2123
2275
|
face: obj.attrs.face ?? "o",
|
|
2124
|
-
size: obj.attrs.size ?? 4
|
|
2276
|
+
size: obj.attrs.size ?? 4,
|
|
2277
|
+
...labelOpts(obj.attrs.labelOffset, [10, 10])
|
|
2125
2278
|
};
|
|
2126
2279
|
}
|
|
2127
2280
|
var init_shared = __esm({
|
|
2128
2281
|
"src/core/scene/kinds/point-constraints/shared.ts"() {
|
|
2282
|
+
init_label();
|
|
2129
2283
|
}
|
|
2130
2284
|
});
|
|
2131
2285
|
|
|
@@ -2434,6 +2588,7 @@ var init_registry2 = __esm({
|
|
|
2434
2588
|
init_centroid();
|
|
2435
2589
|
init_arcMidpoint();
|
|
2436
2590
|
init_excenter();
|
|
2591
|
+
init_mixtilinearPoint();
|
|
2437
2592
|
init_pointAtDistance();
|
|
2438
2593
|
init_circleIntersection();
|
|
2439
2594
|
init_circleSecondIntersection();
|
|
@@ -2459,6 +2614,7 @@ var init_registry2 = __esm({
|
|
|
2459
2614
|
centroidConstraint,
|
|
2460
2615
|
arcMidpointConstraint,
|
|
2461
2616
|
excenterConstraint,
|
|
2617
|
+
mixtilinearPointConstraint,
|
|
2462
2618
|
pointAtDistanceConstraint,
|
|
2463
2619
|
circleIntersectionConstraint,
|
|
2464
2620
|
circleSecondIntersectionConstraint,
|
|
@@ -2483,6 +2639,7 @@ var init_point = __esm({
|
|
|
2483
2639
|
init_d_constraint2();
|
|
2484
2640
|
init_registry2();
|
|
2485
2641
|
init_shared();
|
|
2642
|
+
init_label();
|
|
2486
2643
|
def3 = {
|
|
2487
2644
|
type: "point",
|
|
2488
2645
|
schemaVersion: 1,
|
|
@@ -2550,7 +2707,8 @@ var init_point = __esm({
|
|
|
2550
2707
|
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
2551
2708
|
fillColor: obj.attrs.color ?? "#1e40af",
|
|
2552
2709
|
face: obj.attrs.face ?? "o",
|
|
2553
|
-
size: obj.attrs.size ?? 4
|
|
2710
|
+
size: obj.attrs.size ?? 4,
|
|
2711
|
+
...labelOpts(obj.attrs.labelOffset, [10, 10])
|
|
2554
2712
|
});
|
|
2555
2713
|
} catch {
|
|
2556
2714
|
}
|
|
@@ -2570,6 +2728,7 @@ var init_segment = __esm({
|
|
|
2570
2728
|
"src/core/scene/kinds/segment.ts"() {
|
|
2571
2729
|
init_registry();
|
|
2572
2730
|
init_labelOf();
|
|
2731
|
+
init_label();
|
|
2573
2732
|
def4 = {
|
|
2574
2733
|
type: "segment",
|
|
2575
2734
|
schemaVersion: 1,
|
|
@@ -2601,7 +2760,8 @@ var init_segment = __esm({
|
|
|
2601
2760
|
strokeWidth: obj.attrs.width ?? 2,
|
|
2602
2761
|
dash: obj.attrs.dash ?? 0,
|
|
2603
2762
|
visible: obj.visible,
|
|
2604
|
-
fixed: obj.locked
|
|
2763
|
+
fixed: obj.locked,
|
|
2764
|
+
...labelOpts(obj.attrs.labelOffset)
|
|
2605
2765
|
});
|
|
2606
2766
|
}
|
|
2607
2767
|
};
|
|
@@ -2638,6 +2798,7 @@ var init_line = __esm({
|
|
|
2638
2798
|
"src/core/scene/kinds/line.ts"() {
|
|
2639
2799
|
init_registry();
|
|
2640
2800
|
init_labelOf();
|
|
2801
|
+
init_label();
|
|
2641
2802
|
init_pointConstructions();
|
|
2642
2803
|
def5 = {
|
|
2643
2804
|
type: "line",
|
|
@@ -2680,7 +2841,8 @@ var init_line = __esm({
|
|
|
2680
2841
|
strokeWidth: obj.attrs.width ?? 2,
|
|
2681
2842
|
dash: obj.attrs.dash ?? 0,
|
|
2682
2843
|
visible: obj.visible,
|
|
2683
|
-
fixed: obj.locked
|
|
2844
|
+
fixed: obj.locked,
|
|
2845
|
+
...labelOpts(obj.attrs.labelOffset)
|
|
2684
2846
|
};
|
|
2685
2847
|
const c = obj.attrs.construction;
|
|
2686
2848
|
if (!c) {
|
|
@@ -2936,6 +3098,7 @@ var init_circle = __esm({
|
|
|
2936
3098
|
"src/core/scene/kinds/circle.ts"() {
|
|
2937
3099
|
init_registry();
|
|
2938
3100
|
init_labelOf();
|
|
3101
|
+
init_label();
|
|
2939
3102
|
init_pointConstructions();
|
|
2940
3103
|
def8 = {
|
|
2941
3104
|
type: "circle",
|
|
@@ -2997,15 +3160,17 @@ var init_circle = __esm({
|
|
|
2997
3160
|
const board = ctx.jxg;
|
|
2998
3161
|
const isCenterLabel = (l) => /^[A-Z]['′]?\d*$/u.test(l);
|
|
2999
3162
|
const isCenter = isCenterLabel(obj.label);
|
|
3163
|
+
const isRenamedCircle = /_c$/.test(obj.label);
|
|
3000
3164
|
const baseOpts = {
|
|
3001
3165
|
name: obj.label,
|
|
3002
|
-
withLabel: isCenter ? obj.attrs.showLabel ?? false : true,
|
|
3166
|
+
withLabel: isCenter ? obj.attrs.showLabel ?? false : isRenamedCircle ? false : true,
|
|
3003
3167
|
strokeColor: obj.attrs.color ?? "#0f172a",
|
|
3004
3168
|
strokeWidth: obj.attrs.width ?? 2,
|
|
3005
3169
|
dash: obj.attrs.dash ?? 0,
|
|
3006
3170
|
fillColor: "none",
|
|
3007
3171
|
visible: obj.visible,
|
|
3008
|
-
fixed: obj.locked
|
|
3172
|
+
fixed: obj.locked,
|
|
3173
|
+
...labelOpts(obj.attrs.labelOffset)
|
|
3009
3174
|
};
|
|
3010
3175
|
const c = asConstruction(obj.attrs);
|
|
3011
3176
|
if (c?.kind === "circumscribed") {
|
|
@@ -3490,8 +3655,11 @@ var init_intersection = __esm({
|
|
|
3490
3655
|
const opts = {
|
|
3491
3656
|
name: obj.label,
|
|
3492
3657
|
withLabel: true,
|
|
3493
|
-
|
|
3494
|
-
|
|
3658
|
+
// Cùng màu xanh với mọi điểm khác (buildPointOpts dùng '#1e40af').
|
|
3659
|
+
// Trước đây điểm giao tô đỏ '#dc2626' → tách biệt thị giác nhưng gây
|
|
3660
|
+
// khó hiểu ("vì sao điểm này khác màu?"). Giữ chung một màu.
|
|
3661
|
+
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
3662
|
+
fillColor: obj.attrs.color ?? "#1e40af",
|
|
3495
3663
|
visible: obj.visible,
|
|
3496
3664
|
fixed: obj.locked
|
|
3497
3665
|
};
|
|
@@ -3500,6 +3668,38 @@ var init_intersection = __esm({
|
|
|
3500
3668
|
}
|
|
3501
3669
|
const branch = obj.attrs.branch ?? 0;
|
|
3502
3670
|
return board.create("intersection", [a, b, branch], opts);
|
|
3671
|
+
},
|
|
3672
|
+
/**
|
|
3673
|
+
* Cập nhật TẠI CHỖ các thuộc tính "trang trí" (tên/màu/ẩn-hiện/khoá) qua
|
|
3674
|
+
* setAttribute — giữ nguyên JxgObj identity nên các object phụ thuộc điểm
|
|
3675
|
+
* giao (đường thẳng qua nó, …) KHÔNG bị stale parent ref. Mô phỏng update
|
|
3676
|
+
* hook của point.ts.
|
|
3677
|
+
*
|
|
3678
|
+
* Nếu định nghĩa hình học đổi (kind/ref1/ref2/branch) thì throw → renderer
|
|
3679
|
+
* fallback remove + create để JSXGraph dựng lại phép giao đúng.
|
|
3680
|
+
*/
|
|
3681
|
+
update: (obj, prev, ctx, existing) => {
|
|
3682
|
+
const a = obj.attrs;
|
|
3683
|
+
const p = prev.attrs;
|
|
3684
|
+
const branchA = a.branch;
|
|
3685
|
+
const branchP = p.branch;
|
|
3686
|
+
if (a.kind !== p.kind || a.ref1 !== p.ref1 || a.ref2 !== p.ref2 || branchA !== branchP) {
|
|
3687
|
+
throw new Error("intersection: \u0111\u1ECBnh ngh\u0129a h\xECnh h\u1ECDc \u0111\u1ED5i \u2014 recreate");
|
|
3688
|
+
}
|
|
3689
|
+
const el = existing;
|
|
3690
|
+
if (typeof el.setAttribute === "function") {
|
|
3691
|
+
try {
|
|
3692
|
+
el.setAttribute({
|
|
3693
|
+
name: obj.label,
|
|
3694
|
+
withLabel: true,
|
|
3695
|
+
strokeColor: a.color ?? "#1e40af",
|
|
3696
|
+
fillColor: a.color ?? "#1e40af",
|
|
3697
|
+
visible: obj.visible,
|
|
3698
|
+
fixed: obj.locked
|
|
3699
|
+
});
|
|
3700
|
+
} catch {
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3503
3703
|
}
|
|
3504
3704
|
};
|
|
3505
3705
|
registerKind(def12);
|
|
@@ -6258,11 +6458,17 @@ async function insertStampImage(api, opts) {
|
|
|
6258
6458
|
const elements = api.getSceneElements();
|
|
6259
6459
|
const editingId = opts.editingElementId ?? null;
|
|
6260
6460
|
if (editingId) {
|
|
6461
|
+
const old = elements.find((e) => e.id === editingId) ;
|
|
6462
|
+
const oldLongest = old ? Math.max(old.width ?? 0, old.height ?? 0) : 0;
|
|
6463
|
+
const newLongest = Math.max(width, height);
|
|
6464
|
+
const scale = oldLongest > 0 && newLongest > 0 ? oldLongest / newLongest : 1;
|
|
6465
|
+
const w = width * scale;
|
|
6466
|
+
const h = height * scale;
|
|
6261
6467
|
const updated = elements.map(
|
|
6262
|
-
(e) => e.id === editingId ? { ...e, fileId, customData, width, height } : e
|
|
6468
|
+
(e) => e.id === editingId ? { ...e, fileId, customData, width: w, height: h } : e
|
|
6263
6469
|
);
|
|
6264
6470
|
api.updateScene({ elements: updated, appState: clearAppStateAfterInsert() });
|
|
6265
|
-
return { fileId, width, height, elementId: editingId };
|
|
6471
|
+
return { fileId, width: w, height: h, elementId: editingId };
|
|
6266
6472
|
}
|
|
6267
6473
|
const newElement = buildStampImageElement(
|
|
6268
6474
|
api,
|
|
@@ -6736,7 +6942,8 @@ var init_host = __esm({
|
|
|
6736
6942
|
version: 2,
|
|
6737
6943
|
jsonState
|
|
6738
6944
|
}),
|
|
6739
|
-
editingElementId: editingElement?.id ?? null
|
|
6945
|
+
editingElementId: editingElement?.id ?? null,
|
|
6946
|
+
preserveExistingSize: true
|
|
6740
6947
|
});
|
|
6741
6948
|
} catch (err) {
|
|
6742
6949
|
console.error("Graph2D insert failed:", err);
|