@xom11/whiteboard 0.25.0 → 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/dist/{ExcalidrawWithMenus-WENZRYYE.mjs → ExcalidrawWithMenus-2QPPTXJM.mjs} +3 -2
- package/dist/ExcalidrawWithMenus-2QPPTXJM.mjs.map +1 -0
- package/dist/ai.d.mts +3035 -108
- package/dist/ai.d.ts +3035 -108
- package/dist/ai.js +6736 -775
- package/dist/ai.js.map +1 -1
- package/dist/ai.mjs +5110 -578
- package/dist/ai.mjs.map +1 -1
- package/dist/catalog.json +5 -5
- package/dist/{chunk-ESVPQWHX.mjs → chunk-4ETJ4CDY.mjs} +5 -5
- package/dist/{chunk-ESVPQWHX.mjs.map → chunk-4ETJ4CDY.mjs.map} +1 -1
- package/dist/{chunk-VNCCIV6O.mjs → chunk-AJAHD35N.mjs} +779 -9
- 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-M42TGYT6.mjs → chunk-BNBOIDO5.mjs} +3 -3
- package/dist/{chunk-M42TGYT6.mjs.map → chunk-BNBOIDO5.mjs.map} +1 -1
- package/dist/{chunk-CJBLJUWG.mjs → chunk-CXHNVYMD.mjs} +4 -4
- package/dist/{chunk-CJBLJUWG.mjs.map → chunk-CXHNVYMD.mjs.map} +1 -1
- package/dist/{chunk-NDEZJKNY.mjs → chunk-D5JLJ3PT.mjs} +4 -4
- package/dist/{chunk-NDEZJKNY.mjs.map → chunk-D5JLJ3PT.mjs.map} +1 -1
- package/dist/{chunk-REIJZDVZ.mjs → chunk-D5LWSN2Y.mjs} +942 -195
- package/dist/chunk-D5LWSN2Y.mjs.map +1 -0
- package/dist/{chunk-I24QOHPU.mjs → chunk-HLAOGXEK.mjs} +3 -3
- package/dist/{chunk-I24QOHPU.mjs.map → chunk-HLAOGXEK.mjs.map} +1 -1
- package/dist/{chunk-TB4CL25L.mjs → chunk-I3L56GVH.mjs} +206 -66
- package/dist/chunk-I3L56GVH.mjs.map +1 -0
- package/dist/chunk-J5LGTIGS.mjs +10 -0
- package/dist/chunk-J5LGTIGS.mjs.map +1 -0
- package/dist/{chunk-ONBCUWVI.mjs → chunk-KYMBUTPO.mjs} +3 -3
- package/dist/{chunk-ONBCUWVI.mjs.map → chunk-KYMBUTPO.mjs.map} +1 -1
- package/dist/{chunk-YSJOVBCD.mjs → chunk-KZGPSTZI.mjs} +4 -4
- package/dist/{chunk-YSJOVBCD.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-AYSFWUPK.mjs → chunk-SZDAS7LK.mjs} +79 -2
- 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 +1 -1
- package/dist/geometry-2d.d.ts +1 -1
- package/dist/geometry-2d.js +5389 -1362
- package/dist/geometry-2d.js.map +1 -1
- package/dist/geometry-2d.mjs +5 -4
- package/dist/geometry-3d.d.mts +1 -1
- package/dist/geometry-3d.d.ts +1 -1
- package/dist/geometry-3d.js +1333 -251
- package/dist/geometry-3d.js.map +1 -1
- package/dist/geometry-3d.mjs +4 -3
- package/dist/graph-2d.d.mts +1 -1
- package/dist/graph-2d.d.ts +1 -1
- package/dist/graph-2d.js +1499 -340
- package/dist/graph-2d.js.map +1 -1
- package/dist/graph-2d.mjs +7 -6
- package/dist/handleExtractProblem-C-U5KluK.d.mts +158 -0
- package/dist/handleExtractProblem-C-U5KluK.d.ts +158 -0
- package/dist/{host-L7FMFZUW.mjs → host-HAYCJJ2T.mjs} +1258 -441
- package/dist/host-HAYCJJ2T.mjs.map +1 -0
- package/dist/{host-QK53UYMD.mjs → host-LTJHAY5A.mjs} +10 -9
- package/dist/host-LTJHAY5A.mjs.map +1 -0
- package/dist/{host-A64ITWVX.mjs → host-M26FS244.mjs} +6 -5
- 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 +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +5621 -1590
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +22 -20
- package/dist/index.mjs.map +1 -1
- package/dist/latex.d.mts +1 -1
- package/dist/latex.d.ts +1 -1
- package/dist/latex.mjs +2 -1
- package/dist/render-ZX2O2IK7.mjs +10 -0
- package/dist/{render-3WTY7NZB.mjs.map → render-ZX2O2IK7.mjs.map} +1 -1
- package/dist/serialize-C3LSUMSA.mjs +9 -0
- package/dist/{serialize-SRJVKYUG.mjs.map → serialize-C3LSUMSA.mjs.map} +1 -1
- package/dist/{types-DWRyCa2m.d.ts → types-zc_Pa0mp.d.mts} +105 -0
- package/dist/{types-DWRyCa2m.d.mts → types-zc_Pa0mp.d.ts} +105 -0
- package/package.json +10 -1
- package/dist/ExcalidrawWithMenus-WENZRYYE.mjs.map +0 -1
- package/dist/chunk-AYSFWUPK.mjs.map +0 -1
- package/dist/chunk-REIJZDVZ.mjs.map +0 -1
- package/dist/chunk-TB4CL25L.mjs.map +0 -1
- package/dist/chunk-VNCCIV6O.mjs.map +0 -1
- package/dist/chunk-VRHWDZ66.mjs +0 -96
- package/dist/chunk-VRHWDZ66.mjs.map +0 -1
- package/dist/host-A64ITWVX.mjs.map +0 -1
- package/dist/host-L7FMFZUW.mjs.map +0 -1
- package/dist/host-QK53UYMD.mjs.map +0 -1
- package/dist/host-QS2EOTRJ.mjs.map +0 -1
- package/dist/render-3WTY7NZB.mjs +0 -9
- package/dist/serialize-SRJVKYUG.mjs +0 -8
package/dist/geometry-3d.js
CHANGED
|
@@ -923,6 +923,31 @@ function constraintRefs2D(c) {
|
|
|
923
923
|
return [c.vertices[0], c.vertices[1], c.vertices[2]];
|
|
924
924
|
case "orthocenter":
|
|
925
925
|
return [c.vertices[0], c.vertices[1], c.vertices[2]];
|
|
926
|
+
case "onPerpendicular":
|
|
927
|
+
return [c.through, c.perpToA, c.perpToB];
|
|
928
|
+
case "onPerpBisector":
|
|
929
|
+
return [c.p1, c.p2];
|
|
930
|
+
case "onCircleAroundPoint":
|
|
931
|
+
return [c.center, c.radiusPoint];
|
|
932
|
+
case "tangentPointExt":
|
|
933
|
+
return [c.from, c.circle];
|
|
934
|
+
case "circleIntersection":
|
|
935
|
+
return [c.c1, c.c2];
|
|
936
|
+
case "circleSecondIntersection":
|
|
937
|
+
return [c.c1, c.c2, c.exclude];
|
|
938
|
+
case "secondIntersection":
|
|
939
|
+
return [c.line, c.circle, c.other];
|
|
940
|
+
case "tangencyPoint":
|
|
941
|
+
return [c.circle, c.onLine];
|
|
942
|
+
case "arcMidpoint":
|
|
943
|
+
return [c.circle, c.a, c.b, c.notContaining];
|
|
944
|
+
case "pointAtDistance": {
|
|
945
|
+
const d = c.distance;
|
|
946
|
+
const extra = d.kind === "circleRadius" ? [d.circle] : d.kind === "segmentLength" ? [d.p1, d.p2] : [];
|
|
947
|
+
return [c.from, c.through, ...extra];
|
|
948
|
+
}
|
|
949
|
+
case "excenter":
|
|
950
|
+
return [c.vertices[0], c.vertices[1], c.vertices[2]];
|
|
926
951
|
default:
|
|
927
952
|
return [];
|
|
928
953
|
}
|
|
@@ -932,7 +957,412 @@ var init_d_constraint2 = __esm({
|
|
|
932
957
|
}
|
|
933
958
|
});
|
|
934
959
|
|
|
935
|
-
// src/core/scene/kinds/point.ts
|
|
960
|
+
// src/core/scene/kinds/point-constraints/_types.ts
|
|
961
|
+
function definePointConstraint(m) {
|
|
962
|
+
return m;
|
|
963
|
+
}
|
|
964
|
+
var init_types2 = __esm({
|
|
965
|
+
"src/core/scene/kinds/point-constraints/_types.ts"() {
|
|
966
|
+
}
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
// src/core/scene/kinds/point-constraints/free.ts
|
|
970
|
+
var freeConstraint;
|
|
971
|
+
var init_free = __esm({
|
|
972
|
+
"src/core/scene/kinds/point-constraints/free.ts"() {
|
|
973
|
+
init_types2();
|
|
974
|
+
freeConstraint = definePointConstraint({
|
|
975
|
+
kind: "free",
|
|
976
|
+
describe: (obj) => `\u0110i\u1EC3m ${obj.label}`,
|
|
977
|
+
render: (obj, ctx, c, opts) => {
|
|
978
|
+
const board = ctx.jxg;
|
|
979
|
+
return board.create("point", [c.x, c.y], opts);
|
|
980
|
+
}
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
// src/core/scene/kinds/point-constraints/onAxis.ts
|
|
986
|
+
var onAxisConstraint;
|
|
987
|
+
var init_onAxis = __esm({
|
|
988
|
+
"src/core/scene/kinds/point-constraints/onAxis.ts"() {
|
|
989
|
+
init_types2();
|
|
990
|
+
onAxisConstraint = definePointConstraint({
|
|
991
|
+
kind: "onAxis",
|
|
992
|
+
describe: (obj, _state, c) => `${obj.label} tr\xEAn tr\u1EE5c ${c.axis}`,
|
|
993
|
+
render: (obj, ctx, c, opts) => {
|
|
994
|
+
const board = ctx.jxg;
|
|
995
|
+
const coords = c.axis === "x" ? [c.t, 0] : [0, c.t];
|
|
996
|
+
return board.create("point", coords, opts);
|
|
997
|
+
}
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
// src/core/scene/kinds/point-constraints/midpoint.ts
|
|
1003
|
+
var midpointConstraint;
|
|
1004
|
+
var init_midpoint = __esm({
|
|
1005
|
+
"src/core/scene/kinds/point-constraints/midpoint.ts"() {
|
|
1006
|
+
init_types2();
|
|
1007
|
+
midpointConstraint = definePointConstraint({
|
|
1008
|
+
kind: "midpoint",
|
|
1009
|
+
describe: (obj, state, c) => {
|
|
1010
|
+
const l1 = state?.objects[c.p1]?.label ?? c.p1;
|
|
1011
|
+
const l2 = state?.objects[c.p2]?.label ?? c.p2;
|
|
1012
|
+
return `${obj.label} = trung \u0111i\u1EC3m ${l1}${l2}`;
|
|
1013
|
+
},
|
|
1014
|
+
render: (obj, ctx, c, opts) => {
|
|
1015
|
+
const board = ctx.jxg;
|
|
1016
|
+
const p1 = ctx.resolveRef(c.p1);
|
|
1017
|
+
const p2 = ctx.resolveRef(c.p2);
|
|
1018
|
+
return board.create("midpoint", [p1, p2], opts);
|
|
1019
|
+
}
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
});
|
|
1023
|
+
|
|
1024
|
+
// src/core/scene/kinds/point-constraints/perpFoot.ts
|
|
1025
|
+
var perpFootConstraint;
|
|
1026
|
+
var init_perpFoot = __esm({
|
|
1027
|
+
"src/core/scene/kinds/point-constraints/perpFoot.ts"() {
|
|
1028
|
+
init_types2();
|
|
1029
|
+
perpFootConstraint = definePointConstraint({
|
|
1030
|
+
kind: "perpFoot",
|
|
1031
|
+
validate: (c) => {
|
|
1032
|
+
if (!c.from || !c.onLine) {
|
|
1033
|
+
throw new Error("point.perpFoot: from v\xE0 onLine b\u1EAFt bu\u1ED9c");
|
|
1034
|
+
}
|
|
1035
|
+
},
|
|
1036
|
+
describe: (obj, state, c) => {
|
|
1037
|
+
const fromLabel = state?.objects[c.from]?.label ?? c.from;
|
|
1038
|
+
const lineLabel = state?.objects[c.onLine]?.label ?? c.onLine;
|
|
1039
|
+
return `${obj.label} = ch\xE2n \u27C2 t\u1EEB ${fromLabel} xu\u1ED1ng ${lineLabel}`;
|
|
1040
|
+
},
|
|
1041
|
+
render: (obj, ctx, c, opts) => {
|
|
1042
|
+
const board = ctx.jxg;
|
|
1043
|
+
const from = ctx.resolveRef(c.from);
|
|
1044
|
+
const onLine = ctx.resolveRef(c.onLine);
|
|
1045
|
+
return board.create("perpendicularpoint", [onLine, from], opts);
|
|
1046
|
+
}
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
|
|
1051
|
+
// src/core/scene/kinds/point-constraints/circumcenter.ts
|
|
1052
|
+
var circumcenterConstraint;
|
|
1053
|
+
var init_circumcenter = __esm({
|
|
1054
|
+
"src/core/scene/kinds/point-constraints/circumcenter.ts"() {
|
|
1055
|
+
init_types2();
|
|
1056
|
+
circumcenterConstraint = definePointConstraint({
|
|
1057
|
+
kind: "circumcenter",
|
|
1058
|
+
validate: (c) => {
|
|
1059
|
+
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
1060
|
+
throw new Error("point.circumcenter: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
1061
|
+
}
|
|
1062
|
+
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
1063
|
+
throw new Error("point.circumcenter: 3 vertex id ph\u1EA3i non-empty");
|
|
1064
|
+
}
|
|
1065
|
+
},
|
|
1066
|
+
describe: (obj, state, c) => {
|
|
1067
|
+
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
1068
|
+
return `${obj.label} = t\xE2m ngo\u1EA1i ti\u1EBFp \u0394${labels}`;
|
|
1069
|
+
},
|
|
1070
|
+
render: (obj, ctx, c, opts) => {
|
|
1071
|
+
const board = ctx.jxg;
|
|
1072
|
+
const a = ctx.resolveRef(c.vertices[0]);
|
|
1073
|
+
const b = ctx.resolveRef(c.vertices[1]);
|
|
1074
|
+
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
1075
|
+
return board.create("circumcenter", [a, b, c3], opts);
|
|
1076
|
+
}
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
|
|
1081
|
+
// src/core/scene/kinds/point-constraints/incenter.ts
|
|
1082
|
+
var incenterConstraint;
|
|
1083
|
+
var init_incenter = __esm({
|
|
1084
|
+
"src/core/scene/kinds/point-constraints/incenter.ts"() {
|
|
1085
|
+
init_types2();
|
|
1086
|
+
incenterConstraint = definePointConstraint({
|
|
1087
|
+
kind: "incenter",
|
|
1088
|
+
validate: (c) => {
|
|
1089
|
+
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
1090
|
+
throw new Error("point.incenter: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
1091
|
+
}
|
|
1092
|
+
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
1093
|
+
throw new Error("point.incenter: 3 vertex id ph\u1EA3i non-empty");
|
|
1094
|
+
}
|
|
1095
|
+
},
|
|
1096
|
+
describe: (obj, state, c) => {
|
|
1097
|
+
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
1098
|
+
return `${obj.label} = t\xE2m n\u1ED9i ti\u1EBFp \u0394${labels}`;
|
|
1099
|
+
},
|
|
1100
|
+
render: (obj, ctx, c, opts) => {
|
|
1101
|
+
const board = ctx.jxg;
|
|
1102
|
+
const a = ctx.resolveRef(c.vertices[0]);
|
|
1103
|
+
const b = ctx.resolveRef(c.vertices[1]);
|
|
1104
|
+
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
1105
|
+
return board.create("incenter", [a, b, c3], opts);
|
|
1106
|
+
}
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1110
|
+
|
|
1111
|
+
// src/core/scene/kinds/point-constraints/onLine.ts
|
|
1112
|
+
var onLineConstraint;
|
|
1113
|
+
var init_onLine = __esm({
|
|
1114
|
+
"src/core/scene/kinds/point-constraints/onLine.ts"() {
|
|
1115
|
+
init_types2();
|
|
1116
|
+
onLineConstraint = definePointConstraint({
|
|
1117
|
+
kind: "onLine",
|
|
1118
|
+
describe: (obj, state, c) => `${obj.label} tr\xEAn \u0111\u01B0\u1EDDng ${state?.objects[c.lineId]?.label ?? c.lineId}`,
|
|
1119
|
+
render: (obj, ctx, c, opts) => {
|
|
1120
|
+
const board = ctx.jxg;
|
|
1121
|
+
const line = ctx.resolveRef(c.lineId);
|
|
1122
|
+
const p1 = line.point1;
|
|
1123
|
+
const p2 = line.point2;
|
|
1124
|
+
const sx = p1 && p2 ? p1.X() + c.t * (p2.X() - p1.X()) : c.t;
|
|
1125
|
+
const sy = p1 && p2 ? p1.Y() + c.t * (p2.Y() - p1.Y()) : c.t;
|
|
1126
|
+
return board.create("glider", [sx, sy, line], opts);
|
|
1127
|
+
}
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
});
|
|
1131
|
+
|
|
1132
|
+
// src/core/scene/kinds/point-constraints/onSegment.ts
|
|
1133
|
+
var onSegmentConstraint;
|
|
1134
|
+
var init_onSegment = __esm({
|
|
1135
|
+
"src/core/scene/kinds/point-constraints/onSegment.ts"() {
|
|
1136
|
+
init_types2();
|
|
1137
|
+
onSegmentConstraint = definePointConstraint({
|
|
1138
|
+
kind: "onSegment",
|
|
1139
|
+
describe: (obj, state, c) => `${obj.label} tr\xEAn \u0111o\u1EA1n ${state?.objects[c.segmentId]?.label ?? c.segmentId}`,
|
|
1140
|
+
render: (obj, ctx, c, opts) => {
|
|
1141
|
+
const board = ctx.jxg;
|
|
1142
|
+
const seg = ctx.resolveRef(c.segmentId);
|
|
1143
|
+
const p1 = seg.point1;
|
|
1144
|
+
const p2 = seg.point2;
|
|
1145
|
+
const sx = p1 && p2 ? p1.X() + c.t * (p2.X() - p1.X()) : c.t;
|
|
1146
|
+
const sy = p1 && p2 ? p1.Y() + c.t * (p2.Y() - p1.Y()) : c.t;
|
|
1147
|
+
return board.create("glider", [sx, sy, seg], opts);
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
});
|
|
1152
|
+
|
|
1153
|
+
// src/core/scene/kinds/point-constraints/onCircle.ts
|
|
1154
|
+
var onCircleConstraint;
|
|
1155
|
+
var init_onCircle = __esm({
|
|
1156
|
+
"src/core/scene/kinds/point-constraints/onCircle.ts"() {
|
|
1157
|
+
init_types2();
|
|
1158
|
+
onCircleConstraint = definePointConstraint({
|
|
1159
|
+
kind: "onCircle",
|
|
1160
|
+
describe: (obj, state, c) => `${obj.label} tr\xEAn \u0111\u01B0\u1EDDng tr\xF2n ${state?.objects[c.circleId]?.label ?? c.circleId}`,
|
|
1161
|
+
render: (obj, ctx, c, opts) => {
|
|
1162
|
+
const board = ctx.jxg;
|
|
1163
|
+
const circle = ctx.resolveRef(c.circleId);
|
|
1164
|
+
const O = circle.center ?? circle.midpoint;
|
|
1165
|
+
const ox = O ? O.X() : 0;
|
|
1166
|
+
const oy = O ? O.Y() : 0;
|
|
1167
|
+
return board.create("glider", [ox + Math.cos(c.theta), oy + Math.sin(c.theta), circle], opts);
|
|
1168
|
+
}
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
});
|
|
1172
|
+
|
|
1173
|
+
// src/core/scene/kinds/point-constraints/onPolygon.ts
|
|
1174
|
+
var onPolygonConstraint;
|
|
1175
|
+
var init_onPolygon = __esm({
|
|
1176
|
+
"src/core/scene/kinds/point-constraints/onPolygon.ts"() {
|
|
1177
|
+
init_types2();
|
|
1178
|
+
onPolygonConstraint = definePointConstraint({
|
|
1179
|
+
kind: "onPolygon",
|
|
1180
|
+
describe: (obj, state, c) => `${obj.label} tr\xEAn \u0111a gi\xE1c ${state?.objects[c.polygonId]?.label ?? c.polygonId}`,
|
|
1181
|
+
render: (obj, ctx, c, opts) => {
|
|
1182
|
+
const board = ctx.jxg;
|
|
1183
|
+
const poly = ctx.resolveRef(c.polygonId);
|
|
1184
|
+
return board.create("glider", [c.u, c.v, poly], opts);
|
|
1185
|
+
}
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
});
|
|
1189
|
+
|
|
1190
|
+
// src/core/scene/kinds/point-constraints/centroid.ts
|
|
1191
|
+
var centroidConstraint;
|
|
1192
|
+
var init_centroid = __esm({
|
|
1193
|
+
"src/core/scene/kinds/point-constraints/centroid.ts"() {
|
|
1194
|
+
init_types2();
|
|
1195
|
+
centroidConstraint = definePointConstraint({
|
|
1196
|
+
kind: "centroid",
|
|
1197
|
+
validate: (c) => {
|
|
1198
|
+
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
1199
|
+
throw new Error("point.centroid: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
1200
|
+
}
|
|
1201
|
+
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
1202
|
+
throw new Error("point.centroid: 3 vertex id ph\u1EA3i non-empty");
|
|
1203
|
+
}
|
|
1204
|
+
},
|
|
1205
|
+
describe: (obj, state, c) => {
|
|
1206
|
+
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
1207
|
+
return `${obj.label} = tr\u1ECDng t\xE2m \u0394${labels}`;
|
|
1208
|
+
},
|
|
1209
|
+
render: (obj, ctx, c, opts) => {
|
|
1210
|
+
const board = ctx.jxg;
|
|
1211
|
+
const a = ctx.resolveRef(c.vertices[0]);
|
|
1212
|
+
const b = ctx.resolveRef(c.vertices[1]);
|
|
1213
|
+
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
1214
|
+
return board.create("point", [
|
|
1215
|
+
() => (a.X() + b.X() + c3.X()) / 3,
|
|
1216
|
+
() => (a.Y() + b.Y() + c3.Y()) / 3
|
|
1217
|
+
], opts);
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1223
|
+
// src/core/scene/kinds/pointConstructions.ts
|
|
1224
|
+
function dist(p, q) {
|
|
1225
|
+
return Math.hypot(p[0] - q[0], p[1] - q[1]);
|
|
1226
|
+
}
|
|
1227
|
+
function sideOf(a, b, p) {
|
|
1228
|
+
return (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]);
|
|
1229
|
+
}
|
|
1230
|
+
function arcMidpoint(center, radius, a, b, notContaining) {
|
|
1231
|
+
const mcx = (a[0] + b[0]) / 2;
|
|
1232
|
+
const mcy = (a[1] + b[1]) / 2;
|
|
1233
|
+
let ux = mcx - center[0];
|
|
1234
|
+
let uy = mcy - center[1];
|
|
1235
|
+
let len = Math.hypot(ux, uy);
|
|
1236
|
+
if (len < 1e-9) {
|
|
1237
|
+
ux = -(b[1] - a[1]);
|
|
1238
|
+
uy = b[0] - a[0];
|
|
1239
|
+
len = Math.hypot(ux, uy) || 1;
|
|
1240
|
+
}
|
|
1241
|
+
ux /= len;
|
|
1242
|
+
uy /= len;
|
|
1243
|
+
const cand1 = [center[0] + radius * ux, center[1] + radius * uy];
|
|
1244
|
+
const cand2 = [center[0] - radius * ux, center[1] - radius * uy];
|
|
1245
|
+
const sN = sideOf(a, b, notContaining);
|
|
1246
|
+
if (Math.abs(sN) < 1e-9) {
|
|
1247
|
+
return dist(cand1, notContaining) >= dist(cand2, notContaining) ? cand1 : cand2;
|
|
1248
|
+
}
|
|
1249
|
+
const s1 = sideOf(a, b, cand1);
|
|
1250
|
+
return s1 * sN < 0 ? cand1 : cand2;
|
|
1251
|
+
}
|
|
1252
|
+
function excenter(vertices, oppositeIndex) {
|
|
1253
|
+
const [A, B, C] = vertices;
|
|
1254
|
+
const a = dist(B, C);
|
|
1255
|
+
const b = dist(C, A);
|
|
1256
|
+
const c = dist(A, B);
|
|
1257
|
+
const w = [a, b, c];
|
|
1258
|
+
w[oppositeIndex] = -w[oppositeIndex];
|
|
1259
|
+
const sum = w[0] + w[1] + w[2];
|
|
1260
|
+
if (Math.abs(sum) < 1e-9) return A;
|
|
1261
|
+
return [
|
|
1262
|
+
(w[0] * A[0] + w[1] * B[0] + w[2] * C[0]) / sum,
|
|
1263
|
+
(w[0] * A[1] + w[1] * B[1] + w[2] * C[1]) / sum
|
|
1264
|
+
];
|
|
1265
|
+
}
|
|
1266
|
+
function pointAtDistanceCoord(from, through, d) {
|
|
1267
|
+
const dx = through[0] - from[0];
|
|
1268
|
+
const dy = through[1] - from[1];
|
|
1269
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
1270
|
+
return [through[0] + d * dx / len, through[1] + d * dy / len];
|
|
1271
|
+
}
|
|
1272
|
+
function radicalAxisFoot(o1, r1, o2, r2) {
|
|
1273
|
+
const dx = o2[0] - o1[0], dy = o2[1] - o1[1];
|
|
1274
|
+
const d2 = dx * dx + dy * dy;
|
|
1275
|
+
if (d2 < 1e-12) return o1;
|
|
1276
|
+
const t = (d2 + r1 * r1 - r2 * r2) / (2 * d2);
|
|
1277
|
+
return [o1[0] + t * dx, o1[1] + t * dy];
|
|
1278
|
+
}
|
|
1279
|
+
var init_pointConstructions = __esm({
|
|
1280
|
+
"src/core/scene/kinds/pointConstructions.ts"() {
|
|
1281
|
+
}
|
|
1282
|
+
});
|
|
1283
|
+
|
|
1284
|
+
// src/core/scene/kinds/point-constraints/arcMidpoint.ts
|
|
1285
|
+
var arcMidpointConstraint;
|
|
1286
|
+
var init_arcMidpoint = __esm({
|
|
1287
|
+
"src/core/scene/kinds/point-constraints/arcMidpoint.ts"() {
|
|
1288
|
+
init_pointConstructions();
|
|
1289
|
+
init_types2();
|
|
1290
|
+
arcMidpointConstraint = definePointConstraint({
|
|
1291
|
+
kind: "arcMidpoint",
|
|
1292
|
+
validate: (c) => {
|
|
1293
|
+
if (!c.circle || !c.a || !c.b || !c.notContaining) {
|
|
1294
|
+
throw new Error("point.arcMidpoint: circle, a, b, notContaining b\u1EAFt bu\u1ED9c");
|
|
1295
|
+
}
|
|
1296
|
+
},
|
|
1297
|
+
describe: (obj, state, c) => {
|
|
1298
|
+
const al = state?.objects[c.a]?.label ?? c.a;
|
|
1299
|
+
const bl = state?.objects[c.b]?.label ?? c.b;
|
|
1300
|
+
const nl = state?.objects[c.notContaining]?.label ?? c.notContaining;
|
|
1301
|
+
return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl} (kh\xF4ng ch\u1EE9a ${nl})`;
|
|
1302
|
+
},
|
|
1303
|
+
render: (obj, ctx, c, opts) => {
|
|
1304
|
+
const board = ctx.jxg;
|
|
1305
|
+
const circle = ctx.resolveRef(c.circle);
|
|
1306
|
+
const A = ctx.resolveRef(c.a);
|
|
1307
|
+
const B = ctx.resolveRef(c.b);
|
|
1308
|
+
const N = ctx.resolveRef(c.notContaining);
|
|
1309
|
+
const O = circle?.center ?? circle?.midpoint ?? circle;
|
|
1310
|
+
const am = () => arcMidpoint(
|
|
1311
|
+
[O.X(), O.Y()],
|
|
1312
|
+
circle.Radius(),
|
|
1313
|
+
[A.X(), A.Y()],
|
|
1314
|
+
[B.X(), B.Y()],
|
|
1315
|
+
[N.X(), N.Y()]
|
|
1316
|
+
);
|
|
1317
|
+
return board.create("point", [() => am()[0], () => am()[1]], opts);
|
|
1318
|
+
}
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
});
|
|
1322
|
+
|
|
1323
|
+
// src/core/scene/kinds/point-constraints/excenter.ts
|
|
1324
|
+
var excenterConstraint;
|
|
1325
|
+
var init_excenter = __esm({
|
|
1326
|
+
"src/core/scene/kinds/point-constraints/excenter.ts"() {
|
|
1327
|
+
init_pointConstructions();
|
|
1328
|
+
init_types2();
|
|
1329
|
+
excenterConstraint = definePointConstraint({
|
|
1330
|
+
kind: "excenter",
|
|
1331
|
+
validate: (c) => {
|
|
1332
|
+
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
1333
|
+
throw new Error("point.excenter: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
1334
|
+
}
|
|
1335
|
+
if (!c.opposite) throw new Error("point.excenter: opposite b\u1EAFt bu\u1ED9c");
|
|
1336
|
+
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
1337
|
+
throw new Error("point.excenter: 3 vertex id ph\u1EA3i non-empty");
|
|
1338
|
+
}
|
|
1339
|
+
if (!c.vertices.includes(c.opposite)) {
|
|
1340
|
+
throw new Error("point.excenter: opposite ph\u1EA3i l\xE0 m\u1ED9t trong vertices");
|
|
1341
|
+
}
|
|
1342
|
+
},
|
|
1343
|
+
describe: (obj, state, c) => {
|
|
1344
|
+
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
1345
|
+
const opp = state?.objects[c.opposite]?.label ?? c.opposite;
|
|
1346
|
+
return `${obj.label} = t\xE2m b\xE0ng ti\u1EBFp \u0394${labels} \u0111\u1ED1i di\u1EC7n ${opp}`;
|
|
1347
|
+
},
|
|
1348
|
+
render: (obj, ctx, c, opts) => {
|
|
1349
|
+
const board = ctx.jxg;
|
|
1350
|
+
const a = ctx.resolveRef(c.vertices[0]);
|
|
1351
|
+
const b = ctx.resolveRef(c.vertices[1]);
|
|
1352
|
+
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
1353
|
+
const oppIdx = c.vertices.indexOf(c.opposite);
|
|
1354
|
+
const idx = oppIdx < 0 ? 0 : oppIdx;
|
|
1355
|
+
const ex = () => excenter(
|
|
1356
|
+
[[a.X(), a.Y()], [b.X(), b.Y()], [c3.X(), c3.Y()]],
|
|
1357
|
+
idx
|
|
1358
|
+
);
|
|
1359
|
+
return board.create("point", [() => ex()[0], () => ex()[1]], opts);
|
|
1360
|
+
}
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
});
|
|
1364
|
+
|
|
1365
|
+
// src/core/scene/kinds/point-constraints/shared.ts
|
|
936
1366
|
function buildJxgTransforms(board, ctx, t) {
|
|
937
1367
|
switch (t.kind) {
|
|
938
1368
|
case "translate":
|
|
@@ -959,11 +1389,392 @@ function buildJxgTransforms(board, ctx, t) {
|
|
|
959
1389
|
}
|
|
960
1390
|
}
|
|
961
1391
|
}
|
|
1392
|
+
function makeDistanceFn(ctx, d) {
|
|
1393
|
+
const scale3 = d.scale ?? 1;
|
|
1394
|
+
const offset = d.offset ?? 0;
|
|
1395
|
+
if (d.kind === "literal") {
|
|
1396
|
+
const v = d.value;
|
|
1397
|
+
return () => scale3 * v + offset;
|
|
1398
|
+
}
|
|
1399
|
+
if (d.kind === "segmentLength") {
|
|
1400
|
+
const p = ctx.resolveRef(d.p1);
|
|
1401
|
+
const q = ctx.resolveRef(d.p2);
|
|
1402
|
+
return () => scale3 * Math.hypot(p.X() - q.X(), p.Y() - q.Y()) + offset;
|
|
1403
|
+
}
|
|
1404
|
+
const circle = ctx.resolveRef(d.circle);
|
|
1405
|
+
return () => scale3 * circle.Radius() + offset;
|
|
1406
|
+
}
|
|
1407
|
+
function buildPointOpts(obj) {
|
|
1408
|
+
return {
|
|
1409
|
+
name: obj.label,
|
|
1410
|
+
withLabel: obj.attrs.showLabel ?? true,
|
|
1411
|
+
visible: obj.visible,
|
|
1412
|
+
fixed: obj.locked,
|
|
1413
|
+
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
1414
|
+
fillColor: obj.attrs.color ?? "#1e40af",
|
|
1415
|
+
face: obj.attrs.face ?? "o",
|
|
1416
|
+
size: obj.attrs.size ?? 4
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
var init_shared = __esm({
|
|
1420
|
+
"src/core/scene/kinds/point-constraints/shared.ts"() {
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
|
|
1424
|
+
// src/core/scene/kinds/point-constraints/pointAtDistance.ts
|
|
1425
|
+
var pointAtDistanceConstraint;
|
|
1426
|
+
var init_pointAtDistance = __esm({
|
|
1427
|
+
"src/core/scene/kinds/point-constraints/pointAtDistance.ts"() {
|
|
1428
|
+
init_pointConstructions();
|
|
1429
|
+
init_types2();
|
|
1430
|
+
init_shared();
|
|
1431
|
+
pointAtDistanceConstraint = definePointConstraint({
|
|
1432
|
+
kind: "pointAtDistance",
|
|
1433
|
+
describe: (obj, state, c) => {
|
|
1434
|
+
const fromL = state?.objects[c.from]?.label ?? c.from;
|
|
1435
|
+
const thrL = state?.objects[c.through]?.label ?? c.through;
|
|
1436
|
+
const d = c.distance;
|
|
1437
|
+
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})`;
|
|
1438
|
+
return `${obj.label} = tr\xEAn tia ${fromL}${thrL} k\xE9o d\xE0i, c\xE1ch ${thrL} kho\u1EA3ng ${dLabel}`;
|
|
1439
|
+
},
|
|
1440
|
+
render: (obj, ctx, c, opts) => {
|
|
1441
|
+
const board = ctx.jxg;
|
|
1442
|
+
const A = ctx.resolveRef(c.from);
|
|
1443
|
+
const B = ctx.resolveRef(c.through);
|
|
1444
|
+
const dFn = makeDistanceFn(ctx, c.distance);
|
|
1445
|
+
const pc = () => pointAtDistanceCoord([A.X(), A.Y()], [B.X(), B.Y()], dFn());
|
|
1446
|
+
return board.create("point", [() => pc()[0], () => pc()[1]], opts);
|
|
1447
|
+
}
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
|
|
1452
|
+
// src/core/scene/kinds/point-constraints/circleIntersection.ts
|
|
1453
|
+
var circleIntersectionConstraint;
|
|
1454
|
+
var init_circleIntersection = __esm({
|
|
1455
|
+
"src/core/scene/kinds/point-constraints/circleIntersection.ts"() {
|
|
1456
|
+
init_types2();
|
|
1457
|
+
circleIntersectionConstraint = definePointConstraint({
|
|
1458
|
+
kind: "circleIntersection",
|
|
1459
|
+
// Không có describe-arm riêng trong point.ts → giữ fallback `Điểm ${label}`.
|
|
1460
|
+
describe: (obj) => `\u0110i\u1EC3m ${obj.label}`,
|
|
1461
|
+
render: (obj, ctx, c, opts) => {
|
|
1462
|
+
const board = ctx.jxg;
|
|
1463
|
+
const k1 = ctx.resolveRef(c.c1);
|
|
1464
|
+
const k2 = ctx.resolveRef(c.c2);
|
|
1465
|
+
return board.create("intersection", [k1, k2, c.which], opts);
|
|
1466
|
+
}
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
});
|
|
1470
|
+
|
|
1471
|
+
// src/core/scene/kinds/point-constraints/circleSecondIntersection.ts
|
|
1472
|
+
var circleSecondIntersectionConstraint;
|
|
1473
|
+
var init_circleSecondIntersection = __esm({
|
|
1474
|
+
"src/core/scene/kinds/point-constraints/circleSecondIntersection.ts"() {
|
|
1475
|
+
init_types2();
|
|
1476
|
+
circleSecondIntersectionConstraint = definePointConstraint({
|
|
1477
|
+
kind: "circleSecondIntersection",
|
|
1478
|
+
// Không có describe-arm riêng trong point.ts → giữ fallback `Điểm ${label}`.
|
|
1479
|
+
describe: (obj) => `\u0110i\u1EC3m ${obj.label}`,
|
|
1480
|
+
render: (obj, ctx, c, opts) => {
|
|
1481
|
+
const board = ctx.jxg;
|
|
1482
|
+
const k1 = ctx.resolveRef(c.c1);
|
|
1483
|
+
const k2 = ctx.resolveRef(c.c2);
|
|
1484
|
+
const ex = ctx.resolveRef(c.exclude);
|
|
1485
|
+
return board.create("otherintersection", [k1, k2, ex], opts);
|
|
1486
|
+
}
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
});
|
|
1490
|
+
|
|
1491
|
+
// src/core/scene/kinds/point-constraints/secondIntersection.ts
|
|
1492
|
+
var secondIntersectionConstraint;
|
|
1493
|
+
var init_secondIntersection = __esm({
|
|
1494
|
+
"src/core/scene/kinds/point-constraints/secondIntersection.ts"() {
|
|
1495
|
+
init_types2();
|
|
1496
|
+
secondIntersectionConstraint = definePointConstraint({
|
|
1497
|
+
kind: "secondIntersection",
|
|
1498
|
+
// Không có describe-arm riêng trong point.ts → giữ fallback `Điểm ${label}`.
|
|
1499
|
+
describe: (obj) => `\u0110i\u1EC3m ${obj.label}`,
|
|
1500
|
+
render: (obj, ctx, c, opts) => {
|
|
1501
|
+
const board = ctx.jxg;
|
|
1502
|
+
const line = ctx.resolveRef(c.line);
|
|
1503
|
+
const circle = ctx.resolveRef(c.circle);
|
|
1504
|
+
const other = ctx.resolveRef(c.other);
|
|
1505
|
+
return board.create("otherintersection", [circle, line, other], opts);
|
|
1506
|
+
}
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
});
|
|
1510
|
+
|
|
1511
|
+
// src/core/scene/kinds/point-constraints/tangencyPoint.ts
|
|
1512
|
+
var tangencyPointConstraint;
|
|
1513
|
+
var init_tangencyPoint = __esm({
|
|
1514
|
+
"src/core/scene/kinds/point-constraints/tangencyPoint.ts"() {
|
|
1515
|
+
init_types2();
|
|
1516
|
+
tangencyPointConstraint = definePointConstraint({
|
|
1517
|
+
kind: "tangencyPoint",
|
|
1518
|
+
// Không có describe-arm riêng trong point.ts → giữ fallback `Điểm ${label}`.
|
|
1519
|
+
describe: (obj) => `\u0110i\u1EC3m ${obj.label}`,
|
|
1520
|
+
render: (obj, ctx, c, opts) => {
|
|
1521
|
+
const board = ctx.jxg;
|
|
1522
|
+
const circle = ctx.resolveRef(c.circle);
|
|
1523
|
+
const line = ctx.resolveRef(c.onLine);
|
|
1524
|
+
const O = circle?.center ?? circle?.midpoint ?? circle;
|
|
1525
|
+
return board.create("perpendicularpoint", [line, O], opts);
|
|
1526
|
+
}
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
|
|
1531
|
+
// src/core/scene/kinds/point-constraints/transformed.ts
|
|
1532
|
+
var transformedConstraint;
|
|
1533
|
+
var init_transformed = __esm({
|
|
1534
|
+
"src/core/scene/kinds/point-constraints/transformed.ts"() {
|
|
1535
|
+
init_types2();
|
|
1536
|
+
init_shared();
|
|
1537
|
+
transformedConstraint = definePointConstraint({
|
|
1538
|
+
kind: "transformed",
|
|
1539
|
+
describe: (obj, state, c) => {
|
|
1540
|
+
const t = c.transform;
|
|
1541
|
+
const labelRef = (id) => state?.objects[id]?.label ?? id;
|
|
1542
|
+
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)}` : "";
|
|
1543
|
+
return `${obj.label} = \u1EA3nh c\u1EE7a ${labelRef(c.source)} (${op})`;
|
|
1544
|
+
},
|
|
1545
|
+
render: (obj, ctx, c, opts) => {
|
|
1546
|
+
const board = ctx.jxg;
|
|
1547
|
+
const src = ctx.resolveRef(c.source);
|
|
1548
|
+
const transforms = buildJxgTransforms(board, ctx, c.transform);
|
|
1549
|
+
const parent = transforms.length === 1 ? transforms[0] : transforms;
|
|
1550
|
+
const pt = board.create("point", [src, parent], opts);
|
|
1551
|
+
pt._helpers = transforms;
|
|
1552
|
+
return pt;
|
|
1553
|
+
}
|
|
1554
|
+
});
|
|
1555
|
+
}
|
|
1556
|
+
});
|
|
1557
|
+
|
|
1558
|
+
// src/core/scene/kinds/point-constraints/orthocenter.ts
|
|
1559
|
+
var orthocenterConstraint;
|
|
1560
|
+
var init_orthocenter = __esm({
|
|
1561
|
+
"src/core/scene/kinds/point-constraints/orthocenter.ts"() {
|
|
1562
|
+
init_types2();
|
|
1563
|
+
orthocenterConstraint = definePointConstraint({
|
|
1564
|
+
kind: "orthocenter",
|
|
1565
|
+
validate: (c) => {
|
|
1566
|
+
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
1567
|
+
throw new Error("point.orthocenter: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
1568
|
+
}
|
|
1569
|
+
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
1570
|
+
throw new Error("point.orthocenter: 3 vertex id ph\u1EA3i non-empty");
|
|
1571
|
+
}
|
|
1572
|
+
},
|
|
1573
|
+
describe: (obj, state, c) => {
|
|
1574
|
+
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
1575
|
+
return `${obj.label} = tr\u1EF1c t\xE2m \u0394${labels}`;
|
|
1576
|
+
},
|
|
1577
|
+
render: (obj, ctx, c, opts) => {
|
|
1578
|
+
const board = ctx.jxg;
|
|
1579
|
+
const a = ctx.resolveRef(c.vertices[0]);
|
|
1580
|
+
const b = ctx.resolveRef(c.vertices[1]);
|
|
1581
|
+
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
1582
|
+
const hide = { visible: false, withLabel: false, fixed: true, name: "" };
|
|
1583
|
+
const lineBC = board.create("line", [b, c3], hide);
|
|
1584
|
+
const altA = board.create("perpendicular", [lineBC, a], hide);
|
|
1585
|
+
const lineAC = board.create("line", [a, c3], hide);
|
|
1586
|
+
const altB = board.create("perpendicular", [lineAC, b], hide);
|
|
1587
|
+
const ortho = board.create("intersection", [altA, altB, 0], opts);
|
|
1588
|
+
ortho._helpers = [lineBC, altA, lineAC, altB];
|
|
1589
|
+
return ortho;
|
|
1590
|
+
}
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
});
|
|
1594
|
+
|
|
1595
|
+
// src/core/scene/kinds/point-constraints/onPerpendicular.ts
|
|
1596
|
+
var onPerpendicularConstraint;
|
|
1597
|
+
var init_onPerpendicular = __esm({
|
|
1598
|
+
"src/core/scene/kinds/point-constraints/onPerpendicular.ts"() {
|
|
1599
|
+
init_types2();
|
|
1600
|
+
onPerpendicularConstraint = definePointConstraint({
|
|
1601
|
+
kind: "onPerpendicular",
|
|
1602
|
+
describe: (obj) => `\u0110i\u1EC3m ${obj.label}`,
|
|
1603
|
+
render: (obj, ctx, c, opts) => {
|
|
1604
|
+
const board = ctx.jxg;
|
|
1605
|
+
const T = ctx.resolveRef(c.through);
|
|
1606
|
+
const A = ctx.resolveRef(c.perpToA);
|
|
1607
|
+
const B = ctx.resolveRef(c.perpToB);
|
|
1608
|
+
const hide = { visible: false, withLabel: false, fixed: true, name: "" };
|
|
1609
|
+
const refLine = board.create("line", [A, B], hide);
|
|
1610
|
+
const perpLine = board.create("perpendicular", [refLine, T], hide);
|
|
1611
|
+
const dx = B.X() - A.X();
|
|
1612
|
+
const dy = B.Y() - A.Y();
|
|
1613
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
1614
|
+
const ux = -dy / len;
|
|
1615
|
+
const uy = dx / len;
|
|
1616
|
+
const x0 = T.X() + c.t * ux;
|
|
1617
|
+
const y0 = T.Y() + c.t * uy;
|
|
1618
|
+
const gl = board.create("glider", [x0, y0, perpLine], opts);
|
|
1619
|
+
gl._helpers = [refLine, perpLine];
|
|
1620
|
+
return gl;
|
|
1621
|
+
}
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
});
|
|
1625
|
+
|
|
1626
|
+
// src/core/scene/kinds/point-constraints/onPerpBisector.ts
|
|
1627
|
+
var onPerpBisectorConstraint;
|
|
1628
|
+
var init_onPerpBisector = __esm({
|
|
1629
|
+
"src/core/scene/kinds/point-constraints/onPerpBisector.ts"() {
|
|
1630
|
+
init_types2();
|
|
1631
|
+
onPerpBisectorConstraint = definePointConstraint({
|
|
1632
|
+
kind: "onPerpBisector",
|
|
1633
|
+
describe: (obj) => `\u0110i\u1EC3m ${obj.label}`,
|
|
1634
|
+
render: (obj, ctx, c, opts) => {
|
|
1635
|
+
const board = ctx.jxg;
|
|
1636
|
+
const A = ctx.resolveRef(c.p1);
|
|
1637
|
+
const B = ctx.resolveRef(c.p2);
|
|
1638
|
+
const hide = { visible: false, withLabel: false, fixed: true, name: "" };
|
|
1639
|
+
const refLine = board.create("line", [A, B], hide);
|
|
1640
|
+
const mid = board.create("midpoint", [A, B], hide);
|
|
1641
|
+
const bisLine = board.create("perpendicular", [refLine, mid], hide);
|
|
1642
|
+
const Mx = (A.X() + B.X()) / 2;
|
|
1643
|
+
const My = (A.Y() + B.Y()) / 2;
|
|
1644
|
+
const dx = B.X() - A.X();
|
|
1645
|
+
const dy = B.Y() - A.Y();
|
|
1646
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
1647
|
+
const ux = -dy / len;
|
|
1648
|
+
const uy = dx / len;
|
|
1649
|
+
const x0 = Mx + c.t * ux;
|
|
1650
|
+
const y0 = My + c.t * uy;
|
|
1651
|
+
const gl = board.create("glider", [x0, y0, bisLine], opts);
|
|
1652
|
+
gl._helpers = [refLine, mid, bisLine];
|
|
1653
|
+
return gl;
|
|
1654
|
+
}
|
|
1655
|
+
});
|
|
1656
|
+
}
|
|
1657
|
+
});
|
|
1658
|
+
|
|
1659
|
+
// src/core/scene/kinds/point-constraints/onCircleAroundPoint.ts
|
|
1660
|
+
var onCircleAroundPointConstraint;
|
|
1661
|
+
var init_onCircleAroundPoint = __esm({
|
|
1662
|
+
"src/core/scene/kinds/point-constraints/onCircleAroundPoint.ts"() {
|
|
1663
|
+
init_types2();
|
|
1664
|
+
onCircleAroundPointConstraint = definePointConstraint({
|
|
1665
|
+
kind: "onCircleAroundPoint",
|
|
1666
|
+
describe: (obj) => `\u0110i\u1EC3m ${obj.label}`,
|
|
1667
|
+
render: (obj, ctx, c, opts) => {
|
|
1668
|
+
const board = ctx.jxg;
|
|
1669
|
+
const C = ctx.resolveRef(c.center);
|
|
1670
|
+
const R = ctx.resolveRef(c.radiusPoint);
|
|
1671
|
+
const hide = { visible: false, withLabel: false, fixed: true, name: "" };
|
|
1672
|
+
const auxCircle = board.create("circle", [C, R], hide);
|
|
1673
|
+
const r = Math.hypot(R.X() - C.X(), R.Y() - C.Y());
|
|
1674
|
+
const x0 = C.X() + r * Math.cos(c.theta);
|
|
1675
|
+
const y0 = C.Y() + r * Math.sin(c.theta);
|
|
1676
|
+
const gl = board.create("glider", [x0, y0, auxCircle], opts);
|
|
1677
|
+
gl._helpers = [auxCircle];
|
|
1678
|
+
return gl;
|
|
1679
|
+
}
|
|
1680
|
+
});
|
|
1681
|
+
}
|
|
1682
|
+
});
|
|
1683
|
+
|
|
1684
|
+
// src/core/scene/kinds/point-constraints/tangentPointExt.ts
|
|
1685
|
+
var tangentPointExtConstraint;
|
|
1686
|
+
var init_tangentPointExt = __esm({
|
|
1687
|
+
"src/core/scene/kinds/point-constraints/tangentPointExt.ts"() {
|
|
1688
|
+
init_types2();
|
|
1689
|
+
tangentPointExtConstraint = definePointConstraint({
|
|
1690
|
+
kind: "tangentPointExt",
|
|
1691
|
+
describe: (obj, state, c) => {
|
|
1692
|
+
const fromLabel = state?.objects[c.from]?.label ?? c.from;
|
|
1693
|
+
const circleLabel = state?.objects[c.circle]?.label ?? c.circle;
|
|
1694
|
+
return `${obj.label} = ti\u1EBFp \u0111i\u1EC3m c\u1EE7a (${circleLabel}) v\u1EDBi ti\u1EBFp tuy\u1EBFn t\u1EEB ${fromLabel}`;
|
|
1695
|
+
},
|
|
1696
|
+
render: (obj, ctx, c, opts) => {
|
|
1697
|
+
const board = ctx.jxg;
|
|
1698
|
+
const P = ctx.resolveRef(c.from);
|
|
1699
|
+
const K = ctx.resolveRef(c.circle);
|
|
1700
|
+
const O = K.center ?? K.midpoint;
|
|
1701
|
+
const hide = { visible: false, withLabel: false, fixed: true, name: "" };
|
|
1702
|
+
const mid = board.create("midpoint", [P, O], hide);
|
|
1703
|
+
const thales = board.create("circle", [mid, P], hide);
|
|
1704
|
+
const inter = board.create("intersection", [thales, K, c.which], opts);
|
|
1705
|
+
inter._helpers = [mid, thales];
|
|
1706
|
+
return inter;
|
|
1707
|
+
}
|
|
1708
|
+
});
|
|
1709
|
+
}
|
|
1710
|
+
});
|
|
1711
|
+
|
|
1712
|
+
// src/core/scene/kinds/point-constraints/registry.ts
|
|
1713
|
+
var ALL, POINT_CONSTRAINTS;
|
|
1714
|
+
var init_registry2 = __esm({
|
|
1715
|
+
"src/core/scene/kinds/point-constraints/registry.ts"() {
|
|
1716
|
+
init_free();
|
|
1717
|
+
init_onAxis();
|
|
1718
|
+
init_midpoint();
|
|
1719
|
+
init_perpFoot();
|
|
1720
|
+
init_circumcenter();
|
|
1721
|
+
init_incenter();
|
|
1722
|
+
init_onLine();
|
|
1723
|
+
init_onSegment();
|
|
1724
|
+
init_onCircle();
|
|
1725
|
+
init_onPolygon();
|
|
1726
|
+
init_centroid();
|
|
1727
|
+
init_arcMidpoint();
|
|
1728
|
+
init_excenter();
|
|
1729
|
+
init_pointAtDistance();
|
|
1730
|
+
init_circleIntersection();
|
|
1731
|
+
init_circleSecondIntersection();
|
|
1732
|
+
init_secondIntersection();
|
|
1733
|
+
init_tangencyPoint();
|
|
1734
|
+
init_transformed();
|
|
1735
|
+
init_orthocenter();
|
|
1736
|
+
init_onPerpendicular();
|
|
1737
|
+
init_onPerpBisector();
|
|
1738
|
+
init_onCircleAroundPoint();
|
|
1739
|
+
init_tangentPointExt();
|
|
1740
|
+
ALL = [
|
|
1741
|
+
freeConstraint,
|
|
1742
|
+
onAxisConstraint,
|
|
1743
|
+
midpointConstraint,
|
|
1744
|
+
perpFootConstraint,
|
|
1745
|
+
circumcenterConstraint,
|
|
1746
|
+
incenterConstraint,
|
|
1747
|
+
onLineConstraint,
|
|
1748
|
+
onSegmentConstraint,
|
|
1749
|
+
onCircleConstraint,
|
|
1750
|
+
onPolygonConstraint,
|
|
1751
|
+
centroidConstraint,
|
|
1752
|
+
arcMidpointConstraint,
|
|
1753
|
+
excenterConstraint,
|
|
1754
|
+
pointAtDistanceConstraint,
|
|
1755
|
+
circleIntersectionConstraint,
|
|
1756
|
+
circleSecondIntersectionConstraint,
|
|
1757
|
+
secondIntersectionConstraint,
|
|
1758
|
+
tangencyPointConstraint,
|
|
1759
|
+
transformedConstraint,
|
|
1760
|
+
orthocenterConstraint,
|
|
1761
|
+
onPerpendicularConstraint,
|
|
1762
|
+
onPerpBisectorConstraint,
|
|
1763
|
+
onCircleAroundPointConstraint,
|
|
1764
|
+
tangentPointExtConstraint
|
|
1765
|
+
];
|
|
1766
|
+
POINT_CONSTRAINTS = new Map(ALL.map((m) => [m.kind, m]));
|
|
1767
|
+
}
|
|
1768
|
+
});
|
|
1769
|
+
|
|
1770
|
+
// src/core/scene/kinds/point.ts
|
|
962
1771
|
var def3;
|
|
963
1772
|
var init_point = __esm({
|
|
964
1773
|
"src/core/scene/kinds/point.ts"() {
|
|
965
1774
|
init_registry();
|
|
966
1775
|
init_d_constraint2();
|
|
1776
|
+
init_registry2();
|
|
1777
|
+
init_shared();
|
|
967
1778
|
def3 = {
|
|
968
1779
|
type: "point",
|
|
969
1780
|
schemaVersion: 1,
|
|
@@ -973,43 +1784,7 @@ var init_point = __esm({
|
|
|
973
1784
|
throw new Error("point: constraint required");
|
|
974
1785
|
}
|
|
975
1786
|
const c = a.constraint;
|
|
976
|
-
|
|
977
|
-
if (!c.from || !c.onLine) {
|
|
978
|
-
throw new Error("point.perpFoot: from v\xE0 onLine b\u1EAFt bu\u1ED9c");
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
if (c.kind === "circumcenter") {
|
|
982
|
-
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
983
|
-
throw new Error("point.circumcenter: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
984
|
-
}
|
|
985
|
-
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
986
|
-
throw new Error("point.circumcenter: 3 vertex id ph\u1EA3i non-empty");
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
if (c.kind === "incenter") {
|
|
990
|
-
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
991
|
-
throw new Error("point.incenter: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
992
|
-
}
|
|
993
|
-
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
994
|
-
throw new Error("point.incenter: 3 vertex id ph\u1EA3i non-empty");
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
if (c.kind === "centroid") {
|
|
998
|
-
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
999
|
-
throw new Error("point.centroid: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
1000
|
-
}
|
|
1001
|
-
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
1002
|
-
throw new Error("point.centroid: 3 vertex id ph\u1EA3i non-empty");
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
if (c.kind === "orthocenter") {
|
|
1006
|
-
if (!Array.isArray(c.vertices) || c.vertices.length !== 3) {
|
|
1007
|
-
throw new Error("point.orthocenter: vertices ph\u1EA3i l\xE0 tuple 3 id");
|
|
1008
|
-
}
|
|
1009
|
-
if (!c.vertices[0] || !c.vertices[1] || !c.vertices[2]) {
|
|
1010
|
-
throw new Error("point.orthocenter: 3 vertex id ph\u1EA3i non-empty");
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1787
|
+
POINT_CONSTRAINTS.get(c.kind)?.validate?.(c);
|
|
1013
1788
|
},
|
|
1014
1789
|
dependsOn: (a) => constraintRefs2D(a.constraint),
|
|
1015
1790
|
measure: (obj) => {
|
|
@@ -1024,132 +1799,16 @@ var init_point = __esm({
|
|
|
1024
1799
|
},
|
|
1025
1800
|
describe: (obj, state) => {
|
|
1026
1801
|
const c = obj.attrs.constraint;
|
|
1027
|
-
|
|
1028
|
-
if (
|
|
1029
|
-
if (c.kind === "onLine") return `${obj.label} tr\xEAn \u0111\u01B0\u1EDDng ${state?.objects[c.lineId]?.label ?? c.lineId}`;
|
|
1030
|
-
if (c.kind === "onSegment") return `${obj.label} tr\xEAn \u0111o\u1EA1n ${state?.objects[c.segmentId]?.label ?? c.segmentId}`;
|
|
1031
|
-
if (c.kind === "onCircle") return `${obj.label} tr\xEAn \u0111\u01B0\u1EDDng tr\xF2n ${state?.objects[c.circleId]?.label ?? c.circleId}`;
|
|
1032
|
-
if (c.kind === "onPolygon") return `${obj.label} tr\xEAn \u0111a gi\xE1c ${state?.objects[c.polygonId]?.label ?? c.polygonId}`;
|
|
1033
|
-
if (c.kind === "midpoint") {
|
|
1034
|
-
const l1 = state?.objects[c.p1]?.label ?? c.p1;
|
|
1035
|
-
const l2 = state?.objects[c.p2]?.label ?? c.p2;
|
|
1036
|
-
return `${obj.label} = trung \u0111i\u1EC3m ${l1}${l2}`;
|
|
1037
|
-
}
|
|
1038
|
-
if (c.kind === "transformed") {
|
|
1039
|
-
const t = c.transform;
|
|
1040
|
-
const labelRef = (id) => state?.objects[id]?.label ?? id;
|
|
1041
|
-
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)}` : "";
|
|
1042
|
-
return `${obj.label} = \u1EA3nh c\u1EE7a ${labelRef(c.source)} (${op})`;
|
|
1043
|
-
}
|
|
1044
|
-
if (c.kind === "perpFoot") {
|
|
1045
|
-
const fromLabel = state?.objects[c.from]?.label ?? c.from;
|
|
1046
|
-
const lineLabel = state?.objects[c.onLine]?.label ?? c.onLine;
|
|
1047
|
-
return `${obj.label} = ch\xE2n \u27C2 t\u1EEB ${fromLabel} xu\u1ED1ng ${lineLabel}`;
|
|
1048
|
-
}
|
|
1049
|
-
if (c.kind === "circumcenter") {
|
|
1050
|
-
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
1051
|
-
return `${obj.label} = t\xE2m ngo\u1EA1i ti\u1EBFp \u0394${labels}`;
|
|
1052
|
-
}
|
|
1053
|
-
if (c.kind === "incenter") {
|
|
1054
|
-
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
1055
|
-
return `${obj.label} = t\xE2m n\u1ED9i ti\u1EBFp \u0394${labels}`;
|
|
1056
|
-
}
|
|
1057
|
-
if (c.kind === "centroid") {
|
|
1058
|
-
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
1059
|
-
return `${obj.label} = tr\u1ECDng t\xE2m \u0394${labels}`;
|
|
1060
|
-
}
|
|
1061
|
-
if (c.kind === "orthocenter") {
|
|
1062
|
-
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
1063
|
-
return `${obj.label} = tr\u1EF1c t\xE2m \u0394${labels}`;
|
|
1064
|
-
}
|
|
1802
|
+
const mod = POINT_CONSTRAINTS.get(c.kind);
|
|
1803
|
+
if (mod) return mod.describe(obj, state, c);
|
|
1065
1804
|
return `\u0110i\u1EC3m ${obj.label}`;
|
|
1066
1805
|
},
|
|
1067
1806
|
render: (obj, ctx) => {
|
|
1068
1807
|
const board = ctx.jxg;
|
|
1069
1808
|
const c = obj.attrs.constraint;
|
|
1070
|
-
const opts =
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
visible: obj.visible,
|
|
1074
|
-
fixed: obj.locked,
|
|
1075
|
-
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
1076
|
-
fillColor: obj.attrs.color ?? "#1e40af",
|
|
1077
|
-
face: obj.attrs.face ?? "o",
|
|
1078
|
-
size: obj.attrs.size ?? 4
|
|
1079
|
-
};
|
|
1080
|
-
if (c.kind === "free") return board.create("point", [c.x, c.y], opts);
|
|
1081
|
-
if (c.kind === "onAxis") {
|
|
1082
|
-
const coords = c.axis === "x" ? [c.t, 0] : [0, c.t];
|
|
1083
|
-
return board.create("point", coords, opts);
|
|
1084
|
-
}
|
|
1085
|
-
if (c.kind === "onLine") {
|
|
1086
|
-
const line = ctx.resolveRef(c.lineId);
|
|
1087
|
-
return board.create("glider", [c.t, c.t, line], opts);
|
|
1088
|
-
}
|
|
1089
|
-
if (c.kind === "onSegment") {
|
|
1090
|
-
const seg = ctx.resolveRef(c.segmentId);
|
|
1091
|
-
return board.create("glider", [c.t, c.t, seg], opts);
|
|
1092
|
-
}
|
|
1093
|
-
if (c.kind === "onCircle") {
|
|
1094
|
-
const circle = ctx.resolveRef(c.circleId);
|
|
1095
|
-
return board.create("glider", [Math.cos(c.theta), Math.sin(c.theta), circle], opts);
|
|
1096
|
-
}
|
|
1097
|
-
if (c.kind === "onPolygon") {
|
|
1098
|
-
const poly = ctx.resolveRef(c.polygonId);
|
|
1099
|
-
return board.create("glider", [c.u, c.v, poly], opts);
|
|
1100
|
-
}
|
|
1101
|
-
if (c.kind === "midpoint") {
|
|
1102
|
-
const p1 = ctx.resolveRef(c.p1);
|
|
1103
|
-
const p2 = ctx.resolveRef(c.p2);
|
|
1104
|
-
return board.create("midpoint", [p1, p2], opts);
|
|
1105
|
-
}
|
|
1106
|
-
if (c.kind === "transformed") {
|
|
1107
|
-
const src = ctx.resolveRef(c.source);
|
|
1108
|
-
const transforms = buildJxgTransforms(board, ctx, c.transform);
|
|
1109
|
-
const parent = transforms.length === 1 ? transforms[0] : transforms;
|
|
1110
|
-
const pt = board.create("point", [src, parent], opts);
|
|
1111
|
-
pt._helpers = transforms;
|
|
1112
|
-
return pt;
|
|
1113
|
-
}
|
|
1114
|
-
if (c.kind === "perpFoot") {
|
|
1115
|
-
const from = ctx.resolveRef(c.from);
|
|
1116
|
-
const onLine = ctx.resolveRef(c.onLine);
|
|
1117
|
-
return board.create("perpendicularpoint", [onLine, from], opts);
|
|
1118
|
-
}
|
|
1119
|
-
if (c.kind === "circumcenter") {
|
|
1120
|
-
const a = ctx.resolveRef(c.vertices[0]);
|
|
1121
|
-
const b = ctx.resolveRef(c.vertices[1]);
|
|
1122
|
-
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
1123
|
-
return board.create("circumcenter", [a, b, c3], opts);
|
|
1124
|
-
}
|
|
1125
|
-
if (c.kind === "incenter") {
|
|
1126
|
-
const a = ctx.resolveRef(c.vertices[0]);
|
|
1127
|
-
const b = ctx.resolveRef(c.vertices[1]);
|
|
1128
|
-
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
1129
|
-
return board.create("incenter", [a, b, c3], opts);
|
|
1130
|
-
}
|
|
1131
|
-
if (c.kind === "centroid") {
|
|
1132
|
-
const a = ctx.resolveRef(c.vertices[0]);
|
|
1133
|
-
const b = ctx.resolveRef(c.vertices[1]);
|
|
1134
|
-
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
1135
|
-
return board.create("point", [
|
|
1136
|
-
() => (a.X() + b.X() + c3.X()) / 3,
|
|
1137
|
-
() => (a.Y() + b.Y() + c3.Y()) / 3
|
|
1138
|
-
], opts);
|
|
1139
|
-
}
|
|
1140
|
-
if (c.kind === "orthocenter") {
|
|
1141
|
-
const a = ctx.resolveRef(c.vertices[0]);
|
|
1142
|
-
const b = ctx.resolveRef(c.vertices[1]);
|
|
1143
|
-
const c3 = ctx.resolveRef(c.vertices[2]);
|
|
1144
|
-
const hide = { visible: false, withLabel: false, fixed: true, name: "" };
|
|
1145
|
-
const lineBC = board.create("line", [b, c3], hide);
|
|
1146
|
-
const altA = board.create("perpendicular", [lineBC, a], hide);
|
|
1147
|
-
const lineAC = board.create("line", [a, c3], hide);
|
|
1148
|
-
const altB = board.create("perpendicular", [lineAC, b], hide);
|
|
1149
|
-
const ortho = board.create("intersection", [altA, altB, 0], opts);
|
|
1150
|
-
ortho._helpers = [lineBC, altA, lineAC, altB];
|
|
1151
|
-
return ortho;
|
|
1152
|
-
}
|
|
1809
|
+
const opts = buildPointOpts(obj);
|
|
1810
|
+
const mod = POINT_CONSTRAINTS.get(c.kind);
|
|
1811
|
+
if (mod) return mod.render(obj, ctx, c, opts);
|
|
1153
1812
|
return board.create("point", [0, 0], opts);
|
|
1154
1813
|
},
|
|
1155
1814
|
/**
|
|
@@ -1258,6 +1917,10 @@ function constructionRefs(c) {
|
|
|
1258
1917
|
return [c.p1, c.vertex, c.p2];
|
|
1259
1918
|
case "angleBisectorLines":
|
|
1260
1919
|
return [stripBorderSuffix(c.line1), stripBorderSuffix(c.line2)];
|
|
1920
|
+
case "lineThrough":
|
|
1921
|
+
return [...c.points];
|
|
1922
|
+
case "radicalAxis":
|
|
1923
|
+
return [c.circle1, c.circle2];
|
|
1261
1924
|
case "tangent":
|
|
1262
1925
|
return [c.throughPoint, c.toCircle];
|
|
1263
1926
|
}
|
|
@@ -1267,6 +1930,7 @@ var init_line = __esm({
|
|
|
1267
1930
|
"src/core/scene/kinds/line.ts"() {
|
|
1268
1931
|
init_registry();
|
|
1269
1932
|
init_labelOf();
|
|
1933
|
+
init_pointConstructions();
|
|
1270
1934
|
def5 = {
|
|
1271
1935
|
type: "line",
|
|
1272
1936
|
schemaVersion: 1,
|
|
@@ -1291,6 +1955,10 @@ var init_line = __esm({
|
|
|
1291
1955
|
return `${obj.label}: ph\xE2n gi\xE1c g\xF3c ${L(c.p1)}${L(c.vertex)}${L(c.p2)}`;
|
|
1292
1956
|
case "angleBisectorLines":
|
|
1293
1957
|
return `${obj.label}: ph\xE2n gi\xE1c ${L(c.line1)} & ${L(c.line2)} (${c.branch === 0 ? "1" : "2"})`;
|
|
1958
|
+
case "lineThrough":
|
|
1959
|
+
return `${obj.label}: \u0111\u01B0\u1EDDng qua ${c.points.map(L).join("")}`;
|
|
1960
|
+
case "radicalAxis":
|
|
1961
|
+
return `${obj.label}: tr\u1EE5c \u0111\u1EB3ng ph\u01B0\u01A1ng ${L(c.circle1)} & ${L(c.circle2)}`;
|
|
1294
1962
|
case "tangent":
|
|
1295
1963
|
return `${obj.label}: ti\u1EBFp tuy\u1EBFn ${L(c.toCircle)} qua ${L(c.throughPoint)}`;
|
|
1296
1964
|
}
|
|
@@ -1371,6 +2039,47 @@ var init_line = __esm({
|
|
|
1371
2039
|
selected._helpers = [other];
|
|
1372
2040
|
return selected;
|
|
1373
2041
|
}
|
|
2042
|
+
case "lineThrough": {
|
|
2043
|
+
const pts = c.points.map((id) => ctx.resolveRef(id));
|
|
2044
|
+
let bi = 0, bj = 1, best = -1;
|
|
2045
|
+
for (let i = 0; i < pts.length; i++) {
|
|
2046
|
+
for (let j = i + 1; j < pts.length; j++) {
|
|
2047
|
+
const dx = pts[i].X() - pts[j].X();
|
|
2048
|
+
const dy = pts[i].Y() - pts[j].Y();
|
|
2049
|
+
const d = dx * dx + dy * dy;
|
|
2050
|
+
if (d > best) {
|
|
2051
|
+
best = d;
|
|
2052
|
+
bi = i;
|
|
2053
|
+
bj = j;
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
return board.create("line", [pts[bi], pts[bj]], {
|
|
2058
|
+
...baseOpts,
|
|
2059
|
+
straightFirst: true,
|
|
2060
|
+
straightLast: true
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2063
|
+
case "radicalAxis": {
|
|
2064
|
+
const k1 = ctx.resolveRef(c.circle1);
|
|
2065
|
+
const k2 = ctx.resolveRef(c.circle2);
|
|
2066
|
+
const o1 = () => [k1.center.X(), k1.center.Y()];
|
|
2067
|
+
const o2 = () => [k2.center.X(), k2.center.Y()];
|
|
2068
|
+
const foot = () => radicalAxisFoot(o1(), k1.Radius(), o2(), k2.Radius());
|
|
2069
|
+
const hide = { visible: false, withLabel: false, fixed: true, name: "" };
|
|
2070
|
+
const f1 = board.create("point", [() => foot()[0], () => foot()[1]], hide);
|
|
2071
|
+
const f2 = board.create("point", [
|
|
2072
|
+
() => foot()[0] - (o2()[1] - o1()[1]),
|
|
2073
|
+
() => foot()[1] + (o2()[0] - o1()[0])
|
|
2074
|
+
], hide);
|
|
2075
|
+
const line = board.create("line", [f1, f2], {
|
|
2076
|
+
...baseOpts,
|
|
2077
|
+
straightFirst: true,
|
|
2078
|
+
straightLast: true
|
|
2079
|
+
});
|
|
2080
|
+
line._helpers = [f1, f2];
|
|
2081
|
+
return line;
|
|
2082
|
+
}
|
|
1374
2083
|
case "tangent": {
|
|
1375
2084
|
const through = ctx.resolveRef(c.throughPoint);
|
|
1376
2085
|
const toCircle = ctx.resolveRef(c.toCircle);
|
|
@@ -1489,10 +2198,29 @@ var init_vector = __esm({
|
|
|
1489
2198
|
});
|
|
1490
2199
|
|
|
1491
2200
|
// src/core/scene/kinds/circle.ts
|
|
2201
|
+
function asConstruction(a) {
|
|
2202
|
+
if (a.construction) return a.construction;
|
|
2203
|
+
const raw = a;
|
|
2204
|
+
if (raw.kind === "incircle" && raw.vertices) {
|
|
2205
|
+
return {
|
|
2206
|
+
kind: "incircle",
|
|
2207
|
+
p1: raw.vertices[0],
|
|
2208
|
+
p2: raw.vertices[1],
|
|
2209
|
+
p3: raw.vertices[2]
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
return void 0;
|
|
2213
|
+
}
|
|
1492
2214
|
function constructionRefs2(c) {
|
|
1493
2215
|
switch (c.kind) {
|
|
1494
2216
|
case "circumscribed":
|
|
1495
2217
|
return [c.p1, c.p2, c.p3];
|
|
2218
|
+
case "incircle":
|
|
2219
|
+
return [c.p1, c.p2, c.p3];
|
|
2220
|
+
case "excircle":
|
|
2221
|
+
return [c.p1, c.p2, c.p3];
|
|
2222
|
+
case "diameter":
|
|
2223
|
+
return [c.p1, c.p2];
|
|
1496
2224
|
}
|
|
1497
2225
|
}
|
|
1498
2226
|
var def8;
|
|
@@ -1500,19 +2228,33 @@ var init_circle = __esm({
|
|
|
1500
2228
|
"src/core/scene/kinds/circle.ts"() {
|
|
1501
2229
|
init_registry();
|
|
1502
2230
|
init_labelOf();
|
|
2231
|
+
init_pointConstructions();
|
|
1503
2232
|
def8 = {
|
|
1504
2233
|
type: "circle",
|
|
1505
2234
|
schemaVersion: 1,
|
|
1506
2235
|
migrate: {},
|
|
1507
2236
|
validate: (a) => {
|
|
1508
|
-
if (a
|
|
2237
|
+
if (asConstruction(a)) return;
|
|
2238
|
+
if (typeof a?.radius === "number") {
|
|
2239
|
+
if (!a.center) throw new Error("circle: center b\u1EAFt bu\u1ED9c khi d\xF9ng radius");
|
|
2240
|
+
if (!(a.radius > 0)) throw new Error("circle: radius ph\u1EA3i > 0");
|
|
2241
|
+
return;
|
|
2242
|
+
}
|
|
1509
2243
|
if (!a?.center || !a?.surfacePoint) {
|
|
1510
|
-
throw new Error("circle: center v\xE0 surfacePoint b\u1EAFt bu\u1ED9c (ho\u1EB7c construction)");
|
|
2244
|
+
throw new Error("circle: center v\xE0 surfacePoint b\u1EAFt bu\u1ED9c (ho\u1EB7c construction / radius)");
|
|
1511
2245
|
}
|
|
1512
2246
|
},
|
|
1513
|
-
dependsOn: (a) =>
|
|
2247
|
+
dependsOn: (a) => {
|
|
2248
|
+
const c = asConstruction(a);
|
|
2249
|
+
if (c) return constructionRefs2(c);
|
|
2250
|
+
if (typeof a.radius === "number") return [a.center];
|
|
2251
|
+
return [a.center, a.surfacePoint];
|
|
2252
|
+
},
|
|
1514
2253
|
measure: (obj, state) => {
|
|
1515
|
-
if (obj.attrs
|
|
2254
|
+
if (asConstruction(obj.attrs)) return null;
|
|
2255
|
+
if (typeof obj.attrs.radius === "number") {
|
|
2256
|
+
return [{ label: "r", value: obj.attrs.radius }];
|
|
2257
|
+
}
|
|
1516
2258
|
const center = obj.attrs.center ? state.objects[obj.attrs.center] : void 0;
|
|
1517
2259
|
const surface = obj.attrs.surfacePoint ? state.objects[obj.attrs.surfacePoint] : void 0;
|
|
1518
2260
|
if (!center || !surface) return null;
|
|
@@ -1525,10 +2267,22 @@ var init_circle = __esm({
|
|
|
1525
2267
|
},
|
|
1526
2268
|
describe: (obj, state) => {
|
|
1527
2269
|
const L = (id) => labelOf(id, state);
|
|
1528
|
-
const c = obj.attrs
|
|
2270
|
+
const c = asConstruction(obj.attrs);
|
|
1529
2271
|
if (c?.kind === "circumscribed") {
|
|
1530
2272
|
return `\u0110\u01B0\u1EDDng tr\xF2n \u0111i qua ${L(c.p1)}${L(c.p2)}${L(c.p3)}`;
|
|
1531
2273
|
}
|
|
2274
|
+
if (c?.kind === "incircle") {
|
|
2275
|
+
return `\u0110\u01B0\u1EDDng tr\xF2n n\u1ED9i ti\u1EBFp \u0394${L(c.p1)}${L(c.p2)}${L(c.p3)}`;
|
|
2276
|
+
}
|
|
2277
|
+
if (c?.kind === "excircle") {
|
|
2278
|
+
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)}`;
|
|
2279
|
+
}
|
|
2280
|
+
if (c?.kind === "diameter") {
|
|
2281
|
+
return `\u0110\u01B0\u1EDDng tr\xF2n \u0111\u01B0\u1EDDng k\xEDnh ${L(c.p1)}${L(c.p2)}`;
|
|
2282
|
+
}
|
|
2283
|
+
if (typeof obj.attrs.radius === "number") {
|
|
2284
|
+
return `\u0110\u01B0\u1EDDng tr\xF2n t\xE2m ${L(obj.attrs.center)} b\xE1n k\xEDnh ${obj.attrs.radius}`;
|
|
2285
|
+
}
|
|
1532
2286
|
return `\u0110\u01B0\u1EDDng tr\xF2n t\xE2m ${L(obj.attrs.center)} b\xE1n k\xEDnh ${L(obj.attrs.center)}${L(obj.attrs.surfacePoint)}`;
|
|
1533
2287
|
},
|
|
1534
2288
|
render: (obj, ctx) => {
|
|
@@ -1543,13 +2297,66 @@ var init_circle = __esm({
|
|
|
1543
2297
|
visible: obj.visible,
|
|
1544
2298
|
fixed: obj.locked
|
|
1545
2299
|
};
|
|
1546
|
-
const c = obj.attrs
|
|
2300
|
+
const c = asConstruction(obj.attrs);
|
|
1547
2301
|
if (c?.kind === "circumscribed") {
|
|
1548
2302
|
const p1 = ctx.resolveRef(c.p1);
|
|
1549
2303
|
const p2 = ctx.resolveRef(c.p2);
|
|
1550
2304
|
const p3 = ctx.resolveRef(c.p3);
|
|
1551
2305
|
return board.create("circumcircle", [p1, p2, p3], baseOpts);
|
|
1552
2306
|
}
|
|
2307
|
+
if (c?.kind === "incircle") {
|
|
2308
|
+
const p1 = ctx.resolveRef(c.p1);
|
|
2309
|
+
const p2 = ctx.resolveRef(c.p2);
|
|
2310
|
+
const p3 = ctx.resolveRef(c.p3);
|
|
2311
|
+
const center2 = board.create("incenter", [p1, p2, p3], {
|
|
2312
|
+
visible: obj.visible,
|
|
2313
|
+
withLabel: true,
|
|
2314
|
+
fixed: true,
|
|
2315
|
+
name: obj.label
|
|
2316
|
+
});
|
|
2317
|
+
const circ = board.create("incircle", [p1, p2, p3], baseOpts);
|
|
2318
|
+
circ.center = circ.center ?? center2;
|
|
2319
|
+
circ._helpers = [center2];
|
|
2320
|
+
return circ;
|
|
2321
|
+
}
|
|
2322
|
+
if (c?.kind === "excircle") {
|
|
2323
|
+
const P = [ctx.resolveRef(c.p1), ctx.resolveRef(c.p2), ctx.resolveRef(c.p3)];
|
|
2324
|
+
const ids = [c.p1, c.p2, c.p3];
|
|
2325
|
+
const oppIdx = Math.max(0, ids.indexOf(c.opposite));
|
|
2326
|
+
const verts = () => [
|
|
2327
|
+
[P[0].X(), P[0].Y()],
|
|
2328
|
+
[P[1].X(), P[1].Y()],
|
|
2329
|
+
[P[2].X(), P[2].Y()]
|
|
2330
|
+
];
|
|
2331
|
+
const ctr = () => excenter(verts(), oppIdx);
|
|
2332
|
+
const radius = () => {
|
|
2333
|
+
const I = ctr();
|
|
2334
|
+
const others = [0, 1, 2].filter((i) => i !== oppIdx);
|
|
2335
|
+
const v = verts();
|
|
2336
|
+
const a = v[others[0]];
|
|
2337
|
+
const b = v[others[1]];
|
|
2338
|
+
const dx = b[0] - a[0];
|
|
2339
|
+
const dy = b[1] - a[1];
|
|
2340
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
2341
|
+
return Math.abs((I[0] - a[0]) * dy - (I[1] - a[1]) * dx) / len;
|
|
2342
|
+
};
|
|
2343
|
+
const center2 = board.create("point", [() => ctr()[0], () => ctr()[1]], { visible: false, withLabel: false, fixed: true, name: "" });
|
|
2344
|
+
const circ = board.create("circle", [center2, () => radius()], baseOpts);
|
|
2345
|
+
circ._helpers = [center2];
|
|
2346
|
+
return circ;
|
|
2347
|
+
}
|
|
2348
|
+
if (c?.kind === "diameter") {
|
|
2349
|
+
const p1 = ctx.resolveRef(c.p1);
|
|
2350
|
+
const p2 = ctx.resolveRef(c.p2);
|
|
2351
|
+
const center2 = board.create("midpoint", [p1, p2], { visible: false, withLabel: false, fixed: true, name: "" });
|
|
2352
|
+
const circ = board.create("circle", [center2, p2], baseOpts);
|
|
2353
|
+
circ._helpers = [center2];
|
|
2354
|
+
return circ;
|
|
2355
|
+
}
|
|
2356
|
+
if (typeof obj.attrs.radius === "number") {
|
|
2357
|
+
const center2 = ctx.resolveRef(obj.attrs.center);
|
|
2358
|
+
return board.create("circle", [center2, obj.attrs.radius], baseOpts);
|
|
2359
|
+
}
|
|
1553
2360
|
const center = ctx.resolveRef(obj.attrs.center);
|
|
1554
2361
|
const surface = ctx.resolveRef(obj.attrs.surfacePoint);
|
|
1555
2362
|
return board.create("circle", [center, surface], baseOpts);
|
|
@@ -1709,6 +2516,26 @@ function regularVertexLabels(p1Label, p2Label, n) {
|
|
|
1709
2516
|
}
|
|
1710
2517
|
return `${p1Label}${p2Label}\u2026`;
|
|
1711
2518
|
}
|
|
2519
|
+
function specialShapeName(kind) {
|
|
2520
|
+
switch (kind) {
|
|
2521
|
+
case "square":
|
|
2522
|
+
return "H\xECnh vu\xF4ng";
|
|
2523
|
+
case "rectangle":
|
|
2524
|
+
return "H\xECnh ch\u1EEF nh\u1EADt";
|
|
2525
|
+
case "rhombus":
|
|
2526
|
+
return "H\xECnh thoi";
|
|
2527
|
+
case "parallelogram":
|
|
2528
|
+
return "H\xECnh b\xECnh h\xE0nh";
|
|
2529
|
+
case "isoTrapezoid":
|
|
2530
|
+
return "H\xECnh thang c\xE2n";
|
|
2531
|
+
case "isoTriangle":
|
|
2532
|
+
return "Tam gi\xE1c c\xE2n";
|
|
2533
|
+
case "rightTriangle":
|
|
2534
|
+
return "Tam gi\xE1c vu\xF4ng";
|
|
2535
|
+
case "regular":
|
|
2536
|
+
return "";
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
1712
2539
|
var def11;
|
|
1713
2540
|
var init_polygon = __esm({
|
|
1714
2541
|
"src/core/scene/kinds/polygon.ts"() {
|
|
@@ -1720,13 +2547,27 @@ var init_polygon = __esm({
|
|
|
1720
2547
|
migrate: {},
|
|
1721
2548
|
validate: (a) => {
|
|
1722
2549
|
if (a?.construction) {
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
2550
|
+
const c = a.construction;
|
|
2551
|
+
if (c.kind === "regular") {
|
|
2552
|
+
if (!c.p1 || !c.p2) throw new Error("polygon (regular): p1 v\xE0 p2 b\u1EAFt bu\u1ED9c");
|
|
2553
|
+
if (!Number.isFinite(c.n) || c.n < 3) throw new Error("polygon (regular): n \u2265 3");
|
|
2554
|
+
return;
|
|
2555
|
+
}
|
|
2556
|
+
if (c.kind === "square") {
|
|
2557
|
+
if (!c.p1 || !c.p2) throw new Error("polygon (square): p1 v\xE0 p2 b\u1EAFt bu\u1ED9c");
|
|
2558
|
+
return;
|
|
2559
|
+
}
|
|
2560
|
+
if (c.kind === "rectangle" || c.kind === "rhombus" || c.kind === "parallelogram" || c.kind === "isoTrapezoid") {
|
|
2561
|
+
if (!c.p1 || !c.p2 || !c.p3) throw new Error(`polygon (${c.kind}): p1, p2, p3 b\u1EAFt bu\u1ED9c`);
|
|
2562
|
+
return;
|
|
2563
|
+
}
|
|
2564
|
+
if (c.kind === "isoTriangle") {
|
|
2565
|
+
if (!c.base1 || !c.base2 || !c.apex) throw new Error("polygon (isoTriangle): base1, base2, apex b\u1EAFt bu\u1ED9c");
|
|
2566
|
+
return;
|
|
2567
|
+
}
|
|
2568
|
+
if (c.kind === "rightTriangle") {
|
|
2569
|
+
if (!c.rightAngle || !c.leg1End || !c.leg2End) throw new Error("polygon (rightTriangle): rightAngle, leg1End, leg2End b\u1EAFt bu\u1ED9c");
|
|
2570
|
+
return;
|
|
1730
2571
|
}
|
|
1731
2572
|
return;
|
|
1732
2573
|
}
|
|
@@ -1735,14 +2576,51 @@ var init_polygon = __esm({
|
|
|
1735
2576
|
}
|
|
1736
2577
|
},
|
|
1737
2578
|
dependsOn: (a) => {
|
|
1738
|
-
|
|
1739
|
-
return [...a.vertices ?? []];
|
|
2579
|
+
const c = a.construction;
|
|
2580
|
+
if (!c) return [...a.vertices ?? []];
|
|
2581
|
+
switch (c.kind) {
|
|
2582
|
+
case "regular":
|
|
2583
|
+
return [c.p1, c.p2];
|
|
2584
|
+
case "square":
|
|
2585
|
+
return [c.p1, c.p2];
|
|
2586
|
+
case "rectangle":
|
|
2587
|
+
case "rhombus":
|
|
2588
|
+
case "parallelogram":
|
|
2589
|
+
case "isoTrapezoid":
|
|
2590
|
+
return [c.p1, c.p2, c.p3];
|
|
2591
|
+
case "isoTriangle":
|
|
2592
|
+
return [c.base1, c.base2, c.apex];
|
|
2593
|
+
case "rightTriangle":
|
|
2594
|
+
return [c.rightAngle, c.leg1End, c.leg2End];
|
|
2595
|
+
}
|
|
1740
2596
|
},
|
|
1741
2597
|
describe: (obj, state) => {
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
2598
|
+
const c = obj.attrs.construction;
|
|
2599
|
+
if (c) {
|
|
2600
|
+
if (c.kind === "regular") {
|
|
2601
|
+
const labels2 = regularVertexLabels(labelOf(c.p1, state), labelOf(c.p2, state), c.n);
|
|
2602
|
+
return `${regularPolygonName(c.n)} ${labels2}`;
|
|
2603
|
+
}
|
|
2604
|
+
const name = specialShapeName(c.kind);
|
|
2605
|
+
let labels = [];
|
|
2606
|
+
switch (c.kind) {
|
|
2607
|
+
case "square":
|
|
2608
|
+
labels = [labelOf(c.p1, state), labelOf(c.p2, state)];
|
|
2609
|
+
break;
|
|
2610
|
+
case "rectangle":
|
|
2611
|
+
case "rhombus":
|
|
2612
|
+
case "parallelogram":
|
|
2613
|
+
case "isoTrapezoid":
|
|
2614
|
+
labels = [labelOf(c.p1, state), labelOf(c.p2, state), labelOf(c.p3, state)];
|
|
2615
|
+
break;
|
|
2616
|
+
case "isoTriangle":
|
|
2617
|
+
labels = [labelOf(c.apex, state), labelOf(c.base1, state), labelOf(c.base2, state)];
|
|
2618
|
+
break;
|
|
2619
|
+
case "rightTriangle":
|
|
2620
|
+
labels = [labelOf(c.rightAngle, state), labelOf(c.leg1End, state), labelOf(c.leg2End, state)];
|
|
2621
|
+
break;
|
|
2622
|
+
}
|
|
2623
|
+
return `${name} ${labels.join("")}`;
|
|
1746
2624
|
}
|
|
1747
2625
|
return `\u0110a gi\xE1c ${(obj.attrs.vertices ?? []).map((id) => labelOf(id, state)).join("")}`;
|
|
1748
2626
|
},
|
|
@@ -1750,22 +2628,85 @@ var init_polygon = __esm({
|
|
|
1750
2628
|
const board = ctx.jxg;
|
|
1751
2629
|
const label = obj.label;
|
|
1752
2630
|
const showValue = obj.attrs.showValue ?? false;
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
2631
|
+
const cons = obj.attrs.construction;
|
|
2632
|
+
const commonAttrs = {
|
|
2633
|
+
name: label,
|
|
2634
|
+
withLabel: obj.attrs.showLabel ?? false,
|
|
2635
|
+
borders: {
|
|
2636
|
+
strokeColor: obj.attrs.color ?? "#0f172a",
|
|
2637
|
+
strokeWidth: obj.attrs.width ?? 2
|
|
2638
|
+
},
|
|
2639
|
+
fillColor: obj.attrs.color ?? "#60a5fa",
|
|
2640
|
+
fillOpacity: obj.attrs.fillOpacity ?? 0.15,
|
|
2641
|
+
visible: obj.visible,
|
|
2642
|
+
fixed: obj.locked
|
|
2643
|
+
};
|
|
2644
|
+
if (cons?.kind === "regular") {
|
|
2645
|
+
const p1 = ctx.resolveRef(cons.p1);
|
|
2646
|
+
const p2 = ctx.resolveRef(cons.p2);
|
|
2647
|
+
return board.create("regularpolygon", [p1, p2, cons.n], commonAttrs);
|
|
2648
|
+
}
|
|
2649
|
+
if (cons?.kind === "square") {
|
|
2650
|
+
const p1 = ctx.resolveRef(cons.p1);
|
|
2651
|
+
const p2 = ctx.resolveRef(cons.p2);
|
|
2652
|
+
return board.create("regularpolygon", [p1, p2, 4], commonAttrs);
|
|
2653
|
+
}
|
|
2654
|
+
if (cons?.kind === "rectangle" || cons?.kind === "rhombus" || cons?.kind === "parallelogram") {
|
|
2655
|
+
const A = ctx.resolveRef(cons.p1);
|
|
2656
|
+
const B = ctx.resolveRef(cons.p2);
|
|
2657
|
+
const C = ctx.resolveRef(cons.p3);
|
|
2658
|
+
const D = board.create(
|
|
2659
|
+
"point",
|
|
2660
|
+
[() => A.X() + C.X() - B.X(), () => A.Y() + C.Y() - B.Y()],
|
|
2661
|
+
{ visible: false, withLabel: false, fixed: true, name: "" }
|
|
2662
|
+
);
|
|
2663
|
+
const poly2 = board.create("polygon", [A, B, C, D], commonAttrs);
|
|
2664
|
+
poly2._helpers = [D];
|
|
2665
|
+
return poly2;
|
|
2666
|
+
}
|
|
2667
|
+
if (cons?.kind === "isoTrapezoid") {
|
|
2668
|
+
const A = ctx.resolveRef(cons.p1);
|
|
2669
|
+
const B = ctx.resolveRef(cons.p2);
|
|
2670
|
+
const C = ctx.resolveRef(cons.p3);
|
|
2671
|
+
const Dx = () => {
|
|
2672
|
+
const Mx = (A.X() + B.X()) / 2;
|
|
2673
|
+
const My = (A.Y() + B.Y()) / 2;
|
|
2674
|
+
const ux = B.X() - A.X();
|
|
2675
|
+
const uy = B.Y() - A.Y();
|
|
2676
|
+
const len2 = ux * ux + uy * uy || 1;
|
|
2677
|
+
const proj = ((C.X() - Mx) * ux + (C.Y() - My) * uy) / len2;
|
|
2678
|
+
return C.X() - 2 * proj * ux;
|
|
2679
|
+
};
|
|
2680
|
+
const Dy = () => {
|
|
2681
|
+
const Mx = (A.X() + B.X()) / 2;
|
|
2682
|
+
const My = (A.Y() + B.Y()) / 2;
|
|
2683
|
+
const ux = B.X() - A.X();
|
|
2684
|
+
const uy = B.Y() - A.Y();
|
|
2685
|
+
const len2 = ux * ux + uy * uy || 1;
|
|
2686
|
+
const proj = ((C.X() - Mx) * ux + (C.Y() - My) * uy) / len2;
|
|
2687
|
+
return C.Y() - 2 * proj * uy;
|
|
2688
|
+
};
|
|
2689
|
+
const D = board.create("point", [Dx, Dy], {
|
|
2690
|
+
visible: false,
|
|
2691
|
+
withLabel: false,
|
|
2692
|
+
fixed: true,
|
|
2693
|
+
name: ""
|
|
1768
2694
|
});
|
|
2695
|
+
const poly2 = board.create("polygon", [A, B, C, D], commonAttrs);
|
|
2696
|
+
poly2._helpers = [D];
|
|
2697
|
+
return poly2;
|
|
2698
|
+
}
|
|
2699
|
+
if (cons?.kind === "isoTriangle") {
|
|
2700
|
+
const Apex = ctx.resolveRef(cons.apex);
|
|
2701
|
+
const B1 = ctx.resolveRef(cons.base1);
|
|
2702
|
+
const B2 = ctx.resolveRef(cons.base2);
|
|
2703
|
+
return board.create("polygon", [Apex, B1, B2], commonAttrs);
|
|
2704
|
+
}
|
|
2705
|
+
if (cons?.kind === "rightTriangle") {
|
|
2706
|
+
const R = ctx.resolveRef(cons.rightAngle);
|
|
2707
|
+
const P = ctx.resolveRef(cons.leg1End);
|
|
2708
|
+
const Q = ctx.resolveRef(cons.leg2End);
|
|
2709
|
+
return board.create("polygon", [R, P, Q], commonAttrs);
|
|
1769
2710
|
}
|
|
1770
2711
|
const verts = (obj.attrs.vertices ?? []).map((id) => ctx.resolveRef(id));
|
|
1771
2712
|
const poly = board.create("polygon", verts, {
|
|
@@ -2464,7 +3405,7 @@ var init_serialize = __esm({
|
|
|
2464
3405
|
|
|
2465
3406
|
// src/core/scene/render/types.ts
|
|
2466
3407
|
var DEFAULT_THEME_3D;
|
|
2467
|
-
var
|
|
3408
|
+
var init_types3 = __esm({
|
|
2468
3409
|
"src/core/scene/render/types.ts"() {
|
|
2469
3410
|
DEFAULT_THEME_3D = {
|
|
2470
3411
|
point: { size: 4, color: "#1e40af" },
|
|
@@ -2479,7 +3420,7 @@ var JxgRenderer3D;
|
|
|
2479
3420
|
var init_JxgRenderer3D = __esm({
|
|
2480
3421
|
"src/core/scene/render/JxgRenderer3D.ts"() {
|
|
2481
3422
|
init_registry();
|
|
2482
|
-
|
|
3423
|
+
init_types3();
|
|
2483
3424
|
JxgRenderer3D = class {
|
|
2484
3425
|
constructor(store, view, options = {}) {
|
|
2485
3426
|
this.elements = /* @__PURE__ */ new Map();
|
|
@@ -5499,6 +6440,20 @@ var init_EditorPanel = __esm({
|
|
|
5499
6440
|
);
|
|
5500
6441
|
}
|
|
5501
6442
|
});
|
|
6443
|
+
function clamp(n, min, max) {
|
|
6444
|
+
return Math.max(min, Math.min(max, n));
|
|
6445
|
+
}
|
|
6446
|
+
function readStoredWidth(key, fallback, min, max) {
|
|
6447
|
+
if (!key || typeof window === "undefined") return fallback;
|
|
6448
|
+
try {
|
|
6449
|
+
const raw = window.localStorage.getItem(key);
|
|
6450
|
+
if (!raw) return fallback;
|
|
6451
|
+
const n = parseInt(raw, 10);
|
|
6452
|
+
if (Number.isFinite(n)) return clamp(n, min, max);
|
|
6453
|
+
} catch {
|
|
6454
|
+
}
|
|
6455
|
+
return fallback;
|
|
6456
|
+
}
|
|
5502
6457
|
function CloseIcon() {
|
|
5503
6458
|
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
5504
6459
|
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" }),
|
|
@@ -5506,8 +6461,64 @@ function CloseIcon() {
|
|
|
5506
6461
|
] });
|
|
5507
6462
|
}
|
|
5508
6463
|
function LeftPanelShell(props) {
|
|
5509
|
-
const {
|
|
6464
|
+
const {
|
|
6465
|
+
title,
|
|
6466
|
+
icon,
|
|
6467
|
+
onClose,
|
|
6468
|
+
isDark,
|
|
6469
|
+
tabs,
|
|
6470
|
+
activeTab,
|
|
6471
|
+
onTabChange,
|
|
6472
|
+
testId,
|
|
6473
|
+
resizable,
|
|
6474
|
+
widthStorageKey,
|
|
6475
|
+
defaultWidth,
|
|
6476
|
+
minWidth,
|
|
6477
|
+
maxWidth,
|
|
6478
|
+
children
|
|
6479
|
+
} = props;
|
|
5510
6480
|
const showTabs = !!tabs && tabs.length >= 2;
|
|
6481
|
+
const min = minWidth ?? FALLBACK_MIN_WIDTH;
|
|
6482
|
+
const max = maxWidth ?? FALLBACK_MAX_WIDTH;
|
|
6483
|
+
const initial = clamp(defaultWidth ?? FALLBACK_DEFAULT_WIDTH, min, max);
|
|
6484
|
+
const [width, setWidth] = React5__namespace.useState(
|
|
6485
|
+
() => resizable ? readStoredWidth(widthStorageKey, initial, min, max) : initial
|
|
6486
|
+
);
|
|
6487
|
+
const widthRef = React5__namespace.useRef(width);
|
|
6488
|
+
widthRef.current = width;
|
|
6489
|
+
React5__namespace.useEffect(() => {
|
|
6490
|
+
if (!resizable || !widthStorageKey || typeof window === "undefined") return;
|
|
6491
|
+
try {
|
|
6492
|
+
window.localStorage.setItem(widthStorageKey, String(width));
|
|
6493
|
+
} catch {
|
|
6494
|
+
}
|
|
6495
|
+
}, [resizable, widthStorageKey, width]);
|
|
6496
|
+
const onResizeStart = React5__namespace.useCallback(
|
|
6497
|
+
(e) => {
|
|
6498
|
+
if (!resizable) return;
|
|
6499
|
+
e.preventDefault();
|
|
6500
|
+
const startX = e.clientX;
|
|
6501
|
+
const startW = widthRef.current;
|
|
6502
|
+
const onMove = (ev) => {
|
|
6503
|
+
setWidth(clamp(startW + (ev.clientX - startX), min, max));
|
|
6504
|
+
};
|
|
6505
|
+
const onUp = () => {
|
|
6506
|
+
window.removeEventListener("mousemove", onMove);
|
|
6507
|
+
window.removeEventListener("mouseup", onUp);
|
|
6508
|
+
document.body.style.cursor = "";
|
|
6509
|
+
document.body.style.userSelect = "";
|
|
6510
|
+
};
|
|
6511
|
+
window.addEventListener("mousemove", onMove);
|
|
6512
|
+
window.addEventListener("mouseup", onUp);
|
|
6513
|
+
document.body.style.cursor = "ew-resize";
|
|
6514
|
+
document.body.style.userSelect = "none";
|
|
6515
|
+
},
|
|
6516
|
+
[resizable, min, max]
|
|
6517
|
+
);
|
|
6518
|
+
const onResizeDoubleClick = React5__namespace.useCallback(() => {
|
|
6519
|
+
if (!resizable) return;
|
|
6520
|
+
setWidth(initial);
|
|
6521
|
+
}, [resizable, initial]);
|
|
5511
6522
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5512
6523
|
"aside",
|
|
5513
6524
|
{
|
|
@@ -5515,10 +6526,12 @@ function LeftPanelShell(props) {
|
|
|
5515
6526
|
"aria-label": title,
|
|
5516
6527
|
"data-testid": testId ?? "left-panel",
|
|
5517
6528
|
"data-stamp-area": "true",
|
|
6529
|
+
style: resizable ? { width: `${width}px` } : void 0,
|
|
5518
6530
|
className: [
|
|
5519
6531
|
isDark ? "theme--dark " : "",
|
|
5520
|
-
"absolute left-0 top-0 z-30 flex h-full
|
|
5521
|
-
|
|
6532
|
+
"absolute left-0 top-0 z-30 flex h-full flex-col border-r border-slate-200 bg-white shadow-md animate-in slide-in-from-left duration-200",
|
|
6533
|
+
resizable ? "" : "w-60"
|
|
6534
|
+
].join(" "),
|
|
5522
6535
|
children: [
|
|
5523
6536
|
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex items-center justify-between border-b border-slate-200 bg-gradient-to-r from-slate-50 to-white px-3 py-2", children: [
|
|
5524
6537
|
/* @__PURE__ */ jsxRuntime.jsxs("h3", { className: "flex items-center gap-2 text-sm font-semibold text-slate-800", children: [
|
|
@@ -5553,6 +6566,20 @@ function LeftPanelShell(props) {
|
|
|
5553
6566
|
className: "min-h-0 flex-1 overflow-y-auto p-3 space-y-3",
|
|
5554
6567
|
children
|
|
5555
6568
|
}
|
|
6569
|
+
),
|
|
6570
|
+
resizable && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6571
|
+
"div",
|
|
6572
|
+
{
|
|
6573
|
+
role: "separator",
|
|
6574
|
+
"aria-orientation": "vertical",
|
|
6575
|
+
"aria-label": "K\xE9o \u0111\u1EC3 \u0111\u1ED5i r\u1ED9ng panel",
|
|
6576
|
+
"data-testid": "left-panel-resizer",
|
|
6577
|
+
onMouseDown: onResizeStart,
|
|
6578
|
+
onDoubleClick: onResizeDoubleClick,
|
|
6579
|
+
className: "group absolute right-0 top-0 z-40 h-full w-1.5 -mr-0.5 cursor-ew-resize select-none",
|
|
6580
|
+
title: "K\xE9o \u0111\u1EC3 \u0111\u1ED5i r\u1ED9ng (double-click \u0111\u1EC3 reset)",
|
|
6581
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute inset-y-0 right-0 w-px bg-slate-200 transition group-hover:bg-emerald-400 group-hover:w-0.5 group-active:bg-emerald-500 group-active:w-0.5" })
|
|
6582
|
+
}
|
|
5556
6583
|
)
|
|
5557
6584
|
]
|
|
5558
6585
|
}
|
|
@@ -5582,9 +6609,13 @@ function Section(props) {
|
|
|
5582
6609
|
props.children
|
|
5583
6610
|
] });
|
|
5584
6611
|
}
|
|
6612
|
+
var FALLBACK_DEFAULT_WIDTH, FALLBACK_MIN_WIDTH, FALLBACK_MAX_WIDTH;
|
|
5585
6613
|
var init_LeftPanelShell = __esm({
|
|
5586
6614
|
"src/core/scene/ui/LeftPanelShell.tsx"() {
|
|
5587
6615
|
"use client";
|
|
6616
|
+
FALLBACK_DEFAULT_WIDTH = 240;
|
|
6617
|
+
FALLBACK_MIN_WIDTH = 220;
|
|
6618
|
+
FALLBACK_MAX_WIDTH = 480;
|
|
5588
6619
|
}
|
|
5589
6620
|
});
|
|
5590
6621
|
|
|
@@ -5936,7 +6967,7 @@ var init_AxisGridSection = __esm({
|
|
|
5936
6967
|
|
|
5937
6968
|
// src/stamps/shared/StampLeftPanel/types.ts
|
|
5938
6969
|
var TOOLTIP_DELAY_MS;
|
|
5939
|
-
var
|
|
6970
|
+
var init_types4 = __esm({
|
|
5940
6971
|
"src/stamps/shared/StampLeftPanel/types.ts"() {
|
|
5941
6972
|
TOOLTIP_DELAY_MS = 400;
|
|
5942
6973
|
}
|
|
@@ -5969,27 +7000,118 @@ function useToolHoverTooltip() {
|
|
|
5969
7000
|
}
|
|
5970
7001
|
var init_useToolHoverTooltip = __esm({
|
|
5971
7002
|
"src/stamps/shared/StampLeftPanel/useToolHoverTooltip.ts"() {
|
|
5972
|
-
|
|
7003
|
+
init_types4();
|
|
5973
7004
|
}
|
|
5974
7005
|
});
|
|
7006
|
+
function normalize2(s) {
|
|
7007
|
+
return s.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/đ/g, "d").replace(/Đ/g, "d");
|
|
7008
|
+
}
|
|
7009
|
+
function SearchIcon() {
|
|
7010
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
7011
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "11", r: "7" }),
|
|
7012
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "20", y1: "20", x2: "16.5", y2: "16.5" })
|
|
7013
|
+
] });
|
|
7014
|
+
}
|
|
7015
|
+
function ClearIcon() {
|
|
7016
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
7017
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" }),
|
|
7018
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" })
|
|
7019
|
+
] });
|
|
7020
|
+
}
|
|
7021
|
+
function ToolResultList(props) {
|
|
7022
|
+
const { tools, activeTool, onToolChange } = props;
|
|
7023
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-0.5", "data-testid": "tool-result-list", children: tools.map((t) => {
|
|
7024
|
+
const active = activeTool === t.key;
|
|
7025
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7026
|
+
"button",
|
|
7027
|
+
{
|
|
7028
|
+
type: "button",
|
|
7029
|
+
"data-tool": t.key,
|
|
7030
|
+
"aria-label": t.label,
|
|
7031
|
+
"aria-pressed": active,
|
|
7032
|
+
onClick: () => onToolChange(t.key),
|
|
7033
|
+
className: [
|
|
7034
|
+
"flex items-center gap-2 rounded-md px-2 py-1.5 text-left transition",
|
|
7035
|
+
active ? "bg-emerald-600 text-white" : "text-slate-700 hover:bg-slate-100"
|
|
7036
|
+
].join(" "),
|
|
7037
|
+
children: [
|
|
7038
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-6 w-6 shrink-0 items-center justify-center", children: t.icon }),
|
|
7039
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0", children: [
|
|
7040
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate text-[12px] font-medium leading-tight", children: t.label }),
|
|
7041
|
+
t.hint && /* @__PURE__ */ jsxRuntime.jsx("span", { className: ["block truncate text-[10px] leading-tight", active ? "text-emerald-50" : "text-slate-400"].join(" "), children: t.hint })
|
|
7042
|
+
] })
|
|
7043
|
+
]
|
|
7044
|
+
},
|
|
7045
|
+
t.key
|
|
7046
|
+
);
|
|
7047
|
+
}) });
|
|
7048
|
+
}
|
|
5975
7049
|
function ToolGrid(props) {
|
|
5976
7050
|
const { tools, groupOrder, groupLabels, activeTool, onToolChange, chord } = props;
|
|
5977
7051
|
const { hover, portalReady, showHover, hideHover } = useToolHoverTooltip();
|
|
7052
|
+
const [query, setQuery] = React5.useState("");
|
|
7053
|
+
const normalizedQuery = React5.useMemo(() => normalize2(query.trim()), [query]);
|
|
7054
|
+
const filteredTools = React5.useMemo(() => {
|
|
7055
|
+
if (!normalizedQuery) return tools;
|
|
7056
|
+
return tools.filter((t) => {
|
|
7057
|
+
if (normalize2(t.label).includes(normalizedQuery)) return true;
|
|
7058
|
+
if (t.hint && normalize2(t.hint).includes(normalizedQuery)) return true;
|
|
7059
|
+
return false;
|
|
7060
|
+
});
|
|
7061
|
+
}, [tools, normalizedQuery]);
|
|
5978
7062
|
const grouped = React5.useMemo(() => {
|
|
5979
7063
|
var _a;
|
|
5980
7064
|
const acc = {};
|
|
5981
|
-
for (const t of
|
|
7065
|
+
for (const t of filteredTools) {
|
|
5982
7066
|
(acc[_a = t.group] ?? (acc[_a] = [])).push(t);
|
|
5983
7067
|
}
|
|
5984
7068
|
return acc;
|
|
5985
|
-
}, [
|
|
7069
|
+
}, [filteredTools]);
|
|
5986
7070
|
const groupKeys = React5.useMemo(
|
|
5987
|
-
() => groupOrder.filter((g) => grouped[g]),
|
|
7071
|
+
() => groupOrder.filter((g) => grouped[g] && grouped[g].length > 0),
|
|
5988
7072
|
[grouped, groupOrder]
|
|
5989
7073
|
);
|
|
5990
|
-
const
|
|
7074
|
+
const noMatch = normalizedQuery !== "" && groupKeys.length === 0;
|
|
5991
7075
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5992
|
-
|
|
7076
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
7077
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "pointer-events-none absolute left-2 top-1/2 -translate-y-1/2 text-slate-400", children: /* @__PURE__ */ jsxRuntime.jsx(SearchIcon, {}) }),
|
|
7078
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7079
|
+
"input",
|
|
7080
|
+
{
|
|
7081
|
+
type: "search",
|
|
7082
|
+
value: query,
|
|
7083
|
+
onChange: (e) => setQuery(e.target.value),
|
|
7084
|
+
placeholder: "T\xECm c\xF4ng c\u1EE5\u2026",
|
|
7085
|
+
"aria-label": "T\xECm c\xF4ng c\u1EE5",
|
|
7086
|
+
"data-testid": "tool-search-input",
|
|
7087
|
+
className: "w-full rounded-md border border-slate-200 bg-slate-50 py-1.5 pl-7 pr-7 text-[12px] text-slate-800 placeholder:text-slate-400 focus:border-emerald-400 focus:bg-white focus:outline-none focus:ring-1 focus:ring-emerald-300"
|
|
7088
|
+
}
|
|
7089
|
+
),
|
|
7090
|
+
query && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7091
|
+
"button",
|
|
7092
|
+
{
|
|
7093
|
+
type: "button",
|
|
7094
|
+
onClick: () => setQuery(""),
|
|
7095
|
+
"aria-label": "Xo\xE1 t\xECm ki\u1EBFm",
|
|
7096
|
+
"data-testid": "tool-search-clear",
|
|
7097
|
+
className: "absolute right-1.5 top-1/2 -translate-y-1/2 rounded p-0.5 text-slate-400 transition hover:bg-slate-200 hover:text-slate-700",
|
|
7098
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ClearIcon, {})
|
|
7099
|
+
}
|
|
7100
|
+
)
|
|
7101
|
+
] }),
|
|
7102
|
+
noMatch && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7103
|
+
"div",
|
|
7104
|
+
{
|
|
7105
|
+
"data-testid": "tool-search-empty",
|
|
7106
|
+
className: "rounded-md border border-dashed border-slate-200 bg-slate-50 px-3 py-4 text-center text-[11px] text-slate-500",
|
|
7107
|
+
children: [
|
|
7108
|
+
"Kh\xF4ng c\xF3 c\xF4ng c\u1EE5 n\xE0o kh\u1EDBp \u201C",
|
|
7109
|
+
query.trim(),
|
|
7110
|
+
"\u201D."
|
|
7111
|
+
]
|
|
7112
|
+
}
|
|
7113
|
+
),
|
|
7114
|
+
normalizedQuery !== "" && !noMatch ? /* @__PURE__ */ jsxRuntime.jsx(ToolResultList, { tools: filteredTools, activeTool, onToolChange }) : groupKeys.map((group) => {
|
|
5993
7115
|
const isChordActive = chord?.activeGroup === group;
|
|
5994
7116
|
const dimmed = chord?.activeGroup != null && !isChordActive;
|
|
5995
7117
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -6003,23 +7125,10 @@ function ToolGrid(props) {
|
|
|
6003
7125
|
dimmed ? "opacity-55" : "opacity-100"
|
|
6004
7126
|
].join(" "),
|
|
6005
7127
|
children: [
|
|
6006
|
-
/* @__PURE__ */ jsxRuntime.
|
|
6007
|
-
|
|
6008
|
-
chord && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6009
|
-
"span",
|
|
6010
|
-
{
|
|
6011
|
-
"data-testid": `chord-letter-${group}`,
|
|
6012
|
-
className: [
|
|
6013
|
-
"font-mono text-[10px] leading-none transition",
|
|
6014
|
-
isChordActive ? "text-emerald-700 font-bold" : "text-slate-400"
|
|
6015
|
-
].join(" "),
|
|
6016
|
-
children: chord.letterForGroup(group)
|
|
6017
|
-
}
|
|
6018
|
-
)
|
|
6019
|
-
] }),
|
|
6020
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-4 gap-1", children: grouped[group].map((t, i) => {
|
|
7128
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "mb-1.5 text-[10px] font-semibold uppercase tracking-wider text-slate-500", children: groupLabels[group] }),
|
|
7129
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-4 gap-1", children: grouped[group].map((t) => {
|
|
6021
7130
|
const active = activeTool === t.key;
|
|
6022
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
7131
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6023
7132
|
"button",
|
|
6024
7133
|
{
|
|
6025
7134
|
type: "button",
|
|
@@ -6036,20 +7145,7 @@ function ToolGrid(props) {
|
|
|
6036
7145
|
"relative flex h-10 items-center justify-center rounded-md transition",
|
|
6037
7146
|
active ? "bg-emerald-600 text-white shadow-sm" : "text-slate-700 hover:bg-slate-100 hover:text-slate-900"
|
|
6038
7147
|
].join(" "),
|
|
6039
|
-
children:
|
|
6040
|
-
t.icon,
|
|
6041
|
-
chord && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6042
|
-
"span",
|
|
6043
|
-
{
|
|
6044
|
-
"data-testid": `chord-num-${t.key}`,
|
|
6045
|
-
className: [
|
|
6046
|
-
"pointer-events-none absolute bottom-0 right-0.5 font-mono text-[9px] leading-none transition",
|
|
6047
|
-
active ? "text-white/70" : isChordActive ? "text-emerald-700 font-bold" : "text-slate-400"
|
|
6048
|
-
].join(" "),
|
|
6049
|
-
children: i + 1
|
|
6050
|
-
}
|
|
6051
|
-
)
|
|
6052
|
-
]
|
|
7148
|
+
children: t.icon
|
|
6053
7149
|
},
|
|
6054
7150
|
t.key
|
|
6055
7151
|
);
|
|
@@ -6059,22 +7155,6 @@ function ToolGrid(props) {
|
|
|
6059
7155
|
group
|
|
6060
7156
|
);
|
|
6061
7157
|
}),
|
|
6062
|
-
chord?.activeGroup && activeGroupTools && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6063
|
-
"div",
|
|
6064
|
-
{
|
|
6065
|
-
"data-testid": "chord-hint",
|
|
6066
|
-
className: "mt-1 rounded border border-emerald-200 bg-emerald-50/60 px-2 py-1 text-[11px] leading-snug text-slate-600",
|
|
6067
|
-
children: [
|
|
6068
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono font-semibold text-emerald-700", children: chord.letterForGroup(chord.activeGroup) }),
|
|
6069
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mx-1 text-slate-400", children: "\u2192" }),
|
|
6070
|
-
activeGroupTools.map((t, i) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mr-2 inline-block", children: [
|
|
6071
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono font-semibold text-emerald-700", children: i + 1 }),
|
|
6072
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1", children: t.label })
|
|
6073
|
-
] }, t.key)),
|
|
6074
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-400", children: "Esc hu\u1EF7" })
|
|
6075
|
-
]
|
|
6076
|
-
}
|
|
6077
|
-
),
|
|
6078
7158
|
portalReady && hover && typeof document !== "undefined" ? reactDom.createPortal(
|
|
6079
7159
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
6080
7160
|
"div",
|
|
@@ -6141,6 +7221,8 @@ function StampLeftPanelDesktop(props) {
|
|
|
6141
7221
|
tabs: tabSpecs,
|
|
6142
7222
|
activeTab: hasObjects ? tab : void 0,
|
|
6143
7223
|
onTabChange: hasObjects ? setTab : void 0,
|
|
7224
|
+
resizable: true,
|
|
7225
|
+
widthStorageKey: "xom11.stamp-left-panel.width",
|
|
6144
7226
|
children: !hasObjects || tab === "tools" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6145
7227
|
/* @__PURE__ */ jsxRuntime.jsx(AxisGridSection, { view, history }),
|
|
6146
7228
|
/* @__PURE__ */ jsxRuntime.jsx(
|