@xom11/whiteboard 0.31.0 → 0.32.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 +148 -27
- package/dist/ai.d.ts +148 -27
- package/dist/ai.js +2652 -430
- package/dist/ai.js.map +1 -1
- package/dist/ai.mjs +2482 -426
- package/dist/ai.mjs.map +1 -1
- package/dist/catalog.json +4 -4
- package/dist/{chunk-JJ4FPCBE.mjs → chunk-4EXRNZRX.mjs} +641 -75
- package/dist/chunk-4EXRNZRX.mjs.map +1 -0
- package/dist/{chunk-BMYC2ILT.mjs → chunk-DBG6DZ2V.mjs} +4 -4
- package/dist/{chunk-BMYC2ILT.mjs.map → chunk-DBG6DZ2V.mjs.map} +1 -1
- package/dist/{chunk-T3N4BSJV.mjs → chunk-DCFZTKFE.mjs} +13 -5
- package/dist/chunk-DCFZTKFE.mjs.map +1 -0
- package/dist/{chunk-DWIEVCGK.mjs → chunk-DJM5EWTT.mjs} +53 -3
- package/dist/chunk-DJM5EWTT.mjs.map +1 -0
- package/dist/{chunk-44JY2AKC.mjs → chunk-F42MZ745.mjs} +3 -3
- package/dist/{chunk-44JY2AKC.mjs.map → chunk-F42MZ745.mjs.map} +1 -1
- package/dist/{chunk-TMRFSOM7.mjs → chunk-FI2MGX5H.mjs} +4 -4
- package/dist/{chunk-TMRFSOM7.mjs.map → chunk-FI2MGX5H.mjs.map} +1 -1
- package/dist/{chunk-KOXOC2FI.mjs → chunk-KLGPAMJS.mjs} +10 -7
- package/dist/chunk-KLGPAMJS.mjs.map +1 -0
- package/dist/{chunk-73Q7ADVL.mjs → chunk-MNHOFOM5.mjs} +2 -2
- package/dist/{chunk-73Q7ADVL.mjs.map → chunk-MNHOFOM5.mjs.map} +1 -1
- package/dist/{chunk-K5BS2H56.mjs → chunk-MOAXHVKI.mjs} +4 -4
- package/dist/{chunk-K5BS2H56.mjs.map → chunk-MOAXHVKI.mjs.map} +1 -1
- package/dist/chunk-SUXIMLYN.mjs +238 -0
- package/dist/chunk-SUXIMLYN.mjs.map +1 -0
- package/dist/{chunk-LTLLQUMN.mjs → chunk-XSTGTEGP.mjs} +4 -4
- package/dist/{chunk-LTLLQUMN.mjs.map → chunk-XSTGTEGP.mjs.map} +1 -1
- package/dist/geometry-2d.js +883 -119
- package/dist/geometry-2d.js.map +1 -1
- package/dist/geometry-2d.mjs +4 -4
- package/dist/geometry-3d.js +3995 -471
- package/dist/geometry-3d.js.map +1 -1
- package/dist/geometry-3d.mjs +4 -4
- package/dist/graph-2d.js +706 -111
- package/dist/graph-2d.js.map +1 -1
- package/dist/graph-2d.mjs +4 -4
- package/dist/{handleExtractProblem-BrDY9ifM.d.ts → handleExtractProblem-mtJyhEw3.d.mts} +2 -0
- package/dist/{handleExtractProblem-BrDY9ifM.d.mts → handleExtractProblem-mtJyhEw3.d.ts} +2 -0
- package/dist/{host-KMWP7KBT.mjs → host-JNLJ5BVR.mjs} +8 -8
- package/dist/{host-KMWP7KBT.mjs.map → host-JNLJ5BVR.mjs.map} +1 -1
- package/dist/{host-4FIUNIDQ.mjs → host-VRWLQSMH.mjs} +7 -7
- package/dist/{host-4FIUNIDQ.mjs.map → host-VRWLQSMH.mjs.map} +1 -1
- package/dist/host-W4U6YCSX.mjs +4973 -0
- package/dist/host-W4U6YCSX.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4090 -392
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +27 -21
- package/dist/index.mjs.map +1 -1
- package/dist/serialize-7UZWQQ4I.mjs +9 -0
- package/dist/{serialize-PGHQZEPV.mjs.map → serialize-7UZWQQ4I.mjs.map} +1 -1
- package/package.json +4 -1
- package/dist/chunk-DWIEVCGK.mjs.map +0 -1
- package/dist/chunk-JJ4FPCBE.mjs.map +0 -1
- package/dist/chunk-KOXOC2FI.mjs.map +0 -1
- package/dist/chunk-QK6OVDLC.mjs +0 -103
- package/dist/chunk-QK6OVDLC.mjs.map +0 -1
- package/dist/chunk-T3N4BSJV.mjs.map +0 -1
- package/dist/host-4ZB4XD4S.mjs +0 -2378
- package/dist/host-4ZB4XD4S.mjs.map +0 -1
- package/dist/serialize-PGHQZEPV.mjs +0 -9
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { labelOpts, compile, validate } from './chunk-CH6SFONH.mjs';
|
|
3
|
-
import { createEmptyState } from './chunk-
|
|
3
|
+
import { createEmptyState } from './chunk-MNHOFOM5.mjs';
|
|
4
4
|
import { registerKind } from './chunk-B4NJJZFR.mjs';
|
|
5
5
|
import * as React from 'react';
|
|
6
6
|
|
|
@@ -81,8 +81,377 @@ function constraintRefs(c) {
|
|
|
81
81
|
return [c.polygonId];
|
|
82
82
|
case "onSphere":
|
|
83
83
|
return [c.sphereId];
|
|
84
|
-
|
|
84
|
+
case "midpoint":
|
|
85
|
+
return [c.p1, c.p2];
|
|
86
|
+
case "centroid":
|
|
87
|
+
return [...c.vertices];
|
|
88
|
+
case "intersectionLines":
|
|
89
|
+
return [c.a1, c.b1, c.a2, c.b2];
|
|
90
|
+
case "intersectionLinePlane":
|
|
91
|
+
return [c.a, c.b, c.plane];
|
|
92
|
+
case "perpFootLine":
|
|
93
|
+
return [c.from, c.a, c.b];
|
|
94
|
+
case "perpFootPlane":
|
|
95
|
+
return [c.from, c.plane];
|
|
96
|
+
case "circumsphereCenter":
|
|
97
|
+
return [...c.vertices];
|
|
98
|
+
case "pyramidInsphereCenter":
|
|
99
|
+
return [c.apex, ...c.vertices];
|
|
100
|
+
case "faceCircumcenter":
|
|
101
|
+
return [...c.vertices];
|
|
102
|
+
case "pointAboveFace":
|
|
103
|
+
return [c.base, c.apex, ...c.vertices];
|
|
104
|
+
// Kind KHÔNG có ref scene-object (toạ độ literal): liệt kê TƯỜNG MINH để
|
|
105
|
+
// exhaustive never-guard bên dưới buộc khai báo khi thêm constraint kind mới
|
|
106
|
+
// (mô phỏng constraintRefs2D — quên case = cascade-delete/deps sai âm thầm).
|
|
107
|
+
case "free":
|
|
108
|
+
case "onGround":
|
|
109
|
+
case "onAxis":
|
|
110
|
+
return [];
|
|
111
|
+
default: {
|
|
85
112
|
return [];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/core/scene/kinds/constraint3d-math.ts
|
|
118
|
+
function sub(a, b) {
|
|
119
|
+
return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
|
120
|
+
}
|
|
121
|
+
function add(a, b) {
|
|
122
|
+
return [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
|
|
123
|
+
}
|
|
124
|
+
function scale(a, k) {
|
|
125
|
+
return [a[0] * k, a[1] * k, a[2] * k];
|
|
126
|
+
}
|
|
127
|
+
function dot(a, b) {
|
|
128
|
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
129
|
+
}
|
|
130
|
+
function cross(a, b) {
|
|
131
|
+
return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]];
|
|
132
|
+
}
|
|
133
|
+
function norm(a) {
|
|
134
|
+
return Math.sqrt(dot(a, a));
|
|
135
|
+
}
|
|
136
|
+
function normalize(a) {
|
|
137
|
+
const n = norm(a);
|
|
138
|
+
return n === 0 ? a : scale(a, 1 / n);
|
|
139
|
+
}
|
|
140
|
+
var MAX_REC_DEPTH = 512;
|
|
141
|
+
var _recDepth = 0;
|
|
142
|
+
function getPointWorld(id, state) {
|
|
143
|
+
const obj = state.objects[id];
|
|
144
|
+
if (!obj || obj.kind !== "point3d") {
|
|
145
|
+
throw new Error(`constraintMath: point ${id} not found`);
|
|
146
|
+
}
|
|
147
|
+
const attrs = obj.attrs;
|
|
148
|
+
return constraintToWorld(attrs.constraint, state);
|
|
149
|
+
}
|
|
150
|
+
function getPlaneBasis(planeObj, state) {
|
|
151
|
+
const a = planeObj.attrs;
|
|
152
|
+
let p1, p2, p3;
|
|
153
|
+
if (a.construction) {
|
|
154
|
+
const r = planeConstructionWorld(a.construction, state);
|
|
155
|
+
p1 = r.p1;
|
|
156
|
+
p2 = r.p2;
|
|
157
|
+
p3 = r.p3;
|
|
158
|
+
} else {
|
|
159
|
+
p1 = getPointWorld(a.p1, state);
|
|
160
|
+
p2 = getPointWorld(a.p2, state);
|
|
161
|
+
p3 = getPointWorld(a.p3, state);
|
|
162
|
+
}
|
|
163
|
+
const basis1 = sub(p2, p1);
|
|
164
|
+
const tmp = sub(p3, p1);
|
|
165
|
+
const normal = normalize(cross(basis1, tmp));
|
|
166
|
+
const basis2 = cross(normal, basis1);
|
|
167
|
+
return { origin: p1, basis1, basis2, normal };
|
|
168
|
+
}
|
|
169
|
+
function orthoBasisFromNormal(n) {
|
|
170
|
+
const seed = Math.abs(n[0]) < 0.9 ? [1, 0, 0] : [0, 1, 0];
|
|
171
|
+
const basis1 = normalize(cross(n, seed));
|
|
172
|
+
const basis2 = cross(n, basis1);
|
|
173
|
+
return { basis1, basis2 };
|
|
174
|
+
}
|
|
175
|
+
function planeConstructionWorld(c, state) {
|
|
176
|
+
if (_recDepth >= MAX_REC_DEPTH) throw new Error("planeConstructionWorld: \u0111\u1EC7 quy qu\xE1 s\xE2u \u2014 chu tr\xECnh tham chi\u1EBFu m\u1EB7t?");
|
|
177
|
+
_recDepth++;
|
|
178
|
+
try {
|
|
179
|
+
return planeConstructionWorldInner(c, state);
|
|
180
|
+
} finally {
|
|
181
|
+
_recDepth--;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function planeConstructionWorldInner(c, state) {
|
|
185
|
+
switch (c.kind) {
|
|
186
|
+
case "planeParallelThrough": {
|
|
187
|
+
const P = getPointWorld(c.point, state);
|
|
188
|
+
const ref = state.objects[c.refPlane];
|
|
189
|
+
if (!ref || ref.kind !== "plane3d") throw new Error("planeParallelThrough: m\u1EB7t tham chi\u1EBFu thi\u1EBFu");
|
|
190
|
+
const { basis1, basis2 } = getPlaneBasis(ref, state);
|
|
191
|
+
if (norm(basis1) < 1e-12 || norm(basis2) < 1e-12) {
|
|
192
|
+
return { p1: P, p2: add(P, [1, 0, 0]), p3: add(P, [0, 1, 0]) };
|
|
193
|
+
}
|
|
194
|
+
return { p1: P, p2: add(P, basis1), p3: add(P, basis2) };
|
|
195
|
+
}
|
|
196
|
+
case "planePerpToLine": {
|
|
197
|
+
const P = getPointWorld(c.point, state);
|
|
198
|
+
const dir = sub(getPointWorld(c.lineB, state), getPointWorld(c.lineA, state));
|
|
199
|
+
const dn = norm(dir);
|
|
200
|
+
if (dn < 1e-12) return { p1: P, p2: add(P, [1, 0, 0]), p3: add(P, [0, 1, 0]) };
|
|
201
|
+
const { basis1, basis2 } = orthoBasisFromNormal(scale(dir, 1 / dn));
|
|
202
|
+
return { p1: P, p2: add(P, basis1), p3: add(P, basis2) };
|
|
203
|
+
}
|
|
204
|
+
default: {
|
|
205
|
+
throw new Error("planeConstructionWorld: kind kh\xF4ng h\u1ED7 tr\u1EE3");
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function lineDefiningPointIds(line) {
|
|
210
|
+
if (line.kind === "line3d" || line.kind === "segment3d") {
|
|
211
|
+
const a = line.attrs;
|
|
212
|
+
return a.p1 && a.p2 ? [a.p1, a.p2] : null;
|
|
213
|
+
}
|
|
214
|
+
if (line.kind === "ray3d") {
|
|
215
|
+
const a = line.attrs;
|
|
216
|
+
return [a.origin, a.through];
|
|
217
|
+
}
|
|
218
|
+
if (line.kind === "vector3d") {
|
|
219
|
+
const a = line.attrs;
|
|
220
|
+
return [a.from, a.to];
|
|
221
|
+
}
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
function lineEndpointsWorld(lineId, state, ctx) {
|
|
225
|
+
const line = state.objects[lineId];
|
|
226
|
+
if (!line) throw new Error(`${ctx}: \u0111\u01B0\u1EDDng ${lineId} kh\xF4ng t\u1ED3n t\u1EA1i`);
|
|
227
|
+
const ids = lineDefiningPointIds(line);
|
|
228
|
+
if (!ids) throw new Error(`${ctx}: kind ${line.kind} kh\xF4ng ph\u1EA3i \u0111\u01B0\u1EDDng`);
|
|
229
|
+
return { a: getPointWorld(ids[0], state), b: getPointWorld(ids[1], state) };
|
|
230
|
+
}
|
|
231
|
+
function lineLineClosestMidpoint(A, B, C, D) {
|
|
232
|
+
const u = sub(B, A), v = sub(D, C), w0 = sub(A, C);
|
|
233
|
+
const a = dot(u, u), b = dot(u, v), cc = dot(v, v), d = dot(u, w0), e = dot(v, w0);
|
|
234
|
+
const denom = a * cc - b * b;
|
|
235
|
+
if (Math.abs(denom) < 1e-12) return scale(add(A, C), 0.5);
|
|
236
|
+
const sc = (b * e - cc * d) / denom;
|
|
237
|
+
const tc = (a * e - b * d) / denom;
|
|
238
|
+
return scale(add(add(A, scale(u, sc)), add(C, scale(v, tc))), 0.5);
|
|
239
|
+
}
|
|
240
|
+
function planeOriginNormal(planeId, state, ctx) {
|
|
241
|
+
const plane = state.objects[planeId];
|
|
242
|
+
if (!plane || plane.kind !== "plane3d") throw new Error(`${ctx}: m\u1EB7t ph\u1EB3ng ${planeId} thi\u1EBFu`);
|
|
243
|
+
const { origin, normal } = getPlaneBasis(plane, state);
|
|
244
|
+
return { origin, normal };
|
|
245
|
+
}
|
|
246
|
+
function lineConstructionWorld(c, state) {
|
|
247
|
+
switch (c.kind) {
|
|
248
|
+
case "planePlaneIntersection": {
|
|
249
|
+
const P1 = planeOriginNormal(c.plane1, state, "planePlaneIntersection");
|
|
250
|
+
const P2 = planeOriginNormal(c.plane2, state, "planePlaneIntersection");
|
|
251
|
+
const d = cross(P1.normal, P2.normal);
|
|
252
|
+
const dd = dot(d, d);
|
|
253
|
+
if (dd < 1e-12) {
|
|
254
|
+
return { a: P1.origin, b: add(P1.origin, P2.normal) };
|
|
255
|
+
}
|
|
256
|
+
const c1 = dot(P1.normal, P1.origin);
|
|
257
|
+
const c2 = dot(P2.normal, P2.origin);
|
|
258
|
+
const p0 = scale(add(scale(cross(P2.normal, d), c1), scale(cross(d, P1.normal), c2)), 1 / dd);
|
|
259
|
+
return { a: p0, b: add(p0, d) };
|
|
260
|
+
}
|
|
261
|
+
case "lineParallelThrough": {
|
|
262
|
+
const P = getPointWorld(c.point, state);
|
|
263
|
+
const dir = sub(getPointWorld(c.dirB, state), getPointWorld(c.dirA, state));
|
|
264
|
+
if (norm(dir) < 1e-12) return { a: P, b: add(P, [1, 0, 0]) };
|
|
265
|
+
return { a: P, b: add(P, dir) };
|
|
266
|
+
}
|
|
267
|
+
case "linePerpToPlane": {
|
|
268
|
+
const P = getPointWorld(c.point, state);
|
|
269
|
+
const { normal } = planeOriginNormal(c.plane, state, "linePerpToPlane");
|
|
270
|
+
return { a: P, b: add(P, normal) };
|
|
271
|
+
}
|
|
272
|
+
default: {
|
|
273
|
+
throw new Error("lineConstructionWorld: kind kh\xF4ng h\u1ED7 tr\u1EE3");
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function solve3(M, b) {
|
|
278
|
+
const det3 = (m) => m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
|
|
279
|
+
const det = det3(M);
|
|
280
|
+
if (Math.abs(det) < 1e-9) return null;
|
|
281
|
+
const colDet = (j) => {
|
|
282
|
+
const A = M.map((r) => r.slice());
|
|
283
|
+
for (let i = 0; i < 3; i++) A[i][j] = b[i];
|
|
284
|
+
return det3(A);
|
|
285
|
+
};
|
|
286
|
+
return [colDet(0) / det, colDet(1) / det, colDet(2) / det];
|
|
287
|
+
}
|
|
288
|
+
function constraintToWorld(c, state) {
|
|
289
|
+
if (_recDepth >= MAX_REC_DEPTH) throw new Error("constraintToWorld: \u0111\u1EC7 quy qu\xE1 s\xE2u \u2014 chu tr\xECnh tham chi\u1EBFu?");
|
|
290
|
+
_recDepth++;
|
|
291
|
+
try {
|
|
292
|
+
return constraintToWorldInner(c, state);
|
|
293
|
+
} finally {
|
|
294
|
+
_recDepth--;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function constraintToWorldInner(c, state) {
|
|
298
|
+
switch (c.kind) {
|
|
299
|
+
case "free":
|
|
300
|
+
return [c.x, c.y, c.z];
|
|
301
|
+
case "onGround":
|
|
302
|
+
return [c.x, c.y, 0];
|
|
303
|
+
case "onAxis": {
|
|
304
|
+
if (c.axis === "x") return [c.t, 0, 0];
|
|
305
|
+
if (c.axis === "y") return [0, c.t, 0];
|
|
306
|
+
return [0, 0, c.t];
|
|
307
|
+
}
|
|
308
|
+
case "onPlane": {
|
|
309
|
+
const plane = state.objects[c.planeId];
|
|
310
|
+
if (!plane || plane.kind !== "plane3d") throw new Error("onPlane: plane missing");
|
|
311
|
+
const { origin, basis1, basis2 } = getPlaneBasis(plane, state);
|
|
312
|
+
return add(add(origin, scale(basis1, c.u)), scale(basis2, c.v));
|
|
313
|
+
}
|
|
314
|
+
case "onLine": {
|
|
315
|
+
const { a, b } = lineEndpointsWorld(c.lineId, state, "onLine");
|
|
316
|
+
return add(a, scale(sub(b, a), c.t));
|
|
317
|
+
}
|
|
318
|
+
case "onPolygon": {
|
|
319
|
+
const pg = state.objects[c.polygonId];
|
|
320
|
+
if (!pg || pg.kind !== "polygon3d") throw new Error("onPolygon: parent missing");
|
|
321
|
+
const v = pg.attrs.vertices;
|
|
322
|
+
if (v.length < 3) throw new Error("onPolygon: < 3 vertices");
|
|
323
|
+
const p1 = getPointWorld(v[0], state);
|
|
324
|
+
const p2 = getPointWorld(v[1], state);
|
|
325
|
+
const p3 = getPointWorld(v[2], state);
|
|
326
|
+
const basis1 = sub(p2, p1);
|
|
327
|
+
const tmp = sub(p3, p1);
|
|
328
|
+
const normal = normalize(cross(basis1, tmp));
|
|
329
|
+
const basis2 = cross(normal, basis1);
|
|
330
|
+
return add(add(p1, scale(basis1, c.u)), scale(basis2, c.v));
|
|
331
|
+
}
|
|
332
|
+
case "onSphere": {
|
|
333
|
+
const sph = state.objects[c.sphereId];
|
|
334
|
+
if (!sph || sph.kind !== "sphere3d") throw new Error("onSphere: parent missing");
|
|
335
|
+
const a = sph.attrs;
|
|
336
|
+
const center = getPointWorld(a.center, state);
|
|
337
|
+
const surface = getPointWorld(a.surfacePoint, state);
|
|
338
|
+
const radius = norm(sub(surface, center));
|
|
339
|
+
const x = center[0] + radius * Math.sin(c.phi) * Math.cos(c.theta);
|
|
340
|
+
const y = center[1] + radius * Math.sin(c.phi) * Math.sin(c.theta);
|
|
341
|
+
const z = center[2] + radius * Math.cos(c.phi);
|
|
342
|
+
return [x, y, z];
|
|
343
|
+
}
|
|
344
|
+
// ───── Điểm phái sinh (v1) ─────
|
|
345
|
+
case "midpoint": {
|
|
346
|
+
const p1 = getPointWorld(c.p1, state);
|
|
347
|
+
const p2 = getPointWorld(c.p2, state);
|
|
348
|
+
return scale(add(p1, p2), 0.5);
|
|
349
|
+
}
|
|
350
|
+
case "centroid": {
|
|
351
|
+
const n = c.vertices.length;
|
|
352
|
+
if (n === 0) return [0, 0, 0];
|
|
353
|
+
let acc = [0, 0, 0];
|
|
354
|
+
for (const id of c.vertices) acc = add(acc, getPointWorld(id, state));
|
|
355
|
+
return scale(acc, 1 / n);
|
|
356
|
+
}
|
|
357
|
+
case "circumsphereCenter": {
|
|
358
|
+
const P = c.vertices.map((id) => getPointWorld(id, state));
|
|
359
|
+
if (P.length < 4) return P.length ? P[0] : [0, 0, 0];
|
|
360
|
+
const p0 = P[0];
|
|
361
|
+
const M = [[0, 0, 0], [0, 0, 0], [0, 0, 0]];
|
|
362
|
+
const rhs = [0, 0, 0];
|
|
363
|
+
for (let i = 1; i < P.length; i++) {
|
|
364
|
+
const r = [2 * (P[i][0] - p0[0]), 2 * (P[i][1] - p0[1]), 2 * (P[i][2] - p0[2])];
|
|
365
|
+
const bi = dot(P[i], P[i]) - dot(p0, p0);
|
|
366
|
+
for (let a = 0; a < 3; a++) {
|
|
367
|
+
for (let bcol = 0; bcol < 3; bcol++) M[a][bcol] += r[a] * r[bcol];
|
|
368
|
+
rhs[a] += r[a] * bi;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
const sol = solve3(M, rhs);
|
|
372
|
+
if (sol) return sol;
|
|
373
|
+
let acc = [0, 0, 0];
|
|
374
|
+
for (const p of P) acc = add(acc, p);
|
|
375
|
+
return scale(acc, 1 / P.length);
|
|
376
|
+
}
|
|
377
|
+
case "pyramidInsphereCenter": {
|
|
378
|
+
const S = getPointWorld(c.apex, state);
|
|
379
|
+
const base = c.vertices.map((id) => getPointWorld(id, state));
|
|
380
|
+
const n = base.length;
|
|
381
|
+
if (n < 3) return S;
|
|
382
|
+
let G = [0, 0, 0];
|
|
383
|
+
for (const p of base) G = add(G, p);
|
|
384
|
+
G = scale(G, 1 / n);
|
|
385
|
+
let nb = normalize(cross(sub(base[1], base[0]), sub(base[2], base[0])));
|
|
386
|
+
if (dot(nb, sub(S, G)) < 0) nb = scale(nb, -1);
|
|
387
|
+
let m = normalize(cross(sub(base[0], S), sub(base[1], S)));
|
|
388
|
+
if (dot(m, sub(G, base[0])) < 0) m = scale(m, -1);
|
|
389
|
+
const denom = 1 - dot(m, nb);
|
|
390
|
+
const s = Math.abs(denom) < 1e-9 ? NaN : dot(m, sub(G, base[0])) / denom;
|
|
391
|
+
if (!Number.isFinite(s) || s <= 0) {
|
|
392
|
+
return scale(add(S, scale(G, n)), 1 / (n + 1));
|
|
393
|
+
}
|
|
394
|
+
return add(G, scale(nb, s));
|
|
395
|
+
}
|
|
396
|
+
case "faceCircumcenter": {
|
|
397
|
+
const P = c.vertices.map((id) => getPointWorld(id, state));
|
|
398
|
+
if (P.length < 3) return P.length ? P[0] : [0, 0, 0];
|
|
399
|
+
const [p0, p1, p2] = P;
|
|
400
|
+
const e1 = sub(p1, p0), e2 = sub(p2, p0);
|
|
401
|
+
const nrm = cross(e1, e2);
|
|
402
|
+
const M = [
|
|
403
|
+
[2 * e1[0], 2 * e1[1], 2 * e1[2]],
|
|
404
|
+
[2 * e2[0], 2 * e2[1], 2 * e2[2]],
|
|
405
|
+
[nrm[0], nrm[1], nrm[2]]
|
|
406
|
+
];
|
|
407
|
+
const rhs = [dot(p1, p1) - dot(p0, p0), dot(p2, p2) - dot(p0, p0), dot(nrm, p0)];
|
|
408
|
+
const sol = solve3(M, rhs);
|
|
409
|
+
if (sol) return sol;
|
|
410
|
+
return scale(add(add(p0, p1), p2), 1 / 3);
|
|
411
|
+
}
|
|
412
|
+
case "pointAboveFace": {
|
|
413
|
+
const G = getPointWorld(c.base, state);
|
|
414
|
+
const P = c.vertices.map((id) => getPointWorld(id, state));
|
|
415
|
+
if (P.length < 3) return G;
|
|
416
|
+
let n = normalize(cross(sub(P[1], P[0]), sub(P[2], P[0])));
|
|
417
|
+
const S = getPointWorld(c.apex, state);
|
|
418
|
+
if (dot(n, sub(S, P[0])) < 0) n = scale(n, -1);
|
|
419
|
+
const h = dot(sub(S, P[0]), n);
|
|
420
|
+
if (!Number.isFinite(h) || h <= 1e-9) return G;
|
|
421
|
+
return add(G, scale(n, h));
|
|
422
|
+
}
|
|
423
|
+
case "intersectionLines": {
|
|
424
|
+
const A = getPointWorld(c.a1, state), B = getPointWorld(c.b1, state);
|
|
425
|
+
const C = getPointWorld(c.a2, state), D = getPointWorld(c.b2, state);
|
|
426
|
+
return lineLineClosestMidpoint(A, B, C, D);
|
|
427
|
+
}
|
|
428
|
+
case "intersectionLinePlane": {
|
|
429
|
+
const A = getPointWorld(c.a, state), B = getPointWorld(c.b, state);
|
|
430
|
+
const plane = state.objects[c.plane];
|
|
431
|
+
if (!plane || plane.kind !== "plane3d") throw new Error("intersectionLinePlane: m\u1EB7t ph\u1EB3ng thi\u1EBFu");
|
|
432
|
+
const { origin, normal } = getPlaneBasis(plane, state);
|
|
433
|
+
const dir = sub(B, A);
|
|
434
|
+
const dn = dot(dir, normal);
|
|
435
|
+
if (Math.abs(dn) < 1e-12) return A;
|
|
436
|
+
const t = dot(sub(origin, A), normal) / dn;
|
|
437
|
+
return add(A, scale(dir, t));
|
|
438
|
+
}
|
|
439
|
+
case "perpFootLine": {
|
|
440
|
+
const P = getPointWorld(c.from, state);
|
|
441
|
+
const A = getPointWorld(c.a, state), B = getPointWorld(c.b, state);
|
|
442
|
+
const u = sub(B, A);
|
|
443
|
+
const uu = dot(u, u);
|
|
444
|
+
const t = uu === 0 ? 0 : dot(sub(P, A), u) / uu;
|
|
445
|
+
return add(A, scale(u, t));
|
|
446
|
+
}
|
|
447
|
+
case "perpFootPlane": {
|
|
448
|
+
const P = getPointWorld(c.from, state);
|
|
449
|
+
const plane = state.objects[c.plane];
|
|
450
|
+
if (!plane || plane.kind !== "plane3d") throw new Error("perpFootPlane: m\u1EB7t ph\u1EB3ng thi\u1EBFu");
|
|
451
|
+
const { origin, normal } = getPlaneBasis(plane, state);
|
|
452
|
+
const dist2 = dot(sub(P, origin), normal);
|
|
453
|
+
return sub(P, scale(normal, dist2));
|
|
454
|
+
}
|
|
86
455
|
}
|
|
87
456
|
}
|
|
88
457
|
|
|
@@ -124,6 +493,15 @@ var def = {
|
|
|
124
493
|
if (c.kind === "onLine") return `${obj.label} tr\xEAn \u0111\u01B0\u1EDDng ${c.lineId}`;
|
|
125
494
|
if (c.kind === "onPolygon") return `${obj.label} tr\xEAn \u0111a gi\xE1c ${c.polygonId}`;
|
|
126
495
|
if (c.kind === "onSphere") return `${obj.label} tr\xEAn m\u1EB7t c\u1EA7u ${c.sphereId}`;
|
|
496
|
+
if (c.kind === "midpoint") return `${obj.label} = trung \u0111i\u1EC3m ${c.p1}${c.p2}`;
|
|
497
|
+
if (c.kind === "centroid") return `${obj.label} = tr\u1ECDng t\xE2m ${c.vertices.join("")}`;
|
|
498
|
+
if (c.kind === "intersectionLines") return `${obj.label} = giao 2 \u0111\u01B0\u1EDDng (${c.a1}${c.b1}, ${c.a2}${c.b2})`;
|
|
499
|
+
if (c.kind === "intersectionLinePlane") return `${obj.label} = giao ${c.a}${c.b} \u2229 ${c.plane}`;
|
|
500
|
+
if (c.kind === "perpFootLine") return `${obj.label} = ch\xE2n \u22A5 t\u1EEB ${c.from} xu\u1ED1ng ${c.a}${c.b}`;
|
|
501
|
+
if (c.kind === "perpFootPlane") return `${obj.label} = ch\xE2n \u22A5 t\u1EEB ${c.from} xu\u1ED1ng ${c.plane}`;
|
|
502
|
+
if (c.kind === "pyramidInsphereCenter") return `${obj.label} = t\xE2m c\u1EA7u n\u1ED9i ti\u1EBFp ch\xF3p ${c.apex}.${c.vertices.join("")}`;
|
|
503
|
+
if (c.kind === "faceCircumcenter") return `${obj.label} = t\xE2m ngo\u1EA1i ti\u1EBFp ${c.vertices.join("")}`;
|
|
504
|
+
if (c.kind === "pointAboveFace") return `${obj.label} = \u0111\u1EC9nh tr\u1EE5c tr\u1EE5 \u22A5 m\u1EB7t ${c.vertices.join("")}`;
|
|
127
505
|
return obj.label;
|
|
128
506
|
},
|
|
129
507
|
render: (obj, ctx) => {
|
|
@@ -173,6 +551,15 @@ var def = {
|
|
|
173
551
|
() => sph.F(c.theta, c.phi)[2]
|
|
174
552
|
], opts);
|
|
175
553
|
}
|
|
554
|
+
const getState = ctx.getState;
|
|
555
|
+
if (getState) {
|
|
556
|
+
const cw = () => constraintToWorld(c, getState());
|
|
557
|
+
return view.create("point3d", [
|
|
558
|
+
() => cw()[0],
|
|
559
|
+
() => cw()[1],
|
|
560
|
+
() => cw()[2]
|
|
561
|
+
], { ...opts, needsRegularUpdate: true });
|
|
562
|
+
}
|
|
176
563
|
return view.create("point3d", [0, 0, 0], opts);
|
|
177
564
|
}
|
|
178
565
|
};
|
|
@@ -221,28 +608,71 @@ var def2 = {
|
|
|
221
608
|
registerKind(def2);
|
|
222
609
|
|
|
223
610
|
// src/core/scene/kinds/line3d.ts
|
|
224
|
-
|
|
611
|
+
function line3dConstructionRefs(c) {
|
|
612
|
+
switch (c.kind) {
|
|
613
|
+
case "planePlaneIntersection":
|
|
614
|
+
return [c.plane1, c.plane2];
|
|
615
|
+
case "lineParallelThrough":
|
|
616
|
+
return [c.point, c.dirA, c.dirB];
|
|
617
|
+
case "linePerpToPlane":
|
|
618
|
+
return [c.point, c.plane];
|
|
619
|
+
default: {
|
|
620
|
+
return [];
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
var def3 = {
|
|
225
625
|
type: "line3d",
|
|
226
626
|
schemaVersion: 1,
|
|
227
627
|
migrate: {},
|
|
228
628
|
validate: (a) => {
|
|
229
|
-
if (
|
|
629
|
+
if (a?.construction) return;
|
|
630
|
+
if (!a?.p1 || !a?.p2) throw new Error("line3d: p1/p2 required (ho\u1EB7c construction)");
|
|
631
|
+
},
|
|
632
|
+
dependsOn: (a) => a.construction ? line3dConstructionRefs(a.construction) : [a.p1, a.p2],
|
|
633
|
+
describe: (obj, state) => {
|
|
634
|
+
const c = obj.attrs.construction;
|
|
635
|
+
if (!c) return `\u0110\u01B0\u1EDDng ${obj.label} qua ${labelOf(obj.attrs.p1, state)}, ${labelOf(obj.attrs.p2, state)}`;
|
|
636
|
+
switch (c.kind) {
|
|
637
|
+
case "planePlaneIntersection":
|
|
638
|
+
return `Giao tuy\u1EBFn ${obj.label} = ${labelOf(c.plane1, state)} \u2229 ${labelOf(c.plane2, state)}`;
|
|
639
|
+
case "lineParallelThrough":
|
|
640
|
+
return `${obj.label} \u2225 ${labelOf(c.dirA, state)}${labelOf(c.dirB, state)} qua ${labelOf(c.point, state)}`;
|
|
641
|
+
case "linePerpToPlane":
|
|
642
|
+
return `${obj.label} \u22A5 ${labelOf(c.plane, state)} qua ${labelOf(c.point, state)}`;
|
|
643
|
+
default:
|
|
644
|
+
return obj.label;
|
|
645
|
+
}
|
|
230
646
|
},
|
|
231
|
-
dependsOn: (a) => [a.p1, a.p2],
|
|
232
|
-
describe: (obj, state) => `\u0110\u01B0\u1EDDng ${obj.label} qua ${labelOf(obj.attrs.p1, state)}, ${labelOf(obj.attrs.p2, state)}`,
|
|
233
647
|
render: (obj, ctx) => {
|
|
234
648
|
const view = ctx.jxg;
|
|
649
|
+
const baseOpts = {
|
|
650
|
+
strokeColor: obj.attrs.color ?? "#0f172a",
|
|
651
|
+
strokeWidth: 2,
|
|
652
|
+
visible: obj.visible
|
|
653
|
+
};
|
|
654
|
+
const c = obj.attrs.construction;
|
|
655
|
+
if (c) {
|
|
656
|
+
const getState = ctx.getState;
|
|
657
|
+
if (!getState) throw new Error("line3d construction: c\u1EA7n ctx.getState");
|
|
658
|
+
const lc = () => lineConstructionWorld(c, getState());
|
|
659
|
+
return view.create("line3d", [() => lc().a, () => lc().b], {
|
|
660
|
+
...baseOpts,
|
|
661
|
+
straightFirst: true,
|
|
662
|
+
straightLast: true,
|
|
663
|
+
needsRegularUpdate: true
|
|
664
|
+
});
|
|
665
|
+
}
|
|
235
666
|
const pA = ctx.resolveRef(obj.attrs.p1);
|
|
236
667
|
const pB = ctx.resolveRef(obj.attrs.p2);
|
|
237
668
|
return view.create("line3d", [pA, pB], {
|
|
669
|
+
...baseOpts,
|
|
238
670
|
straightFirst: true,
|
|
239
|
-
straightLast: true
|
|
240
|
-
strokeColor: obj.attrs.color ?? "#0f172a",
|
|
241
|
-
strokeWidth: 2,
|
|
242
|
-
visible: obj.visible
|
|
671
|
+
straightLast: true
|
|
243
672
|
});
|
|
244
673
|
}
|
|
245
|
-
}
|
|
674
|
+
};
|
|
675
|
+
registerKind(def3);
|
|
246
676
|
|
|
247
677
|
// src/core/scene/kinds/ray3d.ts
|
|
248
678
|
registerKind({
|
|
@@ -294,29 +724,71 @@ registerKind({
|
|
|
294
724
|
});
|
|
295
725
|
|
|
296
726
|
// src/core/scene/kinds/plane3d.ts
|
|
297
|
-
|
|
727
|
+
function plane3dConstructionRefs(c) {
|
|
728
|
+
switch (c.kind) {
|
|
729
|
+
case "planeParallelThrough":
|
|
730
|
+
return [c.point, c.refPlane];
|
|
731
|
+
case "planePerpToLine":
|
|
732
|
+
return [c.point, c.lineA, c.lineB];
|
|
733
|
+
default: {
|
|
734
|
+
return [];
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
var def4 = {
|
|
298
739
|
type: "plane3d",
|
|
299
740
|
schemaVersion: 1,
|
|
300
741
|
migrate: {},
|
|
301
742
|
validate: (a) => {
|
|
302
|
-
if (
|
|
743
|
+
if (a?.construction) return;
|
|
744
|
+
if (!a?.p1 || !a?.p2 || !a?.p3) throw new Error("plane3d: c\u1EA7n 3 \u0111i\u1EC3m (ho\u1EB7c construction)");
|
|
745
|
+
},
|
|
746
|
+
dependsOn: (a) => a.construction ? plane3dConstructionRefs(a.construction) : [a.p1, a.p2, a.p3],
|
|
747
|
+
describe: (obj, state) => {
|
|
748
|
+
const c = obj.attrs.construction;
|
|
749
|
+
if (!c) {
|
|
750
|
+
return `M\u1EB7t ${obj.label} qua ${labelOf(obj.attrs.p1, state)}, ${labelOf(obj.attrs.p2, state)}, ${labelOf(obj.attrs.p3, state)}`;
|
|
751
|
+
}
|
|
752
|
+
switch (c.kind) {
|
|
753
|
+
case "planeParallelThrough":
|
|
754
|
+
return `${obj.label} \u2225 ${labelOf(c.refPlane, state)} qua ${labelOf(c.point, state)}`;
|
|
755
|
+
case "planePerpToLine":
|
|
756
|
+
return `${obj.label} \u22A5 ${labelOf(c.lineA, state)}${labelOf(c.lineB, state)} qua ${labelOf(c.point, state)}`;
|
|
757
|
+
default:
|
|
758
|
+
return obj.label;
|
|
759
|
+
}
|
|
303
760
|
},
|
|
304
|
-
dependsOn: (a) => [a.p1, a.p2, a.p3],
|
|
305
|
-
describe: (obj, state) => `M\u1EB7t ${obj.label} qua ${labelOf(obj.attrs.p1, state)}, ${labelOf(obj.attrs.p2, state)}, ${labelOf(obj.attrs.p3, state)}`,
|
|
306
761
|
render: (obj, ctx) => {
|
|
307
762
|
const view = ctx.jxg;
|
|
308
|
-
|
|
309
|
-
ctx.resolveRef(obj.attrs.p1),
|
|
310
|
-
ctx.resolveRef(obj.attrs.p2),
|
|
311
|
-
ctx.resolveRef(obj.attrs.p3)
|
|
312
|
-
], {
|
|
763
|
+
const opts = {
|
|
313
764
|
fillOpacity: 0.15,
|
|
314
765
|
fillColor: obj.attrs.color ?? "#60a5fa",
|
|
315
766
|
strokeColor: obj.attrs.color ?? "#60a5fa",
|
|
316
767
|
visible: obj.visible
|
|
317
|
-
}
|
|
768
|
+
};
|
|
769
|
+
const c = obj.attrs.construction;
|
|
770
|
+
if (c) {
|
|
771
|
+
const getState = ctx.getState;
|
|
772
|
+
if (!getState) throw new Error("plane3d construction: c\u1EA7n ctx.getState");
|
|
773
|
+
const pc = () => planeConstructionWorld(c, getState());
|
|
774
|
+
const diff = (which) => {
|
|
775
|
+
const r = pc();
|
|
776
|
+
const q = r[which];
|
|
777
|
+
return [q[0] - r.p1[0], q[1] - r.p1[1], q[2] - r.p1[2]];
|
|
778
|
+
};
|
|
779
|
+
return view.create("plane3d", [() => pc().p1, () => diff("p2"), () => diff("p3")], {
|
|
780
|
+
...opts,
|
|
781
|
+
needsRegularUpdate: true
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
return view.create("plane3d", [
|
|
785
|
+
ctx.resolveRef(obj.attrs.p1),
|
|
786
|
+
ctx.resolveRef(obj.attrs.p2),
|
|
787
|
+
ctx.resolveRef(obj.attrs.p3)
|
|
788
|
+
], opts);
|
|
318
789
|
}
|
|
319
|
-
}
|
|
790
|
+
};
|
|
791
|
+
registerKind(def4);
|
|
320
792
|
|
|
321
793
|
// src/core/scene/kinds/polygon3d.ts
|
|
322
794
|
registerKind({
|
|
@@ -396,6 +868,27 @@ registerKind({
|
|
|
396
868
|
}
|
|
397
869
|
});
|
|
398
870
|
|
|
871
|
+
// src/core/scene/kinds/_ringBasis.ts
|
|
872
|
+
function perpBasis(axis) {
|
|
873
|
+
const n = Math.hypot(axis[0], axis[1], axis[2]);
|
|
874
|
+
if (n < 1e-12) return [[1, 0, 0], [0, 1, 0]];
|
|
875
|
+
const a = [axis[0] / n, axis[1] / n, axis[2] / n];
|
|
876
|
+
const seed = Math.abs(a[2]) < 0.9 ? [0, 0, 1] : [1, 0, 0];
|
|
877
|
+
let u = [
|
|
878
|
+
seed[1] * a[2] - seed[2] * a[1],
|
|
879
|
+
seed[2] * a[0] - seed[0] * a[2],
|
|
880
|
+
seed[0] * a[1] - seed[1] * a[0]
|
|
881
|
+
];
|
|
882
|
+
const un = Math.hypot(u[0], u[1], u[2]) || 1;
|
|
883
|
+
u = [u[0] / un, u[1] / un, u[2] / un];
|
|
884
|
+
const v = [
|
|
885
|
+
a[1] * u[2] - a[2] * u[1],
|
|
886
|
+
a[2] * u[0] - a[0] * u[2],
|
|
887
|
+
a[0] * u[1] - a[1] * u[0]
|
|
888
|
+
];
|
|
889
|
+
return [u, v];
|
|
890
|
+
}
|
|
891
|
+
|
|
399
892
|
// src/core/scene/kinds/cylinder3d.ts
|
|
400
893
|
var CURVED_SEGMENTS = 16;
|
|
401
894
|
registerKind({
|
|
@@ -415,14 +908,17 @@ registerKind({
|
|
|
415
908
|
const r = obj.attrs.radius;
|
|
416
909
|
const ax = a.X?.() ?? 0, ay = a.Y?.() ?? 0, az = a.Z?.() ?? 0;
|
|
417
910
|
const bx = b.X?.() ?? 0, by = b.Y?.() ?? 0, bz = b.Z?.() ?? 0;
|
|
911
|
+
const [u, v] = perpBasis([bx - ax, by - ay, bz - az]);
|
|
418
912
|
const baseRing = [];
|
|
419
913
|
const topRing = [];
|
|
420
914
|
for (let i = 0; i < CURVED_SEGMENTS; i++) {
|
|
421
915
|
const theta = i / CURVED_SEGMENTS * Math.PI * 2;
|
|
422
|
-
const
|
|
423
|
-
const
|
|
424
|
-
|
|
425
|
-
|
|
916
|
+
const c = Math.cos(theta), s = Math.sin(theta);
|
|
917
|
+
const ox = r * (c * u[0] + s * v[0]);
|
|
918
|
+
const oy = r * (c * u[1] + s * v[1]);
|
|
919
|
+
const oz = r * (c * u[2] + s * v[2]);
|
|
920
|
+
baseRing.push([ax + ox, ay + oy, az + oz]);
|
|
921
|
+
topRing.push([bx + ox, by + oy, bz + oz]);
|
|
426
922
|
}
|
|
427
923
|
const vertices = [...baseRing, ...topRing];
|
|
428
924
|
const faces = [];
|
|
@@ -433,7 +929,7 @@ registerKind({
|
|
|
433
929
|
faces.push([i, next, CURVED_SEGMENTS + next, CURVED_SEGMENTS + i]);
|
|
434
930
|
}
|
|
435
931
|
const vertJxgs = vertices.map(
|
|
436
|
-
(
|
|
932
|
+
(v2, i) => view.create("point3d", v2, {
|
|
437
933
|
id: `${obj.id}.v${i}`,
|
|
438
934
|
visible: false,
|
|
439
935
|
fixed: true,
|
|
@@ -477,10 +973,16 @@ registerKind({
|
|
|
477
973
|
apexPt.Y?.() ?? 0,
|
|
478
974
|
apexPt.Z?.() ?? 0
|
|
479
975
|
];
|
|
976
|
+
const [u, v] = perpBasis([apexCoords[0] - bx, apexCoords[1] - by, apexCoords[2] - bz]);
|
|
480
977
|
const baseRing = [];
|
|
481
978
|
for (let i = 0; i < CURVED_SEGMENTS2; i++) {
|
|
482
979
|
const theta = i / CURVED_SEGMENTS2 * Math.PI * 2;
|
|
483
|
-
|
|
980
|
+
const c = Math.cos(theta), s = Math.sin(theta);
|
|
981
|
+
baseRing.push([
|
|
982
|
+
bx + r * (c * u[0] + s * v[0]),
|
|
983
|
+
by + r * (c * u[1] + s * v[1]),
|
|
984
|
+
bz + r * (c * u[2] + s * v[2])
|
|
985
|
+
]);
|
|
484
986
|
}
|
|
485
987
|
const apexIdx = baseRing.length;
|
|
486
988
|
const vertices = [...baseRing, apexCoords];
|
|
@@ -489,7 +991,7 @@ registerKind({
|
|
|
489
991
|
faces.push([i, (i + 1) % CURVED_SEGMENTS2, apexIdx]);
|
|
490
992
|
}
|
|
491
993
|
const vertJxgs = vertices.map(
|
|
492
|
-
(
|
|
994
|
+
(v2, i) => view.create("point3d", v2, {
|
|
493
995
|
id: `${obj.id}.v${i}`,
|
|
494
996
|
visible: false,
|
|
495
997
|
fixed: true,
|
|
@@ -576,8 +1078,16 @@ function constraintRefs2D(c) {
|
|
|
576
1078
|
}
|
|
577
1079
|
case "excenter":
|
|
578
1080
|
return [c.vertices[0], c.vertices[1], c.vertices[2]];
|
|
579
|
-
|
|
1081
|
+
case "commonTangentPoint":
|
|
1082
|
+
return [c.circles[0], c.circles[1]];
|
|
1083
|
+
// Các kind KHÔNG có ref scene-object (toạ độ literal): liệt kê TƯỜNG MINH để
|
|
1084
|
+
// exhaustive never-guard bên dưới buộc khai báo khi thêm constraint kind mới.
|
|
1085
|
+
case "free":
|
|
1086
|
+
case "onAxis":
|
|
580
1087
|
return [];
|
|
1088
|
+
default: {
|
|
1089
|
+
return [];
|
|
1090
|
+
}
|
|
581
1091
|
}
|
|
582
1092
|
}
|
|
583
1093
|
|
|
@@ -1000,19 +1510,19 @@ function buildJxgTransforms(board, ctx, t) {
|
|
|
1000
1510
|
}
|
|
1001
1511
|
}
|
|
1002
1512
|
function makeDistanceFn(ctx, d) {
|
|
1003
|
-
const
|
|
1513
|
+
const scale2 = d.scale ?? 1;
|
|
1004
1514
|
const offset = d.offset ?? 0;
|
|
1005
1515
|
if (d.kind === "literal") {
|
|
1006
1516
|
const v = d.value;
|
|
1007
|
-
return () =>
|
|
1517
|
+
return () => scale2 * v + offset;
|
|
1008
1518
|
}
|
|
1009
1519
|
if (d.kind === "segmentLength") {
|
|
1010
1520
|
const p = ctx.resolveRef(d.p1);
|
|
1011
1521
|
const q = ctx.resolveRef(d.p2);
|
|
1012
|
-
return () =>
|
|
1522
|
+
return () => scale2 * Math.hypot(p.X() - q.X(), p.Y() - q.Y()) + offset;
|
|
1013
1523
|
}
|
|
1014
1524
|
const circle = ctx.resolveRef(d.circle);
|
|
1015
|
-
return () =>
|
|
1525
|
+
return () => scale2 * circle.Radius() + offset;
|
|
1016
1526
|
}
|
|
1017
1527
|
function buildPointOpts(obj) {
|
|
1018
1528
|
return {
|
|
@@ -1048,6 +1558,61 @@ var pointAtDistanceConstraint = definePointConstraint({
|
|
|
1048
1558
|
}
|
|
1049
1559
|
});
|
|
1050
1560
|
|
|
1561
|
+
// src/stamps/geometry-2d/geometry/commonTangent.ts
|
|
1562
|
+
var EPS = 1e-9;
|
|
1563
|
+
function computeCommonTangentPoint(O1, r1, O2, r2, on, variant, side) {
|
|
1564
|
+
const dx = O2[0] - O1[0];
|
|
1565
|
+
const dy = O2[1] - O1[1];
|
|
1566
|
+
const d = Math.hypot(dx, dy);
|
|
1567
|
+
if (d < EPS) return null;
|
|
1568
|
+
const beta = Math.atan2(dy, dx);
|
|
1569
|
+
const ratio = variant === "external" ? (r1 - r2) / d : (r1 + r2) / d;
|
|
1570
|
+
if (Math.abs(ratio) > 1) return null;
|
|
1571
|
+
const gamma = Math.acos(ratio);
|
|
1572
|
+
const s = side === 0 ? 1 : -1;
|
|
1573
|
+
const angle = beta + s * gamma;
|
|
1574
|
+
const nx = Math.cos(angle);
|
|
1575
|
+
const ny = Math.sin(angle);
|
|
1576
|
+
if (on === 0) {
|
|
1577
|
+
return [O1[0] + r1 * nx, O1[1] + r1 * ny];
|
|
1578
|
+
}
|
|
1579
|
+
if (variant === "external") {
|
|
1580
|
+
return [O2[0] + r2 * nx, O2[1] + r2 * ny];
|
|
1581
|
+
}
|
|
1582
|
+
return [O2[0] - r2 * nx, O2[1] - r2 * ny];
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
// src/core/scene/kinds/point-constraints/commonTangentPoint.ts
|
|
1586
|
+
var commonTangentPointConstraint = definePointConstraint({
|
|
1587
|
+
kind: "commonTangentPoint",
|
|
1588
|
+
describe: (obj, state, c) => {
|
|
1589
|
+
const c1 = state?.objects[c.circles[0]]?.label ?? c.circles[0];
|
|
1590
|
+
const c2 = state?.objects[c.circles[1]]?.label ?? c.circles[1];
|
|
1591
|
+
const v = c.variant === "internal" ? "trong" : "ngo\xE0i";
|
|
1592
|
+
return `${obj.label} = ti\u1EBFp \u0111i\u1EC3m ti\u1EBFp tuy\u1EBFn chung ${v} ${c1},${c2} (tr\xEAn ${c.on === 0 ? c1 : c2})`;
|
|
1593
|
+
},
|
|
1594
|
+
render: (obj, ctx, c, opts) => {
|
|
1595
|
+
const board = ctx.jxg;
|
|
1596
|
+
const k1 = ctx.resolveRef(c.circles[0]);
|
|
1597
|
+
const k2 = ctx.resolveRef(c.circles[1]);
|
|
1598
|
+
const O1 = k1?.center ?? k1?.midpoint ?? k1;
|
|
1599
|
+
const O2 = k2?.center ?? k2?.midpoint ?? k2;
|
|
1600
|
+
const compute = () => {
|
|
1601
|
+
const t = computeCommonTangentPoint(
|
|
1602
|
+
[O1.X(), O1.Y()],
|
|
1603
|
+
k1.Radius(),
|
|
1604
|
+
[O2.X(), O2.Y()],
|
|
1605
|
+
k2.Radius(),
|
|
1606
|
+
c.on,
|
|
1607
|
+
c.variant,
|
|
1608
|
+
c.side
|
|
1609
|
+
);
|
|
1610
|
+
return t ?? [0, 0];
|
|
1611
|
+
};
|
|
1612
|
+
return board.create("point", [() => compute()[0], () => compute()[1]], opts);
|
|
1613
|
+
}
|
|
1614
|
+
});
|
|
1615
|
+
|
|
1051
1616
|
// src/core/scene/kinds/point-constraints/circleIntersection.ts
|
|
1052
1617
|
var circleIntersectionConstraint = definePointConstraint({
|
|
1053
1618
|
kind: "circleIntersection",
|
|
@@ -1264,6 +1829,7 @@ var ALL = [
|
|
|
1264
1829
|
excenterConstraint,
|
|
1265
1830
|
mixtilinearPointConstraint,
|
|
1266
1831
|
pointAtDistanceConstraint,
|
|
1832
|
+
commonTangentPointConstraint,
|
|
1267
1833
|
circleIntersectionConstraint,
|
|
1268
1834
|
circleSecondIntersectionConstraint,
|
|
1269
1835
|
secondIntersectionConstraint,
|
|
@@ -1278,7 +1844,7 @@ var ALL = [
|
|
|
1278
1844
|
var POINT_CONSTRAINTS = new Map(ALL.map((m) => [m.kind, m]));
|
|
1279
1845
|
|
|
1280
1846
|
// src/core/scene/kinds/point.ts
|
|
1281
|
-
var
|
|
1847
|
+
var def5 = {
|
|
1282
1848
|
type: "point",
|
|
1283
1849
|
schemaVersion: 1,
|
|
1284
1850
|
migrate: {},
|
|
@@ -1356,10 +1922,10 @@ var def3 = {
|
|
|
1356
1922
|
throw new Error("point: constraint kind changed \u2014 recreate");
|
|
1357
1923
|
}
|
|
1358
1924
|
};
|
|
1359
|
-
registerKind(
|
|
1925
|
+
registerKind(def5);
|
|
1360
1926
|
|
|
1361
1927
|
// src/core/scene/kinds/segment.ts
|
|
1362
|
-
var
|
|
1928
|
+
var def6 = {
|
|
1363
1929
|
type: "segment",
|
|
1364
1930
|
schemaVersion: 1,
|
|
1365
1931
|
migrate: {},
|
|
@@ -1395,7 +1961,7 @@ var def4 = {
|
|
|
1395
1961
|
});
|
|
1396
1962
|
}
|
|
1397
1963
|
};
|
|
1398
|
-
registerKind(
|
|
1964
|
+
registerKind(def6);
|
|
1399
1965
|
|
|
1400
1966
|
// src/core/scene/kinds/line.ts
|
|
1401
1967
|
function stripBorderSuffix(id) {
|
|
@@ -1421,7 +1987,7 @@ function constructionRefs(c) {
|
|
|
1421
1987
|
return [c.throughPoint, c.toCircle];
|
|
1422
1988
|
}
|
|
1423
1989
|
}
|
|
1424
|
-
var
|
|
1990
|
+
var def7 = {
|
|
1425
1991
|
type: "line",
|
|
1426
1992
|
schemaVersion: 1,
|
|
1427
1993
|
migrate: {},
|
|
@@ -1617,10 +2183,10 @@ var def5 = {
|
|
|
1617
2183
|
}
|
|
1618
2184
|
}
|
|
1619
2185
|
};
|
|
1620
|
-
registerKind(
|
|
2186
|
+
registerKind(def7);
|
|
1621
2187
|
|
|
1622
2188
|
// src/core/scene/kinds/ray.ts
|
|
1623
|
-
var
|
|
2189
|
+
var def8 = {
|
|
1624
2190
|
type: "ray",
|
|
1625
2191
|
schemaVersion: 1,
|
|
1626
2192
|
migrate: {},
|
|
@@ -1645,10 +2211,10 @@ var def6 = {
|
|
|
1645
2211
|
});
|
|
1646
2212
|
}
|
|
1647
2213
|
};
|
|
1648
|
-
registerKind(
|
|
2214
|
+
registerKind(def8);
|
|
1649
2215
|
|
|
1650
2216
|
// src/core/scene/kinds/vector.ts
|
|
1651
|
-
var
|
|
2217
|
+
var def9 = {
|
|
1652
2218
|
type: "vector",
|
|
1653
2219
|
schemaVersion: 1,
|
|
1654
2220
|
migrate: {},
|
|
@@ -1670,7 +2236,7 @@ var def7 = {
|
|
|
1670
2236
|
});
|
|
1671
2237
|
}
|
|
1672
2238
|
};
|
|
1673
|
-
registerKind(
|
|
2239
|
+
registerKind(def9);
|
|
1674
2240
|
|
|
1675
2241
|
// src/core/scene/kinds/circle.ts
|
|
1676
2242
|
function asConstruction(a) {
|
|
@@ -1698,7 +2264,7 @@ function constructionRefs2(c) {
|
|
|
1698
2264
|
return [c.p1, c.p2];
|
|
1699
2265
|
}
|
|
1700
2266
|
}
|
|
1701
|
-
var
|
|
2267
|
+
var def10 = {
|
|
1702
2268
|
type: "circle",
|
|
1703
2269
|
schemaVersion: 1,
|
|
1704
2270
|
migrate: {},
|
|
@@ -1850,7 +2416,7 @@ var def8 = {
|
|
|
1850
2416
|
return board.create("circle", [center, surface], baseOpts);
|
|
1851
2417
|
}
|
|
1852
2418
|
};
|
|
1853
|
-
registerKind(
|
|
2419
|
+
registerKind(def10);
|
|
1854
2420
|
|
|
1855
2421
|
// src/core/scene/kinds/arc.ts
|
|
1856
2422
|
function constructionRefs3(c) {
|
|
@@ -1863,7 +2429,7 @@ function constructionRefs3(c) {
|
|
|
1863
2429
|
return [c.p1, c.p2, c.p3];
|
|
1864
2430
|
}
|
|
1865
2431
|
}
|
|
1866
|
-
var
|
|
2432
|
+
var def11 = {
|
|
1867
2433
|
type: "arc",
|
|
1868
2434
|
schemaVersion: 1,
|
|
1869
2435
|
migrate: {},
|
|
@@ -1921,10 +2487,10 @@ var def9 = {
|
|
|
1921
2487
|
return board.create("circumcirclearc", [A, B, C], baseOpts);
|
|
1922
2488
|
}
|
|
1923
2489
|
};
|
|
1924
|
-
registerKind(
|
|
2490
|
+
registerKind(def11);
|
|
1925
2491
|
|
|
1926
2492
|
// src/core/scene/kinds/sector.ts
|
|
1927
|
-
var
|
|
2493
|
+
var def12 = {
|
|
1928
2494
|
type: "sector",
|
|
1929
2495
|
schemaVersion: 1,
|
|
1930
2496
|
migrate: {},
|
|
@@ -1964,7 +2530,7 @@ var def10 = {
|
|
|
1964
2530
|
});
|
|
1965
2531
|
}
|
|
1966
2532
|
};
|
|
1967
|
-
registerKind(
|
|
2533
|
+
registerKind(def12);
|
|
1968
2534
|
|
|
1969
2535
|
// src/core/scene/kinds/polygon.ts
|
|
1970
2536
|
function regularPolygonName(n) {
|
|
@@ -2008,7 +2574,7 @@ function specialShapeName(kind) {
|
|
|
2008
2574
|
return "";
|
|
2009
2575
|
}
|
|
2010
2576
|
}
|
|
2011
|
-
var
|
|
2577
|
+
var def13 = {
|
|
2012
2578
|
type: "polygon",
|
|
2013
2579
|
schemaVersion: 1,
|
|
2014
2580
|
migrate: {},
|
|
@@ -2195,10 +2761,10 @@ var def11 = {
|
|
|
2195
2761
|
return poly;
|
|
2196
2762
|
}
|
|
2197
2763
|
};
|
|
2198
|
-
registerKind(
|
|
2764
|
+
registerKind(def13);
|
|
2199
2765
|
|
|
2200
2766
|
// src/core/scene/kinds/intersection.ts
|
|
2201
|
-
var
|
|
2767
|
+
var def14 = {
|
|
2202
2768
|
type: "intersection",
|
|
2203
2769
|
schemaVersion: 1,
|
|
2204
2770
|
migrate: {},
|
|
@@ -2273,10 +2839,10 @@ var def12 = {
|
|
|
2273
2839
|
}
|
|
2274
2840
|
}
|
|
2275
2841
|
};
|
|
2276
|
-
registerKind(
|
|
2842
|
+
registerKind(def14);
|
|
2277
2843
|
|
|
2278
2844
|
// src/core/scene/kinds/angle.ts
|
|
2279
|
-
var
|
|
2845
|
+
var def15 = {
|
|
2280
2846
|
type: "angle",
|
|
2281
2847
|
schemaVersion: 1,
|
|
2282
2848
|
migrate: {},
|
|
@@ -2312,10 +2878,10 @@ var def13 = {
|
|
|
2312
2878
|
});
|
|
2313
2879
|
}
|
|
2314
2880
|
};
|
|
2315
|
-
registerKind(
|
|
2881
|
+
registerKind(def15);
|
|
2316
2882
|
|
|
2317
2883
|
// src/core/scene/kinds/distance.ts
|
|
2318
|
-
var
|
|
2884
|
+
var def16 = {
|
|
2319
2885
|
type: "distance",
|
|
2320
2886
|
schemaVersion: 1,
|
|
2321
2887
|
migrate: {},
|
|
@@ -2344,10 +2910,10 @@ var def14 = {
|
|
|
2344
2910
|
});
|
|
2345
2911
|
}
|
|
2346
2912
|
};
|
|
2347
|
-
registerKind(
|
|
2913
|
+
registerKind(def16);
|
|
2348
2914
|
|
|
2349
2915
|
// src/core/scene/kinds/function2d.ts
|
|
2350
|
-
var
|
|
2916
|
+
var def17 = {
|
|
2351
2917
|
type: "function2d",
|
|
2352
2918
|
schemaVersion: 1,
|
|
2353
2919
|
migrate: {},
|
|
@@ -2386,10 +2952,10 @@ var def15 = {
|
|
|
2386
2952
|
});
|
|
2387
2953
|
}
|
|
2388
2954
|
};
|
|
2389
|
-
registerKind(
|
|
2955
|
+
registerKind(def17);
|
|
2390
2956
|
|
|
2391
2957
|
// src/core/scene/kinds/parameter.ts
|
|
2392
|
-
var
|
|
2958
|
+
var def18 = {
|
|
2393
2959
|
type: "parameter",
|
|
2394
2960
|
schemaVersion: 1,
|
|
2395
2961
|
migrate: {},
|
|
@@ -2407,10 +2973,10 @@ var def16 = {
|
|
|
2407
2973
|
render: () => null
|
|
2408
2974
|
// Không render lên board
|
|
2409
2975
|
};
|
|
2410
|
-
registerKind(
|
|
2976
|
+
registerKind(def18);
|
|
2411
2977
|
|
|
2412
2978
|
// src/core/scene/kinds/pointOnCurve.ts
|
|
2413
|
-
var
|
|
2979
|
+
var def19 = {
|
|
2414
2980
|
type: "pointOnCurve",
|
|
2415
2981
|
schemaVersion: 1,
|
|
2416
2982
|
migrate: {},
|
|
@@ -2437,10 +3003,10 @@ var def17 = {
|
|
|
2437
3003
|
});
|
|
2438
3004
|
}
|
|
2439
3005
|
};
|
|
2440
|
-
registerKind(
|
|
3006
|
+
registerKind(def19);
|
|
2441
3007
|
|
|
2442
3008
|
// src/core/scene/kinds/tangent2d.ts
|
|
2443
|
-
var
|
|
3009
|
+
var def20 = {
|
|
2444
3010
|
type: "tangent2d",
|
|
2445
3011
|
schemaVersion: 1,
|
|
2446
3012
|
migrate: {},
|
|
@@ -2463,7 +3029,7 @@ var def18 = {
|
|
|
2463
3029
|
});
|
|
2464
3030
|
}
|
|
2465
3031
|
};
|
|
2466
|
-
registerKind(
|
|
3032
|
+
registerKind(def20);
|
|
2467
3033
|
|
|
2468
3034
|
// src/core/scene/expressions/evaluator.ts
|
|
2469
3035
|
var DEFAULT_SAMPLES = 1e3;
|
|
@@ -2532,7 +3098,7 @@ function scanExtrema(fn, xMin, xMax, samples = DEFAULT_SAMPLES) {
|
|
|
2532
3098
|
}
|
|
2533
3099
|
|
|
2534
3100
|
// src/core/scene/kinds/extremum2d.ts
|
|
2535
|
-
var
|
|
3101
|
+
var def21 = {
|
|
2536
3102
|
type: "extremum2d",
|
|
2537
3103
|
schemaVersion: 1,
|
|
2538
3104
|
migrate: {},
|
|
@@ -2565,10 +3131,10 @@ var def19 = {
|
|
|
2565
3131
|
}));
|
|
2566
3132
|
}
|
|
2567
3133
|
};
|
|
2568
|
-
registerKind(
|
|
3134
|
+
registerKind(def21);
|
|
2569
3135
|
|
|
2570
3136
|
// src/core/scene/kinds/root2d.ts
|
|
2571
|
-
var
|
|
3137
|
+
var def22 = {
|
|
2572
3138
|
type: "root2d",
|
|
2573
3139
|
schemaVersion: 1,
|
|
2574
3140
|
migrate: {},
|
|
@@ -2598,10 +3164,10 @@ var def20 = {
|
|
|
2598
3164
|
}));
|
|
2599
3165
|
}
|
|
2600
3166
|
};
|
|
2601
|
-
registerKind(
|
|
3167
|
+
registerKind(def22);
|
|
2602
3168
|
|
|
2603
3169
|
// src/core/scene/kinds/slope2d.ts
|
|
2604
|
-
var
|
|
3170
|
+
var def23 = {
|
|
2605
3171
|
type: "slope2d",
|
|
2606
3172
|
schemaVersion: 1,
|
|
2607
3173
|
migrate: {},
|
|
@@ -2625,7 +3191,7 @@ var def21 = {
|
|
|
2625
3191
|
});
|
|
2626
3192
|
}
|
|
2627
3193
|
};
|
|
2628
|
-
registerKind(
|
|
3194
|
+
registerKind(def23);
|
|
2629
3195
|
|
|
2630
3196
|
// src/stamps/shared/serializeScene.ts
|
|
2631
3197
|
function serializeScene(state) {
|
|
@@ -2651,6 +3217,6 @@ function deserializeScene(domain, raw) {
|
|
|
2651
3217
|
return createEmptyState(domain);
|
|
2652
3218
|
}
|
|
2653
3219
|
|
|
2654
|
-
export { deserializeScene, listObjects, nextLabel, serializeScene, useEditorState };
|
|
2655
|
-
//# sourceMappingURL=chunk-
|
|
2656
|
-
//# sourceMappingURL=chunk-
|
|
3220
|
+
export { constraintToWorld, deserializeScene, listObjects, nextLabel, planeConstructionWorld, serializeScene, useEditorState };
|
|
3221
|
+
//# sourceMappingURL=chunk-4EXRNZRX.mjs.map
|
|
3222
|
+
//# sourceMappingURL=chunk-4EXRNZRX.mjs.map
|