@xom11/whiteboard 0.24.2 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -11
- package/dist/{ExcalidrawWithMenus-WENZRYYE.mjs → ExcalidrawWithMenus-2QPPTXJM.mjs} +3 -2
- package/dist/ExcalidrawWithMenus-2QPPTXJM.mjs.map +1 -0
- package/dist/ai.d.mts +3217 -434
- package/dist/ai.d.ts +3217 -434
- package/dist/ai.js +7679 -598
- package/dist/ai.js.map +1 -1
- package/dist/ai.mjs +5707 -679
- package/dist/ai.mjs.map +1 -1
- package/dist/catalog.json +5 -5
- package/dist/{chunk-7WQXXEVR.mjs → chunk-4ETJ4CDY.mjs} +5 -5
- package/dist/{chunk-7WQXXEVR.mjs.map → chunk-4ETJ4CDY.mjs.map} +1 -1
- package/dist/chunk-AJAHD35N.mjs +1708 -0
- package/dist/chunk-AJAHD35N.mjs.map +1 -0
- package/dist/chunk-AYJPOHCI.mjs +265 -0
- package/dist/chunk-AYJPOHCI.mjs.map +1 -0
- package/dist/chunk-B4NJJZFR.mjs +18 -0
- package/dist/chunk-B4NJJZFR.mjs.map +1 -0
- package/dist/{chunk-AZIARTGX.mjs → chunk-BNBOIDO5.mjs} +3 -3
- package/dist/{chunk-AZIARTGX.mjs.map → chunk-BNBOIDO5.mjs.map} +1 -1
- package/dist/{chunk-LVNCYP4J.mjs → chunk-CXHNVYMD.mjs} +5 -5
- package/dist/{chunk-LVNCYP4J.mjs.map → chunk-CXHNVYMD.mjs.map} +1 -1
- package/dist/{chunk-45CGKJ7S.mjs → chunk-D5JLJ3PT.mjs} +4 -4
- package/dist/{chunk-45CGKJ7S.mjs.map → chunk-D5JLJ3PT.mjs.map} +1 -1
- package/dist/{chunk-WM2VDYQA.mjs → chunk-D5LWSN2Y.mjs} +944 -196
- package/dist/chunk-D5LWSN2Y.mjs.map +1 -0
- package/dist/{chunk-KRC2XOIG.mjs → chunk-HLAOGXEK.mjs} +3 -3
- package/dist/{chunk-KRC2XOIG.mjs.map → chunk-HLAOGXEK.mjs.map} +1 -1
- package/dist/{chunk-2WF6KIGF.mjs → chunk-I3L56GVH.mjs} +212 -71
- package/dist/chunk-I3L56GVH.mjs.map +1 -0
- package/dist/{chunk-ZBJBQKJ2.mjs → chunk-IHUFOV7L.mjs} +4 -19
- package/dist/chunk-IHUFOV7L.mjs.map +1 -0
- package/dist/chunk-J5LGTIGS.mjs +10 -0
- package/dist/chunk-J5LGTIGS.mjs.map +1 -0
- package/dist/{chunk-BEZSQKPY.mjs → chunk-KYMBUTPO.mjs} +5 -4
- package/dist/chunk-KYMBUTPO.mjs.map +1 -0
- package/dist/{chunk-4DS3MKID.mjs → chunk-KZGPSTZI.mjs} +4 -4
- package/dist/{chunk-4DS3MKID.mjs.map → chunk-KZGPSTZI.mjs.map} +1 -1
- package/dist/{chunk-SGFJLHHG.mjs → chunk-PPKHCRRE.mjs} +3 -3
- package/dist/{chunk-SGFJLHHG.mjs.map → chunk-PPKHCRRE.mjs.map} +1 -1
- package/dist/{chunk-BKSXPNPQ.mjs → chunk-SZDAS7LK.mjs} +81 -3
- package/dist/chunk-SZDAS7LK.mjs.map +1 -0
- package/dist/chunk-T3SOHYB2.mjs +851 -0
- package/dist/chunk-T3SOHYB2.mjs.map +1 -0
- package/dist/geometry-2d.d.mts +2 -2
- package/dist/geometry-2d.d.ts +2 -2
- package/dist/geometry-2d.js +6288 -901
- package/dist/geometry-2d.js.map +1 -1
- package/dist/geometry-2d.mjs +7 -5
- package/dist/geometry-3d.d.mts +2 -2
- package/dist/geometry-3d.d.ts +2 -2
- package/dist/geometry-3d.js +1335 -253
- package/dist/geometry-3d.js.map +1 -1
- package/dist/geometry-3d.mjs +6 -4
- package/dist/graph-2d.d.mts +2 -2
- package/dist/graph-2d.d.ts +2 -2
- package/dist/graph-2d.js +1501 -342
- package/dist/graph-2d.js.map +1 -1
- package/dist/graph-2d.mjs +9 -7
- package/dist/handleExtractProblem-C-U5KluK.d.mts +158 -0
- package/dist/handleExtractProblem-C-U5KluK.d.ts +158 -0
- package/dist/{host-EPZCNFLH.mjs → host-HAYCJJ2T.mjs} +1390 -376
- package/dist/host-HAYCJJ2T.mjs.map +1 -0
- package/dist/{host-LKCMYEAV.mjs → host-LTJHAY5A.mjs} +12 -10
- package/dist/host-LTJHAY5A.mjs.map +1 -0
- package/dist/{host-ZIQ77W33.mjs → host-M26FS244.mjs} +8 -6
- package/dist/host-M26FS244.mjs.map +1 -0
- package/dist/{host-QS2EOTRJ.mjs → host-ZQCDAT6O.mjs} +3 -2
- package/dist/host-ZQCDAT6O.mjs.map +1 -0
- package/dist/index.d.mts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +6493 -1102
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +24 -21
- package/dist/index.mjs.map +1 -1
- package/dist/latex.d.mts +2 -2
- package/dist/latex.d.ts +2 -2
- package/dist/latex.mjs +2 -1
- package/dist/render-ZX2O2IK7.mjs +10 -0
- package/dist/{render-SA4JTOW3.mjs.map → render-ZX2O2IK7.mjs.map} +1 -1
- package/dist/serialize-C3LSUMSA.mjs +9 -0
- package/dist/{serialize-JAVOU22E.mjs.map → serialize-C3LSUMSA.mjs.map} +1 -1
- package/dist/types-zc_Pa0mp.d.mts +418 -0
- package/dist/types-zc_Pa0mp.d.ts +418 -0
- package/package.json +10 -1
- package/dist/ExcalidrawWithMenus-WENZRYYE.mjs.map +0 -1
- package/dist/chunk-2WF6KIGF.mjs.map +0 -1
- package/dist/chunk-BEZSQKPY.mjs.map +0 -1
- package/dist/chunk-BKSXPNPQ.mjs.map +0 -1
- package/dist/chunk-CGZZO4BX.mjs +0 -96
- package/dist/chunk-CGZZO4BX.mjs.map +0 -1
- package/dist/chunk-WM2VDYQA.mjs.map +0 -1
- package/dist/chunk-ZBJBQKJ2.mjs.map +0 -1
- package/dist/host-EPZCNFLH.mjs.map +0 -1
- package/dist/host-LKCMYEAV.mjs.map +0 -1
- package/dist/host-QS2EOTRJ.mjs.map +0 -1
- package/dist/host-ZIQ77W33.mjs.map +0 -1
- package/dist/render-SA4JTOW3.mjs +0 -8
- package/dist/serialize-JAVOU22E.mjs +0 -7
- package/dist/types-Crbefnfe.d.ts +0 -128
- package/dist/types-DxlMPh-6.d.mts +0 -49
- package/dist/types-DxlMPh-6.d.ts +0 -49
- package/dist/types-vtvyKGAA.d.mts +0 -128
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { compile, validate } from './chunk-IHUFOV7L.mjs';
|
|
3
3
|
import { createEmptyState } from './chunk-73Q7ADVL.mjs';
|
|
4
|
+
import { registerKind } from './chunk-B4NJJZFR.mjs';
|
|
4
5
|
import * as React from 'react';
|
|
5
6
|
|
|
6
7
|
// src/core/scene/selectors.ts
|
|
@@ -546,12 +547,354 @@ function constraintRefs2D(c) {
|
|
|
546
547
|
return [c.vertices[0], c.vertices[1], c.vertices[2]];
|
|
547
548
|
case "orthocenter":
|
|
548
549
|
return [c.vertices[0], c.vertices[1], c.vertices[2]];
|
|
550
|
+
case "onPerpendicular":
|
|
551
|
+
return [c.through, c.perpToA, c.perpToB];
|
|
552
|
+
case "onPerpBisector":
|
|
553
|
+
return [c.p1, c.p2];
|
|
554
|
+
case "onCircleAroundPoint":
|
|
555
|
+
return [c.center, c.radiusPoint];
|
|
556
|
+
case "tangentPointExt":
|
|
557
|
+
return [c.from, c.circle];
|
|
558
|
+
case "circleIntersection":
|
|
559
|
+
return [c.c1, c.c2];
|
|
560
|
+
case "circleSecondIntersection":
|
|
561
|
+
return [c.c1, c.c2, c.exclude];
|
|
562
|
+
case "secondIntersection":
|
|
563
|
+
return [c.line, c.circle, c.other];
|
|
564
|
+
case "tangencyPoint":
|
|
565
|
+
return [c.circle, c.onLine];
|
|
566
|
+
case "arcMidpoint":
|
|
567
|
+
return [c.circle, c.a, c.b, c.notContaining];
|
|
568
|
+
case "pointAtDistance": {
|
|
569
|
+
const d = c.distance;
|
|
570
|
+
const extra = d.kind === "circleRadius" ? [d.circle] : d.kind === "segmentLength" ? [d.p1, d.p2] : [];
|
|
571
|
+
return [c.from, c.through, ...extra];
|
|
572
|
+
}
|
|
573
|
+
case "excenter":
|
|
574
|
+
return [c.vertices[0], c.vertices[1], c.vertices[2]];
|
|
549
575
|
default:
|
|
550
576
|
return [];
|
|
551
577
|
}
|
|
552
578
|
}
|
|
553
579
|
|
|
554
|
-
// src/core/scene/kinds/point.ts
|
|
580
|
+
// src/core/scene/kinds/point-constraints/_types.ts
|
|
581
|
+
function definePointConstraint(m) {
|
|
582
|
+
return m;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// src/core/scene/kinds/point-constraints/free.ts
|
|
586
|
+
var freeConstraint = definePointConstraint({
|
|
587
|
+
kind: "free",
|
|
588
|
+
describe: (obj) => `\u0110i\u1EC3m ${obj.label}`,
|
|
589
|
+
render: (obj, ctx, c, opts) => {
|
|
590
|
+
const board = ctx.jxg;
|
|
591
|
+
return board.create("point", [c.x, c.y], opts);
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
// src/core/scene/kinds/point-constraints/onAxis.ts
|
|
596
|
+
var onAxisConstraint = definePointConstraint({
|
|
597
|
+
kind: "onAxis",
|
|
598
|
+
describe: (obj, _state, c) => `${obj.label} tr\xEAn tr\u1EE5c ${c.axis}`,
|
|
599
|
+
render: (obj, ctx, c, opts) => {
|
|
600
|
+
const board = ctx.jxg;
|
|
601
|
+
const coords = c.axis === "x" ? [c.t, 0] : [0, c.t];
|
|
602
|
+
return board.create("point", coords, opts);
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
// src/core/scene/kinds/point-constraints/midpoint.ts
|
|
607
|
+
var midpointConstraint = definePointConstraint({
|
|
608
|
+
kind: "midpoint",
|
|
609
|
+
describe: (obj, state, c) => {
|
|
610
|
+
const l1 = state?.objects[c.p1]?.label ?? c.p1;
|
|
611
|
+
const l2 = state?.objects[c.p2]?.label ?? c.p2;
|
|
612
|
+
return `${obj.label} = trung \u0111i\u1EC3m ${l1}${l2}`;
|
|
613
|
+
},
|
|
614
|
+
render: (obj, ctx, c, opts) => {
|
|
615
|
+
const board = ctx.jxg;
|
|
616
|
+
const p1 = ctx.resolveRef(c.p1);
|
|
617
|
+
const p2 = ctx.resolveRef(c.p2);
|
|
618
|
+
return board.create("midpoint", [p1, p2], opts);
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
// src/core/scene/kinds/point-constraints/perpFoot.ts
|
|
623
|
+
var perpFootConstraint = definePointConstraint({
|
|
624
|
+
kind: "perpFoot",
|
|
625
|
+
validate: (c) => {
|
|
626
|
+
if (!c.from || !c.onLine) {
|
|
627
|
+
throw new Error("point.perpFoot: from v\xE0 onLine b\u1EAFt bu\u1ED9c");
|
|
628
|
+
}
|
|
629
|
+
},
|
|
630
|
+
describe: (obj, state, c) => {
|
|
631
|
+
const fromLabel = state?.objects[c.from]?.label ?? c.from;
|
|
632
|
+
const lineLabel = state?.objects[c.onLine]?.label ?? c.onLine;
|
|
633
|
+
return `${obj.label} = ch\xE2n \u27C2 t\u1EEB ${fromLabel} xu\u1ED1ng ${lineLabel}`;
|
|
634
|
+
},
|
|
635
|
+
render: (obj, ctx, c, opts) => {
|
|
636
|
+
const board = ctx.jxg;
|
|
637
|
+
const from = ctx.resolveRef(c.from);
|
|
638
|
+
const onLine = ctx.resolveRef(c.onLine);
|
|
639
|
+
return board.create("perpendicularpoint", [onLine, from], opts);
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
// src/core/scene/kinds/point-constraints/circumcenter.ts
|
|
644
|
+
var circumcenterConstraint = definePointConstraint({
|
|
645
|
+
kind: "circumcenter",
|
|
646
|
+
validate: (c) => {
|
|
647
|
+
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
648
|
+
throw new Error("point.circumcenter: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
649
|
+
}
|
|
650
|
+
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
651
|
+
throw new Error("point.circumcenter: 3 vertex id ph\u1EA3i non-empty");
|
|
652
|
+
}
|
|
653
|
+
},
|
|
654
|
+
describe: (obj, state, c) => {
|
|
655
|
+
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
656
|
+
return `${obj.label} = t\xE2m ngo\u1EA1i ti\u1EBFp \u0394${labels}`;
|
|
657
|
+
},
|
|
658
|
+
render: (obj, ctx, c, opts) => {
|
|
659
|
+
const board = ctx.jxg;
|
|
660
|
+
const a = ctx.resolveRef(c.vertices[0]);
|
|
661
|
+
const b = ctx.resolveRef(c.vertices[1]);
|
|
662
|
+
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
663
|
+
return board.create("circumcenter", [a, b, c3], opts);
|
|
664
|
+
}
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
// src/core/scene/kinds/point-constraints/incenter.ts
|
|
668
|
+
var incenterConstraint = definePointConstraint({
|
|
669
|
+
kind: "incenter",
|
|
670
|
+
validate: (c) => {
|
|
671
|
+
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
672
|
+
throw new Error("point.incenter: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
673
|
+
}
|
|
674
|
+
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
675
|
+
throw new Error("point.incenter: 3 vertex id ph\u1EA3i non-empty");
|
|
676
|
+
}
|
|
677
|
+
},
|
|
678
|
+
describe: (obj, state, c) => {
|
|
679
|
+
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
680
|
+
return `${obj.label} = t\xE2m n\u1ED9i ti\u1EBFp \u0394${labels}`;
|
|
681
|
+
},
|
|
682
|
+
render: (obj, ctx, c, opts) => {
|
|
683
|
+
const board = ctx.jxg;
|
|
684
|
+
const a = ctx.resolveRef(c.vertices[0]);
|
|
685
|
+
const b = ctx.resolveRef(c.vertices[1]);
|
|
686
|
+
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
687
|
+
return board.create("incenter", [a, b, c3], opts);
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
// src/core/scene/kinds/point-constraints/onLine.ts
|
|
692
|
+
var onLineConstraint = definePointConstraint({
|
|
693
|
+
kind: "onLine",
|
|
694
|
+
describe: (obj, state, c) => `${obj.label} tr\xEAn \u0111\u01B0\u1EDDng ${state?.objects[c.lineId]?.label ?? c.lineId}`,
|
|
695
|
+
render: (obj, ctx, c, opts) => {
|
|
696
|
+
const board = ctx.jxg;
|
|
697
|
+
const line = ctx.resolveRef(c.lineId);
|
|
698
|
+
const p1 = line.point1;
|
|
699
|
+
const p2 = line.point2;
|
|
700
|
+
const sx = p1 && p2 ? p1.X() + c.t * (p2.X() - p1.X()) : c.t;
|
|
701
|
+
const sy = p1 && p2 ? p1.Y() + c.t * (p2.Y() - p1.Y()) : c.t;
|
|
702
|
+
return board.create("glider", [sx, sy, line], opts);
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
// src/core/scene/kinds/point-constraints/onSegment.ts
|
|
707
|
+
var onSegmentConstraint = definePointConstraint({
|
|
708
|
+
kind: "onSegment",
|
|
709
|
+
describe: (obj, state, c) => `${obj.label} tr\xEAn \u0111o\u1EA1n ${state?.objects[c.segmentId]?.label ?? c.segmentId}`,
|
|
710
|
+
render: (obj, ctx, c, opts) => {
|
|
711
|
+
const board = ctx.jxg;
|
|
712
|
+
const seg = ctx.resolveRef(c.segmentId);
|
|
713
|
+
const p1 = seg.point1;
|
|
714
|
+
const p2 = seg.point2;
|
|
715
|
+
const sx = p1 && p2 ? p1.X() + c.t * (p2.X() - p1.X()) : c.t;
|
|
716
|
+
const sy = p1 && p2 ? p1.Y() + c.t * (p2.Y() - p1.Y()) : c.t;
|
|
717
|
+
return board.create("glider", [sx, sy, seg], opts);
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
// src/core/scene/kinds/point-constraints/onCircle.ts
|
|
722
|
+
var onCircleConstraint = definePointConstraint({
|
|
723
|
+
kind: "onCircle",
|
|
724
|
+
describe: (obj, state, c) => `${obj.label} tr\xEAn \u0111\u01B0\u1EDDng tr\xF2n ${state?.objects[c.circleId]?.label ?? c.circleId}`,
|
|
725
|
+
render: (obj, ctx, c, opts) => {
|
|
726
|
+
const board = ctx.jxg;
|
|
727
|
+
const circle = ctx.resolveRef(c.circleId);
|
|
728
|
+
const O = circle.center ?? circle.midpoint;
|
|
729
|
+
const ox = O ? O.X() : 0;
|
|
730
|
+
const oy = O ? O.Y() : 0;
|
|
731
|
+
return board.create("glider", [ox + Math.cos(c.theta), oy + Math.sin(c.theta), circle], opts);
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
// src/core/scene/kinds/point-constraints/onPolygon.ts
|
|
736
|
+
var onPolygonConstraint = definePointConstraint({
|
|
737
|
+
kind: "onPolygon",
|
|
738
|
+
describe: (obj, state, c) => `${obj.label} tr\xEAn \u0111a gi\xE1c ${state?.objects[c.polygonId]?.label ?? c.polygonId}`,
|
|
739
|
+
render: (obj, ctx, c, opts) => {
|
|
740
|
+
const board = ctx.jxg;
|
|
741
|
+
const poly = ctx.resolveRef(c.polygonId);
|
|
742
|
+
return board.create("glider", [c.u, c.v, poly], opts);
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
// src/core/scene/kinds/point-constraints/centroid.ts
|
|
747
|
+
var centroidConstraint = definePointConstraint({
|
|
748
|
+
kind: "centroid",
|
|
749
|
+
validate: (c) => {
|
|
750
|
+
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
751
|
+
throw new Error("point.centroid: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
752
|
+
}
|
|
753
|
+
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
754
|
+
throw new Error("point.centroid: 3 vertex id ph\u1EA3i non-empty");
|
|
755
|
+
}
|
|
756
|
+
},
|
|
757
|
+
describe: (obj, state, c) => {
|
|
758
|
+
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
759
|
+
return `${obj.label} = tr\u1ECDng t\xE2m \u0394${labels}`;
|
|
760
|
+
},
|
|
761
|
+
render: (obj, ctx, c, opts) => {
|
|
762
|
+
const board = ctx.jxg;
|
|
763
|
+
const a = ctx.resolveRef(c.vertices[0]);
|
|
764
|
+
const b = ctx.resolveRef(c.vertices[1]);
|
|
765
|
+
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
766
|
+
return board.create("point", [
|
|
767
|
+
() => (a.X() + b.X() + c3.X()) / 3,
|
|
768
|
+
() => (a.Y() + b.Y() + c3.Y()) / 3
|
|
769
|
+
], opts);
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
// src/core/scene/kinds/pointConstructions.ts
|
|
774
|
+
function dist(p, q) {
|
|
775
|
+
return Math.hypot(p[0] - q[0], p[1] - q[1]);
|
|
776
|
+
}
|
|
777
|
+
function sideOf(a, b, p) {
|
|
778
|
+
return (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]);
|
|
779
|
+
}
|
|
780
|
+
function arcMidpoint(center, radius, a, b, notContaining) {
|
|
781
|
+
const mcx = (a[0] + b[0]) / 2;
|
|
782
|
+
const mcy = (a[1] + b[1]) / 2;
|
|
783
|
+
let ux = mcx - center[0];
|
|
784
|
+
let uy = mcy - center[1];
|
|
785
|
+
let len = Math.hypot(ux, uy);
|
|
786
|
+
if (len < 1e-9) {
|
|
787
|
+
ux = -(b[1] - a[1]);
|
|
788
|
+
uy = b[0] - a[0];
|
|
789
|
+
len = Math.hypot(ux, uy) || 1;
|
|
790
|
+
}
|
|
791
|
+
ux /= len;
|
|
792
|
+
uy /= len;
|
|
793
|
+
const cand1 = [center[0] + radius * ux, center[1] + radius * uy];
|
|
794
|
+
const cand2 = [center[0] - radius * ux, center[1] - radius * uy];
|
|
795
|
+
const sN = sideOf(a, b, notContaining);
|
|
796
|
+
if (Math.abs(sN) < 1e-9) {
|
|
797
|
+
return dist(cand1, notContaining) >= dist(cand2, notContaining) ? cand1 : cand2;
|
|
798
|
+
}
|
|
799
|
+
const s1 = sideOf(a, b, cand1);
|
|
800
|
+
return s1 * sN < 0 ? cand1 : cand2;
|
|
801
|
+
}
|
|
802
|
+
function excenter(vertices, oppositeIndex) {
|
|
803
|
+
const [A, B, C] = vertices;
|
|
804
|
+
const a = dist(B, C);
|
|
805
|
+
const b = dist(C, A);
|
|
806
|
+
const c = dist(A, B);
|
|
807
|
+
const w = [a, b, c];
|
|
808
|
+
w[oppositeIndex] = -w[oppositeIndex];
|
|
809
|
+
const sum = w[0] + w[1] + w[2];
|
|
810
|
+
if (Math.abs(sum) < 1e-9) return A;
|
|
811
|
+
return [
|
|
812
|
+
(w[0] * A[0] + w[1] * B[0] + w[2] * C[0]) / sum,
|
|
813
|
+
(w[0] * A[1] + w[1] * B[1] + w[2] * C[1]) / sum
|
|
814
|
+
];
|
|
815
|
+
}
|
|
816
|
+
function pointAtDistanceCoord(from, through, d) {
|
|
817
|
+
const dx = through[0] - from[0];
|
|
818
|
+
const dy = through[1] - from[1];
|
|
819
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
820
|
+
return [through[0] + d * dx / len, through[1] + d * dy / len];
|
|
821
|
+
}
|
|
822
|
+
function radicalAxisFoot(o1, r1, o2, r2) {
|
|
823
|
+
const dx = o2[0] - o1[0], dy = o2[1] - o1[1];
|
|
824
|
+
const d2 = dx * dx + dy * dy;
|
|
825
|
+
if (d2 < 1e-12) return o1;
|
|
826
|
+
const t = (d2 + r1 * r1 - r2 * r2) / (2 * d2);
|
|
827
|
+
return [o1[0] + t * dx, o1[1] + t * dy];
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// src/core/scene/kinds/point-constraints/arcMidpoint.ts
|
|
831
|
+
var arcMidpointConstraint = definePointConstraint({
|
|
832
|
+
kind: "arcMidpoint",
|
|
833
|
+
validate: (c) => {
|
|
834
|
+
if (!c.circle || !c.a || !c.b || !c.notContaining) {
|
|
835
|
+
throw new Error("point.arcMidpoint: circle, a, b, notContaining b\u1EAFt bu\u1ED9c");
|
|
836
|
+
}
|
|
837
|
+
},
|
|
838
|
+
describe: (obj, state, c) => {
|
|
839
|
+
const al = state?.objects[c.a]?.label ?? c.a;
|
|
840
|
+
const bl = state?.objects[c.b]?.label ?? c.b;
|
|
841
|
+
const nl = state?.objects[c.notContaining]?.label ?? c.notContaining;
|
|
842
|
+
return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl} (kh\xF4ng ch\u1EE9a ${nl})`;
|
|
843
|
+
},
|
|
844
|
+
render: (obj, ctx, c, opts) => {
|
|
845
|
+
const board = ctx.jxg;
|
|
846
|
+
const circle = ctx.resolveRef(c.circle);
|
|
847
|
+
const A = ctx.resolveRef(c.a);
|
|
848
|
+
const B = ctx.resolveRef(c.b);
|
|
849
|
+
const N = ctx.resolveRef(c.notContaining);
|
|
850
|
+
const O = circle?.center ?? circle?.midpoint ?? circle;
|
|
851
|
+
const am = () => arcMidpoint(
|
|
852
|
+
[O.X(), O.Y()],
|
|
853
|
+
circle.Radius(),
|
|
854
|
+
[A.X(), A.Y()],
|
|
855
|
+
[B.X(), B.Y()],
|
|
856
|
+
[N.X(), N.Y()]
|
|
857
|
+
);
|
|
858
|
+
return board.create("point", [() => am()[0], () => am()[1]], opts);
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
// src/core/scene/kinds/point-constraints/excenter.ts
|
|
863
|
+
var excenterConstraint = definePointConstraint({
|
|
864
|
+
kind: "excenter",
|
|
865
|
+
validate: (c) => {
|
|
866
|
+
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
867
|
+
throw new Error("point.excenter: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
868
|
+
}
|
|
869
|
+
if (!c.opposite) throw new Error("point.excenter: opposite b\u1EAFt bu\u1ED9c");
|
|
870
|
+
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
871
|
+
throw new Error("point.excenter: 3 vertex id ph\u1EA3i non-empty");
|
|
872
|
+
}
|
|
873
|
+
if (!c.vertices.includes(c.opposite)) {
|
|
874
|
+
throw new Error("point.excenter: opposite ph\u1EA3i l\xE0 m\u1ED9t trong vertices");
|
|
875
|
+
}
|
|
876
|
+
},
|
|
877
|
+
describe: (obj, state, c) => {
|
|
878
|
+
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
879
|
+
const opp = state?.objects[c.opposite]?.label ?? c.opposite;
|
|
880
|
+
return `${obj.label} = t\xE2m b\xE0ng ti\u1EBFp \u0394${labels} \u0111\u1ED1i di\u1EC7n ${opp}`;
|
|
881
|
+
},
|
|
882
|
+
render: (obj, ctx, c, opts) => {
|
|
883
|
+
const board = ctx.jxg;
|
|
884
|
+
const a = ctx.resolveRef(c.vertices[0]);
|
|
885
|
+
const b = ctx.resolveRef(c.vertices[1]);
|
|
886
|
+
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
887
|
+
const oppIdx = c.vertices.indexOf(c.opposite);
|
|
888
|
+
const idx = oppIdx < 0 ? 0 : oppIdx;
|
|
889
|
+
const ex = () => excenter(
|
|
890
|
+
[[a.X(), a.Y()], [b.X(), b.Y()], [c3.X(), c3.Y()]],
|
|
891
|
+
idx
|
|
892
|
+
);
|
|
893
|
+
return board.create("point", [() => ex()[0], () => ex()[1]], opts);
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
// src/core/scene/kinds/point-constraints/shared.ts
|
|
555
898
|
function buildJxgTransforms(board, ctx, t) {
|
|
556
899
|
switch (t.kind) {
|
|
557
900
|
case "translate":
|
|
@@ -578,6 +921,283 @@ function buildJxgTransforms(board, ctx, t) {
|
|
|
578
921
|
}
|
|
579
922
|
}
|
|
580
923
|
}
|
|
924
|
+
function makeDistanceFn(ctx, d) {
|
|
925
|
+
const scale = d.scale ?? 1;
|
|
926
|
+
const offset = d.offset ?? 0;
|
|
927
|
+
if (d.kind === "literal") {
|
|
928
|
+
const v = d.value;
|
|
929
|
+
return () => scale * v + offset;
|
|
930
|
+
}
|
|
931
|
+
if (d.kind === "segmentLength") {
|
|
932
|
+
const p = ctx.resolveRef(d.p1);
|
|
933
|
+
const q = ctx.resolveRef(d.p2);
|
|
934
|
+
return () => scale * Math.hypot(p.X() - q.X(), p.Y() - q.Y()) + offset;
|
|
935
|
+
}
|
|
936
|
+
const circle = ctx.resolveRef(d.circle);
|
|
937
|
+
return () => scale * circle.Radius() + offset;
|
|
938
|
+
}
|
|
939
|
+
function buildPointOpts(obj) {
|
|
940
|
+
return {
|
|
941
|
+
name: obj.label,
|
|
942
|
+
withLabel: obj.attrs.showLabel ?? true,
|
|
943
|
+
visible: obj.visible,
|
|
944
|
+
fixed: obj.locked,
|
|
945
|
+
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
946
|
+
fillColor: obj.attrs.color ?? "#1e40af",
|
|
947
|
+
face: obj.attrs.face ?? "o",
|
|
948
|
+
size: obj.attrs.size ?? 4
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// src/core/scene/kinds/point-constraints/pointAtDistance.ts
|
|
953
|
+
var pointAtDistanceConstraint = definePointConstraint({
|
|
954
|
+
kind: "pointAtDistance",
|
|
955
|
+
describe: (obj, state, c) => {
|
|
956
|
+
const fromL = state?.objects[c.from]?.label ?? c.from;
|
|
957
|
+
const thrL = state?.objects[c.through]?.label ?? c.through;
|
|
958
|
+
const d = c.distance;
|
|
959
|
+
const dLabel = d.kind === "literal" ? `${d.value}` : d.kind === "segmentLength" ? `${state?.objects[d.p1]?.label ?? d.p1}${state?.objects[d.p2]?.label ?? d.p2}` : `b\xE1n k\xEDnh (${state?.objects[d.circle]?.label ?? d.circle})`;
|
|
960
|
+
return `${obj.label} = tr\xEAn tia ${fromL}${thrL} k\xE9o d\xE0i, c\xE1ch ${thrL} kho\u1EA3ng ${dLabel}`;
|
|
961
|
+
},
|
|
962
|
+
render: (obj, ctx, c, opts) => {
|
|
963
|
+
const board = ctx.jxg;
|
|
964
|
+
const A = ctx.resolveRef(c.from);
|
|
965
|
+
const B = ctx.resolveRef(c.through);
|
|
966
|
+
const dFn = makeDistanceFn(ctx, c.distance);
|
|
967
|
+
const pc = () => pointAtDistanceCoord([A.X(), A.Y()], [B.X(), B.Y()], dFn());
|
|
968
|
+
return board.create("point", [() => pc()[0], () => pc()[1]], opts);
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
// src/core/scene/kinds/point-constraints/circleIntersection.ts
|
|
973
|
+
var circleIntersectionConstraint = definePointConstraint({
|
|
974
|
+
kind: "circleIntersection",
|
|
975
|
+
// Không có describe-arm riêng trong point.ts → giữ fallback `Điểm ${label}`.
|
|
976
|
+
describe: (obj) => `\u0110i\u1EC3m ${obj.label}`,
|
|
977
|
+
render: (obj, ctx, c, opts) => {
|
|
978
|
+
const board = ctx.jxg;
|
|
979
|
+
const k1 = ctx.resolveRef(c.c1);
|
|
980
|
+
const k2 = ctx.resolveRef(c.c2);
|
|
981
|
+
return board.create("intersection", [k1, k2, c.which], opts);
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
// src/core/scene/kinds/point-constraints/circleSecondIntersection.ts
|
|
986
|
+
var circleSecondIntersectionConstraint = definePointConstraint({
|
|
987
|
+
kind: "circleSecondIntersection",
|
|
988
|
+
// Không có describe-arm riêng trong point.ts → giữ fallback `Điểm ${label}`.
|
|
989
|
+
describe: (obj) => `\u0110i\u1EC3m ${obj.label}`,
|
|
990
|
+
render: (obj, ctx, c, opts) => {
|
|
991
|
+
const board = ctx.jxg;
|
|
992
|
+
const k1 = ctx.resolveRef(c.c1);
|
|
993
|
+
const k2 = ctx.resolveRef(c.c2);
|
|
994
|
+
const ex = ctx.resolveRef(c.exclude);
|
|
995
|
+
return board.create("otherintersection", [k1, k2, ex], opts);
|
|
996
|
+
}
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
// src/core/scene/kinds/point-constraints/secondIntersection.ts
|
|
1000
|
+
var secondIntersectionConstraint = definePointConstraint({
|
|
1001
|
+
kind: "secondIntersection",
|
|
1002
|
+
// Không có describe-arm riêng trong point.ts → giữ fallback `Điểm ${label}`.
|
|
1003
|
+
describe: (obj) => `\u0110i\u1EC3m ${obj.label}`,
|
|
1004
|
+
render: (obj, ctx, c, opts) => {
|
|
1005
|
+
const board = ctx.jxg;
|
|
1006
|
+
const line = ctx.resolveRef(c.line);
|
|
1007
|
+
const circle = ctx.resolveRef(c.circle);
|
|
1008
|
+
const other = ctx.resolveRef(c.other);
|
|
1009
|
+
return board.create("otherintersection", [circle, line, other], opts);
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
// src/core/scene/kinds/point-constraints/tangencyPoint.ts
|
|
1014
|
+
var tangencyPointConstraint = definePointConstraint({
|
|
1015
|
+
kind: "tangencyPoint",
|
|
1016
|
+
// Không có describe-arm riêng trong point.ts → giữ fallback `Điểm ${label}`.
|
|
1017
|
+
describe: (obj) => `\u0110i\u1EC3m ${obj.label}`,
|
|
1018
|
+
render: (obj, ctx, c, opts) => {
|
|
1019
|
+
const board = ctx.jxg;
|
|
1020
|
+
const circle = ctx.resolveRef(c.circle);
|
|
1021
|
+
const line = ctx.resolveRef(c.onLine);
|
|
1022
|
+
const O = circle?.center ?? circle?.midpoint ?? circle;
|
|
1023
|
+
return board.create("perpendicularpoint", [line, O], opts);
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
// src/core/scene/kinds/point-constraints/transformed.ts
|
|
1028
|
+
var transformedConstraint = definePointConstraint({
|
|
1029
|
+
kind: "transformed",
|
|
1030
|
+
describe: (obj, state, c) => {
|
|
1031
|
+
const t = c.transform;
|
|
1032
|
+
const labelRef = (id) => state?.objects[id]?.label ?? id;
|
|
1033
|
+
const op = t.kind === "translate" ? `t\u1ECBnh ti\u1EBFn (${t.dx.toFixed(2)}, ${t.dy.toFixed(2)})` : t.kind === "rotate" ? `quay ${(t.angleRad * 180 / Math.PI).toFixed(0)}\xB0 quanh ${labelRef(t.center)}` : t.kind === "reflectLine" ? `\u0111\u1ED1i x\u1EE9ng qua ${labelRef(t.line)}` : t.kind === "reflectPoint" ? `\u0111\u1ED1i x\u1EE9ng qua \u0111i\u1EC3m ${labelRef(t.center)}` : t.kind === "dilate" ? `v\u1ECB t\u1EF1 k=${t.k} quanh ${labelRef(t.center)}` : "";
|
|
1034
|
+
return `${obj.label} = \u1EA3nh c\u1EE7a ${labelRef(c.source)} (${op})`;
|
|
1035
|
+
},
|
|
1036
|
+
render: (obj, ctx, c, opts) => {
|
|
1037
|
+
const board = ctx.jxg;
|
|
1038
|
+
const src = ctx.resolveRef(c.source);
|
|
1039
|
+
const transforms = buildJxgTransforms(board, ctx, c.transform);
|
|
1040
|
+
const parent = transforms.length === 1 ? transforms[0] : transforms;
|
|
1041
|
+
const pt = board.create("point", [src, parent], opts);
|
|
1042
|
+
pt._helpers = transforms;
|
|
1043
|
+
return pt;
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
|
|
1047
|
+
// src/core/scene/kinds/point-constraints/orthocenter.ts
|
|
1048
|
+
var orthocenterConstraint = definePointConstraint({
|
|
1049
|
+
kind: "orthocenter",
|
|
1050
|
+
validate: (c) => {
|
|
1051
|
+
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
1052
|
+
throw new Error("point.orthocenter: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
1053
|
+
}
|
|
1054
|
+
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
1055
|
+
throw new Error("point.orthocenter: 3 vertex id ph\u1EA3i non-empty");
|
|
1056
|
+
}
|
|
1057
|
+
},
|
|
1058
|
+
describe: (obj, state, c) => {
|
|
1059
|
+
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
1060
|
+
return `${obj.label} = tr\u1EF1c t\xE2m \u0394${labels}`;
|
|
1061
|
+
},
|
|
1062
|
+
render: (obj, ctx, c, opts) => {
|
|
1063
|
+
const board = ctx.jxg;
|
|
1064
|
+
const a = ctx.resolveRef(c.vertices[0]);
|
|
1065
|
+
const b = ctx.resolveRef(c.vertices[1]);
|
|
1066
|
+
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
1067
|
+
const hide = { visible: false, withLabel: false, fixed: true, name: "" };
|
|
1068
|
+
const lineBC = board.create("line", [b, c3], hide);
|
|
1069
|
+
const altA = board.create("perpendicular", [lineBC, a], hide);
|
|
1070
|
+
const lineAC = board.create("line", [a, c3], hide);
|
|
1071
|
+
const altB = board.create("perpendicular", [lineAC, b], hide);
|
|
1072
|
+
const ortho = board.create("intersection", [altA, altB, 0], opts);
|
|
1073
|
+
ortho._helpers = [lineBC, altA, lineAC, altB];
|
|
1074
|
+
return ortho;
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
// src/core/scene/kinds/point-constraints/onPerpendicular.ts
|
|
1079
|
+
var onPerpendicularConstraint = definePointConstraint({
|
|
1080
|
+
kind: "onPerpendicular",
|
|
1081
|
+
describe: (obj) => `\u0110i\u1EC3m ${obj.label}`,
|
|
1082
|
+
render: (obj, ctx, c, opts) => {
|
|
1083
|
+
const board = ctx.jxg;
|
|
1084
|
+
const T = ctx.resolveRef(c.through);
|
|
1085
|
+
const A = ctx.resolveRef(c.perpToA);
|
|
1086
|
+
const B = ctx.resolveRef(c.perpToB);
|
|
1087
|
+
const hide = { visible: false, withLabel: false, fixed: true, name: "" };
|
|
1088
|
+
const refLine = board.create("line", [A, B], hide);
|
|
1089
|
+
const perpLine = board.create("perpendicular", [refLine, T], hide);
|
|
1090
|
+
const dx = B.X() - A.X();
|
|
1091
|
+
const dy = B.Y() - A.Y();
|
|
1092
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
1093
|
+
const ux = -dy / len;
|
|
1094
|
+
const uy = dx / len;
|
|
1095
|
+
const x0 = T.X() + c.t * ux;
|
|
1096
|
+
const y0 = T.Y() + c.t * uy;
|
|
1097
|
+
const gl = board.create("glider", [x0, y0, perpLine], opts);
|
|
1098
|
+
gl._helpers = [refLine, perpLine];
|
|
1099
|
+
return gl;
|
|
1100
|
+
}
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
// src/core/scene/kinds/point-constraints/onPerpBisector.ts
|
|
1104
|
+
var onPerpBisectorConstraint = definePointConstraint({
|
|
1105
|
+
kind: "onPerpBisector",
|
|
1106
|
+
describe: (obj) => `\u0110i\u1EC3m ${obj.label}`,
|
|
1107
|
+
render: (obj, ctx, c, opts) => {
|
|
1108
|
+
const board = ctx.jxg;
|
|
1109
|
+
const A = ctx.resolveRef(c.p1);
|
|
1110
|
+
const B = ctx.resolveRef(c.p2);
|
|
1111
|
+
const hide = { visible: false, withLabel: false, fixed: true, name: "" };
|
|
1112
|
+
const refLine = board.create("line", [A, B], hide);
|
|
1113
|
+
const mid = board.create("midpoint", [A, B], hide);
|
|
1114
|
+
const bisLine = board.create("perpendicular", [refLine, mid], hide);
|
|
1115
|
+
const Mx = (A.X() + B.X()) / 2;
|
|
1116
|
+
const My = (A.Y() + B.Y()) / 2;
|
|
1117
|
+
const dx = B.X() - A.X();
|
|
1118
|
+
const dy = B.Y() - A.Y();
|
|
1119
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
1120
|
+
const ux = -dy / len;
|
|
1121
|
+
const uy = dx / len;
|
|
1122
|
+
const x0 = Mx + c.t * ux;
|
|
1123
|
+
const y0 = My + c.t * uy;
|
|
1124
|
+
const gl = board.create("glider", [x0, y0, bisLine], opts);
|
|
1125
|
+
gl._helpers = [refLine, mid, bisLine];
|
|
1126
|
+
return gl;
|
|
1127
|
+
}
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
// src/core/scene/kinds/point-constraints/onCircleAroundPoint.ts
|
|
1131
|
+
var onCircleAroundPointConstraint = definePointConstraint({
|
|
1132
|
+
kind: "onCircleAroundPoint",
|
|
1133
|
+
describe: (obj) => `\u0110i\u1EC3m ${obj.label}`,
|
|
1134
|
+
render: (obj, ctx, c, opts) => {
|
|
1135
|
+
const board = ctx.jxg;
|
|
1136
|
+
const C = ctx.resolveRef(c.center);
|
|
1137
|
+
const R = ctx.resolveRef(c.radiusPoint);
|
|
1138
|
+
const hide = { visible: false, withLabel: false, fixed: true, name: "" };
|
|
1139
|
+
const auxCircle = board.create("circle", [C, R], hide);
|
|
1140
|
+
const r = Math.hypot(R.X() - C.X(), R.Y() - C.Y());
|
|
1141
|
+
const x0 = C.X() + r * Math.cos(c.theta);
|
|
1142
|
+
const y0 = C.Y() + r * Math.sin(c.theta);
|
|
1143
|
+
const gl = board.create("glider", [x0, y0, auxCircle], opts);
|
|
1144
|
+
gl._helpers = [auxCircle];
|
|
1145
|
+
return gl;
|
|
1146
|
+
}
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
// src/core/scene/kinds/point-constraints/tangentPointExt.ts
|
|
1150
|
+
var tangentPointExtConstraint = definePointConstraint({
|
|
1151
|
+
kind: "tangentPointExt",
|
|
1152
|
+
describe: (obj, state, c) => {
|
|
1153
|
+
const fromLabel = state?.objects[c.from]?.label ?? c.from;
|
|
1154
|
+
const circleLabel = state?.objects[c.circle]?.label ?? c.circle;
|
|
1155
|
+
return `${obj.label} = ti\u1EBFp \u0111i\u1EC3m c\u1EE7a (${circleLabel}) v\u1EDBi ti\u1EBFp tuy\u1EBFn t\u1EEB ${fromLabel}`;
|
|
1156
|
+
},
|
|
1157
|
+
render: (obj, ctx, c, opts) => {
|
|
1158
|
+
const board = ctx.jxg;
|
|
1159
|
+
const P = ctx.resolveRef(c.from);
|
|
1160
|
+
const K = ctx.resolveRef(c.circle);
|
|
1161
|
+
const O = K.center ?? K.midpoint;
|
|
1162
|
+
const hide = { visible: false, withLabel: false, fixed: true, name: "" };
|
|
1163
|
+
const mid = board.create("midpoint", [P, O], hide);
|
|
1164
|
+
const thales = board.create("circle", [mid, P], hide);
|
|
1165
|
+
const inter = board.create("intersection", [thales, K, c.which], opts);
|
|
1166
|
+
inter._helpers = [mid, thales];
|
|
1167
|
+
return inter;
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1170
|
+
|
|
1171
|
+
// src/core/scene/kinds/point-constraints/registry.ts
|
|
1172
|
+
var ALL = [
|
|
1173
|
+
freeConstraint,
|
|
1174
|
+
onAxisConstraint,
|
|
1175
|
+
midpointConstraint,
|
|
1176
|
+
perpFootConstraint,
|
|
1177
|
+
circumcenterConstraint,
|
|
1178
|
+
incenterConstraint,
|
|
1179
|
+
onLineConstraint,
|
|
1180
|
+
onSegmentConstraint,
|
|
1181
|
+
onCircleConstraint,
|
|
1182
|
+
onPolygonConstraint,
|
|
1183
|
+
centroidConstraint,
|
|
1184
|
+
arcMidpointConstraint,
|
|
1185
|
+
excenterConstraint,
|
|
1186
|
+
pointAtDistanceConstraint,
|
|
1187
|
+
circleIntersectionConstraint,
|
|
1188
|
+
circleSecondIntersectionConstraint,
|
|
1189
|
+
secondIntersectionConstraint,
|
|
1190
|
+
tangencyPointConstraint,
|
|
1191
|
+
transformedConstraint,
|
|
1192
|
+
orthocenterConstraint,
|
|
1193
|
+
onPerpendicularConstraint,
|
|
1194
|
+
onPerpBisectorConstraint,
|
|
1195
|
+
onCircleAroundPointConstraint,
|
|
1196
|
+
tangentPointExtConstraint
|
|
1197
|
+
];
|
|
1198
|
+
var POINT_CONSTRAINTS = new Map(ALL.map((m) => [m.kind, m]));
|
|
1199
|
+
|
|
1200
|
+
// src/core/scene/kinds/point.ts
|
|
581
1201
|
var def3 = {
|
|
582
1202
|
type: "point",
|
|
583
1203
|
schemaVersion: 1,
|
|
@@ -587,43 +1207,7 @@ var def3 = {
|
|
|
587
1207
|
throw new Error("point: constraint required");
|
|
588
1208
|
}
|
|
589
1209
|
const c = a.constraint;
|
|
590
|
-
|
|
591
|
-
if (!c.from || !c.onLine) {
|
|
592
|
-
throw new Error("point.perpFoot: from v\xE0 onLine b\u1EAFt bu\u1ED9c");
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
if (c.kind === "circumcenter") {
|
|
596
|
-
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
597
|
-
throw new Error("point.circumcenter: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
598
|
-
}
|
|
599
|
-
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
600
|
-
throw new Error("point.circumcenter: 3 vertex id ph\u1EA3i non-empty");
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
if (c.kind === "incenter") {
|
|
604
|
-
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
605
|
-
throw new Error("point.incenter: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
606
|
-
}
|
|
607
|
-
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
608
|
-
throw new Error("point.incenter: 3 vertex id ph\u1EA3i non-empty");
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
if (c.kind === "centroid") {
|
|
612
|
-
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
613
|
-
throw new Error("point.centroid: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
614
|
-
}
|
|
615
|
-
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
616
|
-
throw new Error("point.centroid: 3 vertex id ph\u1EA3i non-empty");
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
if (c.kind === "orthocenter") {
|
|
620
|
-
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
621
|
-
throw new Error("point.orthocenter: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
622
|
-
}
|
|
623
|
-
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
624
|
-
throw new Error("point.orthocenter: 3 vertex id ph\u1EA3i non-empty");
|
|
625
|
-
}
|
|
626
|
-
}
|
|
1210
|
+
POINT_CONSTRAINTS.get(c.kind)?.validate?.(c);
|
|
627
1211
|
},
|
|
628
1212
|
dependsOn: (a) => constraintRefs2D(a.constraint),
|
|
629
1213
|
measure: (obj) => {
|
|
@@ -638,132 +1222,16 @@ var def3 = {
|
|
|
638
1222
|
},
|
|
639
1223
|
describe: (obj, state) => {
|
|
640
1224
|
const c = obj.attrs.constraint;
|
|
641
|
-
|
|
642
|
-
if (
|
|
643
|
-
if (c.kind === "onLine") return `${obj.label} tr\xEAn \u0111\u01B0\u1EDDng ${state?.objects[c.lineId]?.label ?? c.lineId}`;
|
|
644
|
-
if (c.kind === "onSegment") return `${obj.label} tr\xEAn \u0111o\u1EA1n ${state?.objects[c.segmentId]?.label ?? c.segmentId}`;
|
|
645
|
-
if (c.kind === "onCircle") return `${obj.label} tr\xEAn \u0111\u01B0\u1EDDng tr\xF2n ${state?.objects[c.circleId]?.label ?? c.circleId}`;
|
|
646
|
-
if (c.kind === "onPolygon") return `${obj.label} tr\xEAn \u0111a gi\xE1c ${state?.objects[c.polygonId]?.label ?? c.polygonId}`;
|
|
647
|
-
if (c.kind === "midpoint") {
|
|
648
|
-
const l1 = state?.objects[c.p1]?.label ?? c.p1;
|
|
649
|
-
const l2 = state?.objects[c.p2]?.label ?? c.p2;
|
|
650
|
-
return `${obj.label} = trung \u0111i\u1EC3m ${l1}${l2}`;
|
|
651
|
-
}
|
|
652
|
-
if (c.kind === "transformed") {
|
|
653
|
-
const t = c.transform;
|
|
654
|
-
const labelRef = (id) => state?.objects[id]?.label ?? id;
|
|
655
|
-
const op = t.kind === "translate" ? `t\u1ECBnh ti\u1EBFn (${t.dx.toFixed(2)}, ${t.dy.toFixed(2)})` : t.kind === "rotate" ? `quay ${(t.angleRad * 180 / Math.PI).toFixed(0)}\xB0 quanh ${labelRef(t.center)}` : t.kind === "reflectLine" ? `\u0111\u1ED1i x\u1EE9ng qua ${labelRef(t.line)}` : t.kind === "reflectPoint" ? `\u0111\u1ED1i x\u1EE9ng qua \u0111i\u1EC3m ${labelRef(t.center)}` : t.kind === "dilate" ? `v\u1ECB t\u1EF1 k=${t.k} quanh ${labelRef(t.center)}` : "";
|
|
656
|
-
return `${obj.label} = \u1EA3nh c\u1EE7a ${labelRef(c.source)} (${op})`;
|
|
657
|
-
}
|
|
658
|
-
if (c.kind === "perpFoot") {
|
|
659
|
-
const fromLabel = state?.objects[c.from]?.label ?? c.from;
|
|
660
|
-
const lineLabel = state?.objects[c.onLine]?.label ?? c.onLine;
|
|
661
|
-
return `${obj.label} = ch\xE2n \u27C2 t\u1EEB ${fromLabel} xu\u1ED1ng ${lineLabel}`;
|
|
662
|
-
}
|
|
663
|
-
if (c.kind === "circumcenter") {
|
|
664
|
-
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
665
|
-
return `${obj.label} = t\xE2m ngo\u1EA1i ti\u1EBFp \u0394${labels}`;
|
|
666
|
-
}
|
|
667
|
-
if (c.kind === "incenter") {
|
|
668
|
-
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
669
|
-
return `${obj.label} = t\xE2m n\u1ED9i ti\u1EBFp \u0394${labels}`;
|
|
670
|
-
}
|
|
671
|
-
if (c.kind === "centroid") {
|
|
672
|
-
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
673
|
-
return `${obj.label} = tr\u1ECDng t\xE2m \u0394${labels}`;
|
|
674
|
-
}
|
|
675
|
-
if (c.kind === "orthocenter") {
|
|
676
|
-
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
677
|
-
return `${obj.label} = tr\u1EF1c t\xE2m \u0394${labels}`;
|
|
678
|
-
}
|
|
1225
|
+
const mod = POINT_CONSTRAINTS.get(c.kind);
|
|
1226
|
+
if (mod) return mod.describe(obj, state, c);
|
|
679
1227
|
return `\u0110i\u1EC3m ${obj.label}`;
|
|
680
1228
|
},
|
|
681
1229
|
render: (obj, ctx) => {
|
|
682
1230
|
const board = ctx.jxg;
|
|
683
1231
|
const c = obj.attrs.constraint;
|
|
684
|
-
const opts =
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
visible: obj.visible,
|
|
688
|
-
fixed: obj.locked,
|
|
689
|
-
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
690
|
-
fillColor: obj.attrs.color ?? "#1e40af",
|
|
691
|
-
face: obj.attrs.face ?? "o",
|
|
692
|
-
size: obj.attrs.size ?? 4
|
|
693
|
-
};
|
|
694
|
-
if (c.kind === "free") return board.create("point", [c.x, c.y], opts);
|
|
695
|
-
if (c.kind === "onAxis") {
|
|
696
|
-
const coords = c.axis === "x" ? [c.t, 0] : [0, c.t];
|
|
697
|
-
return board.create("point", coords, opts);
|
|
698
|
-
}
|
|
699
|
-
if (c.kind === "onLine") {
|
|
700
|
-
const line = ctx.resolveRef(c.lineId);
|
|
701
|
-
return board.create("glider", [c.t, c.t, line], opts);
|
|
702
|
-
}
|
|
703
|
-
if (c.kind === "onSegment") {
|
|
704
|
-
const seg = ctx.resolveRef(c.segmentId);
|
|
705
|
-
return board.create("glider", [c.t, c.t, seg], opts);
|
|
706
|
-
}
|
|
707
|
-
if (c.kind === "onCircle") {
|
|
708
|
-
const circle = ctx.resolveRef(c.circleId);
|
|
709
|
-
return board.create("glider", [Math.cos(c.theta), Math.sin(c.theta), circle], opts);
|
|
710
|
-
}
|
|
711
|
-
if (c.kind === "onPolygon") {
|
|
712
|
-
const poly = ctx.resolveRef(c.polygonId);
|
|
713
|
-
return board.create("glider", [c.u, c.v, poly], opts);
|
|
714
|
-
}
|
|
715
|
-
if (c.kind === "midpoint") {
|
|
716
|
-
const p1 = ctx.resolveRef(c.p1);
|
|
717
|
-
const p2 = ctx.resolveRef(c.p2);
|
|
718
|
-
return board.create("midpoint", [p1, p2], opts);
|
|
719
|
-
}
|
|
720
|
-
if (c.kind === "transformed") {
|
|
721
|
-
const src = ctx.resolveRef(c.source);
|
|
722
|
-
const transforms = buildJxgTransforms(board, ctx, c.transform);
|
|
723
|
-
const parent = transforms.length === 1 ? transforms[0] : transforms;
|
|
724
|
-
const pt = board.create("point", [src, parent], opts);
|
|
725
|
-
pt._helpers = transforms;
|
|
726
|
-
return pt;
|
|
727
|
-
}
|
|
728
|
-
if (c.kind === "perpFoot") {
|
|
729
|
-
const from = ctx.resolveRef(c.from);
|
|
730
|
-
const onLine = ctx.resolveRef(c.onLine);
|
|
731
|
-
return board.create("perpendicularpoint", [onLine, from], opts);
|
|
732
|
-
}
|
|
733
|
-
if (c.kind === "circumcenter") {
|
|
734
|
-
const a = ctx.resolveRef(c.vertices[0]);
|
|
735
|
-
const b = ctx.resolveRef(c.vertices[1]);
|
|
736
|
-
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
737
|
-
return board.create("circumcenter", [a, b, c3], opts);
|
|
738
|
-
}
|
|
739
|
-
if (c.kind === "incenter") {
|
|
740
|
-
const a = ctx.resolveRef(c.vertices[0]);
|
|
741
|
-
const b = ctx.resolveRef(c.vertices[1]);
|
|
742
|
-
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
743
|
-
return board.create("incenter", [a, b, c3], opts);
|
|
744
|
-
}
|
|
745
|
-
if (c.kind === "centroid") {
|
|
746
|
-
const a = ctx.resolveRef(c.vertices[0]);
|
|
747
|
-
const b = ctx.resolveRef(c.vertices[1]);
|
|
748
|
-
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
749
|
-
return board.create("point", [
|
|
750
|
-
() => (a.X() + b.X() + c3.X()) / 3,
|
|
751
|
-
() => (a.Y() + b.Y() + c3.Y()) / 3
|
|
752
|
-
], opts);
|
|
753
|
-
}
|
|
754
|
-
if (c.kind === "orthocenter") {
|
|
755
|
-
const a = ctx.resolveRef(c.vertices[0]);
|
|
756
|
-
const b = ctx.resolveRef(c.vertices[1]);
|
|
757
|
-
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
758
|
-
const hide = { visible: false, withLabel: false, fixed: true, name: "" };
|
|
759
|
-
const lineBC = board.create("line", [b, c3], hide);
|
|
760
|
-
const altA = board.create("perpendicular", [lineBC, a], hide);
|
|
761
|
-
const lineAC = board.create("line", [a, c3], hide);
|
|
762
|
-
const altB = board.create("perpendicular", [lineAC, b], hide);
|
|
763
|
-
const ortho = board.create("intersection", [altA, altB, 0], opts);
|
|
764
|
-
ortho._helpers = [lineBC, altA, lineAC, altB];
|
|
765
|
-
return ortho;
|
|
766
|
-
}
|
|
1232
|
+
const opts = buildPointOpts(obj);
|
|
1233
|
+
const mod = POINT_CONSTRAINTS.get(c.kind);
|
|
1234
|
+
if (mod) return mod.render(obj, ctx, c, opts);
|
|
767
1235
|
return board.create("point", [0, 0], opts);
|
|
768
1236
|
},
|
|
769
1237
|
/**
|
|
@@ -863,6 +1331,10 @@ function constructionRefs(c) {
|
|
|
863
1331
|
return [c.p1, c.vertex, c.p2];
|
|
864
1332
|
case "angleBisectorLines":
|
|
865
1333
|
return [stripBorderSuffix(c.line1), stripBorderSuffix(c.line2)];
|
|
1334
|
+
case "lineThrough":
|
|
1335
|
+
return [...c.points];
|
|
1336
|
+
case "radicalAxis":
|
|
1337
|
+
return [c.circle1, c.circle2];
|
|
866
1338
|
case "tangent":
|
|
867
1339
|
return [c.throughPoint, c.toCircle];
|
|
868
1340
|
}
|
|
@@ -891,6 +1363,10 @@ var def5 = {
|
|
|
891
1363
|
return `${obj.label}: ph\xE2n gi\xE1c g\xF3c ${L(c.p1)}${L(c.vertex)}${L(c.p2)}`;
|
|
892
1364
|
case "angleBisectorLines":
|
|
893
1365
|
return `${obj.label}: ph\xE2n gi\xE1c ${L(c.line1)} & ${L(c.line2)} (${c.branch === 0 ? "1" : "2"})`;
|
|
1366
|
+
case "lineThrough":
|
|
1367
|
+
return `${obj.label}: \u0111\u01B0\u1EDDng qua ${c.points.map(L).join("")}`;
|
|
1368
|
+
case "radicalAxis":
|
|
1369
|
+
return `${obj.label}: tr\u1EE5c \u0111\u1EB3ng ph\u01B0\u01A1ng ${L(c.circle1)} & ${L(c.circle2)}`;
|
|
894
1370
|
case "tangent":
|
|
895
1371
|
return `${obj.label}: ti\u1EBFp tuy\u1EBFn ${L(c.toCircle)} qua ${L(c.throughPoint)}`;
|
|
896
1372
|
}
|
|
@@ -971,6 +1447,47 @@ var def5 = {
|
|
|
971
1447
|
selected._helpers = [other];
|
|
972
1448
|
return selected;
|
|
973
1449
|
}
|
|
1450
|
+
case "lineThrough": {
|
|
1451
|
+
const pts = c.points.map((id) => ctx.resolveRef(id));
|
|
1452
|
+
let bi = 0, bj = 1, best = -1;
|
|
1453
|
+
for (let i = 0; i < pts.length; i++) {
|
|
1454
|
+
for (let j = i + 1; j < pts.length; j++) {
|
|
1455
|
+
const dx = pts[i].X() - pts[j].X();
|
|
1456
|
+
const dy = pts[i].Y() - pts[j].Y();
|
|
1457
|
+
const d = dx * dx + dy * dy;
|
|
1458
|
+
if (d > best) {
|
|
1459
|
+
best = d;
|
|
1460
|
+
bi = i;
|
|
1461
|
+
bj = j;
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
return board.create("line", [pts[bi], pts[bj]], {
|
|
1466
|
+
...baseOpts,
|
|
1467
|
+
straightFirst: true,
|
|
1468
|
+
straightLast: true
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
case "radicalAxis": {
|
|
1472
|
+
const k1 = ctx.resolveRef(c.circle1);
|
|
1473
|
+
const k2 = ctx.resolveRef(c.circle2);
|
|
1474
|
+
const o1 = () => [k1.center.X(), k1.center.Y()];
|
|
1475
|
+
const o2 = () => [k2.center.X(), k2.center.Y()];
|
|
1476
|
+
const foot = () => radicalAxisFoot(o1(), k1.Radius(), o2(), k2.Radius());
|
|
1477
|
+
const hide = { visible: false, withLabel: false, fixed: true, name: "" };
|
|
1478
|
+
const f1 = board.create("point", [() => foot()[0], () => foot()[1]], hide);
|
|
1479
|
+
const f2 = board.create("point", [
|
|
1480
|
+
() => foot()[0] - (o2()[1] - o1()[1]),
|
|
1481
|
+
() => foot()[1] + (o2()[0] - o1()[0])
|
|
1482
|
+
], hide);
|
|
1483
|
+
const line = board.create("line", [f1, f2], {
|
|
1484
|
+
...baseOpts,
|
|
1485
|
+
straightFirst: true,
|
|
1486
|
+
straightLast: true
|
|
1487
|
+
});
|
|
1488
|
+
line._helpers = [f1, f2];
|
|
1489
|
+
return line;
|
|
1490
|
+
}
|
|
974
1491
|
case "tangent": {
|
|
975
1492
|
const through = ctx.resolveRef(c.throughPoint);
|
|
976
1493
|
const toCircle = ctx.resolveRef(c.toCircle);
|
|
@@ -1073,10 +1590,29 @@ var def7 = {
|
|
|
1073
1590
|
registerKind(def7);
|
|
1074
1591
|
|
|
1075
1592
|
// src/core/scene/kinds/circle.ts
|
|
1593
|
+
function asConstruction(a) {
|
|
1594
|
+
if (a.construction) return a.construction;
|
|
1595
|
+
const raw = a;
|
|
1596
|
+
if (raw.kind === "incircle" && raw.vertices) {
|
|
1597
|
+
return {
|
|
1598
|
+
kind: "incircle",
|
|
1599
|
+
p1: raw.vertices[0],
|
|
1600
|
+
p2: raw.vertices[1],
|
|
1601
|
+
p3: raw.vertices[2]
|
|
1602
|
+
};
|
|
1603
|
+
}
|
|
1604
|
+
return void 0;
|
|
1605
|
+
}
|
|
1076
1606
|
function constructionRefs2(c) {
|
|
1077
1607
|
switch (c.kind) {
|
|
1078
1608
|
case "circumscribed":
|
|
1079
1609
|
return [c.p1, c.p2, c.p3];
|
|
1610
|
+
case "incircle":
|
|
1611
|
+
return [c.p1, c.p2, c.p3];
|
|
1612
|
+
case "excircle":
|
|
1613
|
+
return [c.p1, c.p2, c.p3];
|
|
1614
|
+
case "diameter":
|
|
1615
|
+
return [c.p1, c.p2];
|
|
1080
1616
|
}
|
|
1081
1617
|
}
|
|
1082
1618
|
var def8 = {
|
|
@@ -1084,14 +1620,27 @@ var def8 = {
|
|
|
1084
1620
|
schemaVersion: 1,
|
|
1085
1621
|
migrate: {},
|
|
1086
1622
|
validate: (a) => {
|
|
1087
|
-
if (a
|
|
1623
|
+
if (asConstruction(a)) return;
|
|
1624
|
+
if (typeof a?.radius === "number") {
|
|
1625
|
+
if (!a.center) throw new Error("circle: center b\u1EAFt bu\u1ED9c khi d\xF9ng radius");
|
|
1626
|
+
if (!(a.radius > 0)) throw new Error("circle: radius ph\u1EA3i > 0");
|
|
1627
|
+
return;
|
|
1628
|
+
}
|
|
1088
1629
|
if (!a?.center || !a?.surfacePoint) {
|
|
1089
|
-
throw new Error("circle: center v\xE0 surfacePoint b\u1EAFt bu\u1ED9c (ho\u1EB7c construction)");
|
|
1630
|
+
throw new Error("circle: center v\xE0 surfacePoint b\u1EAFt bu\u1ED9c (ho\u1EB7c construction / radius)");
|
|
1090
1631
|
}
|
|
1091
1632
|
},
|
|
1092
|
-
dependsOn: (a) =>
|
|
1633
|
+
dependsOn: (a) => {
|
|
1634
|
+
const c = asConstruction(a);
|
|
1635
|
+
if (c) return constructionRefs2(c);
|
|
1636
|
+
if (typeof a.radius === "number") return [a.center];
|
|
1637
|
+
return [a.center, a.surfacePoint];
|
|
1638
|
+
},
|
|
1093
1639
|
measure: (obj, state) => {
|
|
1094
|
-
if (obj.attrs
|
|
1640
|
+
if (asConstruction(obj.attrs)) return null;
|
|
1641
|
+
if (typeof obj.attrs.radius === "number") {
|
|
1642
|
+
return [{ label: "r", value: obj.attrs.radius }];
|
|
1643
|
+
}
|
|
1095
1644
|
const center = obj.attrs.center ? state.objects[obj.attrs.center] : void 0;
|
|
1096
1645
|
const surface = obj.attrs.surfacePoint ? state.objects[obj.attrs.surfacePoint] : void 0;
|
|
1097
1646
|
if (!center || !surface) return null;
|
|
@@ -1104,10 +1653,22 @@ var def8 = {
|
|
|
1104
1653
|
},
|
|
1105
1654
|
describe: (obj, state) => {
|
|
1106
1655
|
const L = (id) => labelOf(id, state);
|
|
1107
|
-
const c = obj.attrs
|
|
1656
|
+
const c = asConstruction(obj.attrs);
|
|
1108
1657
|
if (c?.kind === "circumscribed") {
|
|
1109
1658
|
return `\u0110\u01B0\u1EDDng tr\xF2n \u0111i qua ${L(c.p1)}${L(c.p2)}${L(c.p3)}`;
|
|
1110
1659
|
}
|
|
1660
|
+
if (c?.kind === "incircle") {
|
|
1661
|
+
return `\u0110\u01B0\u1EDDng tr\xF2n n\u1ED9i ti\u1EBFp \u0394${L(c.p1)}${L(c.p2)}${L(c.p3)}`;
|
|
1662
|
+
}
|
|
1663
|
+
if (c?.kind === "excircle") {
|
|
1664
|
+
return `\u0110\u01B0\u1EDDng tr\xF2n b\xE0ng ti\u1EBFp \u0394${L(c.p1)}${L(c.p2)}${L(c.p3)} \u0111\u1ED1i di\u1EC7n ${L(c.opposite)}`;
|
|
1665
|
+
}
|
|
1666
|
+
if (c?.kind === "diameter") {
|
|
1667
|
+
return `\u0110\u01B0\u1EDDng tr\xF2n \u0111\u01B0\u1EDDng k\xEDnh ${L(c.p1)}${L(c.p2)}`;
|
|
1668
|
+
}
|
|
1669
|
+
if (typeof obj.attrs.radius === "number") {
|
|
1670
|
+
return `\u0110\u01B0\u1EDDng tr\xF2n t\xE2m ${L(obj.attrs.center)} b\xE1n k\xEDnh ${obj.attrs.radius}`;
|
|
1671
|
+
}
|
|
1111
1672
|
return `\u0110\u01B0\u1EDDng tr\xF2n t\xE2m ${L(obj.attrs.center)} b\xE1n k\xEDnh ${L(obj.attrs.center)}${L(obj.attrs.surfacePoint)}`;
|
|
1112
1673
|
},
|
|
1113
1674
|
render: (obj, ctx) => {
|
|
@@ -1122,13 +1683,66 @@ var def8 = {
|
|
|
1122
1683
|
visible: obj.visible,
|
|
1123
1684
|
fixed: obj.locked
|
|
1124
1685
|
};
|
|
1125
|
-
const c = obj.attrs
|
|
1686
|
+
const c = asConstruction(obj.attrs);
|
|
1126
1687
|
if (c?.kind === "circumscribed") {
|
|
1127
1688
|
const p1 = ctx.resolveRef(c.p1);
|
|
1128
1689
|
const p2 = ctx.resolveRef(c.p2);
|
|
1129
1690
|
const p3 = ctx.resolveRef(c.p3);
|
|
1130
1691
|
return board.create("circumcircle", [p1, p2, p3], baseOpts);
|
|
1131
1692
|
}
|
|
1693
|
+
if (c?.kind === "incircle") {
|
|
1694
|
+
const p1 = ctx.resolveRef(c.p1);
|
|
1695
|
+
const p2 = ctx.resolveRef(c.p2);
|
|
1696
|
+
const p3 = ctx.resolveRef(c.p3);
|
|
1697
|
+
const center2 = board.create("incenter", [p1, p2, p3], {
|
|
1698
|
+
visible: obj.visible,
|
|
1699
|
+
withLabel: true,
|
|
1700
|
+
fixed: true,
|
|
1701
|
+
name: obj.label
|
|
1702
|
+
});
|
|
1703
|
+
const circ = board.create("incircle", [p1, p2, p3], baseOpts);
|
|
1704
|
+
circ.center = circ.center ?? center2;
|
|
1705
|
+
circ._helpers = [center2];
|
|
1706
|
+
return circ;
|
|
1707
|
+
}
|
|
1708
|
+
if (c?.kind === "excircle") {
|
|
1709
|
+
const P = [ctx.resolveRef(c.p1), ctx.resolveRef(c.p2), ctx.resolveRef(c.p3)];
|
|
1710
|
+
const ids = [c.p1, c.p2, c.p3];
|
|
1711
|
+
const oppIdx = Math.max(0, ids.indexOf(c.opposite));
|
|
1712
|
+
const verts = () => [
|
|
1713
|
+
[P[0].X(), P[0].Y()],
|
|
1714
|
+
[P[1].X(), P[1].Y()],
|
|
1715
|
+
[P[2].X(), P[2].Y()]
|
|
1716
|
+
];
|
|
1717
|
+
const ctr = () => excenter(verts(), oppIdx);
|
|
1718
|
+
const radius = () => {
|
|
1719
|
+
const I = ctr();
|
|
1720
|
+
const others = [0, 1, 2].filter((i) => i !== oppIdx);
|
|
1721
|
+
const v = verts();
|
|
1722
|
+
const a = v[others[0]];
|
|
1723
|
+
const b = v[others[1]];
|
|
1724
|
+
const dx = b[0] - a[0];
|
|
1725
|
+
const dy = b[1] - a[1];
|
|
1726
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
1727
|
+
return Math.abs((I[0] - a[0]) * dy - (I[1] - a[1]) * dx) / len;
|
|
1728
|
+
};
|
|
1729
|
+
const center2 = board.create("point", [() => ctr()[0], () => ctr()[1]], { visible: false, withLabel: false, fixed: true, name: "" });
|
|
1730
|
+
const circ = board.create("circle", [center2, () => radius()], baseOpts);
|
|
1731
|
+
circ._helpers = [center2];
|
|
1732
|
+
return circ;
|
|
1733
|
+
}
|
|
1734
|
+
if (c?.kind === "diameter") {
|
|
1735
|
+
const p1 = ctx.resolveRef(c.p1);
|
|
1736
|
+
const p2 = ctx.resolveRef(c.p2);
|
|
1737
|
+
const center2 = board.create("midpoint", [p1, p2], { visible: false, withLabel: false, fixed: true, name: "" });
|
|
1738
|
+
const circ = board.create("circle", [center2, p2], baseOpts);
|
|
1739
|
+
circ._helpers = [center2];
|
|
1740
|
+
return circ;
|
|
1741
|
+
}
|
|
1742
|
+
if (typeof obj.attrs.radius === "number") {
|
|
1743
|
+
const center2 = ctx.resolveRef(obj.attrs.center);
|
|
1744
|
+
return board.create("circle", [center2, obj.attrs.radius], baseOpts);
|
|
1745
|
+
}
|
|
1132
1746
|
const center = ctx.resolveRef(obj.attrs.center);
|
|
1133
1747
|
const surface = ctx.resolveRef(obj.attrs.surfacePoint);
|
|
1134
1748
|
return board.create("circle", [center, surface], baseOpts);
|
|
@@ -1272,19 +1886,53 @@ function regularVertexLabels(p1Label, p2Label, n) {
|
|
|
1272
1886
|
}
|
|
1273
1887
|
return `${p1Label}${p2Label}\u2026`;
|
|
1274
1888
|
}
|
|
1889
|
+
function specialShapeName(kind) {
|
|
1890
|
+
switch (kind) {
|
|
1891
|
+
case "square":
|
|
1892
|
+
return "H\xECnh vu\xF4ng";
|
|
1893
|
+
case "rectangle":
|
|
1894
|
+
return "H\xECnh ch\u1EEF nh\u1EADt";
|
|
1895
|
+
case "rhombus":
|
|
1896
|
+
return "H\xECnh thoi";
|
|
1897
|
+
case "parallelogram":
|
|
1898
|
+
return "H\xECnh b\xECnh h\xE0nh";
|
|
1899
|
+
case "isoTrapezoid":
|
|
1900
|
+
return "H\xECnh thang c\xE2n";
|
|
1901
|
+
case "isoTriangle":
|
|
1902
|
+
return "Tam gi\xE1c c\xE2n";
|
|
1903
|
+
case "rightTriangle":
|
|
1904
|
+
return "Tam gi\xE1c vu\xF4ng";
|
|
1905
|
+
case "regular":
|
|
1906
|
+
return "";
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1275
1909
|
var def11 = {
|
|
1276
1910
|
type: "polygon",
|
|
1277
1911
|
schemaVersion: 1,
|
|
1278
1912
|
migrate: {},
|
|
1279
1913
|
validate: (a) => {
|
|
1280
1914
|
if (a?.construction) {
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1915
|
+
const c = a.construction;
|
|
1916
|
+
if (c.kind === "regular") {
|
|
1917
|
+
if (!c.p1 || !c.p2) throw new Error("polygon (regular): p1 v\xE0 p2 b\u1EAFt bu\u1ED9c");
|
|
1918
|
+
if (!Number.isFinite(c.n) || c.n < 3) throw new Error("polygon (regular): n \u2265 3");
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1921
|
+
if (c.kind === "square") {
|
|
1922
|
+
if (!c.p1 || !c.p2) throw new Error("polygon (square): p1 v\xE0 p2 b\u1EAFt bu\u1ED9c");
|
|
1923
|
+
return;
|
|
1924
|
+
}
|
|
1925
|
+
if (c.kind === "rectangle" || c.kind === "rhombus" || c.kind === "parallelogram" || c.kind === "isoTrapezoid") {
|
|
1926
|
+
if (!c.p1 || !c.p2 || !c.p3) throw new Error(`polygon (${c.kind}): p1, p2, p3 b\u1EAFt bu\u1ED9c`);
|
|
1927
|
+
return;
|
|
1928
|
+
}
|
|
1929
|
+
if (c.kind === "isoTriangle") {
|
|
1930
|
+
if (!c.base1 || !c.base2 || !c.apex) throw new Error("polygon (isoTriangle): base1, base2, apex b\u1EAFt bu\u1ED9c");
|
|
1931
|
+
return;
|
|
1932
|
+
}
|
|
1933
|
+
if (c.kind === "rightTriangle") {
|
|
1934
|
+
if (!c.rightAngle || !c.leg1End || !c.leg2End) throw new Error("polygon (rightTriangle): rightAngle, leg1End, leg2End b\u1EAFt bu\u1ED9c");
|
|
1935
|
+
return;
|
|
1288
1936
|
}
|
|
1289
1937
|
return;
|
|
1290
1938
|
}
|
|
@@ -1293,14 +1941,51 @@ var def11 = {
|
|
|
1293
1941
|
}
|
|
1294
1942
|
},
|
|
1295
1943
|
dependsOn: (a) => {
|
|
1296
|
-
|
|
1297
|
-
return [...a.vertices ?? []];
|
|
1944
|
+
const c = a.construction;
|
|
1945
|
+
if (!c) return [...a.vertices ?? []];
|
|
1946
|
+
switch (c.kind) {
|
|
1947
|
+
case "regular":
|
|
1948
|
+
return [c.p1, c.p2];
|
|
1949
|
+
case "square":
|
|
1950
|
+
return [c.p1, c.p2];
|
|
1951
|
+
case "rectangle":
|
|
1952
|
+
case "rhombus":
|
|
1953
|
+
case "parallelogram":
|
|
1954
|
+
case "isoTrapezoid":
|
|
1955
|
+
return [c.p1, c.p2, c.p3];
|
|
1956
|
+
case "isoTriangle":
|
|
1957
|
+
return [c.base1, c.base2, c.apex];
|
|
1958
|
+
case "rightTriangle":
|
|
1959
|
+
return [c.rightAngle, c.leg1End, c.leg2End];
|
|
1960
|
+
}
|
|
1298
1961
|
},
|
|
1299
1962
|
describe: (obj, state) => {
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1963
|
+
const c = obj.attrs.construction;
|
|
1964
|
+
if (c) {
|
|
1965
|
+
if (c.kind === "regular") {
|
|
1966
|
+
const labels2 = regularVertexLabels(labelOf(c.p1, state), labelOf(c.p2, state), c.n);
|
|
1967
|
+
return `${regularPolygonName(c.n)} ${labels2}`;
|
|
1968
|
+
}
|
|
1969
|
+
const name = specialShapeName(c.kind);
|
|
1970
|
+
let labels = [];
|
|
1971
|
+
switch (c.kind) {
|
|
1972
|
+
case "square":
|
|
1973
|
+
labels = [labelOf(c.p1, state), labelOf(c.p2, state)];
|
|
1974
|
+
break;
|
|
1975
|
+
case "rectangle":
|
|
1976
|
+
case "rhombus":
|
|
1977
|
+
case "parallelogram":
|
|
1978
|
+
case "isoTrapezoid":
|
|
1979
|
+
labels = [labelOf(c.p1, state), labelOf(c.p2, state), labelOf(c.p3, state)];
|
|
1980
|
+
break;
|
|
1981
|
+
case "isoTriangle":
|
|
1982
|
+
labels = [labelOf(c.apex, state), labelOf(c.base1, state), labelOf(c.base2, state)];
|
|
1983
|
+
break;
|
|
1984
|
+
case "rightTriangle":
|
|
1985
|
+
labels = [labelOf(c.rightAngle, state), labelOf(c.leg1End, state), labelOf(c.leg2End, state)];
|
|
1986
|
+
break;
|
|
1987
|
+
}
|
|
1988
|
+
return `${name} ${labels.join("")}`;
|
|
1304
1989
|
}
|
|
1305
1990
|
return `\u0110a gi\xE1c ${(obj.attrs.vertices ?? []).map((id) => labelOf(id, state)).join("")}`;
|
|
1306
1991
|
},
|
|
@@ -1308,22 +1993,85 @@ var def11 = {
|
|
|
1308
1993
|
const board = ctx.jxg;
|
|
1309
1994
|
const label = obj.label;
|
|
1310
1995
|
const showValue = obj.attrs.showValue ?? false;
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1996
|
+
const cons = obj.attrs.construction;
|
|
1997
|
+
const commonAttrs = {
|
|
1998
|
+
name: label,
|
|
1999
|
+
withLabel: obj.attrs.showLabel ?? false,
|
|
2000
|
+
borders: {
|
|
2001
|
+
strokeColor: obj.attrs.color ?? "#0f172a",
|
|
2002
|
+
strokeWidth: obj.attrs.width ?? 2
|
|
2003
|
+
},
|
|
2004
|
+
fillColor: obj.attrs.color ?? "#60a5fa",
|
|
2005
|
+
fillOpacity: obj.attrs.fillOpacity ?? 0.15,
|
|
2006
|
+
visible: obj.visible,
|
|
2007
|
+
fixed: obj.locked
|
|
2008
|
+
};
|
|
2009
|
+
if (cons?.kind === "regular") {
|
|
2010
|
+
const p1 = ctx.resolveRef(cons.p1);
|
|
2011
|
+
const p2 = ctx.resolveRef(cons.p2);
|
|
2012
|
+
return board.create("regularpolygon", [p1, p2, cons.n], commonAttrs);
|
|
2013
|
+
}
|
|
2014
|
+
if (cons?.kind === "square") {
|
|
2015
|
+
const p1 = ctx.resolveRef(cons.p1);
|
|
2016
|
+
const p2 = ctx.resolveRef(cons.p2);
|
|
2017
|
+
return board.create("regularpolygon", [p1, p2, 4], commonAttrs);
|
|
2018
|
+
}
|
|
2019
|
+
if (cons?.kind === "rectangle" || cons?.kind === "rhombus" || cons?.kind === "parallelogram") {
|
|
2020
|
+
const A = ctx.resolveRef(cons.p1);
|
|
2021
|
+
const B = ctx.resolveRef(cons.p2);
|
|
2022
|
+
const C = ctx.resolveRef(cons.p3);
|
|
2023
|
+
const D = board.create(
|
|
2024
|
+
"point",
|
|
2025
|
+
[() => A.X() + C.X() - B.X(), () => A.Y() + C.Y() - B.Y()],
|
|
2026
|
+
{ visible: false, withLabel: false, fixed: true, name: "" }
|
|
2027
|
+
);
|
|
2028
|
+
const poly2 = board.create("polygon", [A, B, C, D], commonAttrs);
|
|
2029
|
+
poly2._helpers = [D];
|
|
2030
|
+
return poly2;
|
|
2031
|
+
}
|
|
2032
|
+
if (cons?.kind === "isoTrapezoid") {
|
|
2033
|
+
const A = ctx.resolveRef(cons.p1);
|
|
2034
|
+
const B = ctx.resolveRef(cons.p2);
|
|
2035
|
+
const C = ctx.resolveRef(cons.p3);
|
|
2036
|
+
const Dx = () => {
|
|
2037
|
+
const Mx = (A.X() + B.X()) / 2;
|
|
2038
|
+
const My = (A.Y() + B.Y()) / 2;
|
|
2039
|
+
const ux = B.X() - A.X();
|
|
2040
|
+
const uy = B.Y() - A.Y();
|
|
2041
|
+
const len2 = ux * ux + uy * uy || 1;
|
|
2042
|
+
const proj = ((C.X() - Mx) * ux + (C.Y() - My) * uy) / len2;
|
|
2043
|
+
return C.X() - 2 * proj * ux;
|
|
2044
|
+
};
|
|
2045
|
+
const Dy = () => {
|
|
2046
|
+
const Mx = (A.X() + B.X()) / 2;
|
|
2047
|
+
const My = (A.Y() + B.Y()) / 2;
|
|
2048
|
+
const ux = B.X() - A.X();
|
|
2049
|
+
const uy = B.Y() - A.Y();
|
|
2050
|
+
const len2 = ux * ux + uy * uy || 1;
|
|
2051
|
+
const proj = ((C.X() - Mx) * ux + (C.Y() - My) * uy) / len2;
|
|
2052
|
+
return C.Y() - 2 * proj * uy;
|
|
2053
|
+
};
|
|
2054
|
+
const D = board.create("point", [Dx, Dy], {
|
|
2055
|
+
visible: false,
|
|
2056
|
+
withLabel: false,
|
|
2057
|
+
fixed: true,
|
|
2058
|
+
name: ""
|
|
1326
2059
|
});
|
|
2060
|
+
const poly2 = board.create("polygon", [A, B, C, D], commonAttrs);
|
|
2061
|
+
poly2._helpers = [D];
|
|
2062
|
+
return poly2;
|
|
2063
|
+
}
|
|
2064
|
+
if (cons?.kind === "isoTriangle") {
|
|
2065
|
+
const Apex = ctx.resolveRef(cons.apex);
|
|
2066
|
+
const B1 = ctx.resolveRef(cons.base1);
|
|
2067
|
+
const B2 = ctx.resolveRef(cons.base2);
|
|
2068
|
+
return board.create("polygon", [Apex, B1, B2], commonAttrs);
|
|
2069
|
+
}
|
|
2070
|
+
if (cons?.kind === "rightTriangle") {
|
|
2071
|
+
const R = ctx.resolveRef(cons.rightAngle);
|
|
2072
|
+
const P = ctx.resolveRef(cons.leg1End);
|
|
2073
|
+
const Q = ctx.resolveRef(cons.leg2End);
|
|
2074
|
+
return board.create("polygon", [R, P, Q], commonAttrs);
|
|
1327
2075
|
}
|
|
1328
2076
|
const verts = (obj.attrs.vertices ?? []).map((id) => ctx.resolveRef(id));
|
|
1329
2077
|
const poly = board.create("polygon", verts, {
|
|
@@ -1767,5 +2515,5 @@ function deserializeScene(domain, raw) {
|
|
|
1767
2515
|
}
|
|
1768
2516
|
|
|
1769
2517
|
export { deserializeScene, listObjects, nextLabel, serializeScene, useEditorState };
|
|
1770
|
-
//# sourceMappingURL=chunk-
|
|
1771
|
-
//# sourceMappingURL=chunk-
|
|
2518
|
+
//# sourceMappingURL=chunk-D5LWSN2Y.mjs.map
|
|
2519
|
+
//# sourceMappingURL=chunk-D5LWSN2Y.mjs.map
|