@xom11/whiteboard 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai.d.mts +226 -32
- package/dist/ai.d.ts +226 -32
- package/dist/ai.js +5126 -351
- package/dist/ai.js.map +1 -1
- package/dist/ai.mjs +4970 -352
- package/dist/ai.mjs.map +1 -1
- package/dist/catalog.json +5 -5
- package/dist/{chunk-V3YJ6JFL.mjs → chunk-44JY2AKC.mjs} +3 -3
- package/dist/{chunk-V3YJ6JFL.mjs.map → chunk-44JY2AKC.mjs.map} +1 -1
- package/dist/{chunk-XVVLT6B3.mjs → chunk-BMYC2ILT.mjs} +4 -4
- package/dist/{chunk-XVVLT6B3.mjs.map → chunk-BMYC2ILT.mjs.map} +1 -1
- package/dist/{chunk-PPKHCRRE.mjs → chunk-C76SOFXF.mjs} +3 -3
- package/dist/{chunk-PPKHCRRE.mjs.map → chunk-C76SOFXF.mjs.map} +1 -1
- package/dist/{chunk-IHUFOV7L.mjs → chunk-CH6SFONH.mjs} +15 -3
- package/dist/chunk-CH6SFONH.mjs.map +1 -0
- package/dist/{chunk-SF3U7ZF4.mjs → chunk-DWIEVCGK.mjs} +180 -15
- package/dist/chunk-DWIEVCGK.mjs.map +1 -0
- package/dist/{chunk-SZDAS7LK.mjs → chunk-IE2GGHNF.mjs} +131 -81
- package/dist/chunk-IE2GGHNF.mjs.map +1 -0
- package/dist/{chunk-ZTQBUKLJ.mjs → chunk-JJ4FPCBE.mjs} +142 -22
- package/dist/chunk-JJ4FPCBE.mjs.map +1 -0
- package/dist/{chunk-QRUAEXLR.mjs → chunk-K5BS2H56.mjs} +5 -5
- package/dist/{chunk-QRUAEXLR.mjs.map → chunk-K5BS2H56.mjs.map} +1 -1
- package/dist/{chunk-BNBOIDO5.mjs → chunk-K7VJU7LQ.mjs} +3 -3
- package/dist/{chunk-BNBOIDO5.mjs.map → chunk-K7VJU7LQ.mjs.map} +1 -1
- package/dist/{chunk-H22OZYTW.mjs → chunk-KOXOC2FI.mjs} +48 -39
- package/dist/chunk-KOXOC2FI.mjs.map +1 -0
- package/dist/{chunk-CXHNVYMD.mjs → chunk-KWDBVLST.mjs} +5 -5
- package/dist/{chunk-CXHNVYMD.mjs.map → chunk-KWDBVLST.mjs.map} +1 -1
- package/dist/{chunk-OQIQNKPQ.mjs → chunk-LTLLQUMN.mjs} +4 -4
- package/dist/{chunk-OQIQNKPQ.mjs.map → chunk-LTLLQUMN.mjs.map} +1 -1
- package/dist/{chunk-QGNU34T7.mjs → chunk-QLQ4MJNO.mjs} +10 -4
- package/dist/chunk-QLQ4MJNO.mjs.map +1 -0
- package/dist/{chunk-BU5KLO3P.mjs → chunk-T3N4BSJV.mjs} +4 -4
- package/dist/{chunk-BU5KLO3P.mjs.map → chunk-T3N4BSJV.mjs.map} +1 -1
- package/dist/{chunk-5JM35CXV.mjs → chunk-TMRFSOM7.mjs} +4 -4
- package/dist/{chunk-5JM35CXV.mjs.map → chunk-TMRFSOM7.mjs.map} +1 -1
- package/dist/geometry-2d.d.mts +1 -1
- package/dist/geometry-2d.d.ts +1 -1
- package/dist/geometry-2d.js +449 -172
- package/dist/geometry-2d.js.map +1 -1
- package/dist/geometry-2d.mjs +5 -5
- package/dist/geometry-3d.d.mts +1 -1
- package/dist/geometry-3d.d.ts +1 -1
- package/dist/geometry-3d.js +172 -22
- package/dist/geometry-3d.js.map +1 -1
- package/dist/geometry-3d.mjs +4 -4
- package/dist/graph-2d.d.mts +1 -1
- package/dist/graph-2d.d.ts +1 -1
- package/dist/graph-2d.js +307 -100
- package/dist/graph-2d.js.map +1 -1
- package/dist/graph-2d.mjs +7 -7
- package/dist/{host-HOSJHQ5H.mjs → host-4FIUNIDQ.mjs} +13 -12
- package/dist/host-4FIUNIDQ.mjs.map +1 -0
- package/dist/{host-2ISGVO7O.mjs → host-4ZB4XD4S.mjs} +9 -8
- package/dist/host-4ZB4XD4S.mjs.map +1 -0
- package/dist/{host-ZQCDAT6O.mjs → host-H2IGOKJU.mjs} +3 -3
- package/dist/{host-ZQCDAT6O.mjs.map → host-H2IGOKJU.mjs.map} +1 -1
- package/dist/{host-3UFGFMJ2.mjs → host-KMWP7KBT.mjs} +90 -43
- package/dist/host-KMWP7KBT.mjs.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +453 -174
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +21 -21
- package/dist/latex.d.mts +1 -1
- package/dist/latex.d.ts +1 -1
- package/dist/latex.js +8 -2
- package/dist/latex.js.map +1 -1
- package/dist/latex.mjs +1 -1
- package/dist/render-NMS7OAV6.mjs +10 -0
- package/dist/{render-ZX2O2IK7.mjs.map → render-NMS7OAV6.mjs.map} +1 -1
- package/dist/serialize-PGHQZEPV.mjs +9 -0
- package/dist/{serialize-N4G6RFBB.mjs.map → serialize-PGHQZEPV.mjs.map} +1 -1
- package/dist/{types-C3FjpoUi.d.ts → types-tePd94vW.d.mts} +8 -0
- package/dist/{types-C3FjpoUi.d.mts → types-tePd94vW.d.ts} +8 -0
- package/package.json +1 -1
- package/dist/chunk-H22OZYTW.mjs.map +0 -1
- package/dist/chunk-IHUFOV7L.mjs.map +0 -1
- package/dist/chunk-QGNU34T7.mjs.map +0 -1
- package/dist/chunk-SF3U7ZF4.mjs.map +0 -1
- package/dist/chunk-SZDAS7LK.mjs.map +0 -1
- package/dist/chunk-ZTQBUKLJ.mjs.map +0 -1
- package/dist/host-2ISGVO7O.mjs.map +0 -1
- package/dist/host-3UFGFMJ2.mjs.map +0 -1
- package/dist/host-HOSJHQ5H.mjs.map +0 -1
- package/dist/render-ZX2O2IK7.mjs +0 -10
- package/dist/serialize-N4G6RFBB.mjs +0 -9
package/dist/geometry-2d.js
CHANGED
|
@@ -939,8 +939,12 @@ function constraintRefs2D(c) {
|
|
|
939
939
|
return [c.line, c.circle, c.other];
|
|
940
940
|
case "tangencyPoint":
|
|
941
941
|
return [c.circle, c.onLine];
|
|
942
|
-
case "arcMidpoint":
|
|
943
|
-
|
|
942
|
+
case "arcMidpoint": {
|
|
943
|
+
const containment = c.notContaining ?? c.containing;
|
|
944
|
+
return containment ? [c.circle, c.a, c.b, containment] : [c.circle, c.a, c.b];
|
|
945
|
+
}
|
|
946
|
+
case "mixtilinearPoint":
|
|
947
|
+
return [c.vertices[0], c.vertices[1], c.vertices[2]];
|
|
944
948
|
case "pointAtDistance": {
|
|
945
949
|
const d = c.distance;
|
|
946
950
|
const extra = d.kind === "circleRadius" ? [d.circle] : d.kind === "segmentLength" ? [d.p1, d.p2] : [];
|
|
@@ -1227,7 +1231,7 @@ function dist(p, q) {
|
|
|
1227
1231
|
function sideOf(a, b, p) {
|
|
1228
1232
|
return (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]);
|
|
1229
1233
|
}
|
|
1230
|
-
function arcMidpoint(center, radius, a, b,
|
|
1234
|
+
function arcMidpoint(center, radius, a, b, reference, sameSide = false) {
|
|
1231
1235
|
const mcx = (a[0] + b[0]) / 2;
|
|
1232
1236
|
const mcy = (a[1] + b[1]) / 2;
|
|
1233
1237
|
let ux = mcx - center[0];
|
|
@@ -1242,12 +1246,18 @@ function arcMidpoint(center, radius, a, b, notContaining) {
|
|
|
1242
1246
|
uy /= len;
|
|
1243
1247
|
const cand1 = [center[0] + radius * ux, center[1] + radius * uy];
|
|
1244
1248
|
const cand2 = [center[0] - radius * ux, center[1] - radius * uy];
|
|
1249
|
+
if (!reference) return cand1[1] >= cand2[1] ? cand1 : cand2;
|
|
1250
|
+
const notContaining = reference;
|
|
1245
1251
|
const sN = sideOf(a, b, notContaining);
|
|
1246
1252
|
if (Math.abs(sN) < 1e-9) {
|
|
1247
|
-
|
|
1253
|
+
const far = dist(cand1, notContaining) >= dist(cand2, notContaining) ? cand1 : cand2;
|
|
1254
|
+
const near = far === cand1 ? cand2 : cand1;
|
|
1255
|
+
return sameSide ? near : far;
|
|
1248
1256
|
}
|
|
1249
1257
|
const s1 = sideOf(a, b, cand1);
|
|
1250
|
-
|
|
1258
|
+
const opp = s1 * sN < 0 ? cand1 : cand2;
|
|
1259
|
+
const same = opp === cand1 ? cand2 : cand1;
|
|
1260
|
+
return sameSide ? same : opp;
|
|
1251
1261
|
}
|
|
1252
1262
|
function excenter(vertices, oppositeIndex) {
|
|
1253
1263
|
const [A, B, C] = vertices;
|
|
@@ -1263,6 +1273,35 @@ function excenter(vertices, oppositeIndex) {
|
|
|
1263
1273
|
(w[0] * A[1] + w[1] * B[1] + w[2] * C[1]) / sum
|
|
1264
1274
|
];
|
|
1265
1275
|
}
|
|
1276
|
+
function circumcenterXY(a, b, c) {
|
|
1277
|
+
const ax = a[0], ay = a[1], bx = b[0], by = b[1], cx = c[0], cy = c[1];
|
|
1278
|
+
const d = 2 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
|
|
1279
|
+
if (Math.abs(d) < 1e-12) return [(ax + bx + cx) / 3, (ay + by + cy) / 3];
|
|
1280
|
+
const ux = ((ax * ax + ay * ay) * (by - cy) + (bx * bx + by * by) * (cy - ay) + (cx * cx + cy * cy) * (ay - by)) / d;
|
|
1281
|
+
const uy = ((ax * ax + ay * ay) * (cx - bx) + (bx * bx + by * by) * (ax - cx) + (cx * cx + cy * cy) * (bx - ax)) / d;
|
|
1282
|
+
return [ux, uy];
|
|
1283
|
+
}
|
|
1284
|
+
function mixtilinearPoint(a, b, c, which) {
|
|
1285
|
+
const O = circumcenterXY(a, b, c);
|
|
1286
|
+
const R = Math.hypot(O[0] - a[0], O[1] - a[1]);
|
|
1287
|
+
const ux1 = (b[0] - a[0]) / (Math.hypot(b[0] - a[0], b[1] - a[1]) || 1);
|
|
1288
|
+
const uy1 = (b[1] - a[1]) / (Math.hypot(b[0] - a[0], b[1] - a[1]) || 1);
|
|
1289
|
+
const ux2 = (c[0] - a[0]) / (Math.hypot(c[0] - a[0], c[1] - a[1]) || 1);
|
|
1290
|
+
const uy2 = (c[1] - a[1]) / (Math.hypot(c[0] - a[0], c[1] - a[1]) || 1);
|
|
1291
|
+
let bx = ux1 + ux2, by = uy1 + uy2;
|
|
1292
|
+
const bl = Math.hypot(bx, by) || 1;
|
|
1293
|
+
bx /= bl;
|
|
1294
|
+
by /= bl;
|
|
1295
|
+
const cosA = ux1 * ux2 + uy1 * uy2;
|
|
1296
|
+
const sinHalf = Math.sqrt(Math.max(0, (1 - cosA) / 2));
|
|
1297
|
+
const cos2Half = Math.max(1e-9, (1 + cosA) / 2);
|
|
1298
|
+
const dotAO = bx * (O[0] - a[0]) + by * (O[1] - a[1]);
|
|
1299
|
+
const d = 2 * (dotAO - R * sinHalf) / cos2Half;
|
|
1300
|
+
const K = [a[0] + d * bx, a[1] + d * by];
|
|
1301
|
+
if (which === "center") return K;
|
|
1302
|
+
const kl = Math.hypot(K[0] - O[0], K[1] - O[1]) || 1;
|
|
1303
|
+
return [O[0] + R * (K[0] - O[0]) / kl, O[1] + R * (K[1] - O[1]) / kl];
|
|
1304
|
+
}
|
|
1266
1305
|
function pointAtDistanceCoord(from, through, d) {
|
|
1267
1306
|
const dx = through[0] - from[0];
|
|
1268
1307
|
const dy = through[1] - from[1];
|
|
@@ -1290,29 +1329,38 @@ var init_arcMidpoint = __esm({
|
|
|
1290
1329
|
arcMidpointConstraint = definePointConstraint({
|
|
1291
1330
|
kind: "arcMidpoint",
|
|
1292
1331
|
validate: (c) => {
|
|
1293
|
-
if (!c.circle || !c.a || !c.b
|
|
1294
|
-
throw new Error("point.arcMidpoint: circle, a, b
|
|
1332
|
+
if (!c.circle || !c.a || !c.b) {
|
|
1333
|
+
throw new Error("point.arcMidpoint: circle, a, b b\u1EAFt bu\u1ED9c");
|
|
1334
|
+
}
|
|
1335
|
+
if (c.notContaining && c.containing) {
|
|
1336
|
+
throw new Error("point.arcMidpoint: kh\xF4ng th\u1EC3 v\u1EEBa notContaining v\u1EEBa containing");
|
|
1295
1337
|
}
|
|
1296
1338
|
},
|
|
1297
1339
|
describe: (obj, state, c) => {
|
|
1298
1340
|
const al = state?.objects[c.a]?.label ?? c.a;
|
|
1299
1341
|
const bl = state?.objects[c.b]?.label ?? c.b;
|
|
1300
|
-
const
|
|
1301
|
-
return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl}
|
|
1342
|
+
const ref = c.containing ?? c.notContaining;
|
|
1343
|
+
if (!ref) return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl}`;
|
|
1344
|
+
const rl = state?.objects[ref]?.label ?? ref;
|
|
1345
|
+
const rel = c.containing ? "ch\u1EE9a" : "kh\xF4ng ch\u1EE9a";
|
|
1346
|
+
return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl} (${rel} ${rl})`;
|
|
1302
1347
|
},
|
|
1303
1348
|
render: (obj, ctx, c, opts) => {
|
|
1304
1349
|
const board = ctx.jxg;
|
|
1305
1350
|
const circle = ctx.resolveRef(c.circle);
|
|
1306
1351
|
const A = ctx.resolveRef(c.a);
|
|
1307
1352
|
const B = ctx.resolveRef(c.b);
|
|
1308
|
-
const
|
|
1353
|
+
const refName = c.containing ?? c.notContaining;
|
|
1354
|
+
const ref = refName ? ctx.resolveRef(refName) : void 0;
|
|
1355
|
+
const sameSide = !!c.containing;
|
|
1309
1356
|
const O = circle?.center ?? circle?.midpoint ?? circle;
|
|
1310
1357
|
const am = () => arcMidpoint(
|
|
1311
1358
|
[O.X(), O.Y()],
|
|
1312
1359
|
circle.Radius(),
|
|
1313
1360
|
[A.X(), A.Y()],
|
|
1314
1361
|
[B.X(), B.Y()],
|
|
1315
|
-
[
|
|
1362
|
+
ref ? [ref.X(), ref.Y()] : void 0,
|
|
1363
|
+
sameSide
|
|
1316
1364
|
);
|
|
1317
1365
|
return board.create("point", [() => am()[0], () => am()[1]], opts);
|
|
1318
1366
|
}
|
|
@@ -1362,6 +1410,59 @@ var init_excenter = __esm({
|
|
|
1362
1410
|
}
|
|
1363
1411
|
});
|
|
1364
1412
|
|
|
1413
|
+
// src/core/scene/kinds/point-constraints/mixtilinearPoint.ts
|
|
1414
|
+
var mixtilinearPointConstraint;
|
|
1415
|
+
var init_mixtilinearPoint = __esm({
|
|
1416
|
+
"src/core/scene/kinds/point-constraints/mixtilinearPoint.ts"() {
|
|
1417
|
+
init_pointConstructions();
|
|
1418
|
+
init_types2();
|
|
1419
|
+
mixtilinearPointConstraint = definePointConstraint({
|
|
1420
|
+
kind: "mixtilinearPoint",
|
|
1421
|
+
validate: (c) => {
|
|
1422
|
+
if (!Array.isArray(c.vertices) || c.vertices.length !== 3 || !c.vertices.every(Boolean)) {
|
|
1423
|
+
throw new Error("point.mixtilinearPoint: vertices ph\u1EA3i l\xE0 tuple 3 id non-empty");
|
|
1424
|
+
}
|
|
1425
|
+
if (c.which !== "center" && c.which !== "touch") {
|
|
1426
|
+
throw new Error("point.mixtilinearPoint: which ph\u1EA3i 'center' | 'touch'");
|
|
1427
|
+
}
|
|
1428
|
+
},
|
|
1429
|
+
describe: (obj, state, c) => {
|
|
1430
|
+
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
1431
|
+
return c.which === "center" ? `${obj.label} = t\xE2m \u0111\u01B0\u1EDDng tr\xF2n mixtilinear \u0394${labels}` : `${obj.label} = ti\u1EBFp \u0111i\u1EC3m mixtilinear \u0394${labels} v\u1EDBi (O)`;
|
|
1432
|
+
},
|
|
1433
|
+
render: (obj, ctx, c, opts) => {
|
|
1434
|
+
const board = ctx.jxg;
|
|
1435
|
+
const a = ctx.resolveRef(c.vertices[0]);
|
|
1436
|
+
const b = ctx.resolveRef(c.vertices[1]);
|
|
1437
|
+
const d = ctx.resolveRef(c.vertices[2]);
|
|
1438
|
+
const mp = () => mixtilinearPoint(
|
|
1439
|
+
[a.X(), a.Y()],
|
|
1440
|
+
[b.X(), b.Y()],
|
|
1441
|
+
[d.X(), d.Y()],
|
|
1442
|
+
c.which
|
|
1443
|
+
);
|
|
1444
|
+
return board.create("point", [() => mp()[0], () => mp()[1]], opts);
|
|
1445
|
+
}
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
});
|
|
1449
|
+
|
|
1450
|
+
// src/core/scene/kinds/_label.ts
|
|
1451
|
+
function labelOpts(labelOffset, dflt) {
|
|
1452
|
+
const offset = labelOffset ?? dflt;
|
|
1453
|
+
return { label: { fixed: false, ...offset ? { offset } : {} } };
|
|
1454
|
+
}
|
|
1455
|
+
function readLabelOffset(label) {
|
|
1456
|
+
const off = label.evalVisProp?.("offset") ?? label.visProp?.offset;
|
|
1457
|
+
const rel = label.relativeCoords?.scrCoords;
|
|
1458
|
+
if (!off || !rel || off.length < 2 || rel.length < 3) return null;
|
|
1459
|
+
return [Math.round(off[0] + rel[1]), Math.round(off[1] - rel[2])];
|
|
1460
|
+
}
|
|
1461
|
+
var init_label = __esm({
|
|
1462
|
+
"src/core/scene/kinds/_label.ts"() {
|
|
1463
|
+
}
|
|
1464
|
+
});
|
|
1465
|
+
|
|
1365
1466
|
// src/core/scene/kinds/point-constraints/shared.ts
|
|
1366
1467
|
function buildJxgTransforms(board, ctx, t) {
|
|
1367
1468
|
switch (t.kind) {
|
|
@@ -1413,11 +1514,13 @@ function buildPointOpts(obj) {
|
|
|
1413
1514
|
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
1414
1515
|
fillColor: obj.attrs.color ?? "#1e40af",
|
|
1415
1516
|
face: obj.attrs.face ?? "o",
|
|
1416
|
-
size: obj.attrs.size ?? 4
|
|
1517
|
+
size: obj.attrs.size ?? 4,
|
|
1518
|
+
...labelOpts(obj.attrs.labelOffset, [10, 10])
|
|
1417
1519
|
};
|
|
1418
1520
|
}
|
|
1419
1521
|
var init_shared = __esm({
|
|
1420
1522
|
"src/core/scene/kinds/point-constraints/shared.ts"() {
|
|
1523
|
+
init_label();
|
|
1421
1524
|
}
|
|
1422
1525
|
});
|
|
1423
1526
|
|
|
@@ -1726,6 +1829,7 @@ var init_registry2 = __esm({
|
|
|
1726
1829
|
init_centroid();
|
|
1727
1830
|
init_arcMidpoint();
|
|
1728
1831
|
init_excenter();
|
|
1832
|
+
init_mixtilinearPoint();
|
|
1729
1833
|
init_pointAtDistance();
|
|
1730
1834
|
init_circleIntersection();
|
|
1731
1835
|
init_circleSecondIntersection();
|
|
@@ -1751,6 +1855,7 @@ var init_registry2 = __esm({
|
|
|
1751
1855
|
centroidConstraint,
|
|
1752
1856
|
arcMidpointConstraint,
|
|
1753
1857
|
excenterConstraint,
|
|
1858
|
+
mixtilinearPointConstraint,
|
|
1754
1859
|
pointAtDistanceConstraint,
|
|
1755
1860
|
circleIntersectionConstraint,
|
|
1756
1861
|
circleSecondIntersectionConstraint,
|
|
@@ -1775,6 +1880,7 @@ var init_point = __esm({
|
|
|
1775
1880
|
init_d_constraint2();
|
|
1776
1881
|
init_registry2();
|
|
1777
1882
|
init_shared();
|
|
1883
|
+
init_label();
|
|
1778
1884
|
def3 = {
|
|
1779
1885
|
type: "point",
|
|
1780
1886
|
schemaVersion: 1,
|
|
@@ -1842,7 +1948,8 @@ var init_point = __esm({
|
|
|
1842
1948
|
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
1843
1949
|
fillColor: obj.attrs.color ?? "#1e40af",
|
|
1844
1950
|
face: obj.attrs.face ?? "o",
|
|
1845
|
-
size: obj.attrs.size ?? 4
|
|
1951
|
+
size: obj.attrs.size ?? 4,
|
|
1952
|
+
...labelOpts(obj.attrs.labelOffset, [10, 10])
|
|
1846
1953
|
});
|
|
1847
1954
|
} catch {
|
|
1848
1955
|
}
|
|
@@ -1862,6 +1969,7 @@ var init_segment = __esm({
|
|
|
1862
1969
|
"src/core/scene/kinds/segment.ts"() {
|
|
1863
1970
|
init_registry();
|
|
1864
1971
|
init_labelOf();
|
|
1972
|
+
init_label();
|
|
1865
1973
|
def4 = {
|
|
1866
1974
|
type: "segment",
|
|
1867
1975
|
schemaVersion: 1,
|
|
@@ -1893,7 +2001,8 @@ var init_segment = __esm({
|
|
|
1893
2001
|
strokeWidth: obj.attrs.width ?? 2,
|
|
1894
2002
|
dash: obj.attrs.dash ?? 0,
|
|
1895
2003
|
visible: obj.visible,
|
|
1896
|
-
fixed: obj.locked
|
|
2004
|
+
fixed: obj.locked,
|
|
2005
|
+
...labelOpts(obj.attrs.labelOffset)
|
|
1897
2006
|
});
|
|
1898
2007
|
}
|
|
1899
2008
|
};
|
|
@@ -1930,6 +2039,7 @@ var init_line = __esm({
|
|
|
1930
2039
|
"src/core/scene/kinds/line.ts"() {
|
|
1931
2040
|
init_registry();
|
|
1932
2041
|
init_labelOf();
|
|
2042
|
+
init_label();
|
|
1933
2043
|
init_pointConstructions();
|
|
1934
2044
|
def5 = {
|
|
1935
2045
|
type: "line",
|
|
@@ -1972,7 +2082,8 @@ var init_line = __esm({
|
|
|
1972
2082
|
strokeWidth: obj.attrs.width ?? 2,
|
|
1973
2083
|
dash: obj.attrs.dash ?? 0,
|
|
1974
2084
|
visible: obj.visible,
|
|
1975
|
-
fixed: obj.locked
|
|
2085
|
+
fixed: obj.locked,
|
|
2086
|
+
...labelOpts(obj.attrs.labelOffset)
|
|
1976
2087
|
};
|
|
1977
2088
|
const c = obj.attrs.construction;
|
|
1978
2089
|
if (!c) {
|
|
@@ -2228,6 +2339,7 @@ var init_circle = __esm({
|
|
|
2228
2339
|
"src/core/scene/kinds/circle.ts"() {
|
|
2229
2340
|
init_registry();
|
|
2230
2341
|
init_labelOf();
|
|
2342
|
+
init_label();
|
|
2231
2343
|
init_pointConstructions();
|
|
2232
2344
|
def8 = {
|
|
2233
2345
|
type: "circle",
|
|
@@ -2289,15 +2401,17 @@ var init_circle = __esm({
|
|
|
2289
2401
|
const board = ctx.jxg;
|
|
2290
2402
|
const isCenterLabel = (l) => /^[A-Z]['′]?\d*$/u.test(l);
|
|
2291
2403
|
const isCenter = isCenterLabel(obj.label);
|
|
2404
|
+
const isRenamedCircle = /_c$/.test(obj.label);
|
|
2292
2405
|
const baseOpts = {
|
|
2293
2406
|
name: obj.label,
|
|
2294
|
-
withLabel: isCenter ? obj.attrs.showLabel ?? false : true,
|
|
2407
|
+
withLabel: isCenter ? obj.attrs.showLabel ?? false : isRenamedCircle ? false : true,
|
|
2295
2408
|
strokeColor: obj.attrs.color ?? "#0f172a",
|
|
2296
2409
|
strokeWidth: obj.attrs.width ?? 2,
|
|
2297
2410
|
dash: obj.attrs.dash ?? 0,
|
|
2298
2411
|
fillColor: "none",
|
|
2299
2412
|
visible: obj.visible,
|
|
2300
|
-
fixed: obj.locked
|
|
2413
|
+
fixed: obj.locked,
|
|
2414
|
+
...labelOpts(obj.attrs.labelOffset)
|
|
2301
2415
|
};
|
|
2302
2416
|
const c = asConstruction(obj.attrs);
|
|
2303
2417
|
if (c?.kind === "circumscribed") {
|
|
@@ -2782,8 +2896,11 @@ var init_intersection = __esm({
|
|
|
2782
2896
|
const opts = {
|
|
2783
2897
|
name: obj.label,
|
|
2784
2898
|
withLabel: true,
|
|
2785
|
-
|
|
2786
|
-
|
|
2899
|
+
// Cùng màu xanh với mọi điểm khác (buildPointOpts dùng '#1e40af').
|
|
2900
|
+
// Trước đây điểm giao tô đỏ '#dc2626' → tách biệt thị giác nhưng gây
|
|
2901
|
+
// khó hiểu ("vì sao điểm này khác màu?"). Giữ chung một màu.
|
|
2902
|
+
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
2903
|
+
fillColor: obj.attrs.color ?? "#1e40af",
|
|
2787
2904
|
visible: obj.visible,
|
|
2788
2905
|
fixed: obj.locked
|
|
2789
2906
|
};
|
|
@@ -2792,6 +2909,38 @@ var init_intersection = __esm({
|
|
|
2792
2909
|
}
|
|
2793
2910
|
const branch = obj.attrs.branch ?? 0;
|
|
2794
2911
|
return board.create("intersection", [a, b, branch], opts);
|
|
2912
|
+
},
|
|
2913
|
+
/**
|
|
2914
|
+
* Cập nhật TẠI CHỖ các thuộc tính "trang trí" (tên/màu/ẩn-hiện/khoá) qua
|
|
2915
|
+
* setAttribute — giữ nguyên JxgObj identity nên các object phụ thuộc điểm
|
|
2916
|
+
* giao (đường thẳng qua nó, …) KHÔNG bị stale parent ref. Mô phỏng update
|
|
2917
|
+
* hook của point.ts.
|
|
2918
|
+
*
|
|
2919
|
+
* Nếu định nghĩa hình học đổi (kind/ref1/ref2/branch) thì throw → renderer
|
|
2920
|
+
* fallback remove + create để JSXGraph dựng lại phép giao đúng.
|
|
2921
|
+
*/
|
|
2922
|
+
update: (obj, prev, ctx, existing) => {
|
|
2923
|
+
const a = obj.attrs;
|
|
2924
|
+
const p = prev.attrs;
|
|
2925
|
+
const branchA = a.branch;
|
|
2926
|
+
const branchP = p.branch;
|
|
2927
|
+
if (a.kind !== p.kind || a.ref1 !== p.ref1 || a.ref2 !== p.ref2 || branchA !== branchP) {
|
|
2928
|
+
throw new Error("intersection: \u0111\u1ECBnh ngh\u0129a h\xECnh h\u1ECDc \u0111\u1ED5i \u2014 recreate");
|
|
2929
|
+
}
|
|
2930
|
+
const el = existing;
|
|
2931
|
+
if (typeof el.setAttribute === "function") {
|
|
2932
|
+
try {
|
|
2933
|
+
el.setAttribute({
|
|
2934
|
+
name: obj.label,
|
|
2935
|
+
withLabel: true,
|
|
2936
|
+
strokeColor: a.color ?? "#1e40af",
|
|
2937
|
+
fillColor: a.color ?? "#1e40af",
|
|
2938
|
+
visible: obj.visible,
|
|
2939
|
+
fixed: obj.locked
|
|
2940
|
+
});
|
|
2941
|
+
} catch {
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2795
2944
|
}
|
|
2796
2945
|
};
|
|
2797
2946
|
registerKind(def12);
|
|
@@ -3478,6 +3627,7 @@ var init_JxgRenderer = __esm({
|
|
|
3478
3627
|
init_registry();
|
|
3479
3628
|
init_types2d();
|
|
3480
3629
|
init_parser();
|
|
3630
|
+
init_label();
|
|
3481
3631
|
JxgRenderer = class {
|
|
3482
3632
|
constructor(store, board, options = {}) {
|
|
3483
3633
|
this.elements = /* @__PURE__ */ new Map();
|
|
@@ -3546,6 +3696,7 @@ var init_JxgRenderer = __esm({
|
|
|
3546
3696
|
this.elements.set(obj.id, el);
|
|
3547
3697
|
this.attachFreePointDragSync(obj, el);
|
|
3548
3698
|
this.attachGliderDragSync(obj, el);
|
|
3699
|
+
this.attachLabelDragSync(obj, el);
|
|
3549
3700
|
} catch (err) {
|
|
3550
3701
|
console.warn(`[scene/render/2d] kh\xF4ng render \u0111\u01B0\u1EE3c ${obj.kind} id="${obj.id}":`, err);
|
|
3551
3702
|
}
|
|
@@ -3660,6 +3811,54 @@ var init_JxgRenderer = __esm({
|
|
|
3660
3811
|
});
|
|
3661
3812
|
});
|
|
3662
3813
|
}
|
|
3814
|
+
/**
|
|
3815
|
+
* Cho phép kéo NHÃN của bất kỳ object nào có label (point/line/segment/
|
|
3816
|
+
* circle...). Khi user kéo nhãn, JSXGraph cộng dồn vào label.relativeCoords
|
|
3817
|
+
* (drag-delta screen px) nhưng KHÔNG đổi attribute offset → vị trí cuối =
|
|
3818
|
+
* offset + relativeCoords. Ta gộp lại thành offset thuần (readLabelOffset),
|
|
3819
|
+
* zero relativeCoords để khỏi double-count khi update-hook/recreate áp lại
|
|
3820
|
+
* offset, rồi dispatch UPDATE_ATTRS { labelOffset }. Right-click → reset.
|
|
3821
|
+
*/
|
|
3822
|
+
attachLabelDragSync(obj, el) {
|
|
3823
|
+
const label = el?.label;
|
|
3824
|
+
if (!label || typeof label.on !== "function") return;
|
|
3825
|
+
const sceneId = obj.id;
|
|
3826
|
+
label.on("up", () => {
|
|
3827
|
+
if (this.disposed) return;
|
|
3828
|
+
const off = readLabelOffset(label);
|
|
3829
|
+
if (!off) return;
|
|
3830
|
+
const cur = this.store.getState().objects[sceneId];
|
|
3831
|
+
if (!cur) return;
|
|
3832
|
+
const prev = cur.attrs.labelOffset;
|
|
3833
|
+
if (prev && prev[0] === off[0] && prev[1] === off[1]) return;
|
|
3834
|
+
try {
|
|
3835
|
+
label.setAttribute({ offset: off });
|
|
3836
|
+
if (label.relativeCoords?.scrCoords) {
|
|
3837
|
+
label.relativeCoords.scrCoords[1] = 0;
|
|
3838
|
+
label.relativeCoords.scrCoords[2] = 0;
|
|
3839
|
+
}
|
|
3840
|
+
} catch {
|
|
3841
|
+
}
|
|
3842
|
+
this.store.dispatch({
|
|
3843
|
+
type: "UPDATE_ATTRS",
|
|
3844
|
+
payload: { id: sceneId, patch: { labelOffset: off } }
|
|
3845
|
+
});
|
|
3846
|
+
});
|
|
3847
|
+
const node = label.rendNode;
|
|
3848
|
+
if (node?.addEventListener) {
|
|
3849
|
+
node.addEventListener("contextmenu", (ev) => {
|
|
3850
|
+
if (this.disposed) return;
|
|
3851
|
+
ev.preventDefault();
|
|
3852
|
+
const c = this.store.getState().objects[sceneId];
|
|
3853
|
+
if (!c) return;
|
|
3854
|
+
if (c.attrs.labelOffset === void 0) return;
|
|
3855
|
+
this.store.dispatch({
|
|
3856
|
+
type: "UPDATE_ATTRS",
|
|
3857
|
+
payload: { id: sceneId, patch: { labelOffset: void 0 } }
|
|
3858
|
+
});
|
|
3859
|
+
});
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3663
3862
|
remove(id) {
|
|
3664
3863
|
this.removeHalo(id);
|
|
3665
3864
|
this.selectedIds.delete(id);
|
|
@@ -3796,88 +3995,89 @@ var init_JxgRenderer = __esm({
|
|
|
3796
3995
|
layer: 4,
|
|
3797
3996
|
needsRegularUpdate: true
|
|
3798
3997
|
};
|
|
3998
|
+
const elementClass = el.elementClass;
|
|
3999
|
+
const elType = el.elType;
|
|
4000
|
+
const isPointLike = elementClass === 1 || elType === "point" || elType === "glider" || elType === "intersection";
|
|
4001
|
+
const isPolygonLike = Array.isArray(el.vertices) && el.vertices.length >= 3;
|
|
4002
|
+
const isCircleLike = elementClass === 3 || elType === "circle" || elType === "circumcircle" || elType === "incircle";
|
|
4003
|
+
const isSegment = elType === "segment";
|
|
4004
|
+
const isLineLike = elementClass === 2 || elType === "line" || elType === "arrow" || elType === "ray" || elType === "vector" || elType === "tangent" || elType === "normal" || elType === "parallel" || elType === "perpendicular" || elType === "bisector";
|
|
3799
4005
|
const halos = [];
|
|
3800
4006
|
try {
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
});
|
|
3828
|
-
halos.push(halo);
|
|
3829
|
-
}
|
|
3830
|
-
break;
|
|
3831
|
-
}
|
|
3832
|
-
case "line":
|
|
3833
|
-
case "arrow":
|
|
3834
|
-
case "ray":
|
|
3835
|
-
case "vector":
|
|
3836
|
-
case "tangent":
|
|
3837
|
-
case "normal":
|
|
3838
|
-
case "parallel":
|
|
3839
|
-
case "perpendicular":
|
|
3840
|
-
case "bisector": {
|
|
3841
|
-
if (el.point1 && el.point2) {
|
|
3842
|
-
const halo = board.create("line", [el.point1, el.point2], {
|
|
3843
|
-
...haloBase,
|
|
3844
|
-
strokeWidth: 9
|
|
3845
|
-
});
|
|
3846
|
-
halos.push(halo);
|
|
3847
|
-
}
|
|
3848
|
-
break;
|
|
3849
|
-
}
|
|
3850
|
-
case "circle": {
|
|
3851
|
-
if (el.center && typeof el.Radius === "function") {
|
|
3852
|
-
const halo = board.create("circle", [el.center, () => el.Radius?.() ?? 0], {
|
|
3853
|
-
...haloBase,
|
|
3854
|
-
strokeWidth: 9,
|
|
3855
|
-
fillOpacity: 0
|
|
3856
|
-
});
|
|
3857
|
-
halos.push(halo);
|
|
3858
|
-
}
|
|
3859
|
-
break;
|
|
3860
|
-
}
|
|
3861
|
-
case "polygon": {
|
|
3862
|
-
if (Array.isArray(el.vertices) && el.vertices.length >= 3) {
|
|
3863
|
-
const last = el.vertices.length - 1;
|
|
3864
|
-
const verts = el.vertices[last] === el.vertices[0] ? el.vertices.slice(0, last) : el.vertices.slice();
|
|
3865
|
-
const halo = board.create("polygon", verts, {
|
|
3866
|
-
...haloBase,
|
|
3867
|
-
fillOpacity: 0.2,
|
|
3868
|
-
borders: {
|
|
3869
|
-
strokeColor: SEL_STROKE,
|
|
3870
|
-
strokeWidth: 7,
|
|
3871
|
-
strokeOpacity: 0.55,
|
|
3872
|
-
highlight: false
|
|
3873
|
-
}
|
|
3874
|
-
});
|
|
3875
|
-
halos.push(halo);
|
|
4007
|
+
if (isPointLike) {
|
|
4008
|
+
const baseSize = el.getAttribute?.("size") ?? 4;
|
|
4009
|
+
const halo = board.create("point", [
|
|
4010
|
+
() => el.X?.() ?? 0,
|
|
4011
|
+
() => el.Y?.() ?? 0
|
|
4012
|
+
], {
|
|
4013
|
+
...haloBase,
|
|
4014
|
+
size: baseSize + 6,
|
|
4015
|
+
face: "o",
|
|
4016
|
+
strokeWidth: 2,
|
|
4017
|
+
strokeOpacity: 0.75,
|
|
4018
|
+
fillOpacity: 0.25
|
|
4019
|
+
});
|
|
4020
|
+
halos.push(halo);
|
|
4021
|
+
} else if (isPolygonLike) {
|
|
4022
|
+
const verts = el.vertices;
|
|
4023
|
+
const last = verts.length - 1;
|
|
4024
|
+
const trimmed = verts[last] === verts[0] ? verts.slice(0, last) : verts.slice();
|
|
4025
|
+
const halo = board.create("polygon", trimmed, {
|
|
4026
|
+
...haloBase,
|
|
4027
|
+
fillOpacity: 0.2,
|
|
4028
|
+
borders: {
|
|
4029
|
+
strokeColor: SEL_STROKE,
|
|
4030
|
+
strokeWidth: 7,
|
|
4031
|
+
strokeOpacity: 0.55,
|
|
4032
|
+
highlight: false
|
|
3876
4033
|
}
|
|
3877
|
-
|
|
4034
|
+
});
|
|
4035
|
+
halos.push(halo);
|
|
4036
|
+
} else if (isCircleLike && el.center && typeof el.Radius === "function") {
|
|
4037
|
+
const halo = board.create("circle", [el.center, () => el.Radius?.() ?? 0], {
|
|
4038
|
+
...haloBase,
|
|
4039
|
+
strokeWidth: 9,
|
|
4040
|
+
fillOpacity: 0
|
|
4041
|
+
});
|
|
4042
|
+
halos.push(halo);
|
|
4043
|
+
} else if (isSegment && el.point1 && el.point2) {
|
|
4044
|
+
const halo = board.create("segment", [el.point1, el.point2], {
|
|
4045
|
+
...haloBase,
|
|
4046
|
+
strokeWidth: 9,
|
|
4047
|
+
straightFirst: false,
|
|
4048
|
+
straightLast: false
|
|
4049
|
+
});
|
|
4050
|
+
halos.push(halo);
|
|
4051
|
+
} else if (isLineLike && el.point1 && el.point2) {
|
|
4052
|
+
const halo = board.create("line", [el.point1, el.point2], {
|
|
4053
|
+
...haloBase,
|
|
4054
|
+
strokeWidth: 9
|
|
4055
|
+
});
|
|
4056
|
+
halos.push(halo);
|
|
4057
|
+
} else if (el.center && (el.radiuspoint ?? el.radiusPoint) && (el.anglepoint ?? el.anglePoint) && (elType === "arc" || elType === "semicircle" || elType === "circumcirclearc" || elType === "sector" || elType === "angle")) {
|
|
4058
|
+
const rp = el.radiuspoint ?? el.radiusPoint;
|
|
4059
|
+
const ap = el.anglepoint ?? el.anglePoint;
|
|
4060
|
+
if (elType === "sector") {
|
|
4061
|
+
halos.push(board.create("sector", [el.center, rp, ap], {
|
|
4062
|
+
...haloBase,
|
|
4063
|
+
strokeWidth: 7,
|
|
4064
|
+
fillOpacity: 0.2
|
|
4065
|
+
}));
|
|
4066
|
+
} else if (elType === "angle") {
|
|
4067
|
+
halos.push(board.create("angle", [rp, el.center, ap], {
|
|
4068
|
+
...haloBase,
|
|
4069
|
+
strokeWidth: 7,
|
|
4070
|
+
fillOpacity: 0.2,
|
|
4071
|
+
radius: el.getAttribute?.("radius") ?? 1
|
|
4072
|
+
}));
|
|
4073
|
+
} else {
|
|
4074
|
+
halos.push(board.create("arc", [el.center, rp, ap], {
|
|
4075
|
+
...haloBase,
|
|
4076
|
+
strokeWidth: 9,
|
|
4077
|
+
fillColor: "none",
|
|
4078
|
+
fillOpacity: 0
|
|
4079
|
+
}));
|
|
3878
4080
|
}
|
|
3879
|
-
default:
|
|
3880
|
-
break;
|
|
3881
4081
|
}
|
|
3882
4082
|
} catch (err) {
|
|
3883
4083
|
console.warn("[scene/render/2d] halo create fail:", err);
|
|
@@ -4037,6 +4237,49 @@ var init_autoFitBbox = __esm({
|
|
|
4037
4237
|
}
|
|
4038
4238
|
});
|
|
4039
4239
|
|
|
4240
|
+
// src/stamps/geometry-2d/autoFitBoard.ts
|
|
4241
|
+
function isDefaultBbox(bbox) {
|
|
4242
|
+
const d = DEFAULT_VIEW_2D.bbox;
|
|
4243
|
+
return bbox.length === 4 && bbox[0] === d[0] && bbox[1] === d[1] && bbox[2] === d[2] && bbox[3] === d[3];
|
|
4244
|
+
}
|
|
4245
|
+
function fittedBboxFromBoard(board, aspect) {
|
|
4246
|
+
const objs = board?.objectsList;
|
|
4247
|
+
if (!Array.isArray(objs)) return null;
|
|
4248
|
+
const points = [];
|
|
4249
|
+
const circles = [];
|
|
4250
|
+
for (const raw of objs) {
|
|
4251
|
+
const o = raw;
|
|
4252
|
+
if (o?.elementClass === 1 && typeof o.X === "function" && typeof o.Y === "function") {
|
|
4253
|
+
const x = o.X(), y = o.Y();
|
|
4254
|
+
if (Number.isFinite(x) && Number.isFinite(y)) points.push([x, y]);
|
|
4255
|
+
} else if (o?.elementClass === 3 && o.center?.X && typeof o.Radius === "function") {
|
|
4256
|
+
const cx = o.center.X(), cy = o.center.Y(), r = o.Radius();
|
|
4257
|
+
if (Number.isFinite(cx) && Number.isFinite(cy) && Number.isFinite(r)) {
|
|
4258
|
+
circles.push({ cx, cy, r });
|
|
4259
|
+
}
|
|
4260
|
+
}
|
|
4261
|
+
}
|
|
4262
|
+
const safeAspect = Number.isFinite(aspect) && aspect > 0 ? aspect : 1;
|
|
4263
|
+
return computeAutoFitBbox(points, circles, safeAspect);
|
|
4264
|
+
}
|
|
4265
|
+
function autoFitBoardToContent(board, aspect) {
|
|
4266
|
+
const bbox = fittedBboxFromBoard(board, aspect);
|
|
4267
|
+
if (!bbox) return;
|
|
4268
|
+
const b = board;
|
|
4269
|
+
try {
|
|
4270
|
+
b.setBoundingBox?.(bbox);
|
|
4271
|
+
if (typeof b.update === "function") b.update();
|
|
4272
|
+
if (typeof b.fullUpdate === "function") b.fullUpdate();
|
|
4273
|
+
} catch {
|
|
4274
|
+
}
|
|
4275
|
+
}
|
|
4276
|
+
var init_autoFitBoard = __esm({
|
|
4277
|
+
"src/stamps/geometry-2d/autoFitBoard.ts"() {
|
|
4278
|
+
init_types();
|
|
4279
|
+
init_autoFitBbox();
|
|
4280
|
+
}
|
|
4281
|
+
});
|
|
4282
|
+
|
|
4040
4283
|
// src/stamps/geometry-2d/labelLayout.ts
|
|
4041
4284
|
function radialLabelOffsets(points, R = 14) {
|
|
4042
4285
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -4071,8 +4314,9 @@ function containerDimsForBbox(bbox) {
|
|
|
4071
4314
|
if (!Number.isFinite(w) || !Number.isFinite(h) || w <= 0 || h <= 0) {
|
|
4072
4315
|
return { width: FALLBACK_W, height: FALLBACK_H };
|
|
4073
4316
|
}
|
|
4074
|
-
|
|
4075
|
-
let
|
|
4317
|
+
const pxPerUnit = DEFAULT_VIEW_PX / Math.max(w, h);
|
|
4318
|
+
let width = w * pxPerUnit;
|
|
4319
|
+
let height = h * pxPerUnit;
|
|
4076
4320
|
const maxAxis = Math.max(width, height);
|
|
4077
4321
|
if (maxAxis > MAX_DIM) {
|
|
4078
4322
|
const ratio = MAX_DIM / maxAxis;
|
|
@@ -4087,31 +4331,6 @@ function containerDimsForBbox(bbox) {
|
|
|
4087
4331
|
}
|
|
4088
4332
|
return { width: Math.round(width), height: Math.round(height) };
|
|
4089
4333
|
}
|
|
4090
|
-
function autoFitBboxFromBoard(board, aspect) {
|
|
4091
|
-
const objs = board?.objectsList;
|
|
4092
|
-
if (!Array.isArray(objs)) return;
|
|
4093
|
-
const points = [];
|
|
4094
|
-
const circles = [];
|
|
4095
|
-
for (const o of objs) {
|
|
4096
|
-
if (o?.elementClass === 1 && typeof o.X === "function" && typeof o.Y === "function") {
|
|
4097
|
-
const x = o.X(), y = o.Y();
|
|
4098
|
-
if (Number.isFinite(x) && Number.isFinite(y)) points.push([x, y]);
|
|
4099
|
-
} else if (o?.elementClass === 3 && o.center?.X && typeof o.Radius === "function") {
|
|
4100
|
-
const cx = o.center.X(), cy = o.center.Y(), r = o.Radius();
|
|
4101
|
-
if (Number.isFinite(cx) && Number.isFinite(cy) && Number.isFinite(r)) {
|
|
4102
|
-
circles.push({ cx, cy, r });
|
|
4103
|
-
}
|
|
4104
|
-
}
|
|
4105
|
-
}
|
|
4106
|
-
const bbox = computeAutoFitBbox(points, circles, aspect);
|
|
4107
|
-
if (!bbox) return;
|
|
4108
|
-
try {
|
|
4109
|
-
board.setBoundingBox(bbox);
|
|
4110
|
-
if (typeof board.update === "function") board.update();
|
|
4111
|
-
if (typeof board.fullUpdate === "function") board.fullUpdate();
|
|
4112
|
-
} catch {
|
|
4113
|
-
}
|
|
4114
|
-
}
|
|
4115
4334
|
function applyRadialLabelOffsets(board) {
|
|
4116
4335
|
const objs = board?.objectsList;
|
|
4117
4336
|
if (!Array.isArray(objs)) return;
|
|
@@ -4137,10 +4356,6 @@ function applyRadialLabelOffsets(board) {
|
|
|
4137
4356
|
} catch {
|
|
4138
4357
|
}
|
|
4139
4358
|
}
|
|
4140
|
-
function isDefaultBbox(bbox) {
|
|
4141
|
-
const d = DEFAULT_VIEW_2D.bbox;
|
|
4142
|
-
return bbox.length === 4 && bbox[0] === d[0] && bbox[1] === d[1] && bbox[2] === d[2] && bbox[3] === d[3];
|
|
4143
|
-
}
|
|
4144
4359
|
async function renderGeometrySvgFromState(jsonState) {
|
|
4145
4360
|
const state = deserializeBoard(jsonState);
|
|
4146
4361
|
const view = state.meta.domain === "2d" ? state.meta.view : DEFAULT_VIEW_2D;
|
|
@@ -4175,7 +4390,7 @@ async function renderGeometrySvgFromState(jsonState) {
|
|
|
4175
4390
|
const store = createStore(state);
|
|
4176
4391
|
const renderer = new JxgRenderer(store, board);
|
|
4177
4392
|
if (shouldAutoFit) {
|
|
4178
|
-
|
|
4393
|
+
autoFitBoardToContent(board, dims.width / dims.height);
|
|
4179
4394
|
applyRadialLabelOffsets(board);
|
|
4180
4395
|
}
|
|
4181
4396
|
return renderer;
|
|
@@ -4183,7 +4398,7 @@ async function renderGeometrySvgFromState(jsonState) {
|
|
|
4183
4398
|
});
|
|
4184
4399
|
return svgString;
|
|
4185
4400
|
}
|
|
4186
|
-
var
|
|
4401
|
+
var DEFAULT_VIEW_PX, MIN_DIM, MAX_DIM, FALLBACK_W, FALLBACK_H;
|
|
4187
4402
|
var init_render = __esm({
|
|
4188
4403
|
"src/stamps/geometry-2d/render.ts"() {
|
|
4189
4404
|
init_serialize();
|
|
@@ -4192,9 +4407,9 @@ var init_render = __esm({
|
|
|
4192
4407
|
init_types();
|
|
4193
4408
|
init_JxgRenderer();
|
|
4194
4409
|
init_jxgOffscreenRender();
|
|
4195
|
-
|
|
4410
|
+
init_autoFitBoard();
|
|
4196
4411
|
init_labelLayout();
|
|
4197
|
-
|
|
4412
|
+
DEFAULT_VIEW_PX = 500;
|
|
4198
4413
|
MIN_DIM = 100;
|
|
4199
4414
|
MAX_DIM = 1200;
|
|
4200
4415
|
FALLBACK_W = 400;
|
|
@@ -5754,41 +5969,48 @@ var init_icons = __esm({
|
|
|
5754
5969
|
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "4", cy: "4", r: "1.7", fill: C_POINT })
|
|
5755
5970
|
] }),
|
|
5756
5971
|
// ===== Nâng cao / kind chưa có icon =====
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
5972
|
+
// Hệ màu đồng bộ: XANH = input click sẵn, ĐỎ = output suy ra. Toạ độ tính để
|
|
5973
|
+
// điểm nằm ĐÚNG trên đường tròn/cung/tiếp tuyến (không trôi nổi).
|
|
5974
|
+
excenter: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
5975
|
+
/* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "12,3 15,15 9,15", stroke: "currentColor", strokeWidth: "1.4", fill: "none" }),
|
|
5976
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "15", x2: "8", y2: "18.9", stroke: "#94a3b8", strokeWidth: "1", strokeDasharray: "1.6 1.6" }),
|
|
5977
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "15", y1: "15", x2: "16", y2: "18.9", stroke: "#94a3b8", strokeWidth: "1", strokeDasharray: "1.6 1.6" }),
|
|
5978
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "18.9", r: "3.8", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
5979
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "3", r: "1.6", fill: C_POINT }),
|
|
5980
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "15", r: "1.6", fill: C_POINT }),
|
|
5981
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "15", cy: "15", r: "1.6", fill: C_POINT }),
|
|
5982
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "18.9", r: "2.2", fill: C_CONSTRUCT })
|
|
5761
5983
|
] }),
|
|
5762
|
-
tangencyPoint: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
5763
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "
|
|
5764
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "16.5", y1: "3", x2: "16.5", y2: "
|
|
5765
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "16.5", cy: "12", r: "2.
|
|
5984
|
+
tangencyPoint: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", strokeLinecap: "round", children: [
|
|
5985
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9.5", cy: "12", r: "7", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
5986
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "16.5", y1: "3.5", x2: "16.5", y2: "20.5", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
5987
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "16.5", cy: "12", r: "2.3", fill: C_CONSTRUCT })
|
|
5766
5988
|
] }),
|
|
5767
|
-
secondIntersection: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
5768
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "12", r: "6.5", stroke: "currentColor", strokeWidth: "1.
|
|
5769
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "2", y1: "8", x2: "
|
|
5770
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "
|
|
5771
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "
|
|
5989
|
+
secondIntersection: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", strokeLinecap: "round", children: [
|
|
5990
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "12", r: "6.5", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
5991
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "2.4", y1: "8.2", x2: "20.4", y2: "11.4", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
5992
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "5.37", cy: "8.75", r: "1.8", fill: C_POINT }),
|
|
5993
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "17.4", cy: "10.87", r: "2.4", fill: C_CONSTRUCT })
|
|
5772
5994
|
] }),
|
|
5773
5995
|
arcMidpoint: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", strokeLinecap: "round", children: [
|
|
5774
|
-
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 17 A
|
|
5775
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "4", cy: "17", r: "1.
|
|
5776
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "20", cy: "17", r: "1.
|
|
5777
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "
|
|
5996
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 17 A 8.5 8.5 0 0 1 20 17", stroke: "currentColor", strokeWidth: "1.6", fill: "none" }),
|
|
5997
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "4", cy: "17", r: "1.8", fill: C_POINT }),
|
|
5998
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "20", cy: "17", r: "1.8", fill: C_POINT }),
|
|
5999
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "11.4", r: "2.3", fill: C_CONSTRUCT })
|
|
5778
6000
|
] }),
|
|
5779
6001
|
circleIntersection: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
5780
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "12", r: "6", stroke: "currentColor", strokeWidth: "1.
|
|
5781
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "15", cy: "12", r: "6", stroke: "currentColor", strokeWidth: "1.
|
|
5782
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "
|
|
5783
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "
|
|
6002
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "12", r: "6", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
6003
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "15", cy: "12", r: "6", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
6004
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "6.8", r: "2", fill: C_CONSTRUCT }),
|
|
6005
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "17.2", r: "2", fill: C_CONSTRUCT })
|
|
5784
6006
|
] }),
|
|
5785
6007
|
tangentPointExt: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", strokeLinecap: "round", children: [
|
|
5786
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "
|
|
5787
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
5788
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3
|
|
5789
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
5790
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "
|
|
5791
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "
|
|
6008
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "13", cy: "12", r: "6", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
6009
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "12", x2: "11.8", y2: "18.6", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
6010
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "12", x2: "11.8", y2: "5.4", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
6011
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "3", cy: "12", r: "2", fill: C_POINT }),
|
|
6012
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9.4", cy: "16.8", r: "1.8", fill: C_CONSTRUCT }),
|
|
6013
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9.4", cy: "7.2", r: "1.8", fill: C_CONSTRUCT })
|
|
5792
6014
|
] }),
|
|
5793
6015
|
circleCR: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
5794
6016
|
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "7.5", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
@@ -5799,9 +6021,11 @@ var init_icons = __esm({
|
|
|
5799
6021
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 19 L12 4 L21 19 Z", stroke: "currentColor", strokeWidth: "1.3", strokeLinejoin: "round" }),
|
|
5800
6022
|
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "14", r: "4.2", fill: "none", stroke: C_CONSTRUCT, strokeWidth: "1.3" })
|
|
5801
6023
|
] }),
|
|
5802
|
-
excircle: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
5803
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
5804
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
6024
|
+
excircle: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
6025
|
+
/* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "12,3 15,15 9,15", stroke: "currentColor", strokeWidth: "1.4", fill: "none" }),
|
|
6026
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "15", x2: "8", y2: "18.9", stroke: "#94a3b8", strokeWidth: "1", strokeDasharray: "1.6 1.6" }),
|
|
6027
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "15", y1: "15", x2: "16", y2: "18.9", stroke: "#94a3b8", strokeWidth: "1", strokeDasharray: "1.6 1.6" }),
|
|
6028
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "18.9", r: "3.8", stroke: C_CONSTRUCT, strokeWidth: "1.5", fill: C_CONSTRUCT, fillOpacity: "0.12" })
|
|
5805
6029
|
] }),
|
|
5806
6030
|
pointOn: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
5807
6031
|
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "7.5", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
@@ -7811,11 +8035,11 @@ function buildObjectSnapshot(state, id, anchorScreen) {
|
|
|
7811
8035
|
const obj = state.objects[id];
|
|
7812
8036
|
if (!obj) return null;
|
|
7813
8037
|
const k = obj.kind;
|
|
7814
|
-
if (k !== "point" && k !== "line" && k !== "circle" && k !== "segment" && k !== "ray" && k !== "vector") {
|
|
8038
|
+
if (k !== "point" && k !== "line" && k !== "circle" && k !== "segment" && k !== "ray" && k !== "vector" && k !== "intersection") {
|
|
7815
8039
|
return null;
|
|
7816
8040
|
}
|
|
7817
8041
|
const a = obj.attrs;
|
|
7818
|
-
const jKind = k === "point" ? "point" : k === "circle" ? "circle" : "line";
|
|
8042
|
+
const jKind = k === "point" || k === "intersection" ? "point" : k === "circle" ? "circle" : "line";
|
|
7819
8043
|
return {
|
|
7820
8044
|
id,
|
|
7821
8045
|
kind: jKind,
|
|
@@ -8223,6 +8447,7 @@ var init_MiniBoard = __esm({
|
|
|
8223
8447
|
init_safeJsx();
|
|
8224
8448
|
init_attachJxgWheelZoom();
|
|
8225
8449
|
init_initJxgBoard();
|
|
8450
|
+
init_autoFitBoard();
|
|
8226
8451
|
init_snapshot();
|
|
8227
8452
|
init_previewActions();
|
|
8228
8453
|
init_buildHandle();
|
|
@@ -8462,6 +8687,11 @@ var init_MiniBoard = __esm({
|
|
|
8462
8687
|
rendererRef.current = new JxgRenderer(store, board, {
|
|
8463
8688
|
theme: paletteFor(isDarkRef.current)
|
|
8464
8689
|
});
|
|
8690
|
+
if (isDefaultBbox(initialView.bbox)) {
|
|
8691
|
+
const el = containerRef.current;
|
|
8692
|
+
const aspect = el && el.clientHeight > 0 ? el.clientWidth / el.clientHeight : 1;
|
|
8693
|
+
safeJsx("MiniBoard.autoFit", () => autoFitBoardToContent(board, aspect));
|
|
8694
|
+
}
|
|
8465
8695
|
if (containerRef.current) {
|
|
8466
8696
|
wheelCleanup = attachJxgWheelZoom(containerRef.current, board, "MiniBoard.2d");
|
|
8467
8697
|
}
|
|
@@ -9259,6 +9489,7 @@ function useAiFigure(generator) {
|
|
|
9259
9489
|
const [prompt, setPrompt] = React2.useState("");
|
|
9260
9490
|
const [isLoading, setIsLoading] = React2.useState(false);
|
|
9261
9491
|
const [error, setError] = React2.useState(null);
|
|
9492
|
+
const [notice, setNotice] = React2.useState(null);
|
|
9262
9493
|
const [tokens, setTokens] = React2.useState(0);
|
|
9263
9494
|
const abortRef = React2.useRef(null);
|
|
9264
9495
|
const requestIdRef = React2.useRef(0);
|
|
@@ -9279,6 +9510,7 @@ function useAiFigure(generator) {
|
|
|
9279
9510
|
abortRef.current = controller;
|
|
9280
9511
|
setIsLoading(true);
|
|
9281
9512
|
setError(null);
|
|
9513
|
+
setNotice(null);
|
|
9282
9514
|
setTokens(0);
|
|
9283
9515
|
try {
|
|
9284
9516
|
const generated = await generator(problem, {
|
|
@@ -9292,6 +9524,7 @@ function useAiFigure(generator) {
|
|
|
9292
9524
|
setError(generated.message);
|
|
9293
9525
|
return null;
|
|
9294
9526
|
}
|
|
9527
|
+
if (generated.partial) setNotice(generated.partial.message);
|
|
9295
9528
|
return generated.state;
|
|
9296
9529
|
} catch (caught) {
|
|
9297
9530
|
if (controller.signal.aborted || caught instanceof DOMException && caught.name === "AbortError") {
|
|
@@ -9318,6 +9551,7 @@ function useAiFigure(generator) {
|
|
|
9318
9551
|
setPrompt,
|
|
9319
9552
|
isLoading,
|
|
9320
9553
|
error,
|
|
9554
|
+
notice,
|
|
9321
9555
|
submit,
|
|
9322
9556
|
cancel,
|
|
9323
9557
|
tokens
|
|
@@ -9528,6 +9762,7 @@ function AiFigurePrompt({ generator, onGenerated }) {
|
|
|
9528
9762
|
setPrompt,
|
|
9529
9763
|
isLoading,
|
|
9530
9764
|
error,
|
|
9765
|
+
notice,
|
|
9531
9766
|
submit,
|
|
9532
9767
|
cancel,
|
|
9533
9768
|
tokens
|
|
@@ -9541,6 +9776,10 @@ function AiFigurePrompt({ generator, onGenerated }) {
|
|
|
9541
9776
|
const id = setInterval(() => setElapsed((s) => s + 1), 1e3);
|
|
9542
9777
|
return () => clearInterval(id);
|
|
9543
9778
|
}, [isLoading]);
|
|
9779
|
+
const [noticeDismissed, setNoticeDismissed] = React2.useState(false);
|
|
9780
|
+
React2.useEffect(() => {
|
|
9781
|
+
setNoticeDismissed(false);
|
|
9782
|
+
}, [notice]);
|
|
9544
9783
|
const [image, setImage] = React2.useState(null);
|
|
9545
9784
|
const [ocrLoading, setOcrLoading] = React2.useState(false);
|
|
9546
9785
|
const [ocrError, setOcrError] = React2.useState(null);
|
|
@@ -9756,7 +9995,30 @@ function AiFigurePrompt({ generator, onGenerated }) {
|
|
|
9756
9995
|
}
|
|
9757
9996
|
),
|
|
9758
9997
|
ocrError && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "mt-1 px-1 text-xs text-red-600", children: ocrError }),
|
|
9759
|
-
error && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "mt-1 px-1 text-xs text-red-600", children: error })
|
|
9998
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "mt-1 px-1 text-xs text-red-600", children: error }),
|
|
9999
|
+
notice && !noticeDismissed && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10000
|
+
"div",
|
|
10001
|
+
{
|
|
10002
|
+
role: "status",
|
|
10003
|
+
"data-testid": "geometry-ai-partial-notice",
|
|
10004
|
+
className: "relative mt-2 whitespace-pre-wrap rounded-lg border border-amber-200 bg-amber-50 py-2 pl-3 pr-8 text-xs leading-relaxed text-amber-800",
|
|
10005
|
+
children: [
|
|
10006
|
+
notice,
|
|
10007
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10008
|
+
"button",
|
|
10009
|
+
{
|
|
10010
|
+
type: "button",
|
|
10011
|
+
onClick: () => setNoticeDismissed(true),
|
|
10012
|
+
"aria-label": "\u0110\xF3ng th\xF4ng b\xE1o",
|
|
10013
|
+
title: "\u0110\xF3ng th\xF4ng b\xE1o",
|
|
10014
|
+
"data-testid": "geometry-ai-partial-dismiss",
|
|
10015
|
+
className: "absolute right-1 top-1 flex h-5 w-5 items-center justify-center rounded text-amber-500 transition hover:bg-amber-100 hover:text-amber-800",
|
|
10016
|
+
children: "\xD7"
|
|
10017
|
+
}
|
|
10018
|
+
)
|
|
10019
|
+
]
|
|
10020
|
+
}
|
|
10021
|
+
)
|
|
9760
10022
|
] });
|
|
9761
10023
|
}
|
|
9762
10024
|
var ArrowUpIcon, StopIcon, PaperclipIcon;
|
|
@@ -10381,11 +10643,17 @@ async function insertStampImage(api, opts) {
|
|
|
10381
10643
|
const elements = api.getSceneElements();
|
|
10382
10644
|
const editingId = opts.editingElementId ?? null;
|
|
10383
10645
|
if (editingId) {
|
|
10646
|
+
const old = elements.find((e) => e.id === editingId) ;
|
|
10647
|
+
const oldLongest = old ? Math.max(old.width ?? 0, old.height ?? 0) : 0;
|
|
10648
|
+
const newLongest = Math.max(width, height);
|
|
10649
|
+
const scale = oldLongest > 0 && newLongest > 0 ? oldLongest / newLongest : 1;
|
|
10650
|
+
const w = width * scale;
|
|
10651
|
+
const h = height * scale;
|
|
10384
10652
|
const updated = elements.map(
|
|
10385
|
-
(e) => e.id === editingId ? { ...e, fileId, customData, width, height } : e
|
|
10653
|
+
(e) => e.id === editingId ? { ...e, fileId, customData, width: w, height: h } : e
|
|
10386
10654
|
);
|
|
10387
10655
|
api.updateScene({ elements: updated, appState: clearAppStateAfterInsert() });
|
|
10388
|
-
return { fileId, width, height, elementId: editingId };
|
|
10656
|
+
return { fileId, width: w, height: h, elementId: editingId };
|
|
10389
10657
|
}
|
|
10390
10658
|
const newElement = buildStampImageElement(
|
|
10391
10659
|
api,
|
|
@@ -10539,8 +10807,11 @@ function serializePoint(obj, state) {
|
|
|
10539
10807
|
};
|
|
10540
10808
|
}
|
|
10541
10809
|
case "arcMidpoint": {
|
|
10542
|
-
const
|
|
10543
|
-
|
|
10810
|
+
const containment = c.containing ?? c.notContaining;
|
|
10811
|
+
const baseRefs = [c.circle, c.a, c.b];
|
|
10812
|
+
if (containment) baseRefs.push(containment);
|
|
10813
|
+
const refs = resolveRefs(baseRefs, state);
|
|
10814
|
+
if (!refs) return fail("unresolved-ref", baseRefs.join(","));
|
|
10544
10815
|
return {
|
|
10545
10816
|
ok: true,
|
|
10546
10817
|
entity: {
|
|
@@ -10549,7 +10820,7 @@ function serializePoint(obj, state) {
|
|
|
10549
10820
|
circle: refs[0],
|
|
10550
10821
|
a: refs[1],
|
|
10551
10822
|
b: refs[2],
|
|
10552
|
-
notContaining: refs[3]
|
|
10823
|
+
...c.containing ? { containing: refs[3] } : c.notContaining ? { notContaining: refs[3] } : {}
|
|
10553
10824
|
}
|
|
10554
10825
|
};
|
|
10555
10826
|
}
|
|
@@ -10593,6 +10864,7 @@ function serializePoint(obj, state) {
|
|
|
10593
10864
|
case "onPerpendicular":
|
|
10594
10865
|
case "onPerpBisector":
|
|
10595
10866
|
case "onCircleAroundPoint":
|
|
10867
|
+
case "mixtilinearPoint":
|
|
10596
10868
|
return fail("unsupported-constraint", c.kind);
|
|
10597
10869
|
default: {
|
|
10598
10870
|
return fail("unsupported-constraint");
|
|
@@ -10895,9 +11167,11 @@ function describeEntity(e) {
|
|
|
10895
11167
|
return `${e.name} = \u0111\u01B0\u1EDDng tr\xF2n b\xE0ng ti\u1EBFp ${e.vertices.join("")} \u0111\u1ED1i di\u1EC7n ${e.opposite}`;
|
|
10896
11168
|
// Cụm A
|
|
10897
11169
|
case "arcMidpoint":
|
|
10898
|
-
return `${e.name} = trung \u0111i\u1EC3m cung ${e.a}${e.b} (kh\xF4ng ch\u1EE9a ${e.notContaining}) tr\xEAn ${e.circle}`;
|
|
11170
|
+
return `${e.name} = trung \u0111i\u1EC3m cung ${e.a}${e.b}${e.containing || e.notContaining ? ` (${e.containing ? "ch\u1EE9a" : "kh\xF4ng ch\u1EE9a"} ${e.containing ?? e.notContaining})` : ""} tr\xEAn ${e.circle}`;
|
|
10899
11171
|
case "excenter":
|
|
10900
11172
|
return `${e.name} = t\xE2m b\xE0ng ti\u1EBFp ${e.vertices.join("")} \u0111\u1ED1i di\u1EC7n ${e.opposite}`;
|
|
11173
|
+
case "mixtilinearPoint":
|
|
11174
|
+
return `${e.name} = ${e.which === "center" ? "t\xE2m" : "ti\u1EBFp \u0111i\u1EC3m"} mixtilinear ${e.vertices.join("")}`;
|
|
10901
11175
|
case "reflectPoint":
|
|
10902
11176
|
return `${e.name} = \u0111\u1ED1i x\u1EE9ng ${e.of} qua \u0111i\u1EC3m ${e.through}`;
|
|
10903
11177
|
case "reflectLine":
|
|
@@ -10908,6 +11182,8 @@ function describeEntity(e) {
|
|
|
10908
11182
|
const distStr = d.kind === "circleRadius" ? `r(${d.circle})` : d.kind === "segmentLength" ? `|${d.p1}${d.p2}|` : `${d.value}`;
|
|
10909
11183
|
return `${e.name} = \u0111i\u1EC3m tr\xEAn tia ${e.from}${e.through} c\xE1ch ${e.through} m\u1ED9t kho\u1EA3ng ${distStr}`;
|
|
10910
11184
|
}
|
|
11185
|
+
case "onPerpBisector":
|
|
11186
|
+
return `${e.name} = \u0111i\u1EC3m tr\xEAn trung tr\u1EF1c ${e.p1}${e.p2}`;
|
|
10911
11187
|
default: {
|
|
10912
11188
|
return "";
|
|
10913
11189
|
}
|
|
@@ -11030,7 +11306,8 @@ var init_host = __esm({
|
|
|
11030
11306
|
version: 1,
|
|
11031
11307
|
jsonState
|
|
11032
11308
|
}),
|
|
11033
|
-
editingElementId: editingElement?.id ?? null
|
|
11309
|
+
editingElementId: editingElement?.id ?? null,
|
|
11310
|
+
preserveExistingSize: true
|
|
11034
11311
|
});
|
|
11035
11312
|
} catch (err) {
|
|
11036
11313
|
console.error("Geometry insert failed:", err);
|