@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/index.js
CHANGED
|
@@ -942,8 +942,12 @@ function constraintRefs2D(c) {
|
|
|
942
942
|
return [c.line, c.circle, c.other];
|
|
943
943
|
case "tangencyPoint":
|
|
944
944
|
return [c.circle, c.onLine];
|
|
945
|
-
case "arcMidpoint":
|
|
946
|
-
|
|
945
|
+
case "arcMidpoint": {
|
|
946
|
+
const containment = c.notContaining ?? c.containing;
|
|
947
|
+
return containment ? [c.circle, c.a, c.b, containment] : [c.circle, c.a, c.b];
|
|
948
|
+
}
|
|
949
|
+
case "mixtilinearPoint":
|
|
950
|
+
return [c.vertices[0], c.vertices[1], c.vertices[2]];
|
|
947
951
|
case "pointAtDistance": {
|
|
948
952
|
const d = c.distance;
|
|
949
953
|
const extra = d.kind === "circleRadius" ? [d.circle] : d.kind === "segmentLength" ? [d.p1, d.p2] : [];
|
|
@@ -1230,7 +1234,7 @@ function dist(p, q) {
|
|
|
1230
1234
|
function sideOf(a, b, p) {
|
|
1231
1235
|
return (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]);
|
|
1232
1236
|
}
|
|
1233
|
-
function arcMidpoint(center, radius, a, b,
|
|
1237
|
+
function arcMidpoint(center, radius, a, b, reference, sameSide = false) {
|
|
1234
1238
|
const mcx = (a[0] + b[0]) / 2;
|
|
1235
1239
|
const mcy = (a[1] + b[1]) / 2;
|
|
1236
1240
|
let ux = mcx - center[0];
|
|
@@ -1245,12 +1249,18 @@ function arcMidpoint(center, radius, a, b, notContaining) {
|
|
|
1245
1249
|
uy /= len;
|
|
1246
1250
|
const cand1 = [center[0] + radius * ux, center[1] + radius * uy];
|
|
1247
1251
|
const cand2 = [center[0] - radius * ux, center[1] - radius * uy];
|
|
1252
|
+
if (!reference) return cand1[1] >= cand2[1] ? cand1 : cand2;
|
|
1253
|
+
const notContaining = reference;
|
|
1248
1254
|
const sN = sideOf(a, b, notContaining);
|
|
1249
1255
|
if (Math.abs(sN) < 1e-9) {
|
|
1250
|
-
|
|
1256
|
+
const far = dist(cand1, notContaining) >= dist(cand2, notContaining) ? cand1 : cand2;
|
|
1257
|
+
const near = far === cand1 ? cand2 : cand1;
|
|
1258
|
+
return sameSide ? near : far;
|
|
1251
1259
|
}
|
|
1252
1260
|
const s1 = sideOf(a, b, cand1);
|
|
1253
|
-
|
|
1261
|
+
const opp = s1 * sN < 0 ? cand1 : cand2;
|
|
1262
|
+
const same = opp === cand1 ? cand2 : cand1;
|
|
1263
|
+
return sameSide ? same : opp;
|
|
1254
1264
|
}
|
|
1255
1265
|
function excenter(vertices, oppositeIndex) {
|
|
1256
1266
|
const [A, B, C] = vertices;
|
|
@@ -1266,6 +1276,35 @@ function excenter(vertices, oppositeIndex) {
|
|
|
1266
1276
|
(w[0] * A[1] + w[1] * B[1] + w[2] * C[1]) / sum
|
|
1267
1277
|
];
|
|
1268
1278
|
}
|
|
1279
|
+
function circumcenterXY(a, b, c) {
|
|
1280
|
+
const ax = a[0], ay = a[1], bx = b[0], by = b[1], cx = c[0], cy = c[1];
|
|
1281
|
+
const d = 2 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
|
|
1282
|
+
if (Math.abs(d) < 1e-12) return [(ax + bx + cx) / 3, (ay + by + cy) / 3];
|
|
1283
|
+
const ux = ((ax * ax + ay * ay) * (by - cy) + (bx * bx + by * by) * (cy - ay) + (cx * cx + cy * cy) * (ay - by)) / d;
|
|
1284
|
+
const uy = ((ax * ax + ay * ay) * (cx - bx) + (bx * bx + by * by) * (ax - cx) + (cx * cx + cy * cy) * (bx - ax)) / d;
|
|
1285
|
+
return [ux, uy];
|
|
1286
|
+
}
|
|
1287
|
+
function mixtilinearPoint(a, b, c, which) {
|
|
1288
|
+
const O = circumcenterXY(a, b, c);
|
|
1289
|
+
const R = Math.hypot(O[0] - a[0], O[1] - a[1]);
|
|
1290
|
+
const ux1 = (b[0] - a[0]) / (Math.hypot(b[0] - a[0], b[1] - a[1]) || 1);
|
|
1291
|
+
const uy1 = (b[1] - a[1]) / (Math.hypot(b[0] - a[0], b[1] - a[1]) || 1);
|
|
1292
|
+
const ux2 = (c[0] - a[0]) / (Math.hypot(c[0] - a[0], c[1] - a[1]) || 1);
|
|
1293
|
+
const uy2 = (c[1] - a[1]) / (Math.hypot(c[0] - a[0], c[1] - a[1]) || 1);
|
|
1294
|
+
let bx = ux1 + ux2, by = uy1 + uy2;
|
|
1295
|
+
const bl = Math.hypot(bx, by) || 1;
|
|
1296
|
+
bx /= bl;
|
|
1297
|
+
by /= bl;
|
|
1298
|
+
const cosA = ux1 * ux2 + uy1 * uy2;
|
|
1299
|
+
const sinHalf = Math.sqrt(Math.max(0, (1 - cosA) / 2));
|
|
1300
|
+
const cos2Half = Math.max(1e-9, (1 + cosA) / 2);
|
|
1301
|
+
const dotAO = bx * (O[0] - a[0]) + by * (O[1] - a[1]);
|
|
1302
|
+
const d = 2 * (dotAO - R * sinHalf) / cos2Half;
|
|
1303
|
+
const K = [a[0] + d * bx, a[1] + d * by];
|
|
1304
|
+
if (which === "center") return K;
|
|
1305
|
+
const kl = Math.hypot(K[0] - O[0], K[1] - O[1]) || 1;
|
|
1306
|
+
return [O[0] + R * (K[0] - O[0]) / kl, O[1] + R * (K[1] - O[1]) / kl];
|
|
1307
|
+
}
|
|
1269
1308
|
function pointAtDistanceCoord(from, through, d) {
|
|
1270
1309
|
const dx = through[0] - from[0];
|
|
1271
1310
|
const dy = through[1] - from[1];
|
|
@@ -1293,29 +1332,38 @@ var init_arcMidpoint = __esm({
|
|
|
1293
1332
|
arcMidpointConstraint = definePointConstraint({
|
|
1294
1333
|
kind: "arcMidpoint",
|
|
1295
1334
|
validate: (c) => {
|
|
1296
|
-
if (!c.circle || !c.a || !c.b
|
|
1297
|
-
throw new Error("point.arcMidpoint: circle, a, b
|
|
1335
|
+
if (!c.circle || !c.a || !c.b) {
|
|
1336
|
+
throw new Error("point.arcMidpoint: circle, a, b b\u1EAFt bu\u1ED9c");
|
|
1337
|
+
}
|
|
1338
|
+
if (c.notContaining && c.containing) {
|
|
1339
|
+
throw new Error("point.arcMidpoint: kh\xF4ng th\u1EC3 v\u1EEBa notContaining v\u1EEBa containing");
|
|
1298
1340
|
}
|
|
1299
1341
|
},
|
|
1300
1342
|
describe: (obj, state, c) => {
|
|
1301
1343
|
const al = state?.objects[c.a]?.label ?? c.a;
|
|
1302
1344
|
const bl = state?.objects[c.b]?.label ?? c.b;
|
|
1303
|
-
const
|
|
1304
|
-
return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl}
|
|
1345
|
+
const ref = c.containing ?? c.notContaining;
|
|
1346
|
+
if (!ref) return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl}`;
|
|
1347
|
+
const rl = state?.objects[ref]?.label ?? ref;
|
|
1348
|
+
const rel = c.containing ? "ch\u1EE9a" : "kh\xF4ng ch\u1EE9a";
|
|
1349
|
+
return `${obj.label} = trung \u0111i\u1EC3m cung ${al}${bl} (${rel} ${rl})`;
|
|
1305
1350
|
},
|
|
1306
1351
|
render: (obj, ctx, c, opts) => {
|
|
1307
1352
|
const board = ctx.jxg;
|
|
1308
1353
|
const circle = ctx.resolveRef(c.circle);
|
|
1309
1354
|
const A = ctx.resolveRef(c.a);
|
|
1310
1355
|
const B = ctx.resolveRef(c.b);
|
|
1311
|
-
const
|
|
1356
|
+
const refName = c.containing ?? c.notContaining;
|
|
1357
|
+
const ref = refName ? ctx.resolveRef(refName) : void 0;
|
|
1358
|
+
const sameSide = !!c.containing;
|
|
1312
1359
|
const O = circle?.center ?? circle?.midpoint ?? circle;
|
|
1313
1360
|
const am = () => arcMidpoint(
|
|
1314
1361
|
[O.X(), O.Y()],
|
|
1315
1362
|
circle.Radius(),
|
|
1316
1363
|
[A.X(), A.Y()],
|
|
1317
1364
|
[B.X(), B.Y()],
|
|
1318
|
-
[
|
|
1365
|
+
ref ? [ref.X(), ref.Y()] : void 0,
|
|
1366
|
+
sameSide
|
|
1319
1367
|
);
|
|
1320
1368
|
return board.create("point", [() => am()[0], () => am()[1]], opts);
|
|
1321
1369
|
}
|
|
@@ -1365,6 +1413,59 @@ var init_excenter = __esm({
|
|
|
1365
1413
|
}
|
|
1366
1414
|
});
|
|
1367
1415
|
|
|
1416
|
+
// src/core/scene/kinds/point-constraints/mixtilinearPoint.ts
|
|
1417
|
+
var mixtilinearPointConstraint;
|
|
1418
|
+
var init_mixtilinearPoint = __esm({
|
|
1419
|
+
"src/core/scene/kinds/point-constraints/mixtilinearPoint.ts"() {
|
|
1420
|
+
init_pointConstructions();
|
|
1421
|
+
init_types2();
|
|
1422
|
+
mixtilinearPointConstraint = definePointConstraint({
|
|
1423
|
+
kind: "mixtilinearPoint",
|
|
1424
|
+
validate: (c) => {
|
|
1425
|
+
if (!Array.isArray(c.vertices) || c.vertices.length !== 3 || !c.vertices.every(Boolean)) {
|
|
1426
|
+
throw new Error("point.mixtilinearPoint: vertices ph\u1EA3i l\xE0 tuple 3 id non-empty");
|
|
1427
|
+
}
|
|
1428
|
+
if (c.which !== "center" && c.which !== "touch") {
|
|
1429
|
+
throw new Error("point.mixtilinearPoint: which ph\u1EA3i 'center' | 'touch'");
|
|
1430
|
+
}
|
|
1431
|
+
},
|
|
1432
|
+
describe: (obj, state, c) => {
|
|
1433
|
+
const labels = c.vertices.map((id) => state?.objects[id]?.label ?? id).join("");
|
|
1434
|
+
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)`;
|
|
1435
|
+
},
|
|
1436
|
+
render: (obj, ctx, c, opts) => {
|
|
1437
|
+
const board = ctx.jxg;
|
|
1438
|
+
const a = ctx.resolveRef(c.vertices[0]);
|
|
1439
|
+
const b = ctx.resolveRef(c.vertices[1]);
|
|
1440
|
+
const d = ctx.resolveRef(c.vertices[2]);
|
|
1441
|
+
const mp = () => mixtilinearPoint(
|
|
1442
|
+
[a.X(), a.Y()],
|
|
1443
|
+
[b.X(), b.Y()],
|
|
1444
|
+
[d.X(), d.Y()],
|
|
1445
|
+
c.which
|
|
1446
|
+
);
|
|
1447
|
+
return board.create("point", [() => mp()[0], () => mp()[1]], opts);
|
|
1448
|
+
}
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
});
|
|
1452
|
+
|
|
1453
|
+
// src/core/scene/kinds/_label.ts
|
|
1454
|
+
function labelOpts(labelOffset, dflt) {
|
|
1455
|
+
const offset = labelOffset ?? dflt;
|
|
1456
|
+
return { label: { fixed: false, ...offset ? { offset } : {} } };
|
|
1457
|
+
}
|
|
1458
|
+
function readLabelOffset(label) {
|
|
1459
|
+
const off = label.evalVisProp?.("offset") ?? label.visProp?.offset;
|
|
1460
|
+
const rel = label.relativeCoords?.scrCoords;
|
|
1461
|
+
if (!off || !rel || off.length < 2 || rel.length < 3) return null;
|
|
1462
|
+
return [Math.round(off[0] + rel[1]), Math.round(off[1] - rel[2])];
|
|
1463
|
+
}
|
|
1464
|
+
var init_label = __esm({
|
|
1465
|
+
"src/core/scene/kinds/_label.ts"() {
|
|
1466
|
+
}
|
|
1467
|
+
});
|
|
1468
|
+
|
|
1368
1469
|
// src/core/scene/kinds/point-constraints/shared.ts
|
|
1369
1470
|
function buildJxgTransforms(board, ctx, t) {
|
|
1370
1471
|
switch (t.kind) {
|
|
@@ -1416,11 +1517,13 @@ function buildPointOpts(obj) {
|
|
|
1416
1517
|
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
1417
1518
|
fillColor: obj.attrs.color ?? "#1e40af",
|
|
1418
1519
|
face: obj.attrs.face ?? "o",
|
|
1419
|
-
size: obj.attrs.size ?? 4
|
|
1520
|
+
size: obj.attrs.size ?? 4,
|
|
1521
|
+
...labelOpts(obj.attrs.labelOffset, [10, 10])
|
|
1420
1522
|
};
|
|
1421
1523
|
}
|
|
1422
1524
|
var init_shared = __esm({
|
|
1423
1525
|
"src/core/scene/kinds/point-constraints/shared.ts"() {
|
|
1526
|
+
init_label();
|
|
1424
1527
|
}
|
|
1425
1528
|
});
|
|
1426
1529
|
|
|
@@ -1729,6 +1832,7 @@ var init_registry2 = __esm({
|
|
|
1729
1832
|
init_centroid();
|
|
1730
1833
|
init_arcMidpoint();
|
|
1731
1834
|
init_excenter();
|
|
1835
|
+
init_mixtilinearPoint();
|
|
1732
1836
|
init_pointAtDistance();
|
|
1733
1837
|
init_circleIntersection();
|
|
1734
1838
|
init_circleSecondIntersection();
|
|
@@ -1754,6 +1858,7 @@ var init_registry2 = __esm({
|
|
|
1754
1858
|
centroidConstraint,
|
|
1755
1859
|
arcMidpointConstraint,
|
|
1756
1860
|
excenterConstraint,
|
|
1861
|
+
mixtilinearPointConstraint,
|
|
1757
1862
|
pointAtDistanceConstraint,
|
|
1758
1863
|
circleIntersectionConstraint,
|
|
1759
1864
|
circleSecondIntersectionConstraint,
|
|
@@ -1778,6 +1883,7 @@ var init_point = __esm({
|
|
|
1778
1883
|
init_d_constraint2();
|
|
1779
1884
|
init_registry2();
|
|
1780
1885
|
init_shared();
|
|
1886
|
+
init_label();
|
|
1781
1887
|
def3 = {
|
|
1782
1888
|
type: "point",
|
|
1783
1889
|
schemaVersion: 1,
|
|
@@ -1845,7 +1951,8 @@ var init_point = __esm({
|
|
|
1845
1951
|
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
1846
1952
|
fillColor: obj.attrs.color ?? "#1e40af",
|
|
1847
1953
|
face: obj.attrs.face ?? "o",
|
|
1848
|
-
size: obj.attrs.size ?? 4
|
|
1954
|
+
size: obj.attrs.size ?? 4,
|
|
1955
|
+
...labelOpts(obj.attrs.labelOffset, [10, 10])
|
|
1849
1956
|
});
|
|
1850
1957
|
} catch {
|
|
1851
1958
|
}
|
|
@@ -1865,6 +1972,7 @@ var init_segment = __esm({
|
|
|
1865
1972
|
"src/core/scene/kinds/segment.ts"() {
|
|
1866
1973
|
init_registry();
|
|
1867
1974
|
init_labelOf();
|
|
1975
|
+
init_label();
|
|
1868
1976
|
def4 = {
|
|
1869
1977
|
type: "segment",
|
|
1870
1978
|
schemaVersion: 1,
|
|
@@ -1896,7 +2004,8 @@ var init_segment = __esm({
|
|
|
1896
2004
|
strokeWidth: obj.attrs.width ?? 2,
|
|
1897
2005
|
dash: obj.attrs.dash ?? 0,
|
|
1898
2006
|
visible: obj.visible,
|
|
1899
|
-
fixed: obj.locked
|
|
2007
|
+
fixed: obj.locked,
|
|
2008
|
+
...labelOpts(obj.attrs.labelOffset)
|
|
1900
2009
|
});
|
|
1901
2010
|
}
|
|
1902
2011
|
};
|
|
@@ -1933,6 +2042,7 @@ var init_line = __esm({
|
|
|
1933
2042
|
"src/core/scene/kinds/line.ts"() {
|
|
1934
2043
|
init_registry();
|
|
1935
2044
|
init_labelOf();
|
|
2045
|
+
init_label();
|
|
1936
2046
|
init_pointConstructions();
|
|
1937
2047
|
def5 = {
|
|
1938
2048
|
type: "line",
|
|
@@ -1975,7 +2085,8 @@ var init_line = __esm({
|
|
|
1975
2085
|
strokeWidth: obj.attrs.width ?? 2,
|
|
1976
2086
|
dash: obj.attrs.dash ?? 0,
|
|
1977
2087
|
visible: obj.visible,
|
|
1978
|
-
fixed: obj.locked
|
|
2088
|
+
fixed: obj.locked,
|
|
2089
|
+
...labelOpts(obj.attrs.labelOffset)
|
|
1979
2090
|
};
|
|
1980
2091
|
const c = obj.attrs.construction;
|
|
1981
2092
|
if (!c) {
|
|
@@ -2231,6 +2342,7 @@ var init_circle = __esm({
|
|
|
2231
2342
|
"src/core/scene/kinds/circle.ts"() {
|
|
2232
2343
|
init_registry();
|
|
2233
2344
|
init_labelOf();
|
|
2345
|
+
init_label();
|
|
2234
2346
|
init_pointConstructions();
|
|
2235
2347
|
def8 = {
|
|
2236
2348
|
type: "circle",
|
|
@@ -2292,15 +2404,17 @@ var init_circle = __esm({
|
|
|
2292
2404
|
const board = ctx.jxg;
|
|
2293
2405
|
const isCenterLabel = (l) => /^[A-Z]['′]?\d*$/u.test(l);
|
|
2294
2406
|
const isCenter = isCenterLabel(obj.label);
|
|
2407
|
+
const isRenamedCircle = /_c$/.test(obj.label);
|
|
2295
2408
|
const baseOpts = {
|
|
2296
2409
|
name: obj.label,
|
|
2297
|
-
withLabel: isCenter ? obj.attrs.showLabel ?? false : true,
|
|
2410
|
+
withLabel: isCenter ? obj.attrs.showLabel ?? false : isRenamedCircle ? false : true,
|
|
2298
2411
|
strokeColor: obj.attrs.color ?? "#0f172a",
|
|
2299
2412
|
strokeWidth: obj.attrs.width ?? 2,
|
|
2300
2413
|
dash: obj.attrs.dash ?? 0,
|
|
2301
2414
|
fillColor: "none",
|
|
2302
2415
|
visible: obj.visible,
|
|
2303
|
-
fixed: obj.locked
|
|
2416
|
+
fixed: obj.locked,
|
|
2417
|
+
...labelOpts(obj.attrs.labelOffset)
|
|
2304
2418
|
};
|
|
2305
2419
|
const c = asConstruction(obj.attrs);
|
|
2306
2420
|
if (c?.kind === "circumscribed") {
|
|
@@ -2785,8 +2899,11 @@ var init_intersection = __esm({
|
|
|
2785
2899
|
const opts = {
|
|
2786
2900
|
name: obj.label,
|
|
2787
2901
|
withLabel: true,
|
|
2788
|
-
|
|
2789
|
-
|
|
2902
|
+
// Cùng màu xanh với mọi điểm khác (buildPointOpts dùng '#1e40af').
|
|
2903
|
+
// Trước đây điểm giao tô đỏ '#dc2626' → tách biệt thị giác nhưng gây
|
|
2904
|
+
// khó hiểu ("vì sao điểm này khác màu?"). Giữ chung một màu.
|
|
2905
|
+
strokeColor: obj.attrs.color ?? "#1e40af",
|
|
2906
|
+
fillColor: obj.attrs.color ?? "#1e40af",
|
|
2790
2907
|
visible: obj.visible,
|
|
2791
2908
|
fixed: obj.locked
|
|
2792
2909
|
};
|
|
@@ -2795,6 +2912,38 @@ var init_intersection = __esm({
|
|
|
2795
2912
|
}
|
|
2796
2913
|
const branch = obj.attrs.branch ?? 0;
|
|
2797
2914
|
return board.create("intersection", [a, b, branch], opts);
|
|
2915
|
+
},
|
|
2916
|
+
/**
|
|
2917
|
+
* Cập nhật TẠI CHỖ các thuộc tính "trang trí" (tên/màu/ẩn-hiện/khoá) qua
|
|
2918
|
+
* setAttribute — giữ nguyên JxgObj identity nên các object phụ thuộc điểm
|
|
2919
|
+
* giao (đường thẳng qua nó, …) KHÔNG bị stale parent ref. Mô phỏng update
|
|
2920
|
+
* hook của point.ts.
|
|
2921
|
+
*
|
|
2922
|
+
* Nếu định nghĩa hình học đổi (kind/ref1/ref2/branch) thì throw → renderer
|
|
2923
|
+
* fallback remove + create để JSXGraph dựng lại phép giao đúng.
|
|
2924
|
+
*/
|
|
2925
|
+
update: (obj, prev, ctx, existing) => {
|
|
2926
|
+
const a = obj.attrs;
|
|
2927
|
+
const p = prev.attrs;
|
|
2928
|
+
const branchA = a.branch;
|
|
2929
|
+
const branchP = p.branch;
|
|
2930
|
+
if (a.kind !== p.kind || a.ref1 !== p.ref1 || a.ref2 !== p.ref2 || branchA !== branchP) {
|
|
2931
|
+
throw new Error("intersection: \u0111\u1ECBnh ngh\u0129a h\xECnh h\u1ECDc \u0111\u1ED5i \u2014 recreate");
|
|
2932
|
+
}
|
|
2933
|
+
const el = existing;
|
|
2934
|
+
if (typeof el.setAttribute === "function") {
|
|
2935
|
+
try {
|
|
2936
|
+
el.setAttribute({
|
|
2937
|
+
name: obj.label,
|
|
2938
|
+
withLabel: true,
|
|
2939
|
+
strokeColor: a.color ?? "#1e40af",
|
|
2940
|
+
fillColor: a.color ?? "#1e40af",
|
|
2941
|
+
visible: obj.visible,
|
|
2942
|
+
fixed: obj.locked
|
|
2943
|
+
});
|
|
2944
|
+
} catch {
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2798
2947
|
}
|
|
2799
2948
|
};
|
|
2800
2949
|
registerKind(def12);
|
|
@@ -3481,6 +3630,7 @@ var init_JxgRenderer = __esm({
|
|
|
3481
3630
|
init_registry();
|
|
3482
3631
|
init_types2d();
|
|
3483
3632
|
init_parser();
|
|
3633
|
+
init_label();
|
|
3484
3634
|
JxgRenderer = class {
|
|
3485
3635
|
constructor(store, board, options = {}) {
|
|
3486
3636
|
this.elements = /* @__PURE__ */ new Map();
|
|
@@ -3549,6 +3699,7 @@ var init_JxgRenderer = __esm({
|
|
|
3549
3699
|
this.elements.set(obj.id, el);
|
|
3550
3700
|
this.attachFreePointDragSync(obj, el);
|
|
3551
3701
|
this.attachGliderDragSync(obj, el);
|
|
3702
|
+
this.attachLabelDragSync(obj, el);
|
|
3552
3703
|
} catch (err) {
|
|
3553
3704
|
console.warn(`[scene/render/2d] kh\xF4ng render \u0111\u01B0\u1EE3c ${obj.kind} id="${obj.id}":`, err);
|
|
3554
3705
|
}
|
|
@@ -3663,6 +3814,54 @@ var init_JxgRenderer = __esm({
|
|
|
3663
3814
|
});
|
|
3664
3815
|
});
|
|
3665
3816
|
}
|
|
3817
|
+
/**
|
|
3818
|
+
* Cho phép kéo NHÃN của bất kỳ object nào có label (point/line/segment/
|
|
3819
|
+
* circle...). Khi user kéo nhãn, JSXGraph cộng dồn vào label.relativeCoords
|
|
3820
|
+
* (drag-delta screen px) nhưng KHÔNG đổi attribute offset → vị trí cuối =
|
|
3821
|
+
* offset + relativeCoords. Ta gộp lại thành offset thuần (readLabelOffset),
|
|
3822
|
+
* zero relativeCoords để khỏi double-count khi update-hook/recreate áp lại
|
|
3823
|
+
* offset, rồi dispatch UPDATE_ATTRS { labelOffset }. Right-click → reset.
|
|
3824
|
+
*/
|
|
3825
|
+
attachLabelDragSync(obj, el) {
|
|
3826
|
+
const label = el?.label;
|
|
3827
|
+
if (!label || typeof label.on !== "function") return;
|
|
3828
|
+
const sceneId = obj.id;
|
|
3829
|
+
label.on("up", () => {
|
|
3830
|
+
if (this.disposed) return;
|
|
3831
|
+
const off = readLabelOffset(label);
|
|
3832
|
+
if (!off) return;
|
|
3833
|
+
const cur = this.store.getState().objects[sceneId];
|
|
3834
|
+
if (!cur) return;
|
|
3835
|
+
const prev = cur.attrs.labelOffset;
|
|
3836
|
+
if (prev && prev[0] === off[0] && prev[1] === off[1]) return;
|
|
3837
|
+
try {
|
|
3838
|
+
label.setAttribute({ offset: off });
|
|
3839
|
+
if (label.relativeCoords?.scrCoords) {
|
|
3840
|
+
label.relativeCoords.scrCoords[1] = 0;
|
|
3841
|
+
label.relativeCoords.scrCoords[2] = 0;
|
|
3842
|
+
}
|
|
3843
|
+
} catch {
|
|
3844
|
+
}
|
|
3845
|
+
this.store.dispatch({
|
|
3846
|
+
type: "UPDATE_ATTRS",
|
|
3847
|
+
payload: { id: sceneId, patch: { labelOffset: off } }
|
|
3848
|
+
});
|
|
3849
|
+
});
|
|
3850
|
+
const node = label.rendNode;
|
|
3851
|
+
if (node?.addEventListener) {
|
|
3852
|
+
node.addEventListener("contextmenu", (ev) => {
|
|
3853
|
+
if (this.disposed) return;
|
|
3854
|
+
ev.preventDefault();
|
|
3855
|
+
const c = this.store.getState().objects[sceneId];
|
|
3856
|
+
if (!c) return;
|
|
3857
|
+
if (c.attrs.labelOffset === void 0) return;
|
|
3858
|
+
this.store.dispatch({
|
|
3859
|
+
type: "UPDATE_ATTRS",
|
|
3860
|
+
payload: { id: sceneId, patch: { labelOffset: void 0 } }
|
|
3861
|
+
});
|
|
3862
|
+
});
|
|
3863
|
+
}
|
|
3864
|
+
}
|
|
3666
3865
|
remove(id) {
|
|
3667
3866
|
this.removeHalo(id);
|
|
3668
3867
|
this.selectedIds.delete(id);
|
|
@@ -3799,88 +3998,89 @@ var init_JxgRenderer = __esm({
|
|
|
3799
3998
|
layer: 4,
|
|
3800
3999
|
needsRegularUpdate: true
|
|
3801
4000
|
};
|
|
4001
|
+
const elementClass = el.elementClass;
|
|
4002
|
+
const elType = el.elType;
|
|
4003
|
+
const isPointLike = elementClass === 1 || elType === "point" || elType === "glider" || elType === "intersection";
|
|
4004
|
+
const isPolygonLike = Array.isArray(el.vertices) && el.vertices.length >= 3;
|
|
4005
|
+
const isCircleLike = elementClass === 3 || elType === "circle" || elType === "circumcircle" || elType === "incircle";
|
|
4006
|
+
const isSegment = elType === "segment";
|
|
4007
|
+
const isLineLike = elementClass === 2 || elType === "line" || elType === "arrow" || elType === "ray" || elType === "vector" || elType === "tangent" || elType === "normal" || elType === "parallel" || elType === "perpendicular" || elType === "bisector";
|
|
3802
4008
|
const halos = [];
|
|
3803
4009
|
try {
|
|
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
|
-
|
|
3829
|
-
|
|
3830
|
-
});
|
|
3831
|
-
halos.push(halo);
|
|
3832
|
-
}
|
|
3833
|
-
break;
|
|
3834
|
-
}
|
|
3835
|
-
case "line":
|
|
3836
|
-
case "arrow":
|
|
3837
|
-
case "ray":
|
|
3838
|
-
case "vector":
|
|
3839
|
-
case "tangent":
|
|
3840
|
-
case "normal":
|
|
3841
|
-
case "parallel":
|
|
3842
|
-
case "perpendicular":
|
|
3843
|
-
case "bisector": {
|
|
3844
|
-
if (el.point1 && el.point2) {
|
|
3845
|
-
const halo = board.create("line", [el.point1, el.point2], {
|
|
3846
|
-
...haloBase,
|
|
3847
|
-
strokeWidth: 9
|
|
3848
|
-
});
|
|
3849
|
-
halos.push(halo);
|
|
3850
|
-
}
|
|
3851
|
-
break;
|
|
3852
|
-
}
|
|
3853
|
-
case "circle": {
|
|
3854
|
-
if (el.center && typeof el.Radius === "function") {
|
|
3855
|
-
const halo = board.create("circle", [el.center, () => el.Radius?.() ?? 0], {
|
|
3856
|
-
...haloBase,
|
|
3857
|
-
strokeWidth: 9,
|
|
3858
|
-
fillOpacity: 0
|
|
3859
|
-
});
|
|
3860
|
-
halos.push(halo);
|
|
3861
|
-
}
|
|
3862
|
-
break;
|
|
3863
|
-
}
|
|
3864
|
-
case "polygon": {
|
|
3865
|
-
if (Array.isArray(el.vertices) && el.vertices.length >= 3) {
|
|
3866
|
-
const last = el.vertices.length - 1;
|
|
3867
|
-
const verts = el.vertices[last] === el.vertices[0] ? el.vertices.slice(0, last) : el.vertices.slice();
|
|
3868
|
-
const halo = board.create("polygon", verts, {
|
|
3869
|
-
...haloBase,
|
|
3870
|
-
fillOpacity: 0.2,
|
|
3871
|
-
borders: {
|
|
3872
|
-
strokeColor: SEL_STROKE,
|
|
3873
|
-
strokeWidth: 7,
|
|
3874
|
-
strokeOpacity: 0.55,
|
|
3875
|
-
highlight: false
|
|
3876
|
-
}
|
|
3877
|
-
});
|
|
3878
|
-
halos.push(halo);
|
|
4010
|
+
if (isPointLike) {
|
|
4011
|
+
const baseSize = el.getAttribute?.("size") ?? 4;
|
|
4012
|
+
const halo = board.create("point", [
|
|
4013
|
+
() => el.X?.() ?? 0,
|
|
4014
|
+
() => el.Y?.() ?? 0
|
|
4015
|
+
], {
|
|
4016
|
+
...haloBase,
|
|
4017
|
+
size: baseSize + 6,
|
|
4018
|
+
face: "o",
|
|
4019
|
+
strokeWidth: 2,
|
|
4020
|
+
strokeOpacity: 0.75,
|
|
4021
|
+
fillOpacity: 0.25
|
|
4022
|
+
});
|
|
4023
|
+
halos.push(halo);
|
|
4024
|
+
} else if (isPolygonLike) {
|
|
4025
|
+
const verts = el.vertices;
|
|
4026
|
+
const last = verts.length - 1;
|
|
4027
|
+
const trimmed = verts[last] === verts[0] ? verts.slice(0, last) : verts.slice();
|
|
4028
|
+
const halo = board.create("polygon", trimmed, {
|
|
4029
|
+
...haloBase,
|
|
4030
|
+
fillOpacity: 0.2,
|
|
4031
|
+
borders: {
|
|
4032
|
+
strokeColor: SEL_STROKE,
|
|
4033
|
+
strokeWidth: 7,
|
|
4034
|
+
strokeOpacity: 0.55,
|
|
4035
|
+
highlight: false
|
|
3879
4036
|
}
|
|
3880
|
-
|
|
4037
|
+
});
|
|
4038
|
+
halos.push(halo);
|
|
4039
|
+
} else if (isCircleLike && el.center && typeof el.Radius === "function") {
|
|
4040
|
+
const halo = board.create("circle", [el.center, () => el.Radius?.() ?? 0], {
|
|
4041
|
+
...haloBase,
|
|
4042
|
+
strokeWidth: 9,
|
|
4043
|
+
fillOpacity: 0
|
|
4044
|
+
});
|
|
4045
|
+
halos.push(halo);
|
|
4046
|
+
} else if (isSegment && el.point1 && el.point2) {
|
|
4047
|
+
const halo = board.create("segment", [el.point1, el.point2], {
|
|
4048
|
+
...haloBase,
|
|
4049
|
+
strokeWidth: 9,
|
|
4050
|
+
straightFirst: false,
|
|
4051
|
+
straightLast: false
|
|
4052
|
+
});
|
|
4053
|
+
halos.push(halo);
|
|
4054
|
+
} else if (isLineLike && el.point1 && el.point2) {
|
|
4055
|
+
const halo = board.create("line", [el.point1, el.point2], {
|
|
4056
|
+
...haloBase,
|
|
4057
|
+
strokeWidth: 9
|
|
4058
|
+
});
|
|
4059
|
+
halos.push(halo);
|
|
4060
|
+
} else if (el.center && (el.radiuspoint ?? el.radiusPoint) && (el.anglepoint ?? el.anglePoint) && (elType === "arc" || elType === "semicircle" || elType === "circumcirclearc" || elType === "sector" || elType === "angle")) {
|
|
4061
|
+
const rp = el.radiuspoint ?? el.radiusPoint;
|
|
4062
|
+
const ap = el.anglepoint ?? el.anglePoint;
|
|
4063
|
+
if (elType === "sector") {
|
|
4064
|
+
halos.push(board.create("sector", [el.center, rp, ap], {
|
|
4065
|
+
...haloBase,
|
|
4066
|
+
strokeWidth: 7,
|
|
4067
|
+
fillOpacity: 0.2
|
|
4068
|
+
}));
|
|
4069
|
+
} else if (elType === "angle") {
|
|
4070
|
+
halos.push(board.create("angle", [rp, el.center, ap], {
|
|
4071
|
+
...haloBase,
|
|
4072
|
+
strokeWidth: 7,
|
|
4073
|
+
fillOpacity: 0.2,
|
|
4074
|
+
radius: el.getAttribute?.("radius") ?? 1
|
|
4075
|
+
}));
|
|
4076
|
+
} else {
|
|
4077
|
+
halos.push(board.create("arc", [el.center, rp, ap], {
|
|
4078
|
+
...haloBase,
|
|
4079
|
+
strokeWidth: 9,
|
|
4080
|
+
fillColor: "none",
|
|
4081
|
+
fillOpacity: 0
|
|
4082
|
+
}));
|
|
3881
4083
|
}
|
|
3882
|
-
default:
|
|
3883
|
-
break;
|
|
3884
4084
|
}
|
|
3885
4085
|
} catch (err) {
|
|
3886
4086
|
console.warn("[scene/render/2d] halo create fail:", err);
|
|
@@ -4040,6 +4240,49 @@ var init_autoFitBbox = __esm({
|
|
|
4040
4240
|
}
|
|
4041
4241
|
});
|
|
4042
4242
|
|
|
4243
|
+
// src/stamps/geometry-2d/autoFitBoard.ts
|
|
4244
|
+
function isDefaultBbox(bbox) {
|
|
4245
|
+
const d = DEFAULT_VIEW_2D.bbox;
|
|
4246
|
+
return bbox.length === 4 && bbox[0] === d[0] && bbox[1] === d[1] && bbox[2] === d[2] && bbox[3] === d[3];
|
|
4247
|
+
}
|
|
4248
|
+
function fittedBboxFromBoard(board, aspect) {
|
|
4249
|
+
const objs = board?.objectsList;
|
|
4250
|
+
if (!Array.isArray(objs)) return null;
|
|
4251
|
+
const points = [];
|
|
4252
|
+
const circles = [];
|
|
4253
|
+
for (const raw of objs) {
|
|
4254
|
+
const o = raw;
|
|
4255
|
+
if (o?.elementClass === 1 && typeof o.X === "function" && typeof o.Y === "function") {
|
|
4256
|
+
const x = o.X(), y = o.Y();
|
|
4257
|
+
if (Number.isFinite(x) && Number.isFinite(y)) points.push([x, y]);
|
|
4258
|
+
} else if (o?.elementClass === 3 && o.center?.X && typeof o.Radius === "function") {
|
|
4259
|
+
const cx = o.center.X(), cy = o.center.Y(), r = o.Radius();
|
|
4260
|
+
if (Number.isFinite(cx) && Number.isFinite(cy) && Number.isFinite(r)) {
|
|
4261
|
+
circles.push({ cx, cy, r });
|
|
4262
|
+
}
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
const safeAspect = Number.isFinite(aspect) && aspect > 0 ? aspect : 1;
|
|
4266
|
+
return computeAutoFitBbox(points, circles, safeAspect);
|
|
4267
|
+
}
|
|
4268
|
+
function autoFitBoardToContent(board, aspect) {
|
|
4269
|
+
const bbox = fittedBboxFromBoard(board, aspect);
|
|
4270
|
+
if (!bbox) return;
|
|
4271
|
+
const b = board;
|
|
4272
|
+
try {
|
|
4273
|
+
b.setBoundingBox?.(bbox);
|
|
4274
|
+
if (typeof b.update === "function") b.update();
|
|
4275
|
+
if (typeof b.fullUpdate === "function") b.fullUpdate();
|
|
4276
|
+
} catch {
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4279
|
+
var init_autoFitBoard = __esm({
|
|
4280
|
+
"src/stamps/geometry-2d/autoFitBoard.ts"() {
|
|
4281
|
+
init_types();
|
|
4282
|
+
init_autoFitBbox();
|
|
4283
|
+
}
|
|
4284
|
+
});
|
|
4285
|
+
|
|
4043
4286
|
// src/stamps/geometry-2d/labelLayout.ts
|
|
4044
4287
|
function radialLabelOffsets(points, R = 14) {
|
|
4045
4288
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -4074,8 +4317,9 @@ function containerDimsForBbox(bbox) {
|
|
|
4074
4317
|
if (!Number.isFinite(w) || !Number.isFinite(h) || w <= 0 || h <= 0) {
|
|
4075
4318
|
return { width: FALLBACK_W, height: FALLBACK_H };
|
|
4076
4319
|
}
|
|
4077
|
-
|
|
4078
|
-
let
|
|
4320
|
+
const pxPerUnit = DEFAULT_VIEW_PX / Math.max(w, h);
|
|
4321
|
+
let width = w * pxPerUnit;
|
|
4322
|
+
let height = h * pxPerUnit;
|
|
4079
4323
|
const maxAxis = Math.max(width, height);
|
|
4080
4324
|
if (maxAxis > MAX_DIM) {
|
|
4081
4325
|
const ratio = MAX_DIM / maxAxis;
|
|
@@ -4090,31 +4334,6 @@ function containerDimsForBbox(bbox) {
|
|
|
4090
4334
|
}
|
|
4091
4335
|
return { width: Math.round(width), height: Math.round(height) };
|
|
4092
4336
|
}
|
|
4093
|
-
function autoFitBboxFromBoard(board, aspect) {
|
|
4094
|
-
const objs = board?.objectsList;
|
|
4095
|
-
if (!Array.isArray(objs)) return;
|
|
4096
|
-
const points = [];
|
|
4097
|
-
const circles = [];
|
|
4098
|
-
for (const o of objs) {
|
|
4099
|
-
if (o?.elementClass === 1 && typeof o.X === "function" && typeof o.Y === "function") {
|
|
4100
|
-
const x = o.X(), y = o.Y();
|
|
4101
|
-
if (Number.isFinite(x) && Number.isFinite(y)) points.push([x, y]);
|
|
4102
|
-
} else if (o?.elementClass === 3 && o.center?.X && typeof o.Radius === "function") {
|
|
4103
|
-
const cx = o.center.X(), cy = o.center.Y(), r = o.Radius();
|
|
4104
|
-
if (Number.isFinite(cx) && Number.isFinite(cy) && Number.isFinite(r)) {
|
|
4105
|
-
circles.push({ cx, cy, r });
|
|
4106
|
-
}
|
|
4107
|
-
}
|
|
4108
|
-
}
|
|
4109
|
-
const bbox = computeAutoFitBbox(points, circles, aspect);
|
|
4110
|
-
if (!bbox) return;
|
|
4111
|
-
try {
|
|
4112
|
-
board.setBoundingBox(bbox);
|
|
4113
|
-
if (typeof board.update === "function") board.update();
|
|
4114
|
-
if (typeof board.fullUpdate === "function") board.fullUpdate();
|
|
4115
|
-
} catch {
|
|
4116
|
-
}
|
|
4117
|
-
}
|
|
4118
4337
|
function applyRadialLabelOffsets(board) {
|
|
4119
4338
|
const objs = board?.objectsList;
|
|
4120
4339
|
if (!Array.isArray(objs)) return;
|
|
@@ -4140,10 +4359,6 @@ function applyRadialLabelOffsets(board) {
|
|
|
4140
4359
|
} catch {
|
|
4141
4360
|
}
|
|
4142
4361
|
}
|
|
4143
|
-
function isDefaultBbox(bbox) {
|
|
4144
|
-
const d = DEFAULT_VIEW_2D.bbox;
|
|
4145
|
-
return bbox.length === 4 && bbox[0] === d[0] && bbox[1] === d[1] && bbox[2] === d[2] && bbox[3] === d[3];
|
|
4146
|
-
}
|
|
4147
4362
|
async function renderGeometrySvgFromState(jsonState) {
|
|
4148
4363
|
const state = deserializeBoard(jsonState);
|
|
4149
4364
|
const view = state.meta.domain === "2d" ? state.meta.view : DEFAULT_VIEW_2D;
|
|
@@ -4178,7 +4393,7 @@ async function renderGeometrySvgFromState(jsonState) {
|
|
|
4178
4393
|
const store = createStore(state);
|
|
4179
4394
|
const renderer = new JxgRenderer(store, board);
|
|
4180
4395
|
if (shouldAutoFit) {
|
|
4181
|
-
|
|
4396
|
+
autoFitBoardToContent(board, dims.width / dims.height);
|
|
4182
4397
|
applyRadialLabelOffsets(board);
|
|
4183
4398
|
}
|
|
4184
4399
|
return renderer;
|
|
@@ -4186,7 +4401,7 @@ async function renderGeometrySvgFromState(jsonState) {
|
|
|
4186
4401
|
});
|
|
4187
4402
|
return svgString;
|
|
4188
4403
|
}
|
|
4189
|
-
var
|
|
4404
|
+
var DEFAULT_VIEW_PX, MIN_DIM, MAX_DIM, FALLBACK_W, FALLBACK_H;
|
|
4190
4405
|
var init_render = __esm({
|
|
4191
4406
|
"src/stamps/geometry-2d/render.ts"() {
|
|
4192
4407
|
init_serialize();
|
|
@@ -4195,9 +4410,9 @@ var init_render = __esm({
|
|
|
4195
4410
|
init_types();
|
|
4196
4411
|
init_JxgRenderer();
|
|
4197
4412
|
init_jxgOffscreenRender();
|
|
4198
|
-
|
|
4413
|
+
init_autoFitBoard();
|
|
4199
4414
|
init_labelLayout();
|
|
4200
|
-
|
|
4415
|
+
DEFAULT_VIEW_PX = 500;
|
|
4201
4416
|
MIN_DIM = 100;
|
|
4202
4417
|
MAX_DIM = 1200;
|
|
4203
4418
|
FALLBACK_W = 400;
|
|
@@ -5757,41 +5972,48 @@ var init_icons = __esm({
|
|
|
5757
5972
|
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "4", cy: "4", r: "1.7", fill: C_POINT })
|
|
5758
5973
|
] }),
|
|
5759
5974
|
// ===== Nâng cao / kind chưa có icon =====
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
5975
|
+
// Hệ màu đồng bộ: XANH = input click sẵn, ĐỎ = output suy ra. Toạ độ tính để
|
|
5976
|
+
// điểm nằm ĐÚNG trên đường tròn/cung/tiếp tuyến (không trôi nổi).
|
|
5977
|
+
excenter: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
5978
|
+
/* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "12,3 15,15 9,15", stroke: "currentColor", strokeWidth: "1.4", fill: "none" }),
|
|
5979
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "15", x2: "8", y2: "18.9", stroke: "#94a3b8", strokeWidth: "1", strokeDasharray: "1.6 1.6" }),
|
|
5980
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "15", y1: "15", x2: "16", y2: "18.9", stroke: "#94a3b8", strokeWidth: "1", strokeDasharray: "1.6 1.6" }),
|
|
5981
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "18.9", r: "3.8", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
5982
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "3", r: "1.6", fill: C_POINT }),
|
|
5983
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "15", r: "1.6", fill: C_POINT }),
|
|
5984
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "15", cy: "15", r: "1.6", fill: C_POINT }),
|
|
5985
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "18.9", r: "2.2", fill: C_CONSTRUCT })
|
|
5764
5986
|
] }),
|
|
5765
|
-
tangencyPoint: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
5766
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "
|
|
5767
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "16.5", y1: "3", x2: "16.5", y2: "
|
|
5768
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "16.5", cy: "12", r: "2.
|
|
5987
|
+
tangencyPoint: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", strokeLinecap: "round", children: [
|
|
5988
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9.5", cy: "12", r: "7", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
5989
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "16.5", y1: "3.5", x2: "16.5", y2: "20.5", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
5990
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "16.5", cy: "12", r: "2.3", fill: C_CONSTRUCT })
|
|
5769
5991
|
] }),
|
|
5770
|
-
secondIntersection: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
5771
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "12", r: "6.5", stroke: "currentColor", strokeWidth: "1.
|
|
5772
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "2", y1: "8", x2: "
|
|
5773
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "
|
|
5774
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "
|
|
5992
|
+
secondIntersection: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", strokeLinecap: "round", children: [
|
|
5993
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "12", r: "6.5", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
5994
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "2.4", y1: "8.2", x2: "20.4", y2: "11.4", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
5995
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "5.37", cy: "8.75", r: "1.8", fill: C_POINT }),
|
|
5996
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "17.4", cy: "10.87", r: "2.4", fill: C_CONSTRUCT })
|
|
5775
5997
|
] }),
|
|
5776
5998
|
arcMidpoint: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", strokeLinecap: "round", children: [
|
|
5777
|
-
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 17 A
|
|
5778
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "4", cy: "17", r: "1.
|
|
5779
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "20", cy: "17", r: "1.
|
|
5780
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "
|
|
5999
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 17 A 8.5 8.5 0 0 1 20 17", stroke: "currentColor", strokeWidth: "1.6", fill: "none" }),
|
|
6000
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "4", cy: "17", r: "1.8", fill: C_POINT }),
|
|
6001
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "20", cy: "17", r: "1.8", fill: C_POINT }),
|
|
6002
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "11.4", r: "2.3", fill: C_CONSTRUCT })
|
|
5781
6003
|
] }),
|
|
5782
6004
|
circleIntersection: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
5783
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "12", r: "6", stroke: "currentColor", strokeWidth: "1.
|
|
5784
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "15", cy: "12", r: "6", stroke: "currentColor", strokeWidth: "1.
|
|
5785
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "
|
|
5786
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "
|
|
6005
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "12", r: "6", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
6006
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "15", cy: "12", r: "6", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
6007
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "6.8", r: "2", fill: C_CONSTRUCT }),
|
|
6008
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "17.2", r: "2", fill: C_CONSTRUCT })
|
|
5787
6009
|
] }),
|
|
5788
6010
|
tangentPointExt: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", strokeLinecap: "round", children: [
|
|
5789
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "
|
|
5790
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
5791
|
-
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3
|
|
5792
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
5793
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "
|
|
5794
|
-
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "
|
|
6011
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "13", cy: "12", r: "6", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
6012
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "12", x2: "11.8", y2: "18.6", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
6013
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "12", x2: "11.8", y2: "5.4", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
6014
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "3", cy: "12", r: "2", fill: C_POINT }),
|
|
6015
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9.4", cy: "16.8", r: "1.8", fill: C_CONSTRUCT }),
|
|
6016
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9.4", cy: "7.2", r: "1.8", fill: C_CONSTRUCT })
|
|
5795
6017
|
] }),
|
|
5796
6018
|
circleCR: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
5797
6019
|
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "7.5", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
@@ -5802,9 +6024,11 @@ var init_icons = __esm({
|
|
|
5802
6024
|
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 19 L12 4 L21 19 Z", stroke: "currentColor", strokeWidth: "1.3", strokeLinejoin: "round" }),
|
|
5803
6025
|
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "14", r: "4.2", fill: "none", stroke: C_CONSTRUCT, strokeWidth: "1.3" })
|
|
5804
6026
|
] }),
|
|
5805
|
-
excircle: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
5806
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
5807
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
6027
|
+
excircle: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
6028
|
+
/* @__PURE__ */ jsxRuntime.jsx("polygon", { points: "12,3 15,15 9,15", stroke: "currentColor", strokeWidth: "1.4", fill: "none" }),
|
|
6029
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "15", x2: "8", y2: "18.9", stroke: "#94a3b8", strokeWidth: "1", strokeDasharray: "1.6 1.6" }),
|
|
6030
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "15", y1: "15", x2: "16", y2: "18.9", stroke: "#94a3b8", strokeWidth: "1", strokeDasharray: "1.6 1.6" }),
|
|
6031
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "18.9", r: "3.8", stroke: C_CONSTRUCT, strokeWidth: "1.5", fill: C_CONSTRUCT, fillOpacity: "0.12" })
|
|
5808
6032
|
] }),
|
|
5809
6033
|
pointOn: /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
5810
6034
|
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "7.5", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
@@ -7814,11 +8038,11 @@ function buildObjectSnapshot(state, id, anchorScreen) {
|
|
|
7814
8038
|
const obj = state.objects[id];
|
|
7815
8039
|
if (!obj) return null;
|
|
7816
8040
|
const k = obj.kind;
|
|
7817
|
-
if (k !== "point" && k !== "line" && k !== "circle" && k !== "segment" && k !== "ray" && k !== "vector") {
|
|
8041
|
+
if (k !== "point" && k !== "line" && k !== "circle" && k !== "segment" && k !== "ray" && k !== "vector" && k !== "intersection") {
|
|
7818
8042
|
return null;
|
|
7819
8043
|
}
|
|
7820
8044
|
const a = obj.attrs;
|
|
7821
|
-
const jKind = k === "point" ? "point" : k === "circle" ? "circle" : "line";
|
|
8045
|
+
const jKind = k === "point" || k === "intersection" ? "point" : k === "circle" ? "circle" : "line";
|
|
7822
8046
|
return {
|
|
7823
8047
|
id,
|
|
7824
8048
|
kind: jKind,
|
|
@@ -8226,6 +8450,7 @@ var init_MiniBoard = __esm({
|
|
|
8226
8450
|
init_safeJsx();
|
|
8227
8451
|
init_attachJxgWheelZoom();
|
|
8228
8452
|
init_initJxgBoard();
|
|
8453
|
+
init_autoFitBoard();
|
|
8229
8454
|
init_snapshot();
|
|
8230
8455
|
init_previewActions();
|
|
8231
8456
|
init_buildHandle();
|
|
@@ -8465,6 +8690,11 @@ var init_MiniBoard = __esm({
|
|
|
8465
8690
|
rendererRef.current = new JxgRenderer(store, board, {
|
|
8466
8691
|
theme: paletteFor(isDarkRef.current)
|
|
8467
8692
|
});
|
|
8693
|
+
if (isDefaultBbox(initialView.bbox)) {
|
|
8694
|
+
const el = containerRef.current;
|
|
8695
|
+
const aspect = el && el.clientHeight > 0 ? el.clientWidth / el.clientHeight : 1;
|
|
8696
|
+
safeJsx("MiniBoard.autoFit", () => autoFitBoardToContent(board, aspect));
|
|
8697
|
+
}
|
|
8468
8698
|
if (containerRef.current) {
|
|
8469
8699
|
wheelCleanup = attachJxgWheelZoom(containerRef.current, board, "MiniBoard.2d");
|
|
8470
8700
|
}
|
|
@@ -9262,6 +9492,7 @@ function useAiFigure(generator) {
|
|
|
9262
9492
|
const [prompt, setPrompt] = React19.useState("");
|
|
9263
9493
|
const [isLoading, setIsLoading] = React19.useState(false);
|
|
9264
9494
|
const [error, setError] = React19.useState(null);
|
|
9495
|
+
const [notice, setNotice] = React19.useState(null);
|
|
9265
9496
|
const [tokens, setTokens] = React19.useState(0);
|
|
9266
9497
|
const abortRef = React19.useRef(null);
|
|
9267
9498
|
const requestIdRef = React19.useRef(0);
|
|
@@ -9282,6 +9513,7 @@ function useAiFigure(generator) {
|
|
|
9282
9513
|
abortRef.current = controller;
|
|
9283
9514
|
setIsLoading(true);
|
|
9284
9515
|
setError(null);
|
|
9516
|
+
setNotice(null);
|
|
9285
9517
|
setTokens(0);
|
|
9286
9518
|
try {
|
|
9287
9519
|
const generated = await generator(problem, {
|
|
@@ -9295,6 +9527,7 @@ function useAiFigure(generator) {
|
|
|
9295
9527
|
setError(generated.message);
|
|
9296
9528
|
return null;
|
|
9297
9529
|
}
|
|
9530
|
+
if (generated.partial) setNotice(generated.partial.message);
|
|
9298
9531
|
return generated.state;
|
|
9299
9532
|
} catch (caught) {
|
|
9300
9533
|
if (controller.signal.aborted || caught instanceof DOMException && caught.name === "AbortError") {
|
|
@@ -9321,6 +9554,7 @@ function useAiFigure(generator) {
|
|
|
9321
9554
|
setPrompt,
|
|
9322
9555
|
isLoading,
|
|
9323
9556
|
error,
|
|
9557
|
+
notice,
|
|
9324
9558
|
submit,
|
|
9325
9559
|
cancel,
|
|
9326
9560
|
tokens
|
|
@@ -9531,6 +9765,7 @@ function AiFigurePrompt({ generator, onGenerated }) {
|
|
|
9531
9765
|
setPrompt,
|
|
9532
9766
|
isLoading,
|
|
9533
9767
|
error,
|
|
9768
|
+
notice,
|
|
9534
9769
|
submit,
|
|
9535
9770
|
cancel,
|
|
9536
9771
|
tokens
|
|
@@ -9544,6 +9779,10 @@ function AiFigurePrompt({ generator, onGenerated }) {
|
|
|
9544
9779
|
const id = setInterval(() => setElapsed((s) => s + 1), 1e3);
|
|
9545
9780
|
return () => clearInterval(id);
|
|
9546
9781
|
}, [isLoading]);
|
|
9782
|
+
const [noticeDismissed, setNoticeDismissed] = React19.useState(false);
|
|
9783
|
+
React19.useEffect(() => {
|
|
9784
|
+
setNoticeDismissed(false);
|
|
9785
|
+
}, [notice]);
|
|
9547
9786
|
const [image, setImage] = React19.useState(null);
|
|
9548
9787
|
const [ocrLoading, setOcrLoading] = React19.useState(false);
|
|
9549
9788
|
const [ocrError, setOcrError] = React19.useState(null);
|
|
@@ -9759,7 +9998,30 @@ function AiFigurePrompt({ generator, onGenerated }) {
|
|
|
9759
9998
|
}
|
|
9760
9999
|
),
|
|
9761
10000
|
ocrError && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "mt-1 px-1 text-xs text-red-600", children: ocrError }),
|
|
9762
|
-
error && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "mt-1 px-1 text-xs text-red-600", children: error })
|
|
10001
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "mt-1 px-1 text-xs text-red-600", children: error }),
|
|
10002
|
+
notice && !noticeDismissed && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10003
|
+
"div",
|
|
10004
|
+
{
|
|
10005
|
+
role: "status",
|
|
10006
|
+
"data-testid": "geometry-ai-partial-notice",
|
|
10007
|
+
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",
|
|
10008
|
+
children: [
|
|
10009
|
+
notice,
|
|
10010
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10011
|
+
"button",
|
|
10012
|
+
{
|
|
10013
|
+
type: "button",
|
|
10014
|
+
onClick: () => setNoticeDismissed(true),
|
|
10015
|
+
"aria-label": "\u0110\xF3ng th\xF4ng b\xE1o",
|
|
10016
|
+
title: "\u0110\xF3ng th\xF4ng b\xE1o",
|
|
10017
|
+
"data-testid": "geometry-ai-partial-dismiss",
|
|
10018
|
+
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",
|
|
10019
|
+
children: "\xD7"
|
|
10020
|
+
}
|
|
10021
|
+
)
|
|
10022
|
+
]
|
|
10023
|
+
}
|
|
10024
|
+
)
|
|
9763
10025
|
] });
|
|
9764
10026
|
}
|
|
9765
10027
|
var ArrowUpIcon, StopIcon, PaperclipIcon;
|
|
@@ -10384,11 +10646,17 @@ async function insertStampImage(api, opts) {
|
|
|
10384
10646
|
const elements = api.getSceneElements();
|
|
10385
10647
|
const editingId = opts.editingElementId ?? null;
|
|
10386
10648
|
if (editingId) {
|
|
10649
|
+
const old = opts.preserveExistingSize ? elements.find((e) => e.id === editingId) : void 0;
|
|
10650
|
+
const oldLongest = old ? Math.max(old.width ?? 0, old.height ?? 0) : 0;
|
|
10651
|
+
const newLongest = Math.max(width, height);
|
|
10652
|
+
const scale3 = oldLongest > 0 && newLongest > 0 ? oldLongest / newLongest : 1;
|
|
10653
|
+
const w = width * scale3;
|
|
10654
|
+
const h = height * scale3;
|
|
10387
10655
|
const updated = elements.map(
|
|
10388
|
-
(e) => e.id === editingId ? { ...e, fileId, customData, width, height } : e
|
|
10656
|
+
(e) => e.id === editingId ? { ...e, fileId, customData, width: w, height: h } : e
|
|
10389
10657
|
);
|
|
10390
10658
|
api.updateScene({ elements: updated, appState: clearAppStateAfterInsert() });
|
|
10391
|
-
return { fileId, width, height, elementId: editingId };
|
|
10659
|
+
return { fileId, width: w, height: h, elementId: editingId };
|
|
10392
10660
|
}
|
|
10393
10661
|
const newElement = buildStampImageElement(
|
|
10394
10662
|
api,
|
|
@@ -10542,8 +10810,11 @@ function serializePoint(obj, state) {
|
|
|
10542
10810
|
};
|
|
10543
10811
|
}
|
|
10544
10812
|
case "arcMidpoint": {
|
|
10545
|
-
const
|
|
10546
|
-
|
|
10813
|
+
const containment = c.containing ?? c.notContaining;
|
|
10814
|
+
const baseRefs = [c.circle, c.a, c.b];
|
|
10815
|
+
if (containment) baseRefs.push(containment);
|
|
10816
|
+
const refs = resolveRefs(baseRefs, state);
|
|
10817
|
+
if (!refs) return fail("unresolved-ref", baseRefs.join(","));
|
|
10547
10818
|
return {
|
|
10548
10819
|
ok: true,
|
|
10549
10820
|
entity: {
|
|
@@ -10552,7 +10823,7 @@ function serializePoint(obj, state) {
|
|
|
10552
10823
|
circle: refs[0],
|
|
10553
10824
|
a: refs[1],
|
|
10554
10825
|
b: refs[2],
|
|
10555
|
-
notContaining: refs[3]
|
|
10826
|
+
...c.containing ? { containing: refs[3] } : c.notContaining ? { notContaining: refs[3] } : {}
|
|
10556
10827
|
}
|
|
10557
10828
|
};
|
|
10558
10829
|
}
|
|
@@ -10596,6 +10867,7 @@ function serializePoint(obj, state) {
|
|
|
10596
10867
|
case "onPerpendicular":
|
|
10597
10868
|
case "onPerpBisector":
|
|
10598
10869
|
case "onCircleAroundPoint":
|
|
10870
|
+
case "mixtilinearPoint":
|
|
10599
10871
|
return fail("unsupported-constraint", c.kind);
|
|
10600
10872
|
default: {
|
|
10601
10873
|
return fail("unsupported-constraint");
|
|
@@ -10898,9 +11170,11 @@ function describeEntity(e) {
|
|
|
10898
11170
|
return `${e.name} = \u0111\u01B0\u1EDDng tr\xF2n b\xE0ng ti\u1EBFp ${e.vertices.join("")} \u0111\u1ED1i di\u1EC7n ${e.opposite}`;
|
|
10899
11171
|
// Cụm A
|
|
10900
11172
|
case "arcMidpoint":
|
|
10901
|
-
return `${e.name} = trung \u0111i\u1EC3m cung ${e.a}${e.b} (kh\xF4ng ch\u1EE9a ${e.notContaining}) tr\xEAn ${e.circle}`;
|
|
11173
|
+
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}`;
|
|
10902
11174
|
case "excenter":
|
|
10903
11175
|
return `${e.name} = t\xE2m b\xE0ng ti\u1EBFp ${e.vertices.join("")} \u0111\u1ED1i di\u1EC7n ${e.opposite}`;
|
|
11176
|
+
case "mixtilinearPoint":
|
|
11177
|
+
return `${e.name} = ${e.which === "center" ? "t\xE2m" : "ti\u1EBFp \u0111i\u1EC3m"} mixtilinear ${e.vertices.join("")}`;
|
|
10904
11178
|
case "reflectPoint":
|
|
10905
11179
|
return `${e.name} = \u0111\u1ED1i x\u1EE9ng ${e.of} qua \u0111i\u1EC3m ${e.through}`;
|
|
10906
11180
|
case "reflectLine":
|
|
@@ -10911,6 +11185,8 @@ function describeEntity(e) {
|
|
|
10911
11185
|
const distStr = d.kind === "circleRadius" ? `r(${d.circle})` : d.kind === "segmentLength" ? `|${d.p1}${d.p2}|` : `${d.value}`;
|
|
10912
11186
|
return `${e.name} = \u0111i\u1EC3m tr\xEAn tia ${e.from}${e.through} c\xE1ch ${e.through} m\u1ED9t kho\u1EA3ng ${distStr}`;
|
|
10913
11187
|
}
|
|
11188
|
+
case "onPerpBisector":
|
|
11189
|
+
return `${e.name} = \u0111i\u1EC3m tr\xEAn trung tr\u1EF1c ${e.p1}${e.p2}`;
|
|
10914
11190
|
default: {
|
|
10915
11191
|
return "";
|
|
10916
11192
|
}
|
|
@@ -11033,7 +11309,8 @@ var init_host = __esm({
|
|
|
11033
11309
|
version: 1,
|
|
11034
11310
|
jsonState
|
|
11035
11311
|
}),
|
|
11036
|
-
editingElementId: editingElement?.id ?? null
|
|
11312
|
+
editingElementId: editingElement?.id ?? null,
|
|
11313
|
+
preserveExistingSize: true
|
|
11037
11314
|
});
|
|
11038
11315
|
} catch (err) {
|
|
11039
11316
|
console.error("Geometry insert failed:", err);
|
|
@@ -14576,7 +14853,8 @@ var init_host3 = __esm({
|
|
|
14576
14853
|
version: 2,
|
|
14577
14854
|
jsonState
|
|
14578
14855
|
}),
|
|
14579
|
-
editingElementId: editingElement?.id ?? null
|
|
14856
|
+
editingElementId: editingElement?.id ?? null,
|
|
14857
|
+
preserveExistingSize: true
|
|
14580
14858
|
});
|
|
14581
14859
|
onClose();
|
|
14582
14860
|
},
|
|
@@ -15879,7 +16157,8 @@ var init_host4 = __esm({
|
|
|
15879
16157
|
version: 2,
|
|
15880
16158
|
jsonState
|
|
15881
16159
|
}),
|
|
15882
|
-
editingElementId: editingElement?.id ?? null
|
|
16160
|
+
editingElementId: editingElement?.id ?? null,
|
|
16161
|
+
preserveExistingSize: true
|
|
15883
16162
|
});
|
|
15884
16163
|
} catch (err) {
|
|
15885
16164
|
console.error("Graph2D insert failed:", err);
|