@xom11/whiteboard 0.25.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ExcalidrawWithMenus-WENZRYYE.mjs → ExcalidrawWithMenus-2QPPTXJM.mjs} +3 -2
- package/dist/ExcalidrawWithMenus-2QPPTXJM.mjs.map +1 -0
- package/dist/ai.d.mts +3035 -108
- package/dist/ai.d.ts +3035 -108
- package/dist/ai.js +6780 -788
- package/dist/ai.js.map +1 -1
- package/dist/ai.mjs +5140 -577
- package/dist/ai.mjs.map +1 -1
- package/dist/catalog.json +5 -5
- package/dist/{chunk-NDEZJKNY.mjs → chunk-5JM35CXV.mjs} +4 -4
- package/dist/{chunk-NDEZJKNY.mjs.map → chunk-5JM35CXV.mjs.map} +1 -1
- package/dist/{chunk-VNCCIV6O.mjs → chunk-AJAHD35N.mjs} +779 -9
- package/dist/chunk-AJAHD35N.mjs.map +1 -0
- package/dist/{chunk-M42TGYT6.mjs → chunk-BNBOIDO5.mjs} +3 -3
- package/dist/{chunk-M42TGYT6.mjs.map → chunk-BNBOIDO5.mjs.map} +1 -1
- package/dist/{chunk-ONBCUWVI.mjs → chunk-BU5KLO3P.mjs} +3 -3
- package/dist/{chunk-ONBCUWVI.mjs.map → chunk-BU5KLO3P.mjs.map} +1 -1
- package/dist/{chunk-CJBLJUWG.mjs → chunk-CXHNVYMD.mjs} +4 -4
- package/dist/{chunk-CJBLJUWG.mjs.map → chunk-CXHNVYMD.mjs.map} +1 -1
- package/dist/chunk-H22OZYTW.mjs +265 -0
- package/dist/chunk-H22OZYTW.mjs.map +1 -0
- package/dist/chunk-J5LGTIGS.mjs +10 -0
- package/dist/chunk-J5LGTIGS.mjs.map +1 -0
- package/dist/{chunk-TB4CL25L.mjs → chunk-OQIQNKPQ.mjs} +206 -66
- package/dist/chunk-OQIQNKPQ.mjs.map +1 -0
- package/dist/{chunk-SGFJLHHG.mjs → chunk-PPKHCRRE.mjs} +3 -3
- package/dist/{chunk-SGFJLHHG.mjs.map → chunk-PPKHCRRE.mjs.map} +1 -1
- package/dist/{chunk-YSJOVBCD.mjs → chunk-QCZVFEN4.mjs} +4 -4
- package/dist/{chunk-YSJOVBCD.mjs.map → chunk-QCZVFEN4.mjs.map} +1 -1
- package/dist/{chunk-ESVPQWHX.mjs → chunk-QRUAEXLR.mjs} +5 -5
- package/dist/{chunk-ESVPQWHX.mjs.map → chunk-QRUAEXLR.mjs.map} +1 -1
- package/dist/{chunk-AYSFWUPK.mjs → chunk-SZDAS7LK.mjs} +79 -2
- package/dist/chunk-SZDAS7LK.mjs.map +1 -0
- package/dist/chunk-T3SOHYB2.mjs +851 -0
- package/dist/chunk-T3SOHYB2.mjs.map +1 -0
- package/dist/{chunk-I24QOHPU.mjs → chunk-V3YJ6JFL.mjs} +3 -3
- package/dist/{chunk-I24QOHPU.mjs.map → chunk-V3YJ6JFL.mjs.map} +1 -1
- package/dist/{chunk-REIJZDVZ.mjs → chunk-ZTQBUKLJ.mjs} +960 -196
- package/dist/chunk-ZTQBUKLJ.mjs.map +1 -0
- package/dist/geometry-2d.d.mts +1 -1
- package/dist/geometry-2d.d.ts +1 -1
- package/dist/geometry-2d.js +5521 -1384
- package/dist/geometry-2d.js.map +1 -1
- package/dist/geometry-2d.mjs +5 -4
- package/dist/geometry-3d.d.mts +1 -1
- package/dist/geometry-3d.d.ts +1 -1
- package/dist/geometry-3d.js +1351 -252
- package/dist/geometry-3d.js.map +1 -1
- package/dist/geometry-3d.mjs +4 -3
- package/dist/graph-2d.d.mts +1 -1
- package/dist/graph-2d.d.ts +1 -1
- package/dist/graph-2d.js +1517 -341
- package/dist/graph-2d.js.map +1 -1
- package/dist/graph-2d.mjs +7 -6
- package/dist/handleExtractProblem-C-U5KluK.d.mts +158 -0
- package/dist/handleExtractProblem-C-U5KluK.d.ts +158 -0
- package/dist/{host-A64ITWVX.mjs → host-2ISGVO7O.mjs} +6 -5
- package/dist/host-2ISGVO7O.mjs.map +1 -0
- package/dist/{host-L7FMFZUW.mjs → host-4P766V4J.mjs} +1363 -463
- package/dist/host-4P766V4J.mjs.map +1 -0
- package/dist/{host-QK53UYMD.mjs → host-HOSJHQ5H.mjs} +10 -9
- package/dist/host-HOSJHQ5H.mjs.map +1 -0
- package/dist/{host-QS2EOTRJ.mjs → host-ZQCDAT6O.mjs} +3 -2
- package/dist/host-ZQCDAT6O.mjs.map +1 -0
- package/dist/index.d.mts +10 -4
- package/dist/index.d.ts +10 -4
- package/dist/index.js +5746 -1603
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +26 -22
- package/dist/index.mjs.map +1 -1
- package/dist/latex.d.mts +1 -1
- package/dist/latex.d.ts +1 -1
- package/dist/latex.mjs +2 -1
- package/dist/render-ZX2O2IK7.mjs +10 -0
- package/dist/{render-3WTY7NZB.mjs.map → render-ZX2O2IK7.mjs.map} +1 -1
- package/dist/serialize-N4G6RFBB.mjs +9 -0
- package/dist/{serialize-SRJVKYUG.mjs.map → serialize-N4G6RFBB.mjs.map} +1 -1
- package/dist/{types-DWRyCa2m.d.ts → types-BHYC2Fiw.d.mts} +130 -1
- package/dist/{types-DWRyCa2m.d.mts → types-BHYC2Fiw.d.ts} +130 -1
- package/package.json +10 -1
- package/dist/ExcalidrawWithMenus-WENZRYYE.mjs.map +0 -1
- package/dist/chunk-AYSFWUPK.mjs.map +0 -1
- package/dist/chunk-REIJZDVZ.mjs.map +0 -1
- package/dist/chunk-TB4CL25L.mjs.map +0 -1
- package/dist/chunk-VNCCIV6O.mjs.map +0 -1
- package/dist/chunk-VRHWDZ66.mjs +0 -96
- package/dist/chunk-VRHWDZ66.mjs.map +0 -1
- package/dist/host-A64ITWVX.mjs.map +0 -1
- package/dist/host-L7FMFZUW.mjs.map +0 -1
- package/dist/host-QK53UYMD.mjs.map +0 -1
- package/dist/host-QS2EOTRJ.mjs.map +0 -1
- package/dist/render-3WTY7NZB.mjs +0 -9
- package/dist/serialize-SRJVKYUG.mjs +0 -8
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useChordShortcut } from './chunk-HNQLZIEP.mjs';
|
|
3
3
|
import { useToolStateMachine } from './chunk-NVJ7K3DK.mjs';
|
|
4
|
-
import { safeJsx, initJxgBoard, attachJxgWheelZoom, useToast, ToastHost, STAMP_PANEL_DESKTOP, ToastProvider, useStampStore, StampLeftPanel, ObjectRow } from './chunk-
|
|
5
|
-
import { serializeBoard, renderGeometrySvgFromState, isGeometryCustomData, deserializeBoard } from './chunk-
|
|
4
|
+
import { safeJsx, initJxgBoard, attachJxgWheelZoom, useToast, ToastHost, STAMP_PANEL_DESKTOP, ToastProvider, useStampStore, StampLeftPanel, ObjectRow } from './chunk-OQIQNKPQ.mjs';
|
|
5
|
+
import { serializeBoard, renderGeometrySvgFromState, isGeometryCustomData, deserializeBoard } from './chunk-H22OZYTW.mjs';
|
|
6
6
|
import { themeLabel, paletteFor, themeAxis, themeGrid } from './chunk-R5FL6S7L.mjs';
|
|
7
|
-
import { JxgRenderer } from './chunk-
|
|
7
|
+
import { JxgRenderer } from './chunk-SZDAS7LK.mjs';
|
|
8
8
|
import './chunk-ICR4CVOE.mjs';
|
|
9
|
-
import { nextLabel, useEditorState, listObjects } from './chunk-
|
|
9
|
+
import { nextLabel, useEditorState, listObjects } from './chunk-ZTQBUKLJ.mjs';
|
|
10
10
|
import './chunk-IHUFOV7L.mjs';
|
|
11
|
-
import { describeDsl, serializeState } from './chunk-
|
|
11
|
+
import { validateFile, fileToImagePart, describeDsl, serializeState } from './chunk-AJAHD35N.mjs';
|
|
12
|
+
import { handleExtractProblem } from './chunk-T3SOHYB2.mjs';
|
|
12
13
|
import { DEFAULT_VIEW_2D } from './chunk-73Q7ADVL.mjs';
|
|
13
14
|
import './chunk-B4NJJZFR.mjs';
|
|
14
15
|
import { useIsMobile } from './chunk-P2AOIF7S.mjs';
|
|
15
16
|
import { insertStampImage } from './chunk-QGNU34T7.mjs';
|
|
16
17
|
import './chunk-5UTGXHLJ.mjs';
|
|
18
|
+
import { __export } from './chunk-J5LGTIGS.mjs';
|
|
17
19
|
import { forwardRef, useRef, useId, useState, useEffect, useCallback, useImperativeHandle, useSyncExternalStore, useMemo, useLayoutEffect } from 'react';
|
|
18
20
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
19
21
|
import { createPortal } from 'react-dom';
|
|
@@ -283,6 +285,104 @@ var Icon = {
|
|
|
283
285
|
/* @__PURE__ */ jsx("circle", { cx: "8", cy: "15.5", r: "1.7", fill: C_POINT }),
|
|
284
286
|
/* @__PURE__ */ jsx("circle", { cx: "19", cy: "5.7", r: "1.9", fill: C_CONSTRUCT }),
|
|
285
287
|
/* @__PURE__ */ jsx("text", { x: "10.5", y: "10.5", fontSize: "8", fontFamily: "serif", fontStyle: "italic", fontWeight: "700", fill: "currentColor", children: "k" })
|
|
288
|
+
] }),
|
|
289
|
+
// ===== Special shapes =====
|
|
290
|
+
square: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
291
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "14", height: "14", fill: C_FILL, fillOpacity: "0.18", stroke: "currentColor", strokeWidth: "1.6" }),
|
|
292
|
+
/* @__PURE__ */ jsx("circle", { cx: "5", cy: "5", r: "1.7", fill: C_POINT }),
|
|
293
|
+
/* @__PURE__ */ jsx("circle", { cx: "19", cy: "5", r: "1.7", fill: C_POINT })
|
|
294
|
+
] }),
|
|
295
|
+
rectangle: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
296
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "7", width: "18", height: "10", fill: C_FILL, fillOpacity: "0.18", stroke: "currentColor", strokeWidth: "1.6" }),
|
|
297
|
+
/* @__PURE__ */ jsx("circle", { cx: "3", cy: "17", r: "1.7", fill: C_POINT }),
|
|
298
|
+
/* @__PURE__ */ jsx("circle", { cx: "21", cy: "17", r: "1.7", fill: C_POINT }),
|
|
299
|
+
/* @__PURE__ */ jsx("circle", { cx: "21", cy: "7", r: "1.7", fill: C_POINT })
|
|
300
|
+
] }),
|
|
301
|
+
rhombus: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
302
|
+
/* @__PURE__ */ jsx("path", { d: "M 12 3 L 21 12 L 12 21 L 3 12 Z", fill: C_FILL, fillOpacity: "0.18", stroke: "currentColor", strokeWidth: "1.6" }),
|
|
303
|
+
/* @__PURE__ */ jsx("circle", { cx: "3", cy: "12", r: "1.7", fill: C_POINT }),
|
|
304
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "21", r: "1.7", fill: C_POINT }),
|
|
305
|
+
/* @__PURE__ */ jsx("circle", { cx: "21", cy: "12", r: "1.7", fill: C_POINT })
|
|
306
|
+
] }),
|
|
307
|
+
parallelogram: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
308
|
+
/* @__PURE__ */ jsx("path", { d: "M 5 19 L 21 19 L 19 5 L 3 5 Z", fill: C_FILL, fillOpacity: "0.18", stroke: "currentColor", strokeWidth: "1.6" }),
|
|
309
|
+
/* @__PURE__ */ jsx("circle", { cx: "3", cy: "5", r: "1.7", fill: C_POINT }),
|
|
310
|
+
/* @__PURE__ */ jsx("circle", { cx: "19", cy: "5", r: "1.7", fill: C_POINT }),
|
|
311
|
+
/* @__PURE__ */ jsx("circle", { cx: "21", cy: "19", r: "1.7", fill: C_POINT })
|
|
312
|
+
] }),
|
|
313
|
+
isoTrapezoid: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
314
|
+
/* @__PURE__ */ jsx("path", { d: "M 3 19 L 21 19 L 17 5 L 7 5 Z", fill: C_FILL, fillOpacity: "0.18", stroke: "currentColor", strokeWidth: "1.6" }),
|
|
315
|
+
/* @__PURE__ */ jsx("circle", { cx: "3", cy: "19", r: "1.7", fill: C_POINT }),
|
|
316
|
+
/* @__PURE__ */ jsx("circle", { cx: "21", cy: "19", r: "1.7", fill: C_POINT }),
|
|
317
|
+
/* @__PURE__ */ jsx("circle", { cx: "7", cy: "5", r: "1.7", fill: C_POINT })
|
|
318
|
+
] }),
|
|
319
|
+
isoTriangle: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
320
|
+
/* @__PURE__ */ jsx("path", { d: "M 12 4 L 21 20 L 3 20 Z", fill: C_FILL, fillOpacity: "0.18", stroke: "currentColor", strokeWidth: "1.6" }),
|
|
321
|
+
/* @__PURE__ */ jsx("circle", { cx: "3", cy: "20", r: "1.7", fill: C_POINT }),
|
|
322
|
+
/* @__PURE__ */ jsx("circle", { cx: "21", cy: "20", r: "1.7", fill: C_POINT }),
|
|
323
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "4", r: "1.7", fill: C_POINT }),
|
|
324
|
+
/* @__PURE__ */ jsx("line", { x1: "12", y1: "4", x2: "12", y2: "20", stroke: C_CONSTRUCT, strokeWidth: "0.8", strokeDasharray: "1.5 1.5", opacity: "0.7" })
|
|
325
|
+
] }),
|
|
326
|
+
rightTriangle: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
327
|
+
/* @__PURE__ */ jsx("path", { d: "M 4 20 L 20 20 L 4 4 Z", fill: C_FILL, fillOpacity: "0.18", stroke: "currentColor", strokeWidth: "1.6" }),
|
|
328
|
+
/* @__PURE__ */ jsx("path", { d: "M 4 17 L 7 17 L 7 20", fill: "none", stroke: "currentColor", strokeWidth: "1.2" }),
|
|
329
|
+
/* @__PURE__ */ jsx("circle", { cx: "4", cy: "20", r: "1.7", fill: C_POINT }),
|
|
330
|
+
/* @__PURE__ */ jsx("circle", { cx: "20", cy: "20", r: "1.7", fill: C_POINT }),
|
|
331
|
+
/* @__PURE__ */ jsx("circle", { cx: "4", cy: "4", r: "1.7", fill: C_POINT })
|
|
332
|
+
] }),
|
|
333
|
+
// ===== Nâng cao / kind chưa có icon =====
|
|
334
|
+
excenter: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
335
|
+
/* @__PURE__ */ jsx("path", { d: "M5 16 L12 5 L19 16 Z", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
336
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "20", r: "3", fill: "none", stroke: C_CONSTRUCT, strokeWidth: "1.3" }),
|
|
337
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "20", r: "1.4", fill: C_CONSTRUCT })
|
|
338
|
+
] }),
|
|
339
|
+
tangencyPoint: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
340
|
+
/* @__PURE__ */ jsx("circle", { cx: "10", cy: "12", r: "6.5", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
341
|
+
/* @__PURE__ */ jsx("line", { x1: "16.5", y1: "3", x2: "16.5", y2: "21", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
342
|
+
/* @__PURE__ */ jsx("circle", { cx: "16.5", cy: "12", r: "2.2", fill: C_CONSTRUCT })
|
|
343
|
+
] }),
|
|
344
|
+
secondIntersection: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
345
|
+
/* @__PURE__ */ jsx("circle", { cx: "11", cy: "12", r: "6.5", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
346
|
+
/* @__PURE__ */ jsx("line", { x1: "2", y1: "8", x2: "22", y2: "16", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
347
|
+
/* @__PURE__ */ jsx("circle", { cx: "6.2", cy: "9.6", r: "1.6", fill: "currentColor" }),
|
|
348
|
+
/* @__PURE__ */ jsx("circle", { cx: "15.8", cy: "14.4", r: "2.4", fill: C_CONSTRUCT })
|
|
349
|
+
] }),
|
|
350
|
+
arcMidpoint: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", strokeLinecap: "round", children: [
|
|
351
|
+
/* @__PURE__ */ jsx("path", { d: "M4 17 A 9 9 0 0 1 20 17", stroke: "currentColor", strokeWidth: "1.4" }),
|
|
352
|
+
/* @__PURE__ */ jsx("circle", { cx: "4", cy: "17", r: "1.6", fill: "currentColor" }),
|
|
353
|
+
/* @__PURE__ */ jsx("circle", { cx: "20", cy: "17", r: "1.6", fill: "currentColor" }),
|
|
354
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "8.2", r: "2.4", fill: C_CONSTRUCT })
|
|
355
|
+
] }),
|
|
356
|
+
circleIntersection: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
357
|
+
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "12", r: "6", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
358
|
+
/* @__PURE__ */ jsx("circle", { cx: "15", cy: "12", r: "6", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
359
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "7.6", r: "2", fill: C_CONSTRUCT }),
|
|
360
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "16.4", r: "2", fill: C_CONSTRUCT })
|
|
361
|
+
] }),
|
|
362
|
+
tangentPointExt: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", strokeLinecap: "round", children: [
|
|
363
|
+
/* @__PURE__ */ jsx("circle", { cx: "14", cy: "12", r: "6", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
364
|
+
/* @__PURE__ */ jsx("circle", { cx: "3.5", cy: "12", r: "1.8", fill: C_POINT }),
|
|
365
|
+
/* @__PURE__ */ jsx("line", { x1: "3.5", y1: "12", x2: "17.5", y2: "7.5", stroke: "currentColor", strokeWidth: "1.2" }),
|
|
366
|
+
/* @__PURE__ */ jsx("line", { x1: "3.5", y1: "12", x2: "17.5", y2: "16.5", stroke: "currentColor", strokeWidth: "1.2" }),
|
|
367
|
+
/* @__PURE__ */ jsx("circle", { cx: "17.5", cy: "7.5", r: "1.8", fill: C_CONSTRUCT }),
|
|
368
|
+
/* @__PURE__ */ jsx("circle", { cx: "17.5", cy: "16.5", r: "1.8", fill: C_CONSTRUCT })
|
|
369
|
+
] }),
|
|
370
|
+
circleCR: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
371
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "7.5", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
372
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1.8", fill: C_POINT }),
|
|
373
|
+
/* @__PURE__ */ jsx("line", { x1: "12", y1: "12", x2: "19.5", y2: "12", stroke: C_CONSTRUCT, strokeWidth: "1.3" })
|
|
374
|
+
] }),
|
|
375
|
+
incircle: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
376
|
+
/* @__PURE__ */ jsx("path", { d: "M3 19 L12 4 L21 19 Z", stroke: "currentColor", strokeWidth: "1.3", strokeLinejoin: "round" }),
|
|
377
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "14", r: "4.2", fill: "none", stroke: C_CONSTRUCT, strokeWidth: "1.3" })
|
|
378
|
+
] }),
|
|
379
|
+
excircle: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
380
|
+
/* @__PURE__ */ jsx("path", { d: "M6 9 L14 4 L18 13 Z", stroke: "currentColor", strokeWidth: "1.2" }),
|
|
381
|
+
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "17", r: "4.6", fill: "none", stroke: C_CONSTRUCT, strokeWidth: "1.3" })
|
|
382
|
+
] }),
|
|
383
|
+
pointOn: /* @__PURE__ */ jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: [
|
|
384
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "7.5", stroke: "currentColor", strokeWidth: "1.3" }),
|
|
385
|
+
/* @__PURE__ */ jsx("circle", { cx: "17.3", cy: "6.7", r: "2.4", fill: C_POINT })
|
|
286
386
|
] })
|
|
287
387
|
};
|
|
288
388
|
var GeometryIconHeader = /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
@@ -343,7 +443,83 @@ var TOOLS = [
|
|
|
343
443
|
{ key: "rotate", label: "Quay \u0111\u1ED1i t\u01B0\u1EE3ng", hint: "Click object \u2192 t\xE2m quay \u2192 nh\u1EADp g\xF3c", icon: Icon.rotate, group: "transform", needs: 2, accepts: ["any", "point"] },
|
|
344
444
|
{ key: "reflectLine", label: "\u0110\u1ED1i x\u1EE9ng qua \u0111\u01B0\u1EDDng th\u1EB3ng", hint: "Click object \u2192 \u0111\u01B0\u1EDDng th\u1EB3ng", icon: Icon.reflectLine, group: "transform", needs: 2, accepts: ["any", "line"] },
|
|
345
445
|
{ key: "reflectPoint", label: "\u0110\u1ED1i x\u1EE9ng qua \u0111i\u1EC3m", hint: "Click object \u2192 t\xE2m \u0111\u1ED1i x\u1EE9ng", icon: Icon.reflectPoint, group: "transform", needs: 2, accepts: ["any", "point"] },
|
|
346
|
-
{ key: "dilate", label: "Ph\xE9p v\u1ECB t\u1EF1", hint: "Click object \u2192 t\xE2m \u2192 nh\u1EADp t\u1EF7 s\u1ED1 k", icon: Icon.dilate, group: "transform", needs: 2, accepts: ["any", "point"] }
|
|
446
|
+
{ key: "dilate", label: "Ph\xE9p v\u1ECB t\u1EF1", hint: "Click object \u2192 t\xE2m \u2192 nh\u1EADp t\u1EF7 s\u1ED1 k", icon: Icon.dilate, group: "transform", needs: 2, accepts: ["any", "point"] },
|
|
447
|
+
// ===== Hình đặc biệt (parametric construction) =====
|
|
448
|
+
{
|
|
449
|
+
key: "square",
|
|
450
|
+
label: "H\xECnh vu\xF4ng",
|
|
451
|
+
hint: "Click 2 \u0111i\u1EC3m \u2014 c\u1EA1nh \u0111\u1EA7u (3 \u0111\u1EC9nh c\xF2n l\u1EA1i t\u1EF1 suy, vu\xF4ng g\xF3c + b\u1EB1ng c\u1EA1nh)",
|
|
452
|
+
icon: Icon.square,
|
|
453
|
+
group: "special",
|
|
454
|
+
needs: 2,
|
|
455
|
+
accepts: ["point", "point"]
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
key: "rectangle",
|
|
459
|
+
label: "H\xECnh ch\u1EEF nh\u1EADt",
|
|
460
|
+
hint: "Click 2 \u0111i\u1EC3m \u0111\xE1y + 1 \u0111i\u1EC3m chi\u1EC1u cao (auto vu\xF4ng g\xF3c t\u1EA1i \u0111\u1EC9nh 2)",
|
|
461
|
+
icon: Icon.rectangle,
|
|
462
|
+
group: "special",
|
|
463
|
+
needs: 3,
|
|
464
|
+
accepts: ["point", "point", "point"]
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
key: "rhombus",
|
|
468
|
+
label: "H\xECnh thoi",
|
|
469
|
+
hint: "Click 2 \u0111i\u1EC3m c\u1EA1nh + 1 \u0111i\u1EC3m h\u01B0\u1EDBng (auto b\u1EB1ng \u0111\u1ED9 d\xE0i c\u1EA1nh)",
|
|
470
|
+
icon: Icon.rhombus,
|
|
471
|
+
group: "special",
|
|
472
|
+
needs: 3,
|
|
473
|
+
accepts: ["point", "point", "point"]
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
key: "parallelogram",
|
|
477
|
+
label: "H\xECnh b\xECnh h\xE0nh",
|
|
478
|
+
hint: "Click 3 \u0111i\u1EC3m li\xEAn ti\u1EBFp (\u0111\u1EC9nh 4 t\u1EF1 suy)",
|
|
479
|
+
icon: Icon.parallelogram,
|
|
480
|
+
group: "special",
|
|
481
|
+
needs: 3,
|
|
482
|
+
accepts: ["point", "point", "point"]
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
key: "isoTrapezoid",
|
|
486
|
+
label: "H\xECnh thang c\xE2n",
|
|
487
|
+
hint: "Click 2 \u0111i\u1EC3m \u0111\xE1y l\u1EDBn + 1 \u0111\u1EC9nh tr\xEAn (\u0111\u1EC9nh 4 ph\u1EA3n chi\u1EBFu qua trung tr\u1EF1c)",
|
|
488
|
+
icon: Icon.isoTrapezoid,
|
|
489
|
+
group: "special",
|
|
490
|
+
needs: 3,
|
|
491
|
+
accepts: ["point", "point", "point"]
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
key: "isoTriangle",
|
|
495
|
+
label: "Tam gi\xE1c c\xE2n",
|
|
496
|
+
hint: "Click 2 \u0111i\u1EC3m \u0111\xE1y + 1 \u0111\u1EC9nh (auto tr\xEAn trung tr\u1EF1c)",
|
|
497
|
+
icon: Icon.isoTriangle,
|
|
498
|
+
group: "special",
|
|
499
|
+
needs: 3,
|
|
500
|
+
accepts: ["point", "point", "point"]
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
key: "rightTriangle",
|
|
504
|
+
label: "Tam gi\xE1c vu\xF4ng",
|
|
505
|
+
hint: "Click \u0111\u1EC9nh vu\xF4ng + 2 \u0111\u1EA7u c\u1EA1nh (c\u1EA1nh 2 auto vu\xF4ng g\xF3c)",
|
|
506
|
+
icon: Icon.rightTriangle,
|
|
507
|
+
group: "special",
|
|
508
|
+
needs: 3,
|
|
509
|
+
accepts: ["point", "point", "point"]
|
|
510
|
+
},
|
|
511
|
+
// ===== Điểm / đường tròn thông dụng (kind chưa có icon trước v0.27) =====
|
|
512
|
+
{ key: "pointOn", label: "\u0110i\u1EC3m tr\xEAn \u0111\u1ED1i t\u01B0\u1EE3ng", hint: "Click 1 \u0111\u01B0\u1EDDng/\u0111o\u1EA1n/\u0111\u01B0\u1EDDng tr\xF2n c\xF3 s\u1EB5n", icon: Icon.pointOn, group: "point", needs: 1, accepts: ["lineOrCircle"] },
|
|
513
|
+
{ key: "circleCR", label: "\u0110\u01B0\u1EDDng tr\xF2n (t\xE2m + b\xE1n k\xEDnh)", hint: "Click t\xE2m r\u1ED3i nh\u1EADp b\xE1n k\xEDnh", icon: Icon.circleCR, group: "circle", needs: 1, accepts: ["point"] },
|
|
514
|
+
{ key: "incircle", label: "\u0110\u01B0\u1EDDng tr\xF2n n\u1ED9i ti\u1EBFp", hint: "Click 3 \u0111\u1EC9nh tam gi\xE1c", icon: Icon.incircle, group: "circle", needs: 3, accepts: ["point", "point", "point"] },
|
|
515
|
+
// ===== Nâng cao =====
|
|
516
|
+
{ key: "excenter", label: "T\xE2m \u0111\u01B0\u1EDDng tr\xF2n b\xE0ng ti\u1EBFp", hint: "Click 3 \u0111\u1EC9nh tam gi\xE1c (\u0111\u1EC9nh \u0111\u1EA7u = \u0111\u1EC9nh \u0111\u1ED1i di\u1EC7n)", icon: Icon.excenter, group: "advanced", needs: 3, accepts: ["point", "point", "point"] },
|
|
517
|
+
{ key: "excircle", label: "\u0110\u01B0\u1EDDng tr\xF2n b\xE0ng ti\u1EBFp", hint: "Click 3 \u0111\u1EC9nh tam gi\xE1c (\u0111\u1EC9nh \u0111\u1EA7u = \u0111\u1EC9nh \u0111\u1ED1i di\u1EC7n)", icon: Icon.excircle, group: "advanced", needs: 3, accepts: ["point", "point", "point"] },
|
|
518
|
+
{ key: "tangencyPoint", label: "Ti\u1EBFp \u0111i\u1EC3m (\u0111\u01B0\u1EDDng ti\u1EBFp x\xFAc)", hint: "Click 1 \u0111\u01B0\u1EDDng tr\xF2n + 1 ti\u1EBFp tuy\u1EBFn c\xF3 s\u1EB5n", icon: Icon.tangencyPoint, group: "advanced", needs: 2, accepts: ["circle", "line"] },
|
|
519
|
+
{ key: "secondIntersection", label: "Giao \u0111i\u1EC3m th\u1EE9 hai", hint: "Click 1 \u0111\u01B0\u1EDDng + 1 \u0111\u01B0\u1EDDng tr\xF2n + giao \u0111i\u1EC3m \u0111\xE3 bi\u1EBFt", icon: Icon.secondIntersection, group: "advanced", needs: 3, accepts: ["line", "circle", "point"] },
|
|
520
|
+
{ key: "arcMidpoint", label: "\u0110i\u1EC3m gi\u1EEFa cung", hint: "Click \u0111\u01B0\u1EDDng tr\xF2n \u2192 2 \u0111\u1EA7u cung A,B \u2192 1 \u0111i\u1EC3m ph\xEDa cung KH\xD4NG ch\u1EE9a", icon: Icon.arcMidpoint, group: "advanced", needs: 4, accepts: ["circle", "point", "point", "point"] },
|
|
521
|
+
{ key: "circleIntersection", label: "Giao 2 \u0111\u01B0\u1EDDng tr\xF2n", hint: "Click 2 \u0111\u01B0\u1EDDng tr\xF2n (t\u1EA1o c\u1EA3 2 giao \u0111i\u1EC3m)", icon: Icon.circleIntersection, group: "advanced", needs: 2, accepts: ["circle", "circle"] },
|
|
522
|
+
{ key: "tangentPointExt", label: "Ti\u1EBFp \u0111i\u1EC3m t\u1EEB \u0111i\u1EC3m ngo\xE0i", hint: "Click 1 \u0111i\u1EC3m ngo\xE0i + 1 \u0111\u01B0\u1EDDng tr\xF2n (t\u1EA1o c\u1EA3 2 ti\u1EBFp \u0111i\u1EC3m)", icon: Icon.tangentPointExt, group: "advanced", needs: 2, accepts: ["point", "circle"] }
|
|
347
523
|
];
|
|
348
524
|
var GROUP_LABELS = {
|
|
349
525
|
move: "C\u01A1 b\u1EA3n",
|
|
@@ -355,7 +531,9 @@ var GROUP_LABELS = {
|
|
|
355
531
|
triangle: "Tam gi\xE1c",
|
|
356
532
|
measure: "\u0110o l\u01B0\u1EDDng",
|
|
357
533
|
edit: "Ch\u1EC9nh s\u1EEDa",
|
|
358
|
-
transform: "Ph\xE9p bi\u1EBFn h\xECnh"
|
|
534
|
+
transform: "Ph\xE9p bi\u1EBFn h\xECnh",
|
|
535
|
+
special: "H\xECnh \u0111\u1EB7c bi\u1EC7t",
|
|
536
|
+
advanced: "N\xE2ng cao"
|
|
359
537
|
};
|
|
360
538
|
var GROUP_ORDER = [
|
|
361
539
|
"move",
|
|
@@ -367,7 +545,9 @@ var GROUP_ORDER = [
|
|
|
367
545
|
"triangle",
|
|
368
546
|
"measure",
|
|
369
547
|
"edit",
|
|
370
|
-
"transform"
|
|
548
|
+
"transform",
|
|
549
|
+
"special",
|
|
550
|
+
"advanced"
|
|
371
551
|
];
|
|
372
552
|
var A_CODE = "A".charCodeAt(0);
|
|
373
553
|
function letterForGroup(g) {
|
|
@@ -594,6 +774,20 @@ function handlePolygonTool(ctx, t, toolDef, e, x, y, bestHit) {
|
|
|
594
774
|
return true;
|
|
595
775
|
}
|
|
596
776
|
|
|
777
|
+
// src/stamps/geometry-2d/editor/handlers/finalize/lines.ts
|
|
778
|
+
var lines_exports = {};
|
|
779
|
+
__export(lines_exports, {
|
|
780
|
+
angleBisectorTool: () => angleBisectorTool,
|
|
781
|
+
lineTool: () => lineTool,
|
|
782
|
+
parallelTool: () => parallelTool,
|
|
783
|
+
perpBisectorTool: () => perpBisectorTool,
|
|
784
|
+
perpendicularTool: () => perpendicularTool,
|
|
785
|
+
rayTool: () => rayTool,
|
|
786
|
+
segmentTool: () => segmentTool,
|
|
787
|
+
tangentTool: () => tangentTool,
|
|
788
|
+
vectorTool: () => vectorTool
|
|
789
|
+
});
|
|
790
|
+
|
|
597
791
|
// src/stamps/geometry-2d/editor/handlers/classifyPointVsCircle.ts
|
|
598
792
|
function classifyPointVsCircle(point, circle) {
|
|
599
793
|
if (!point || !circle || !circle.center) return "inside";
|
|
@@ -607,7 +801,7 @@ function classifyPointVsCircle(point, circle) {
|
|
|
607
801
|
return d < r ? "inside" : "outside";
|
|
608
802
|
}
|
|
609
803
|
|
|
610
|
-
// src/stamps/geometry-2d/editor/handlers/
|
|
804
|
+
// src/stamps/geometry-2d/editor/handlers/finalize/shared.ts
|
|
611
805
|
function findPickIdByKind(ctx, kind) {
|
|
612
806
|
const picks = ctx.pendingRef.current;
|
|
613
807
|
const ids = ctx.pendingIdsRef.current;
|
|
@@ -616,362 +810,817 @@ function findPickIdByKind(ctx, kind) {
|
|
|
616
810
|
}
|
|
617
811
|
return null;
|
|
618
812
|
}
|
|
619
|
-
function
|
|
620
|
-
const
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
813
|
+
function readJxgPos(ctx, id) {
|
|
814
|
+
const j = ctx.jxgFromSceneId(id);
|
|
815
|
+
if (!j || typeof j.X !== "function") return { x: 0, y: 0 };
|
|
816
|
+
return { x: j.X(), y: j.Y() };
|
|
817
|
+
}
|
|
818
|
+
function computePerpendicularT(P, T, A, B) {
|
|
819
|
+
const dx = B.x - A.x, dy = B.y - A.y;
|
|
820
|
+
const len = Math.hypot(dx, dy);
|
|
821
|
+
if (len < 1e-12) return 0;
|
|
822
|
+
const ux = -dy / len, uy = dx / len;
|
|
823
|
+
return (P.x - T.x) * ux + (P.y - T.y) * uy;
|
|
824
|
+
}
|
|
825
|
+
function computePerpBisectorT(P, A, B) {
|
|
826
|
+
const Mx = (A.x + B.x) / 2, My = (A.y + B.y) / 2;
|
|
827
|
+
const dx = B.x - A.x, dy = B.y - A.y;
|
|
828
|
+
const len = Math.hypot(dx, dy);
|
|
829
|
+
if (len < 1e-12) return 0;
|
|
830
|
+
const ux = -dy / len, uy = dx / len;
|
|
831
|
+
return (P.x - Mx) * ux + (P.y - My) * uy;
|
|
832
|
+
}
|
|
833
|
+
function computeCircleTheta(P, C) {
|
|
834
|
+
return Math.atan2(P.y - C.y, P.x - C.x);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// src/stamps/geometry-2d/editor/handlers/finalize/lines.ts
|
|
838
|
+
var segmentTool = {
|
|
839
|
+
key: "segment",
|
|
840
|
+
finalize(ctx) {
|
|
841
|
+
const ids = ctx.pendingIdsRef.current;
|
|
842
|
+
const id = freshId(ctx, "s");
|
|
843
|
+
const label = ctx.nextLabel("segment");
|
|
844
|
+
ctx.store.dispatch({
|
|
845
|
+
type: "ADD",
|
|
846
|
+
payload: { obj: mkSceneObj(id, "segment", label, { p1: ids[0], p2: ids[1] }) }
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
var lineTool = {
|
|
851
|
+
key: "line",
|
|
852
|
+
finalize(ctx) {
|
|
853
|
+
const ids = ctx.pendingIdsRef.current;
|
|
854
|
+
const id = freshId(ctx, "l");
|
|
855
|
+
const label = ctx.nextLabel("line");
|
|
856
|
+
ctx.store.dispatch({
|
|
857
|
+
type: "ADD",
|
|
858
|
+
payload: { obj: mkSceneObj(id, "line", label, { p1: ids[0], p2: ids[1] }) }
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
function finalizePerpParallel(ctx, key) {
|
|
863
|
+
const throughPoint = findPickIdByKind(ctx, "point");
|
|
864
|
+
const toLine = findPickIdByKind(ctx, "line");
|
|
865
|
+
if (!throughPoint || !toLine) return;
|
|
866
|
+
const id = freshId(ctx, key === "perpendicular" ? "perp" : "par");
|
|
867
|
+
const label = ctx.nextLabel("line");
|
|
868
|
+
ctx.store.dispatch({
|
|
869
|
+
type: "ADD",
|
|
870
|
+
payload: { obj: mkSceneObj(id, "line", label, {
|
|
871
|
+
construction: { kind: key, throughPoint, toLine }
|
|
872
|
+
}) }
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
var perpendicularTool = {
|
|
876
|
+
key: "perpendicular",
|
|
877
|
+
finalize(ctx) {
|
|
878
|
+
finalizePerpParallel(ctx, "perpendicular");
|
|
879
|
+
}
|
|
880
|
+
};
|
|
881
|
+
var parallelTool = {
|
|
882
|
+
key: "parallel",
|
|
883
|
+
finalize(ctx) {
|
|
884
|
+
finalizePerpParallel(ctx, "parallel");
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
var perpBisectorTool = {
|
|
888
|
+
key: "perpBisector",
|
|
889
|
+
finalize(ctx) {
|
|
890
|
+
const ids = ctx.pendingIdsRef.current;
|
|
891
|
+
const id = freshId(ctx, "pb");
|
|
892
|
+
const label = ctx.nextLabel("line");
|
|
893
|
+
ctx.store.dispatch({
|
|
894
|
+
type: "ADD",
|
|
895
|
+
payload: { obj: mkSceneObj(id, "line", label, {
|
|
896
|
+
construction: { kind: "perpBisector", p1: ids[0], p2: ids[1] }
|
|
897
|
+
}) }
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
var angleBisectorTool = {
|
|
902
|
+
key: "angleBisector",
|
|
903
|
+
finalize(ctx) {
|
|
904
|
+
const ids = ctx.pendingIdsRef.current;
|
|
905
|
+
const picks = ctx.pendingRef.current;
|
|
906
|
+
if (picks.length === 2 && objKind(picks[0]) === "line" && objKind(picks[1]) === "line") {
|
|
907
|
+
for (const branch of [0, 1]) {
|
|
908
|
+
const id2 = freshId(ctx, "ab");
|
|
909
|
+
const label2 = ctx.nextLabel("line");
|
|
910
|
+
ctx.store.dispatch({
|
|
911
|
+
type: "ADD",
|
|
912
|
+
payload: { obj: mkSceneObj(id2, "line", label2, {
|
|
913
|
+
construction: { kind: "angleBisectorLines", line1: ids[0], line2: ids[1], branch }
|
|
914
|
+
}) }
|
|
915
|
+
});
|
|
916
|
+
}
|
|
639
917
|
return;
|
|
640
918
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
919
|
+
const id = freshId(ctx, "ab");
|
|
920
|
+
const label = ctx.nextLabel("line");
|
|
921
|
+
ctx.store.dispatch({
|
|
922
|
+
type: "ADD",
|
|
923
|
+
payload: { obj: mkSceneObj(id, "line", label, {
|
|
924
|
+
construction: { kind: "angleBisector", p1: ids[0], vertex: ids[1], p2: ids[2] }
|
|
925
|
+
}) }
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
};
|
|
929
|
+
var tangentTool = {
|
|
930
|
+
key: "tangent",
|
|
931
|
+
finalize(ctx) {
|
|
932
|
+
const throughId = findPickIdByKind(ctx, "point");
|
|
933
|
+
const circleId = findPickIdByKind(ctx, "circle");
|
|
934
|
+
if (!throughId || !circleId) return;
|
|
935
|
+
const picks = ctx.pendingRef.current;
|
|
936
|
+
const ids = ctx.pendingIdsRef.current;
|
|
937
|
+
const through = picks[ids.indexOf(throughId)];
|
|
938
|
+
const circle = picks[ids.indexOf(circleId)];
|
|
939
|
+
const pos = classifyPointVsCircle(through, circle);
|
|
940
|
+
if (pos === "inside") {
|
|
941
|
+
ctx.toast?.("\u0110i\u1EC3m n\u1EB1m trong \u0111\u01B0\u1EDDng tr\xF2n \u2014 kh\xF4ng c\xF3 ti\u1EBFp tuy\u1EBFn", {
|
|
942
|
+
variant: "warning",
|
|
943
|
+
id: "tangent-invalid-inside"
|
|
653
944
|
});
|
|
654
945
|
return;
|
|
655
946
|
}
|
|
656
|
-
|
|
657
|
-
const id = freshId(ctx, "
|
|
947
|
+
if (pos === "on") {
|
|
948
|
+
const id = freshId(ctx, "t");
|
|
658
949
|
const label = ctx.nextLabel("line");
|
|
659
950
|
ctx.store.dispatch({
|
|
660
951
|
type: "ADD",
|
|
661
952
|
payload: { obj: mkSceneObj(id, "line", label, {
|
|
662
|
-
construction: { kind: "
|
|
953
|
+
construction: { kind: "tangent", throughPoint: throughId, toCircle: circleId, branch: "on" }
|
|
663
954
|
}) }
|
|
664
955
|
});
|
|
665
956
|
return;
|
|
666
957
|
}
|
|
667
|
-
|
|
668
|
-
const
|
|
669
|
-
if (picks.length === 2 && objKind(picks[0]) === "line" && objKind(picks[1]) === "line") {
|
|
670
|
-
for (const branch of [0, 1]) {
|
|
671
|
-
const id2 = freshId(ctx, "ab");
|
|
672
|
-
const label2 = ctx.nextLabel("line");
|
|
673
|
-
ctx.store.dispatch({
|
|
674
|
-
type: "ADD",
|
|
675
|
-
payload: { obj: mkSceneObj(id2, "line", label2, {
|
|
676
|
-
construction: { kind: "angleBisectorLines", line1: ids[0], line2: ids[1], branch }
|
|
677
|
-
}) }
|
|
678
|
-
});
|
|
679
|
-
}
|
|
680
|
-
return;
|
|
681
|
-
}
|
|
682
|
-
const id = freshId(ctx, "ab");
|
|
958
|
+
for (const branch of [0, 1]) {
|
|
959
|
+
const id = freshId(ctx, "t");
|
|
683
960
|
const label = ctx.nextLabel("line");
|
|
684
961
|
ctx.store.dispatch({
|
|
685
962
|
type: "ADD",
|
|
686
963
|
payload: { obj: mkSceneObj(id, "line", label, {
|
|
687
|
-
construction: { kind: "
|
|
964
|
+
construction: { kind: "tangent", throughPoint: throughId, toCircle: circleId, branch }
|
|
688
965
|
}) }
|
|
689
966
|
});
|
|
690
|
-
return;
|
|
691
967
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
968
|
+
}
|
|
969
|
+
};
|
|
970
|
+
var rayTool = {
|
|
971
|
+
key: "ray",
|
|
972
|
+
finalize(ctx) {
|
|
973
|
+
const ids = ctx.pendingIdsRef.current;
|
|
974
|
+
const id = freshId(ctx, "r");
|
|
975
|
+
const label = ctx.nextLabel("ray");
|
|
976
|
+
ctx.store.dispatch({
|
|
977
|
+
type: "ADD",
|
|
978
|
+
payload: { obj: mkSceneObj(id, "ray", label, { origin: ids[0], through: ids[1] }) }
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
};
|
|
982
|
+
var vectorTool = {
|
|
983
|
+
key: "vector",
|
|
984
|
+
finalize(ctx) {
|
|
985
|
+
const ids = ctx.pendingIdsRef.current;
|
|
986
|
+
const id = freshId(ctx, "v");
|
|
987
|
+
const label = ctx.nextLabel("vector");
|
|
988
|
+
ctx.store.dispatch({
|
|
989
|
+
type: "ADD",
|
|
990
|
+
payload: { obj: mkSceneObj(id, "vector", label, { from: ids[0], to: ids[1] }) }
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
};
|
|
994
|
+
|
|
995
|
+
// src/stamps/geometry-2d/editor/handlers/finalize/circles.ts
|
|
996
|
+
var circles_exports = {};
|
|
997
|
+
__export(circles_exports, {
|
|
998
|
+
arc3Tool: () => arc3Tool,
|
|
999
|
+
arcCenterTool: () => arcCenterTool,
|
|
1000
|
+
circle3Tool: () => circle3Tool,
|
|
1001
|
+
circleCenterTool: () => circleCenterTool,
|
|
1002
|
+
excircleTool: () => excircleTool,
|
|
1003
|
+
incircleTool: () => incircleTool,
|
|
1004
|
+
sectorCenterTool: () => sectorCenterTool,
|
|
1005
|
+
semicircleTool: () => semicircleTool
|
|
1006
|
+
});
|
|
1007
|
+
var circleCenterTool = {
|
|
1008
|
+
key: "circleCenter",
|
|
1009
|
+
finalize(ctx) {
|
|
1010
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1011
|
+
const id = freshId(ctx, "c");
|
|
1012
|
+
const label = ctx.nextLabel("circle");
|
|
1013
|
+
ctx.store.dispatch({
|
|
1014
|
+
type: "ADD",
|
|
1015
|
+
payload: {
|
|
1016
|
+
obj: mkSceneObj(id, "circle", label, {
|
|
1017
|
+
center: ids[0],
|
|
1018
|
+
surfacePoint: ids[1]
|
|
1019
|
+
})
|
|
718
1020
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
};
|
|
1024
|
+
var circle3Tool = {
|
|
1025
|
+
key: "circle3",
|
|
1026
|
+
finalize(ctx) {
|
|
1027
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1028
|
+
const id = freshId(ctx, "cc");
|
|
1029
|
+
const label = ctx.nextLabel("circle");
|
|
1030
|
+
ctx.store.dispatch({
|
|
1031
|
+
type: "ADD",
|
|
1032
|
+
payload: {
|
|
1033
|
+
obj: mkSceneObj(id, "circle", label, {
|
|
1034
|
+
construction: { kind: "circumscribed", p1: ids[0], p2: ids[1], p3: ids[2] }
|
|
1035
|
+
})
|
|
728
1036
|
}
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
};
|
|
1040
|
+
var semicircleTool = {
|
|
1041
|
+
key: "semicircle",
|
|
1042
|
+
finalize(ctx) {
|
|
1043
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1044
|
+
if (ids[0] === ids[1]) {
|
|
1045
|
+
ctx.toast?.("C\u1EA7n 2 \u0111i\u1EC3m ph\xE2n bi\u1EC7t", { variant: "warning", id: "semicircle-dup" });
|
|
729
1046
|
return;
|
|
730
1047
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
}
|
|
740
|
-
case "vector": {
|
|
741
|
-
const id = freshId(ctx, "v");
|
|
742
|
-
const label = ctx.nextLabel("vector");
|
|
743
|
-
ctx.store.dispatch({
|
|
744
|
-
type: "ADD",
|
|
745
|
-
payload: { obj: mkSceneObj(id, "vector", label, { from: ids[0], to: ids[1] }) }
|
|
746
|
-
});
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
|
-
case "circleCenter": {
|
|
750
|
-
const id = freshId(ctx, "c");
|
|
751
|
-
const label = ctx.nextLabel("circle");
|
|
752
|
-
ctx.store.dispatch({
|
|
753
|
-
type: "ADD",
|
|
754
|
-
payload: {
|
|
755
|
-
obj: mkSceneObj(id, "circle", label, {
|
|
756
|
-
center: ids[0],
|
|
757
|
-
surfacePoint: ids[1]
|
|
758
|
-
})
|
|
759
|
-
}
|
|
760
|
-
});
|
|
761
|
-
return;
|
|
762
|
-
}
|
|
763
|
-
case "circle3": {
|
|
764
|
-
const id = freshId(ctx, "cc");
|
|
765
|
-
const label = ctx.nextLabel("circle");
|
|
766
|
-
ctx.store.dispatch({
|
|
767
|
-
type: "ADD",
|
|
768
|
-
payload: {
|
|
769
|
-
obj: mkSceneObj(id, "circle", label, {
|
|
770
|
-
construction: { kind: "circumscribed", p1: ids[0], p2: ids[1], p3: ids[2] }
|
|
771
|
-
})
|
|
772
|
-
}
|
|
773
|
-
});
|
|
774
|
-
return;
|
|
775
|
-
}
|
|
776
|
-
case "semicircle": {
|
|
777
|
-
if (ids[0] === ids[1]) {
|
|
778
|
-
ctx.toast?.("C\u1EA7n 2 \u0111i\u1EC3m ph\xE2n bi\u1EC7t", { variant: "warning", id: "semicircle-dup" });
|
|
779
|
-
return;
|
|
1048
|
+
const id = freshId(ctx, "arc");
|
|
1049
|
+
const label = ctx.nextLabel("arc");
|
|
1050
|
+
ctx.store.dispatch({
|
|
1051
|
+
type: "ADD",
|
|
1052
|
+
payload: {
|
|
1053
|
+
obj: mkSceneObj(id, "arc", label, {
|
|
1054
|
+
construction: { kind: "semicircle", p1: ids[0], p2: ids[1] }
|
|
1055
|
+
})
|
|
780
1056
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
});
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
var arcCenterTool = {
|
|
1061
|
+
key: "arcCenter",
|
|
1062
|
+
finalize(ctx) {
|
|
1063
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1064
|
+
if (ids[0] === ids[1] || ids[0] === ids[2] || ids[1] === ids[2]) {
|
|
1065
|
+
ctx.toast?.("C\u1EA7n 3 \u0111i\u1EC3m ph\xE2n bi\u1EC7t", { variant: "warning", id: "arc-center-dup" });
|
|
791
1066
|
return;
|
|
792
1067
|
}
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
1068
|
+
const id = freshId(ctx, "arc");
|
|
1069
|
+
const label = ctx.nextLabel("arc");
|
|
1070
|
+
ctx.store.dispatch({
|
|
1071
|
+
type: "ADD",
|
|
1072
|
+
payload: {
|
|
1073
|
+
obj: mkSceneObj(id, "arc", label, {
|
|
1074
|
+
construction: { kind: "byCenter", center: ids[0], p1: ids[1], p2: ids[2] }
|
|
1075
|
+
})
|
|
797
1076
|
}
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
});
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
var arc3Tool = {
|
|
1081
|
+
key: "arc3",
|
|
1082
|
+
finalize(ctx) {
|
|
1083
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1084
|
+
if (ids[0] === ids[1] || ids[0] === ids[2] || ids[1] === ids[2]) {
|
|
1085
|
+
ctx.toast?.("C\u1EA7n 3 \u0111i\u1EC3m ph\xE2n bi\u1EC7t", { variant: "warning", id: "arc3-dup" });
|
|
808
1086
|
return;
|
|
809
1087
|
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
const cross = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
|
|
820
|
-
if (Math.abs(cross) < 1e-6) {
|
|
821
|
-
ctx.toast?.("Kh\xF4ng v\u1EBD \u0111\u01B0\u1EE3c cung qua 3 \u0111i\u1EC3m th\u1EB3ng h\xE0ng", {
|
|
822
|
-
variant: "warning",
|
|
823
|
-
id: "arc3-collinear"
|
|
824
|
-
});
|
|
825
|
-
return;
|
|
826
|
-
}
|
|
827
|
-
const id = freshId(ctx, "arc");
|
|
828
|
-
const label = ctx.nextLabel("arc");
|
|
829
|
-
ctx.store.dispatch({
|
|
830
|
-
type: "ADD",
|
|
831
|
-
payload: {
|
|
832
|
-
obj: mkSceneObj(id, "arc", label, {
|
|
833
|
-
construction: { kind: "by3Points", p1: ids[0], p2: ids[1], p3: ids[2] }
|
|
834
|
-
})
|
|
835
|
-
}
|
|
1088
|
+
const picks = ctx.pendingRef.current;
|
|
1089
|
+
const ax = picks[0].X(), ay = picks[0].Y();
|
|
1090
|
+
const bx = picks[1].X(), by = picks[1].Y();
|
|
1091
|
+
const cx = picks[2].X(), cy = picks[2].Y();
|
|
1092
|
+
const cross = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
|
|
1093
|
+
if (Math.abs(cross) < 1e-6) {
|
|
1094
|
+
ctx.toast?.("Kh\xF4ng v\u1EBD \u0111\u01B0\u1EE3c cung qua 3 \u0111i\u1EC3m th\u1EB3ng h\xE0ng", {
|
|
1095
|
+
variant: "warning",
|
|
1096
|
+
id: "arc3-collinear"
|
|
836
1097
|
});
|
|
837
1098
|
return;
|
|
838
1099
|
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
1100
|
+
const id = freshId(ctx, "arc");
|
|
1101
|
+
const label = ctx.nextLabel("arc");
|
|
1102
|
+
ctx.store.dispatch({
|
|
1103
|
+
type: "ADD",
|
|
1104
|
+
payload: {
|
|
1105
|
+
obj: mkSceneObj(id, "arc", label, {
|
|
1106
|
+
construction: { kind: "by3Points", p1: ids[0], p2: ids[1], p3: ids[2] }
|
|
1107
|
+
})
|
|
843
1108
|
}
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
});
|
|
854
|
-
return;
|
|
855
|
-
}
|
|
856
|
-
case "midpoint": {
|
|
857
|
-
const id = freshId(ctx, "mp");
|
|
858
|
-
const label = ctx.nextLabel("point");
|
|
859
|
-
ctx.store.dispatch({
|
|
860
|
-
type: "ADD",
|
|
861
|
-
payload: { obj: mkSceneObj(id, "point", label, {
|
|
862
|
-
constraint: { kind: "midpoint", p1: ids[0], p2: ids[1] }
|
|
863
|
-
}) }
|
|
864
|
-
});
|
|
865
|
-
return;
|
|
866
|
-
}
|
|
867
|
-
case "perpFoot": {
|
|
868
|
-
const fromPoint = findPickIdByKind(ctx, "point");
|
|
869
|
-
const onLine = findPickIdByKind(ctx, "line");
|
|
870
|
-
if (!fromPoint || !onLine) return;
|
|
871
|
-
const id = freshId(ctx, "pf");
|
|
872
|
-
const label = ctx.nextLabel("point");
|
|
873
|
-
ctx.store.dispatch({
|
|
874
|
-
type: "ADD",
|
|
875
|
-
payload: { obj: mkSceneObj(id, "point", label, {
|
|
876
|
-
constraint: { kind: "perpFoot", from: fromPoint, onLine }
|
|
877
|
-
}) }
|
|
878
|
-
});
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
};
|
|
1112
|
+
var sectorCenterTool = {
|
|
1113
|
+
key: "sectorCenter",
|
|
1114
|
+
finalize(ctx) {
|
|
1115
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1116
|
+
if (ids[0] === ids[1] || ids[0] === ids[2] || ids[1] === ids[2]) {
|
|
1117
|
+
ctx.toast?.("C\u1EA7n 3 \u0111i\u1EC3m ph\xE2n bi\u1EC7t", { variant: "warning", id: "sector-center-dup" });
|
|
879
1118
|
return;
|
|
880
1119
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
})
|
|
889
|
-
}
|
|
890
|
-
|
|
1120
|
+
const id = freshId(ctx, "sec");
|
|
1121
|
+
const label = ctx.nextLabel("sector");
|
|
1122
|
+
ctx.store.dispatch({
|
|
1123
|
+
type: "ADD",
|
|
1124
|
+
payload: {
|
|
1125
|
+
obj: mkSceneObj(id, "sector", label, {
|
|
1126
|
+
construction: { kind: "byCenter", center: ids[0], p1: ids[1], p2: ids[2] }
|
|
1127
|
+
})
|
|
1128
|
+
}
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
var incircleTool = {
|
|
1133
|
+
key: "incircle",
|
|
1134
|
+
finalize(ctx) {
|
|
1135
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1136
|
+
const id = freshId(ctx, "ic");
|
|
1137
|
+
const label = ctx.nextLabel("circle");
|
|
1138
|
+
ctx.store.dispatch({
|
|
1139
|
+
type: "ADD",
|
|
1140
|
+
payload: { obj: mkSceneObj(id, "circle", label, {
|
|
1141
|
+
construction: { kind: "incircle", p1: ids[0], p2: ids[1], p3: ids[2] }
|
|
1142
|
+
}) }
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
};
|
|
1146
|
+
var excircleTool = {
|
|
1147
|
+
key: "excircle",
|
|
1148
|
+
finalize(ctx) {
|
|
1149
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1150
|
+
const id = freshId(ctx, "exc");
|
|
1151
|
+
const label = ctx.nextLabel("circle");
|
|
1152
|
+
ctx.store.dispatch({
|
|
1153
|
+
type: "ADD",
|
|
1154
|
+
payload: { obj: mkSceneObj(id, "circle", label, {
|
|
1155
|
+
construction: { kind: "excircle", p1: ids[0], p2: ids[1], p3: ids[2], opposite: ids[0] }
|
|
1156
|
+
}) }
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
};
|
|
1160
|
+
|
|
1161
|
+
// src/stamps/geometry-2d/editor/handlers/finalize/points.ts
|
|
1162
|
+
var points_exports = {};
|
|
1163
|
+
__export(points_exports, {
|
|
1164
|
+
arcMidpointTool: () => arcMidpointTool,
|
|
1165
|
+
centroidTool: () => centroidTool,
|
|
1166
|
+
circleIntersectionTool: () => circleIntersectionTool,
|
|
1167
|
+
circumcenterTool: () => circumcenterTool,
|
|
1168
|
+
excenterTool: () => excenterTool,
|
|
1169
|
+
incenterTool: () => incenterTool,
|
|
1170
|
+
midpointTool: () => midpointTool,
|
|
1171
|
+
orthocenterTool: () => orthocenterTool,
|
|
1172
|
+
perpFootTool: () => perpFootTool,
|
|
1173
|
+
pointOnTool: () => pointOnTool,
|
|
1174
|
+
secondIntersectionTool: () => secondIntersectionTool,
|
|
1175
|
+
tangencyPointTool: () => tangencyPointTool,
|
|
1176
|
+
tangentPointExtTool: () => tangentPointExtTool
|
|
1177
|
+
});
|
|
1178
|
+
var midpointTool = {
|
|
1179
|
+
key: "midpoint",
|
|
1180
|
+
finalize(ctx) {
|
|
1181
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1182
|
+
const id = freshId(ctx, "mp");
|
|
1183
|
+
const label = ctx.nextLabel("point");
|
|
1184
|
+
ctx.store.dispatch({
|
|
1185
|
+
type: "ADD",
|
|
1186
|
+
payload: { obj: mkSceneObj(id, "point", label, {
|
|
1187
|
+
constraint: { kind: "midpoint", p1: ids[0], p2: ids[1] }
|
|
1188
|
+
}) }
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
};
|
|
1192
|
+
var perpFootTool = {
|
|
1193
|
+
key: "perpFoot",
|
|
1194
|
+
finalize(ctx) {
|
|
1195
|
+
const fromPoint = findPickIdByKind(ctx, "point");
|
|
1196
|
+
const onLine = findPickIdByKind(ctx, "line");
|
|
1197
|
+
if (!fromPoint || !onLine) return;
|
|
1198
|
+
const id = freshId(ctx, "pf");
|
|
1199
|
+
const label = ctx.nextLabel("point");
|
|
1200
|
+
ctx.store.dispatch({
|
|
1201
|
+
type: "ADD",
|
|
1202
|
+
payload: { obj: mkSceneObj(id, "point", label, {
|
|
1203
|
+
constraint: { kind: "perpFoot", from: fromPoint, onLine }
|
|
1204
|
+
}) }
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
var centroidTool = {
|
|
1209
|
+
key: "centroid",
|
|
1210
|
+
finalize(ctx) {
|
|
1211
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1212
|
+
const id = freshId(ctx, "g");
|
|
1213
|
+
const label = ctx.nextLabel("point");
|
|
1214
|
+
ctx.store.dispatch({
|
|
1215
|
+
type: "ADD",
|
|
1216
|
+
payload: { obj: mkSceneObj(id, "point", label, {
|
|
1217
|
+
constraint: { kind: "centroid", vertices: [ids[0], ids[1], ids[2]] }
|
|
1218
|
+
}) }
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
};
|
|
1222
|
+
var circumcenterTool = {
|
|
1223
|
+
key: "circumcenter",
|
|
1224
|
+
finalize(ctx) {
|
|
1225
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1226
|
+
const id = freshId(ctx, "o");
|
|
1227
|
+
const label = ctx.nextLabel("point");
|
|
1228
|
+
ctx.store.dispatch({
|
|
1229
|
+
type: "ADD",
|
|
1230
|
+
payload: { obj: mkSceneObj(id, "point", label, {
|
|
1231
|
+
constraint: { kind: "circumcenter", vertices: [ids[0], ids[1], ids[2]] }
|
|
1232
|
+
}) }
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
var incenterTool = {
|
|
1237
|
+
key: "incenter",
|
|
1238
|
+
finalize(ctx) {
|
|
1239
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1240
|
+
const id = freshId(ctx, "i");
|
|
1241
|
+
const label = ctx.nextLabel("point");
|
|
1242
|
+
ctx.store.dispatch({
|
|
1243
|
+
type: "ADD",
|
|
1244
|
+
payload: { obj: mkSceneObj(id, "point", label, {
|
|
1245
|
+
constraint: { kind: "incenter", vertices: [ids[0], ids[1], ids[2]] }
|
|
1246
|
+
}) }
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
};
|
|
1250
|
+
var orthocenterTool = {
|
|
1251
|
+
key: "orthocenter",
|
|
1252
|
+
finalize(ctx) {
|
|
1253
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1254
|
+
const id = freshId(ctx, "h");
|
|
1255
|
+
const label = ctx.nextLabel("point");
|
|
1256
|
+
ctx.store.dispatch({
|
|
1257
|
+
type: "ADD",
|
|
1258
|
+
payload: { obj: mkSceneObj(id, "point", label, {
|
|
1259
|
+
constraint: { kind: "orthocenter", vertices: [ids[0], ids[1], ids[2]] }
|
|
1260
|
+
}) }
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
var excenterTool = {
|
|
1265
|
+
key: "excenter",
|
|
1266
|
+
finalize(ctx) {
|
|
1267
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1268
|
+
const id = freshId(ctx, "ex");
|
|
1269
|
+
const label = ctx.nextLabel("point");
|
|
1270
|
+
ctx.store.dispatch({
|
|
1271
|
+
type: "ADD",
|
|
1272
|
+
payload: { obj: mkSceneObj(id, "point", label, {
|
|
1273
|
+
constraint: { kind: "excenter", vertices: [ids[0], ids[1], ids[2]], opposite: ids[0] }
|
|
1274
|
+
}) }
|
|
1275
|
+
});
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
var tangencyPointTool = {
|
|
1279
|
+
key: "tangencyPoint",
|
|
1280
|
+
finalize(ctx) {
|
|
1281
|
+
const circleId = findPickIdByKind(ctx, "circle");
|
|
1282
|
+
const lineId = findPickIdByKind(ctx, "line");
|
|
1283
|
+
if (!circleId || !lineId) return;
|
|
1284
|
+
const id = freshId(ctx, "tp");
|
|
1285
|
+
const label = ctx.nextLabel("point");
|
|
1286
|
+
ctx.store.dispatch({
|
|
1287
|
+
type: "ADD",
|
|
1288
|
+
payload: { obj: mkSceneObj(id, "point", label, {
|
|
1289
|
+
constraint: { kind: "tangencyPoint", circle: circleId, onLine: lineId }
|
|
1290
|
+
}) }
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
var secondIntersectionTool = {
|
|
1295
|
+
key: "secondIntersection",
|
|
1296
|
+
finalize(ctx) {
|
|
1297
|
+
const lineId = findPickIdByKind(ctx, "line");
|
|
1298
|
+
const circleId = findPickIdByKind(ctx, "circle");
|
|
1299
|
+
const otherId = findPickIdByKind(ctx, "point");
|
|
1300
|
+
if (!lineId || !circleId || !otherId) return;
|
|
1301
|
+
const id = freshId(ctx, "X");
|
|
1302
|
+
const label = ctx.nextLabel("point");
|
|
1303
|
+
ctx.store.dispatch({
|
|
1304
|
+
type: "ADD",
|
|
1305
|
+
payload: { obj: mkSceneObj(id, "point", label, {
|
|
1306
|
+
constraint: { kind: "secondIntersection", line: lineId, circle: circleId, other: otherId }
|
|
1307
|
+
}) }
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
var arcMidpointTool = {
|
|
1312
|
+
key: "arcMidpoint",
|
|
1313
|
+
finalize(ctx) {
|
|
1314
|
+
const circleId = findPickIdByKind(ctx, "circle");
|
|
1315
|
+
const picks = ctx.pendingRef.current;
|
|
1316
|
+
const allIds = ctx.pendingIdsRef.current;
|
|
1317
|
+
const ptIds = [];
|
|
1318
|
+
for (let i = 0; i < picks.length; i += 1) {
|
|
1319
|
+
if (objKind(picks[i]) === "point" && allIds[i]) ptIds.push(allIds[i]);
|
|
891
1320
|
}
|
|
892
|
-
|
|
893
|
-
|
|
1321
|
+
if (!circleId || ptIds.length < 3) return;
|
|
1322
|
+
const id = freshId(ctx, "M");
|
|
1323
|
+
const label = ctx.nextLabel("point");
|
|
1324
|
+
ctx.store.dispatch({
|
|
1325
|
+
type: "ADD",
|
|
1326
|
+
payload: { obj: mkSceneObj(id, "point", label, {
|
|
1327
|
+
constraint: { kind: "arcMidpoint", circle: circleId, a: ptIds[0], b: ptIds[1], notContaining: ptIds[2] }
|
|
1328
|
+
}) }
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
var circleIntersectionTool = {
|
|
1333
|
+
key: "circleIntersection",
|
|
1334
|
+
finalize(ctx) {
|
|
1335
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1336
|
+
for (const which of [0, 1]) {
|
|
1337
|
+
const id = freshId(ctx, "X");
|
|
894
1338
|
const label = ctx.nextLabel("point");
|
|
895
1339
|
ctx.store.dispatch({
|
|
896
1340
|
type: "ADD",
|
|
897
1341
|
payload: { obj: mkSceneObj(id, "point", label, {
|
|
898
|
-
constraint: { kind: "
|
|
1342
|
+
constraint: { kind: "circleIntersection", c1: ids[0], c2: ids[1], which }
|
|
899
1343
|
}) }
|
|
900
1344
|
});
|
|
901
|
-
return;
|
|
902
1345
|
}
|
|
903
|
-
|
|
904
|
-
|
|
1346
|
+
}
|
|
1347
|
+
};
|
|
1348
|
+
var tangentPointExtTool = {
|
|
1349
|
+
key: "tangentPointExt",
|
|
1350
|
+
finalize(ctx) {
|
|
1351
|
+
const fromId = findPickIdByKind(ctx, "point");
|
|
1352
|
+
const circleId = findPickIdByKind(ctx, "circle");
|
|
1353
|
+
if (!fromId || !circleId) return;
|
|
1354
|
+
for (const which of [0, 1]) {
|
|
1355
|
+
const id = freshId(ctx, "T");
|
|
905
1356
|
const label = ctx.nextLabel("point");
|
|
906
1357
|
ctx.store.dispatch({
|
|
907
1358
|
type: "ADD",
|
|
908
1359
|
payload: { obj: mkSceneObj(id, "point", label, {
|
|
909
|
-
constraint: { kind: "
|
|
1360
|
+
constraint: { kind: "tangentPointExt", from: fromId, circle: circleId, which }
|
|
910
1361
|
}) }
|
|
911
1362
|
});
|
|
912
|
-
return;
|
|
913
1363
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
1364
|
+
}
|
|
1365
|
+
};
|
|
1366
|
+
var pointOnTool = {
|
|
1367
|
+
key: "pointOn",
|
|
1368
|
+
finalize(ctx, _toolDef, clickXY) {
|
|
1369
|
+
const obj = ctx.pendingRef.current[0];
|
|
1370
|
+
const objId = ctx.pendingIdsRef.current[0];
|
|
1371
|
+
if (!obj || !objId) return;
|
|
1372
|
+
const kind = objKind(obj);
|
|
1373
|
+
const id = freshId(ctx, "p");
|
|
1374
|
+
const label = ctx.nextLabel("point");
|
|
1375
|
+
const px = clickXY?.x ?? 0;
|
|
1376
|
+
const py = clickXY?.y ?? 0;
|
|
1377
|
+
let constraint = null;
|
|
1378
|
+
if (kind === "circle") {
|
|
1379
|
+
const o = obj.center ?? obj.midpoint;
|
|
1380
|
+
const ox = o ? o.X() : 0;
|
|
1381
|
+
const oy = o ? o.Y() : 0;
|
|
1382
|
+
constraint = { kind: "onCircle", circleId: objId, theta: Math.atan2(py - oy, px - ox) };
|
|
1383
|
+
} else if (kind === "line") {
|
|
1384
|
+
const elType = (obj.elType || "").toString().toLowerCase();
|
|
1385
|
+
const p1 = obj.point1;
|
|
1386
|
+
const p2 = obj.point2;
|
|
1387
|
+
let t = 0;
|
|
1388
|
+
if (p1 && p2) {
|
|
1389
|
+
const dx = p2.X() - p1.X();
|
|
1390
|
+
const dy = p2.Y() - p1.Y();
|
|
1391
|
+
const len2 = dx * dx + dy * dy || 1;
|
|
1392
|
+
t = ((px - p1.X()) * dx + (py - p1.Y()) * dy) / len2;
|
|
1393
|
+
}
|
|
1394
|
+
constraint = elType === "segment" ? { kind: "onSegment", segmentId: objId, t } : { kind: "onLine", lineId: objId, t };
|
|
924
1395
|
}
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1396
|
+
if (!constraint) return;
|
|
1397
|
+
ctx.store.dispatch({ type: "ADD", payload: { obj: mkSceneObj(id, "point", label, { constraint }) } });
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
|
|
1401
|
+
// src/stamps/geometry-2d/editor/handlers/finalize/polygons.ts
|
|
1402
|
+
var polygons_exports = {};
|
|
1403
|
+
__export(polygons_exports, {
|
|
1404
|
+
isoTrapezoidTool: () => isoTrapezoidTool,
|
|
1405
|
+
isoTriangleTool: () => isoTriangleTool,
|
|
1406
|
+
parallelogramTool: () => parallelogramTool,
|
|
1407
|
+
rectangleTool: () => rectangleTool,
|
|
1408
|
+
rhombusTool: () => rhombusTool,
|
|
1409
|
+
rightTriangleTool: () => rightTriangleTool,
|
|
1410
|
+
squareTool: () => squareTool
|
|
1411
|
+
});
|
|
1412
|
+
var squareTool = {
|
|
1413
|
+
key: "square",
|
|
1414
|
+
finalize(ctx) {
|
|
1415
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1416
|
+
const id = freshId(ctx, "sq");
|
|
1417
|
+
const label = ctx.nextLabel("polygon");
|
|
1418
|
+
ctx.store.dispatch({
|
|
1419
|
+
type: "ADD",
|
|
1420
|
+
payload: { obj: mkSceneObj(id, "polygon", label, {
|
|
1421
|
+
construction: { kind: "square", p1: ids[0], p2: ids[1] }
|
|
1422
|
+
}) }
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
};
|
|
1426
|
+
var rectangleTool = {
|
|
1427
|
+
key: "rectangle",
|
|
1428
|
+
finalize(ctx) {
|
|
1429
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1430
|
+
const [aId, bId, cId] = ids;
|
|
1431
|
+
const P = readJxgPos(ctx, cId);
|
|
1432
|
+
const Aj = readJxgPos(ctx, aId);
|
|
1433
|
+
const Bj = readJxgPos(ctx, bId);
|
|
1434
|
+
const t = computePerpendicularT(P, Bj, Aj, Bj);
|
|
1435
|
+
const polyId = freshId(ctx, "rect");
|
|
1436
|
+
const label = ctx.nextLabel("polygon");
|
|
1437
|
+
ctx.store.dispatch({
|
|
1438
|
+
type: "TRANSACTION",
|
|
1439
|
+
payload: { actions: [
|
|
1440
|
+
{ type: "UPDATE_ATTRS", payload: { id: cId, patch: {
|
|
1441
|
+
constraint: { kind: "onPerpendicular", through: bId, perpToA: aId, perpToB: bId, t }
|
|
1442
|
+
} } },
|
|
1443
|
+
{ type: "ADD", payload: { obj: mkSceneObj(polyId, "polygon", label, {
|
|
1444
|
+
construction: { kind: "rectangle", p1: aId, p2: bId, p3: cId }
|
|
1445
|
+
}) } }
|
|
1446
|
+
] }
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
};
|
|
1450
|
+
var rhombusTool = {
|
|
1451
|
+
key: "rhombus",
|
|
1452
|
+
finalize(ctx) {
|
|
1453
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1454
|
+
const [aId, bId, cId] = ids;
|
|
1455
|
+
const P = readJxgPos(ctx, cId);
|
|
1456
|
+
const Bj = readJxgPos(ctx, bId);
|
|
1457
|
+
const theta = computeCircleTheta(P, Bj);
|
|
1458
|
+
const polyId = freshId(ctx, "rho");
|
|
1459
|
+
const label = ctx.nextLabel("polygon");
|
|
1460
|
+
ctx.store.dispatch({
|
|
1461
|
+
type: "TRANSACTION",
|
|
1462
|
+
payload: { actions: [
|
|
1463
|
+
{ type: "UPDATE_ATTRS", payload: { id: cId, patch: {
|
|
1464
|
+
constraint: { kind: "onCircleAroundPoint", center: bId, radiusPoint: aId, theta }
|
|
1465
|
+
} } },
|
|
1466
|
+
{ type: "ADD", payload: { obj: mkSceneObj(polyId, "polygon", label, {
|
|
1467
|
+
construction: { kind: "rhombus", p1: aId, p2: bId, p3: cId }
|
|
1468
|
+
}) } }
|
|
1469
|
+
] }
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
};
|
|
1473
|
+
function finalizePgmTrap(ctx, key) {
|
|
1474
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1475
|
+
const [aId, bId, cId] = ids;
|
|
1476
|
+
const prefix = key === "parallelogram" ? "pgm" : "trap";
|
|
1477
|
+
const polyId = freshId(ctx, prefix);
|
|
1478
|
+
const label = ctx.nextLabel("polygon");
|
|
1479
|
+
ctx.store.dispatch({
|
|
1480
|
+
type: "ADD",
|
|
1481
|
+
payload: { obj: mkSceneObj(polyId, "polygon", label, {
|
|
1482
|
+
construction: { kind: key, p1: aId, p2: bId, p3: cId }
|
|
1483
|
+
}) }
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
var parallelogramTool = {
|
|
1487
|
+
key: "parallelogram",
|
|
1488
|
+
finalize(ctx) {
|
|
1489
|
+
finalizePgmTrap(ctx, "parallelogram");
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1492
|
+
var isoTrapezoidTool = {
|
|
1493
|
+
key: "isoTrapezoid",
|
|
1494
|
+
finalize(ctx) {
|
|
1495
|
+
finalizePgmTrap(ctx, "isoTrapezoid");
|
|
1496
|
+
}
|
|
1497
|
+
};
|
|
1498
|
+
var isoTriangleTool = {
|
|
1499
|
+
key: "isoTriangle",
|
|
1500
|
+
finalize(ctx) {
|
|
1501
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1502
|
+
const [b1Id, b2Id, apexId] = ids;
|
|
1503
|
+
const P = readJxgPos(ctx, apexId);
|
|
1504
|
+
const B1 = readJxgPos(ctx, b1Id);
|
|
1505
|
+
const B2 = readJxgPos(ctx, b2Id);
|
|
1506
|
+
const t = computePerpBisectorT(P, B1, B2);
|
|
1507
|
+
const polyId = freshId(ctx, "iso");
|
|
1508
|
+
const label = ctx.nextLabel("polygon");
|
|
1509
|
+
ctx.store.dispatch({
|
|
1510
|
+
type: "TRANSACTION",
|
|
1511
|
+
payload: { actions: [
|
|
1512
|
+
{ type: "UPDATE_ATTRS", payload: { id: apexId, patch: {
|
|
1513
|
+
constraint: { kind: "onPerpBisector", p1: b1Id, p2: b2Id, t }
|
|
1514
|
+
} } },
|
|
1515
|
+
{ type: "ADD", payload: { obj: mkSceneObj(polyId, "polygon", label, {
|
|
1516
|
+
construction: { kind: "isoTriangle", base1: b1Id, base2: b2Id, apex: apexId }
|
|
1517
|
+
}) } }
|
|
1518
|
+
] }
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
};
|
|
1522
|
+
var rightTriangleTool = {
|
|
1523
|
+
key: "rightTriangle",
|
|
1524
|
+
finalize(ctx) {
|
|
1525
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1526
|
+
const [rId, p1Id, p2Id] = ids;
|
|
1527
|
+
const P = readJxgPos(ctx, p2Id);
|
|
1528
|
+
const R = readJxgPos(ctx, rId);
|
|
1529
|
+
const P1 = readJxgPos(ctx, p1Id);
|
|
1530
|
+
const t = computePerpendicularT(P, R, R, P1);
|
|
1531
|
+
const polyId = freshId(ctx, "rtri");
|
|
1532
|
+
const label = ctx.nextLabel("polygon");
|
|
1533
|
+
ctx.store.dispatch({
|
|
1534
|
+
type: "TRANSACTION",
|
|
1535
|
+
payload: { actions: [
|
|
1536
|
+
{ type: "UPDATE_ATTRS", payload: { id: p2Id, patch: {
|
|
1537
|
+
constraint: { kind: "onPerpendicular", through: rId, perpToA: rId, perpToB: p1Id, t }
|
|
1538
|
+
} } },
|
|
1539
|
+
{ type: "ADD", payload: { obj: mkSceneObj(polyId, "polygon", label, {
|
|
1540
|
+
construction: { kind: "rightTriangle", rightAngle: rId, leg1End: p1Id, leg2End: p2Id }
|
|
1541
|
+
}) } }
|
|
1542
|
+
] }
|
|
1543
|
+
});
|
|
1544
|
+
}
|
|
1545
|
+
};
|
|
1546
|
+
|
|
1547
|
+
// src/stamps/geometry-2d/editor/handlers/finalize/measure.ts
|
|
1548
|
+
var measure_exports = {};
|
|
1549
|
+
__export(measure_exports, {
|
|
1550
|
+
angleTool: () => angleTool,
|
|
1551
|
+
distanceTool: () => distanceTool,
|
|
1552
|
+
intersectTool: () => intersectTool
|
|
1553
|
+
});
|
|
1554
|
+
var angleTool = {
|
|
1555
|
+
key: "angle",
|
|
1556
|
+
finalize(ctx) {
|
|
1557
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1558
|
+
const id = freshId(ctx, "ang");
|
|
1559
|
+
const label = ctx.nextLabel("angle");
|
|
1560
|
+
ctx.store.dispatch({
|
|
1561
|
+
type: "ADD",
|
|
1562
|
+
payload: { obj: mkSceneObj(id, "angle", label, {
|
|
1563
|
+
p1: ids[0],
|
|
1564
|
+
vertex: ids[1],
|
|
1565
|
+
p2: ids[2]
|
|
1566
|
+
}) }
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
};
|
|
1570
|
+
var distanceTool = {
|
|
1571
|
+
key: "distance",
|
|
1572
|
+
finalize(ctx) {
|
|
1573
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1574
|
+
const id = freshId(ctx, "d");
|
|
1575
|
+
const label = ctx.nextLabel("distance");
|
|
1576
|
+
ctx.store.dispatch({
|
|
1577
|
+
type: "ADD",
|
|
1578
|
+
payload: { obj: mkSceneObj(id, "distance", label, { p1: ids[0], p2: ids[1] }) }
|
|
1579
|
+
});
|
|
1580
|
+
}
|
|
1581
|
+
};
|
|
1582
|
+
var intersectTool = {
|
|
1583
|
+
key: "intersect",
|
|
1584
|
+
finalize(ctx) {
|
|
1585
|
+
const ids = ctx.pendingIdsRef.current;
|
|
1586
|
+
const picks = ctx.pendingRef.current;
|
|
1587
|
+
const pendIds = ctx.pendingIdsRef.current;
|
|
1588
|
+
const aIdx = pendIds.indexOf(ids[0]);
|
|
1589
|
+
const bIdx = pendIds.indexOf(ids[1]);
|
|
1590
|
+
if (aIdx < 0 || bIdx < 0) return;
|
|
1591
|
+
const aKind = objKind(picks[aIdx]);
|
|
1592
|
+
const bKind = objKind(picks[bIdx]);
|
|
1593
|
+
if (aKind === "line" && bKind === "line") {
|
|
1594
|
+
dispatchAddIntersection(ctx, { kind: "lineLine", ref1: ids[0], ref2: ids[1] });
|
|
936
1595
|
return;
|
|
937
1596
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1597
|
+
const isLineCircle = aKind === "line" && bKind === "circle" || aKind === "circle" && bKind === "line";
|
|
1598
|
+
const isCircleCircle = aKind === "circle" && bKind === "circle";
|
|
1599
|
+
if (!isLineCircle && !isCircleCircle) return;
|
|
1600
|
+
for (const branch of [0, 1]) {
|
|
1601
|
+
dispatchAddIntersection(ctx, {
|
|
1602
|
+
kind: isLineCircle ? "lineCircle" : "circleCircle",
|
|
1603
|
+
ref1: ids[0],
|
|
1604
|
+
ref2: ids[1],
|
|
1605
|
+
branch
|
|
944
1606
|
});
|
|
945
|
-
return;
|
|
946
|
-
}
|
|
947
|
-
case "intersect": {
|
|
948
|
-
const picks = ctx.pendingRef.current;
|
|
949
|
-
const pendIds = ctx.pendingIdsRef.current;
|
|
950
|
-
const aIdx = pendIds.indexOf(ids[0]);
|
|
951
|
-
const bIdx = pendIds.indexOf(ids[1]);
|
|
952
|
-
if (aIdx < 0 || bIdx < 0) return;
|
|
953
|
-
const aKind = objKind(picks[aIdx]);
|
|
954
|
-
const bKind = objKind(picks[bIdx]);
|
|
955
|
-
if (aKind === "line" && bKind === "line") {
|
|
956
|
-
dispatchAddIntersection(ctx, { kind: "lineLine", ref1: ids[0], ref2: ids[1] });
|
|
957
|
-
return;
|
|
958
|
-
}
|
|
959
|
-
const isLineCircle = aKind === "line" && bKind === "circle" || aKind === "circle" && bKind === "line";
|
|
960
|
-
const isCircleCircle = aKind === "circle" && bKind === "circle";
|
|
961
|
-
if (!isLineCircle && !isCircleCircle) return;
|
|
962
|
-
for (const branch of [0, 1]) {
|
|
963
|
-
dispatchAddIntersection(ctx, {
|
|
964
|
-
kind: isLineCircle ? "lineCircle" : "circleCircle",
|
|
965
|
-
ref1: ids[0],
|
|
966
|
-
ref2: ids[1],
|
|
967
|
-
branch
|
|
968
|
-
});
|
|
969
|
-
}
|
|
970
|
-
return;
|
|
971
1607
|
}
|
|
972
|
-
default:
|
|
973
|
-
return;
|
|
974
1608
|
}
|
|
1609
|
+
};
|
|
1610
|
+
|
|
1611
|
+
// src/stamps/geometry-2d/editor/handlers/finalize/registry.ts
|
|
1612
|
+
var ALL = [
|
|
1613
|
+
...Object.values(lines_exports),
|
|
1614
|
+
...Object.values(circles_exports),
|
|
1615
|
+
...Object.values(points_exports),
|
|
1616
|
+
...Object.values(polygons_exports),
|
|
1617
|
+
...Object.values(measure_exports)
|
|
1618
|
+
].filter((m) => !!m && typeof m.finalize === "function");
|
|
1619
|
+
var TOOL_MODULES = new Map(ALL.map((m) => [m.key, m]));
|
|
1620
|
+
|
|
1621
|
+
// src/stamps/geometry-2d/editor/handlers/finalizeShape.ts
|
|
1622
|
+
function finalizeShape(ctx, toolDef, clickXY) {
|
|
1623
|
+
TOOL_MODULES.get(toolDef.key)?.finalize(ctx, toolDef, clickXY);
|
|
975
1624
|
}
|
|
976
1625
|
|
|
977
1626
|
// src/stamps/geometry-2d/editor/transforms.ts
|
|
@@ -1013,6 +1662,20 @@ function finalizeTransform(ctx, tool, pendingIds, value) {
|
|
|
1013
1662
|
});
|
|
1014
1663
|
return;
|
|
1015
1664
|
}
|
|
1665
|
+
if (tool === "circleCR") {
|
|
1666
|
+
const r = Math.abs(value);
|
|
1667
|
+
if (!(r > 0)) {
|
|
1668
|
+
ctx.flashWarn("B\xE1n k\xEDnh ph\u1EA3i > 0");
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
const id = freshId(ctx, "c");
|
|
1672
|
+
const label = ctx.nextLabel("circle");
|
|
1673
|
+
ctx.store.dispatch({
|
|
1674
|
+
type: "ADD",
|
|
1675
|
+
payload: { obj: mkSceneObj(id, "circle", label, { center: pendingIds[0], radius: r }) }
|
|
1676
|
+
});
|
|
1677
|
+
return;
|
|
1678
|
+
}
|
|
1016
1679
|
const sourceId = pendingIds[0];
|
|
1017
1680
|
const state = ctx.store.getState();
|
|
1018
1681
|
const source = state.objects[sourceId];
|
|
@@ -1206,7 +1869,7 @@ function handleMultiClickTool(ctx, toolDef, e, x, y, hits, bestHit) {
|
|
|
1206
1869
|
}
|
|
1207
1870
|
if (ctx.pendingIdsRef.current.length >= toolDef.needs) {
|
|
1208
1871
|
const tk = toolDef.key;
|
|
1209
|
-
if (tk === "rotate" || tk === "dilate" || tk === "regularPolygon") {
|
|
1872
|
+
if (tk === "rotate" || tk === "dilate" || tk === "regularPolygon" || tk === "circleCR") {
|
|
1210
1873
|
const cx = (e.clientX ?? 0) + 8;
|
|
1211
1874
|
const cy = (e.clientY ?? 0) + 8;
|
|
1212
1875
|
ctx.pendingTransformRef.current = {
|
|
@@ -1222,7 +1885,7 @@ function handleMultiClickTool(ctx, toolDef, e, x, y, hits, bestHit) {
|
|
|
1222
1885
|
ctx.clearPending();
|
|
1223
1886
|
return;
|
|
1224
1887
|
}
|
|
1225
|
-
finalizeShape(ctx, toolDef);
|
|
1888
|
+
finalizeShape(ctx, toolDef, { x, y });
|
|
1226
1889
|
ctx.clearPending();
|
|
1227
1890
|
} else {
|
|
1228
1891
|
ctx.refreshPreview();
|
|
@@ -1756,10 +2419,7 @@ function useAxisGridSync({
|
|
|
1756
2419
|
}
|
|
1757
2420
|
function useEditorShortcuts({
|
|
1758
2421
|
store,
|
|
1759
|
-
pendingIdsRef,
|
|
1760
2422
|
selectedSetRef,
|
|
1761
|
-
clearPending,
|
|
1762
|
-
clearSelection,
|
|
1763
2423
|
deleteSelection
|
|
1764
2424
|
}) {
|
|
1765
2425
|
useEffect(() => {
|
|
@@ -1781,18 +2441,6 @@ function useEditorShortcuts({
|
|
|
1781
2441
|
store.redo();
|
|
1782
2442
|
return;
|
|
1783
2443
|
}
|
|
1784
|
-
if (e.key === "Escape" && !inField) {
|
|
1785
|
-
if (pendingIdsRef.current.length > 0) {
|
|
1786
|
-
e.preventDefault();
|
|
1787
|
-
e.stopPropagation();
|
|
1788
|
-
clearPending();
|
|
1789
|
-
}
|
|
1790
|
-
if (selectedSetRef.current.size > 0) {
|
|
1791
|
-
e.preventDefault();
|
|
1792
|
-
e.stopPropagation();
|
|
1793
|
-
clearSelection();
|
|
1794
|
-
}
|
|
1795
|
-
}
|
|
1796
2444
|
if ((e.key === "Delete" || e.key === "Backspace") && !inField && selectedSetRef.current.size > 0) {
|
|
1797
2445
|
e.preventDefault();
|
|
1798
2446
|
e.stopPropagation();
|
|
@@ -1801,7 +2449,7 @@ function useEditorShortcuts({
|
|
|
1801
2449
|
};
|
|
1802
2450
|
window.addEventListener("keydown", onKey, { capture: true });
|
|
1803
2451
|
return () => window.removeEventListener("keydown", onKey, { capture: true });
|
|
1804
|
-
}, [store,
|
|
2452
|
+
}, [store, selectedSetRef, deleteSelection]);
|
|
1805
2453
|
}
|
|
1806
2454
|
function useJxgSceneIdMap({ store, rendererRef }) {
|
|
1807
2455
|
const jxgIdToSceneRef = useRef(/* @__PURE__ */ new Map());
|
|
@@ -2005,10 +2653,7 @@ var MiniBoard2D = forwardRef(function MiniBoard2D2({ onReady, store, selectedToo
|
|
|
2005
2653
|
};
|
|
2006
2654
|
useEditorShortcuts({
|
|
2007
2655
|
store,
|
|
2008
|
-
pendingIdsRef: toolSM.pendingIdsRef,
|
|
2009
2656
|
selectedSetRef,
|
|
2010
|
-
clearPending,
|
|
2011
|
-
clearSelection,
|
|
2012
2657
|
deleteSelection
|
|
2013
2658
|
});
|
|
2014
2659
|
useAxisGridSync({ boardRef, axisObjsRef, isDarkRef, showAxis, showGrid });
|
|
@@ -2550,7 +3195,8 @@ var MultiPropertiesPopover = (props) => {
|
|
|
2550
3195
|
var LABELS = {
|
|
2551
3196
|
rotate: { aria: "G\xF3c quay", label: "G\xF3c (\xB0)", step: 15 },
|
|
2552
3197
|
dilate: { aria: "T\u1EF7 s\u1ED1 k", label: "T\u1EF7 s\u1ED1 k", step: 0.5 },
|
|
2553
|
-
regularPolygon: { aria: "S\u1ED1 c\u1EA1nh \u0111a gi\xE1c \u0111\u1EC1u", label: "S\u1ED1 c\u1EA1nh (n \u2265 3)", step: 1, min: 3 }
|
|
3198
|
+
regularPolygon: { aria: "S\u1ED1 c\u1EA1nh \u0111a gi\xE1c \u0111\u1EC1u", label: "S\u1ED1 c\u1EA1nh (n \u2265 3)", step: 1, min: 3 },
|
|
3199
|
+
circleCR: { aria: "B\xE1n k\xEDnh \u0111\u01B0\u1EDDng tr\xF2n", label: "B\xE1n k\xEDnh", step: 0.5, min: 0 }
|
|
2554
3200
|
};
|
|
2555
3201
|
var TransformParamPopover = ({ kind, anchor, defaultValue, onConfirm, onCancel, isDark }) => {
|
|
2556
3202
|
const [value, setValue] = useState(defaultValue);
|
|
@@ -2723,19 +3369,44 @@ function useAiFigure(generator, options = {}) {
|
|
|
2723
3369
|
hasUnsupported
|
|
2724
3370
|
};
|
|
2725
3371
|
}
|
|
2726
|
-
var
|
|
2727
|
-
"
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
3372
|
+
var PaperclipIcon = (props) => /* @__PURE__ */ jsx(
|
|
3373
|
+
"svg",
|
|
3374
|
+
{
|
|
3375
|
+
viewBox: "0 0 24 24",
|
|
3376
|
+
fill: "none",
|
|
3377
|
+
stroke: "currentColor",
|
|
3378
|
+
strokeWidth: 1.75,
|
|
3379
|
+
strokeLinecap: "round",
|
|
3380
|
+
strokeLinejoin: "round",
|
|
3381
|
+
"aria-hidden": true,
|
|
3382
|
+
...props,
|
|
3383
|
+
children: /* @__PURE__ */ jsx("path", { d: "M21.44 11.05 12.25 20.24a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66L9.41 17.41a2 2 0 1 1-2.83-2.83l8.49-8.49" })
|
|
3384
|
+
}
|
|
3385
|
+
);
|
|
3386
|
+
var ArrowUpIcon = (props) => /* @__PURE__ */ jsxs(
|
|
3387
|
+
"svg",
|
|
3388
|
+
{
|
|
3389
|
+
viewBox: "0 0 24 24",
|
|
3390
|
+
fill: "none",
|
|
3391
|
+
stroke: "currentColor",
|
|
3392
|
+
strokeWidth: 2.25,
|
|
3393
|
+
strokeLinecap: "round",
|
|
3394
|
+
strokeLinejoin: "round",
|
|
3395
|
+
"aria-hidden": true,
|
|
3396
|
+
...props,
|
|
3397
|
+
children: [
|
|
3398
|
+
/* @__PURE__ */ jsx("path", { d: "M12 19V5" }),
|
|
3399
|
+
/* @__PURE__ */ jsx("path", { d: "m5 12 7-7 7 7" })
|
|
3400
|
+
]
|
|
3401
|
+
}
|
|
3402
|
+
);
|
|
3403
|
+
var StopIcon = (props) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, ...props, children: /* @__PURE__ */ jsx("rect", { x: "6", y: "6", width: "12", height: "12", rx: "2" }) });
|
|
3404
|
+
function AiFigurePrompt({
|
|
3405
|
+
generator,
|
|
3406
|
+
onGenerated,
|
|
3407
|
+
currentState,
|
|
3408
|
+
extractProblem = handleExtractProblem
|
|
3409
|
+
}) {
|
|
2739
3410
|
const {
|
|
2740
3411
|
prompt,
|
|
2741
3412
|
setPrompt,
|
|
@@ -2755,18 +3426,96 @@ function AiFigurePrompt({ generator, onGenerated, currentState }) {
|
|
|
2755
3426
|
setElapsed(0);
|
|
2756
3427
|
return;
|
|
2757
3428
|
}
|
|
2758
|
-
setElapsed(0);
|
|
2759
3429
|
const id = setInterval(() => setElapsed((s) => s + 1), 1e3);
|
|
2760
3430
|
return () => clearInterval(id);
|
|
2761
3431
|
}, [isLoading]);
|
|
2762
|
-
const
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
3432
|
+
const [image, setImage] = useState(null);
|
|
3433
|
+
const [ocrLoading, setOcrLoading] = useState(false);
|
|
3434
|
+
const [ocrError, setOcrError] = useState(null);
|
|
3435
|
+
const [ocrWarning, setOcrWarning] = useState(null);
|
|
3436
|
+
const [isDragOver, setIsDragOver] = useState(false);
|
|
3437
|
+
const fileInputRef = useRef(null);
|
|
3438
|
+
const textareaRef = useRef(null);
|
|
3439
|
+
const imagePreview = image ? `data:${image.mediaType};base64,${image.base64}` : null;
|
|
3440
|
+
useEffect(() => {
|
|
3441
|
+
setOcrError(null);
|
|
3442
|
+
setOcrWarning(null);
|
|
3443
|
+
}, [image]);
|
|
3444
|
+
const handleFile = useCallback(
|
|
3445
|
+
async (file) => {
|
|
3446
|
+
if (isLoading || ocrLoading) return;
|
|
3447
|
+
const v = validateFile(file);
|
|
3448
|
+
if (!v.ok) {
|
|
3449
|
+
setOcrError(v.message);
|
|
3450
|
+
return;
|
|
3451
|
+
}
|
|
3452
|
+
try {
|
|
3453
|
+
const part = await fileToImagePart(file);
|
|
3454
|
+
setImage(part);
|
|
3455
|
+
} catch (e) {
|
|
3456
|
+
setOcrError(e instanceof Error ? e.message : "Kh\xF4ng decode \u0111\u01B0\u1EE3c \u1EA3nh");
|
|
3457
|
+
}
|
|
3458
|
+
},
|
|
3459
|
+
[isLoading, ocrLoading]
|
|
3460
|
+
);
|
|
3461
|
+
const handleFileInput = useCallback(
|
|
3462
|
+
(e) => {
|
|
3463
|
+
const file = e.target.files?.[0];
|
|
3464
|
+
if (file) void handleFile(file);
|
|
3465
|
+
e.target.value = "";
|
|
3466
|
+
},
|
|
3467
|
+
[handleFile]
|
|
3468
|
+
);
|
|
3469
|
+
const handlePaste = useCallback(
|
|
3470
|
+
(e) => {
|
|
3471
|
+
const item = Array.from(e.clipboardData.items).find(
|
|
3472
|
+
(it) => it.kind === "file" && it.type.startsWith("image/")
|
|
3473
|
+
);
|
|
3474
|
+
if (!item) return;
|
|
3475
|
+
const file = item.getAsFile();
|
|
3476
|
+
if (!file) return;
|
|
3477
|
+
e.preventDefault();
|
|
3478
|
+
void handleFile(file);
|
|
3479
|
+
},
|
|
3480
|
+
[handleFile]
|
|
3481
|
+
);
|
|
3482
|
+
const handleDrop = useCallback(
|
|
3483
|
+
(e) => {
|
|
3484
|
+
e.preventDefault();
|
|
3485
|
+
setIsDragOver(false);
|
|
3486
|
+
const file = Array.from(e.dataTransfer.files).find(
|
|
3487
|
+
(f) => f.type.startsWith("image/")
|
|
3488
|
+
);
|
|
3489
|
+
if (file) void handleFile(file);
|
|
2767
3490
|
},
|
|
2768
|
-
[
|
|
3491
|
+
[handleFile]
|
|
2769
3492
|
);
|
|
3493
|
+
const runOcr = useCallback(async () => {
|
|
3494
|
+
if (!image) return;
|
|
3495
|
+
setOcrLoading(true);
|
|
3496
|
+
setOcrError(null);
|
|
3497
|
+
setOcrWarning(null);
|
|
3498
|
+
try {
|
|
3499
|
+
const r = await extractProblem(image);
|
|
3500
|
+
if (r.kind === "success" || r.kind === "low-confidence") {
|
|
3501
|
+
setPrompt(r.text);
|
|
3502
|
+
if (r.kind === "low-confidence") setOcrWarning(r.warning);
|
|
3503
|
+
requestAnimationFrame(() => textareaRef.current?.focus());
|
|
3504
|
+
} else {
|
|
3505
|
+
setOcrError(r.message);
|
|
3506
|
+
}
|
|
3507
|
+
} finally {
|
|
3508
|
+
setOcrLoading(false);
|
|
3509
|
+
}
|
|
3510
|
+
}, [image, setPrompt, extractProblem]);
|
|
3511
|
+
const handleSendClick = useCallback(async () => {
|
|
3512
|
+
if (image && !prompt.trim() && !ocrLoading) {
|
|
3513
|
+
await runOcr();
|
|
3514
|
+
return;
|
|
3515
|
+
}
|
|
3516
|
+
const generated = await submit();
|
|
3517
|
+
if (generated) onGenerated(generated);
|
|
3518
|
+
}, [image, prompt, ocrLoading, runOcr, submit, onGenerated]);
|
|
2770
3519
|
const handleSwitchToBuild = useCallback(() => {
|
|
2771
3520
|
if (currentState && currentState.order.length > 0) {
|
|
2772
3521
|
const ok = window.confirm(
|
|
@@ -2776,109 +3525,256 @@ function AiFigurePrompt({ generator, onGenerated, currentState }) {
|
|
|
2776
3525
|
}
|
|
2777
3526
|
setMode("build");
|
|
2778
3527
|
}, [currentState, setMode]);
|
|
2779
|
-
const primaryLabel = isLoading ? tokens > 0 ? `\u0110ang d\u1EF1ng ${tokens}tok / ${elapsed}s \u2014 Hu\u1EF7` : `\u0110ang d\u1EF1ng... ${elapsed}s \u2014 Hu\u1EF7` : "D\u1EF1ng b\u1EB1ng AI";
|
|
2780
3528
|
const hasContent = currentState != null && currentState.order.length > 0;
|
|
2781
|
-
const
|
|
3529
|
+
const promptEmpty = !prompt.trim();
|
|
3530
|
+
const willOcr = image != null && promptEmpty;
|
|
3531
|
+
const sendDisabled = !image && promptEmpty || ocrLoading || isLoading && !willOcr;
|
|
2782
3532
|
const refineChipLabel = entityCount.points + entityCount.shapes > 0 ? `Th\xEAm v\xE0o \xB7 ${entityCount.points}\u0111, ${entityCount.shapes}\u0111o\u1EA1n` : "Th\xEAm v\xE0o";
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
{
|
|
2786
|
-
"
|
|
2787
|
-
|
|
2788
|
-
void handleSubmit(event);
|
|
2789
|
-
},
|
|
2790
|
-
className: "border-b border-slate-200 bg-slate-50 px-3 py-2",
|
|
2791
|
-
children: [
|
|
3533
|
+
const placeholder = willOcr ? "B\u1EA5m g\u1EEDi \u0111\u1EC3 \u0111\u1ECDc \u0111\u1EC1 t\u1EEB \u1EA3nh \u2014 ho\u1EB7c t\u1EF1 g\xF5 \u1EDF \u0111\xE2y\u2026" : mode === "refine" ? "M\xF4 t\u1EA3 ph\u1EA7n c\u1EA7n th\xEAm (vd: trung \u0111i\u1EC3m M c\u1EE7a BC). C\xF3 th\u1EC3 d\xE1n \u1EA3nh \u0111\u1EC1 (Ctrl+V)." : "M\xF4 t\u1EA3 \u0111\u1EC1 b\xE0i c\u1EA7n d\u1EF1ng \u2014 ho\u1EB7c d\xE1n/\u0111\xEDnh \u1EA3nh \u0111\u1EC1 (Ctrl+V).";
|
|
3534
|
+
return /* @__PURE__ */ jsxs("div", { className: "border-b border-slate-200 bg-slate-50 px-3 py-3", children: [
|
|
3535
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
|
|
3536
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium tracking-wide text-slate-600", children: "D\u1EF1ng h\xECnh b\u1EB1ng AI" }),
|
|
3537
|
+
hasContent && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", role: "tablist", "aria-label": "Ch\u1EBF \u0111\u1ED9 AI", children: [
|
|
2792
3538
|
/* @__PURE__ */ jsx(
|
|
2793
|
-
"
|
|
3539
|
+
"button",
|
|
2794
3540
|
{
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
3541
|
+
type: "button",
|
|
3542
|
+
role: "tab",
|
|
3543
|
+
"aria-selected": mode === "refine",
|
|
3544
|
+
"data-testid": "geometry-ai-mode-refine",
|
|
3545
|
+
onClick: () => setMode("refine"),
|
|
3546
|
+
disabled: isLoading || hasUnsupported,
|
|
3547
|
+
title: hasUnsupported ? "H\xECnh c\xF3 \u0111\u1ED1i t\u01B0\u1EE3ng ngo\xE0i DSL \u2014 ch\u1EC9 d\u1EF1ng m\u1EDBi \u0111\u01B0\u1EE3c" : refineChipLabel,
|
|
3548
|
+
className: `rounded-full px-2.5 py-0.5 text-[11px] transition ${mode === "refine" ? "bg-emerald-600 text-white shadow-sm" : "text-slate-500 hover:text-emerald-700"} ${hasUnsupported ? "cursor-not-allowed opacity-40" : ""}`,
|
|
3549
|
+
children: refineChipLabel
|
|
2798
3550
|
}
|
|
2799
3551
|
),
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
3552
|
+
/* @__PURE__ */ jsx(
|
|
3553
|
+
"button",
|
|
3554
|
+
{
|
|
3555
|
+
type: "button",
|
|
3556
|
+
role: "tab",
|
|
3557
|
+
"aria-selected": mode === "build",
|
|
3558
|
+
"data-testid": "geometry-ai-mode-build",
|
|
3559
|
+
onClick: handleSwitchToBuild,
|
|
3560
|
+
disabled: isLoading,
|
|
3561
|
+
className: `rounded-full px-2.5 py-0.5 text-[11px] transition ${mode === "build" ? "bg-emerald-600 text-white shadow-sm" : "text-slate-500 hover:text-emerald-700"}`,
|
|
3562
|
+
children: "D\u1EF1ng m\u1EDBi"
|
|
3563
|
+
}
|
|
3564
|
+
)
|
|
3565
|
+
] })
|
|
3566
|
+
] }),
|
|
3567
|
+
hasUnsupported && /* @__PURE__ */ jsx(
|
|
3568
|
+
"p",
|
|
3569
|
+
{
|
|
3570
|
+
className: "mb-1.5 text-[10px] text-amber-700",
|
|
3571
|
+
"data-testid": "geometry-ai-unsupported-warning",
|
|
3572
|
+
children: "H\xECnh c\xF3 \u0111\u1ED1i t\u01B0\u1EE3ng ngo\xE0i DSL \u2014 ch\u1EC9 d\u1EF1ng m\u1EDBi \u0111\u01B0\u1EE3c"
|
|
3573
|
+
}
|
|
3574
|
+
),
|
|
3575
|
+
/* @__PURE__ */ jsxs(
|
|
3576
|
+
"div",
|
|
3577
|
+
{
|
|
3578
|
+
onDragOver: (e) => {
|
|
3579
|
+
e.preventDefault();
|
|
3580
|
+
setIsDragOver(true);
|
|
3581
|
+
},
|
|
3582
|
+
onDragLeave: () => setIsDragOver(false),
|
|
3583
|
+
onDrop: handleDrop,
|
|
3584
|
+
onPaste: handlePaste,
|
|
3585
|
+
"aria-label": "Khu v\u1EF1c k\xE9o th\u1EA3 \u1EA3nh",
|
|
3586
|
+
role: "region",
|
|
3587
|
+
className: "group relative flex flex-col rounded-2xl bg-white shadow-sm transition-all duration-150 ring-1 ring-slate-200 focus-within:ring-2 focus-within:ring-emerald-400/70 focus-within:shadow-md " + (isDragOver ? "ring-2 ring-emerald-500 bg-emerald-50/40" : ""),
|
|
3588
|
+
children: [
|
|
3589
|
+
image && imagePreview && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 px-3 pt-2.5", children: /* @__PURE__ */ jsxs("div", { className: "group/chip relative", children: [
|
|
3590
|
+
/* @__PURE__ */ jsx(
|
|
3591
|
+
"img",
|
|
3592
|
+
{
|
|
3593
|
+
src: imagePreview,
|
|
3594
|
+
alt: "\u1EA2nh \u0111\u1EC1 b\xE0i",
|
|
3595
|
+
className: "max-h-48 max-w-full h-auto w-auto rounded-lg border border-slate-200 shadow-sm"
|
|
3596
|
+
}
|
|
3597
|
+
),
|
|
3598
|
+
/* @__PURE__ */ jsx(
|
|
3599
|
+
"button",
|
|
3600
|
+
{
|
|
3601
|
+
type: "button",
|
|
3602
|
+
onClick: () => setImage(null),
|
|
3603
|
+
disabled: ocrLoading || isLoading,
|
|
3604
|
+
"aria-label": "Xo\xE1 \u1EA3nh",
|
|
3605
|
+
className: "absolute -right-1.5 -top-1.5 flex h-5 w-5 items-center justify-center rounded-full bg-slate-900/85 text-[11px] font-medium text-white shadow ring-2 ring-white transition hover:bg-slate-900 disabled:opacity-50",
|
|
3606
|
+
children: "\xD7"
|
|
3607
|
+
}
|
|
3608
|
+
)
|
|
3609
|
+
] }) }),
|
|
2834
3610
|
/* @__PURE__ */ jsx(
|
|
2835
3611
|
"textarea",
|
|
2836
3612
|
{
|
|
3613
|
+
ref: textareaRef,
|
|
2837
3614
|
id: "geometry-ai-prompt",
|
|
2838
3615
|
"aria-label": "\u0110\u1EC1 b\xE0i cho AI",
|
|
3616
|
+
"data-testid": "geometry-ai-textarea",
|
|
2839
3617
|
value: prompt,
|
|
2840
|
-
onChange: (
|
|
3618
|
+
onChange: (e) => setPrompt(e.target.value),
|
|
3619
|
+
onKeyDown: (e) => {
|
|
3620
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey) && !sendDisabled) {
|
|
3621
|
+
e.preventDefault();
|
|
3622
|
+
void handleSendClick();
|
|
3623
|
+
}
|
|
3624
|
+
},
|
|
2841
3625
|
disabled: isLoading,
|
|
2842
3626
|
rows: 2,
|
|
2843
|
-
placeholder
|
|
2844
|
-
className: "
|
|
3627
|
+
placeholder,
|
|
3628
|
+
className: "block w-full resize-none rounded-2xl bg-transparent px-3.5 pt-2.5 pb-1 text-sm leading-relaxed text-slate-800 placeholder:text-slate-400 outline-none disabled:opacity-60 field-sizing-content max-h-44"
|
|
2845
3629
|
}
|
|
2846
3630
|
),
|
|
2847
|
-
|
|
2848
|
-
"
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
3631
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-2 pb-2 pt-1", children: [
|
|
3632
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
3633
|
+
/* @__PURE__ */ jsx(
|
|
3634
|
+
"button",
|
|
3635
|
+
{
|
|
3636
|
+
type: "button",
|
|
3637
|
+
onClick: () => fileInputRef.current?.click(),
|
|
3638
|
+
disabled: isLoading || ocrLoading,
|
|
3639
|
+
"aria-label": "\u0110\xEDnh \u1EA3nh \u0111\u1EC1 b\xE0i",
|
|
3640
|
+
title: "\u0110\xEDnh \u1EA3nh (c\u0169ng c\xF3 th\u1EC3 d\xE1n b\u1EB1ng Ctrl+V ho\u1EB7c k\xE9o th\u1EA3)",
|
|
3641
|
+
className: "flex h-8 w-8 items-center justify-center rounded-full text-slate-500 transition hover:bg-slate-100 hover:text-emerald-700 disabled:opacity-40",
|
|
3642
|
+
children: /* @__PURE__ */ jsx(PaperclipIcon, { className: "h-[18px] w-[18px]" })
|
|
3643
|
+
}
|
|
3644
|
+
),
|
|
3645
|
+
/* @__PURE__ */ jsx(
|
|
3646
|
+
"input",
|
|
3647
|
+
{
|
|
3648
|
+
ref: fileInputRef,
|
|
3649
|
+
type: "file",
|
|
3650
|
+
accept: "image/png,image/jpeg,image/webp",
|
|
3651
|
+
className: "sr-only",
|
|
3652
|
+
onChange: handleFileInput,
|
|
3653
|
+
disabled: isLoading || ocrLoading,
|
|
3654
|
+
"aria-label": "Ch\u1ECDn \u1EA3nh \u0111\u1EC1 b\xE0i"
|
|
3655
|
+
}
|
|
3656
|
+
)
|
|
3657
|
+
] }),
|
|
3658
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3659
|
+
(isLoading || ocrLoading) && /* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] tabular-nums text-slate-500", children: ocrLoading ? "\u0111\u1ECDc \u1EA3nh\u2026" : tokens > 0 ? `${tokens}tok \xB7 ${elapsed}s` : `${elapsed}s` }),
|
|
3660
|
+
isLoading ? /* @__PURE__ */ jsx(
|
|
3661
|
+
"button",
|
|
3662
|
+
{
|
|
3663
|
+
type: "button",
|
|
3664
|
+
onClick: cancel,
|
|
3665
|
+
"aria-label": "Hu\u1EF7 d\u1EF1ng h\xECnh AI",
|
|
3666
|
+
"data-testid": "geometry-ai-cancel",
|
|
3667
|
+
title: `\u0110ang d\u1EF1ng\u2026 ${elapsed}s \u2014 b\u1EA5m \u0111\u1EC3 hu\u1EF7`,
|
|
3668
|
+
className: "flex h-8 w-8 items-center justify-center rounded-full bg-amber-500 text-white shadow-sm transition hover:scale-105 hover:bg-amber-600 active:scale-95",
|
|
3669
|
+
children: /* @__PURE__ */ jsx(StopIcon, { className: "h-3.5 w-3.5" })
|
|
3670
|
+
}
|
|
3671
|
+
) : /* @__PURE__ */ jsx(
|
|
3672
|
+
"button",
|
|
3673
|
+
{
|
|
3674
|
+
type: "button",
|
|
3675
|
+
onClick: () => void handleSendClick(),
|
|
3676
|
+
disabled: sendDisabled,
|
|
3677
|
+
"aria-label": willOcr ? "\u0110\u1ECDc \u0111\u1EC1 t\u1EEB \u1EA3nh" : "D\u1EF1ng b\u1EB1ng AI",
|
|
3678
|
+
title: willOcr ? "\u0110\u1ECDc \u0111\u1EC1 t\u1EEB \u1EA3nh (s\u1EBD \u0111i\u1EC1n v\xE0o \xF4 chat)" : "D\u1EF1ng b\u1EB1ng AI (Ctrl/\u2318+Enter)",
|
|
3679
|
+
"data-testid": willOcr ? "geometry-ai-ocr" : "geometry-ai-submit",
|
|
3680
|
+
className: "flex h-8 w-8 items-center justify-center rounded-full bg-emerald-600 text-white shadow-sm transition hover:scale-105 hover:bg-emerald-700 active:scale-95 disabled:cursor-not-allowed disabled:bg-slate-300 disabled:hover:scale-100",
|
|
3681
|
+
children: /* @__PURE__ */ jsx(ArrowUpIcon, { className: "h-[18px] w-[18px]" })
|
|
3682
|
+
}
|
|
3683
|
+
)
|
|
3684
|
+
] })
|
|
3685
|
+
] }),
|
|
3686
|
+
isDragOver && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center rounded-2xl bg-emerald-50/60 text-xs font-medium text-emerald-700", children: "Th\u1EA3 \u1EA3nh v\xE0o \u0111\xE2y" })
|
|
3687
|
+
]
|
|
3688
|
+
}
|
|
3689
|
+
),
|
|
3690
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-1.5 px-1 text-[10px] text-slate-500", children: [
|
|
3691
|
+
"D\xE1n \u1EA3nh (Ctrl+V), k\xE9o th\u1EA3, ho\u1EB7c b\u1EA5m ",
|
|
3692
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\u{1F4CE}" }),
|
|
3693
|
+
" \u0111\u1EC3 \u0111\xEDnh \u1EA3nh \u0111\u1EC1."
|
|
3694
|
+
] }),
|
|
3695
|
+
error && /* @__PURE__ */ jsx("p", { role: "alert", className: "mt-1 px-1 text-xs text-red-600", children: error }),
|
|
3696
|
+
ocrError && /* @__PURE__ */ jsx("p", { role: "alert", className: "mt-1 px-1 text-xs text-red-600", children: ocrError }),
|
|
3697
|
+
ocrWarning && /* @__PURE__ */ jsx("p", { className: "mt-1 rounded bg-amber-50 px-2 py-1 text-[11px] text-amber-700", children: ocrWarning })
|
|
3698
|
+
] });
|
|
3699
|
+
}
|
|
3700
|
+
|
|
3701
|
+
// src/stamps/geometry-2d/draft.ts
|
|
3702
|
+
function svgIntrinsicSize(svg) {
|
|
3703
|
+
const w = svg.match(/<svg[^>]*\swidth="([\d.]+)"/);
|
|
3704
|
+
const h = svg.match(/<svg[^>]*\sheight="([\d.]+)"/);
|
|
3705
|
+
if (w && h) return { width: parseFloat(w[1]), height: parseFloat(h[1]) };
|
|
3706
|
+
const vb = svg.match(/viewBox="0 0 ([\d.]+) ([\d.]+)"/);
|
|
3707
|
+
if (vb) return { width: parseFloat(vb[1]), height: parseFloat(vb[2]) };
|
|
3708
|
+
return { width: 300, height: 200 };
|
|
3709
|
+
}
|
|
3710
|
+
function draftFromViewport(svg, appState, seq) {
|
|
3711
|
+
const { width, height } = svgIntrinsicSize(svg);
|
|
3712
|
+
const zoom = appState.zoom?.value ?? 1;
|
|
3713
|
+
const vw = appState.width ?? 800;
|
|
3714
|
+
const vh = appState.height ?? 600;
|
|
3715
|
+
const cx = appState.scrollX + vw / 2 / zoom;
|
|
3716
|
+
const cy = appState.scrollY + vh / 2 / zoom;
|
|
3717
|
+
return { svg, width, height, x: cx - width / 2, y: cy - height / 2, seq };
|
|
3718
|
+
}
|
|
3719
|
+
function didStateChange(seen, jsonState) {
|
|
3720
|
+
if (seen.last === jsonState) return false;
|
|
3721
|
+
seen.last = jsonState;
|
|
3722
|
+
return true;
|
|
3723
|
+
}
|
|
3724
|
+
|
|
3725
|
+
// src/stamps/geometry-2d/editor/useGeometryDraftEmit.ts
|
|
3726
|
+
function useGeometryDraftEmit({
|
|
3727
|
+
store,
|
|
3728
|
+
handleRef,
|
|
3729
|
+
api,
|
|
3730
|
+
showAxis,
|
|
3731
|
+
showGrid,
|
|
3732
|
+
onGeometryDraft,
|
|
3733
|
+
debounceMs = 350
|
|
3734
|
+
}) {
|
|
3735
|
+
const seqRef = useRef(0);
|
|
3736
|
+
const seenRef = useRef({ last: null });
|
|
3737
|
+
const timerRef = useRef(null);
|
|
3738
|
+
const cbRef = useRef(onGeometryDraft);
|
|
3739
|
+
cbRef.current = onGeometryDraft;
|
|
3740
|
+
useEffect(() => {
|
|
3741
|
+
if (!cbRef.current) return;
|
|
3742
|
+
const emit = () => {
|
|
3743
|
+
const h = handleRef.current;
|
|
3744
|
+
if (!h) return;
|
|
3745
|
+
const state = h.getState();
|
|
3746
|
+
if (Object.keys(state.objects).length === 0) {
|
|
3747
|
+
if (seenRef.current.last !== null) {
|
|
3748
|
+
seenRef.current.last = null;
|
|
3749
|
+
cbRef.current?.(null);
|
|
3750
|
+
}
|
|
3751
|
+
return;
|
|
3752
|
+
}
|
|
3753
|
+
const bbox = h.getBbox();
|
|
3754
|
+
const jsonState = serializeBoard(state, { bbox, showAxis, showGrid });
|
|
3755
|
+
if (!didStateChange(seenRef.current, jsonState)) return;
|
|
3756
|
+
void (async () => {
|
|
3757
|
+
try {
|
|
3758
|
+
const svg = await renderGeometrySvgFromState(jsonState);
|
|
3759
|
+
const appState = api?.getAppState?.() ?? { scrollX: 0, scrollY: 0, width: 800, height: 600, zoom: { value: 1 } };
|
|
3760
|
+
seqRef.current += 1;
|
|
3761
|
+
cbRef.current?.(draftFromViewport(svg, appState, seqRef.current));
|
|
3762
|
+
} catch (err) {
|
|
3763
|
+
console.warn("[geometry] draft render failed:", err);
|
|
3764
|
+
}
|
|
3765
|
+
})();
|
|
3766
|
+
};
|
|
3767
|
+
const schedule = () => {
|
|
3768
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
3769
|
+
timerRef.current = setTimeout(emit, debounceMs);
|
|
3770
|
+
};
|
|
3771
|
+
const unsub = store.subscribe(schedule);
|
|
3772
|
+
return () => {
|
|
3773
|
+
unsub();
|
|
3774
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
3775
|
+
cbRef.current?.(null);
|
|
3776
|
+
};
|
|
3777
|
+
}, [store, handleRef, api, showAxis, showGrid, debounceMs]);
|
|
2882
3778
|
}
|
|
2883
3779
|
var GeometryEditorPanelInner = forwardRef(
|
|
2884
3780
|
function GeometryEditorPanelInner2({
|
|
@@ -2898,7 +3794,9 @@ var GeometryEditorPanelInner = forwardRef(
|
|
|
2898
3794
|
canUndo,
|
|
2899
3795
|
canRedo,
|
|
2900
3796
|
onSelectionChange,
|
|
2901
|
-
generateGeometryFigure
|
|
3797
|
+
generateGeometryFigure,
|
|
3798
|
+
api,
|
|
3799
|
+
onGeometryDraft
|
|
2902
3800
|
}, ref) {
|
|
2903
3801
|
const { showToast } = useToast();
|
|
2904
3802
|
const handleRef = useRef(null);
|
|
@@ -2911,12 +3809,14 @@ var GeometryEditorPanelInner = forwardRef(
|
|
|
2911
3809
|
useEffect(() => {
|
|
2912
3810
|
onSelectionChangeRef.current = onSelectionChange;
|
|
2913
3811
|
}, [onSelectionChange]);
|
|
3812
|
+
const onGeometryDraftRef = useRef(onGeometryDraft);
|
|
3813
|
+
useEffect(() => {
|
|
3814
|
+
onGeometryDraftRef.current = onGeometryDraft;
|
|
3815
|
+
}, [onGeometryDraft]);
|
|
2914
3816
|
useEditorState({ store, onHistoryChange });
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
() => store.getState()
|
|
2919
|
-
);
|
|
3817
|
+
useGeometryDraftEmit({ store, handleRef, api, showAxis, showGrid, onGeometryDraft });
|
|
3818
|
+
const snap = () => store.getState();
|
|
3819
|
+
const currentSceneState = useSyncExternalStore((cb) => store.subscribe(cb), snap, snap);
|
|
2920
3820
|
useEffect(() => {
|
|
2921
3821
|
const sync = () => setHasContent(Object.keys(store.getState().objects).length > 0);
|
|
2922
3822
|
sync();
|
|
@@ -2926,22 +3826,22 @@ var GeometryEditorPanelInner = forwardRef(
|
|
|
2926
3826
|
const h = handleRef.current;
|
|
2927
3827
|
if (!h) return;
|
|
2928
3828
|
setReady(true);
|
|
2929
|
-
h.onSelect((
|
|
2930
|
-
setPropsPopover(
|
|
3829
|
+
h.onSelect((snap2) => {
|
|
3830
|
+
setPropsPopover(snap2);
|
|
2931
3831
|
setMultiSelection(null);
|
|
2932
|
-
onSelectionChangeRef.current?.(
|
|
3832
|
+
onSelectionChangeRef.current?.(snap2.id);
|
|
2933
3833
|
});
|
|
2934
3834
|
h.onTransformParam((info) => setTransformPopover(info));
|
|
2935
|
-
h.onSelectionState((
|
|
2936
|
-
if (!
|
|
3835
|
+
h.onSelectionState((snap2) => {
|
|
3836
|
+
if (!snap2 || snap2.ids.length === 0) {
|
|
2937
3837
|
setPropsPopover(null);
|
|
2938
3838
|
setMultiSelection(null);
|
|
2939
3839
|
onSelectionChangeRef.current?.(void 0);
|
|
2940
3840
|
return;
|
|
2941
3841
|
}
|
|
2942
|
-
if (
|
|
2943
|
-
const id =
|
|
2944
|
-
const single = buildObjectSnapshot(store.getState(), id,
|
|
3842
|
+
if (snap2.ids.length === 1) {
|
|
3843
|
+
const id = snap2.ids[0];
|
|
3844
|
+
const single = buildObjectSnapshot(store.getState(), id, snap2.anchor);
|
|
2945
3845
|
if (single) {
|
|
2946
3846
|
setPropsPopover(single);
|
|
2947
3847
|
setMultiSelection(null);
|
|
@@ -2949,7 +3849,7 @@ var GeometryEditorPanelInner = forwardRef(
|
|
|
2949
3849
|
}
|
|
2950
3850
|
return;
|
|
2951
3851
|
}
|
|
2952
|
-
setMultiSelection(
|
|
3852
|
+
setMultiSelection(snap2);
|
|
2953
3853
|
setPropsPopover(null);
|
|
2954
3854
|
onSelectionChangeRef.current?.(void 0);
|
|
2955
3855
|
});
|
|
@@ -2993,15 +3893,13 @@ var GeometryEditorPanelInner = forwardRef(
|
|
|
2993
3893
|
try {
|
|
2994
3894
|
const svgString = await renderGeometrySvgFromState(jsonState);
|
|
2995
3895
|
onInsert(jsonState, svgString);
|
|
3896
|
+
onGeometryDraftRef.current?.(null);
|
|
2996
3897
|
} catch (err) {
|
|
2997
3898
|
console.error("Geometry insert failed:", err);
|
|
2998
3899
|
}
|
|
2999
3900
|
})();
|
|
3000
3901
|
return true;
|
|
3001
3902
|
}, [onInsert, showAxis, showGrid]);
|
|
3002
|
-
const handleInsert = useCallback(() => {
|
|
3003
|
-
performInsert();
|
|
3004
|
-
}, [performInsert]);
|
|
3005
3903
|
const loadAiFigure = useCallback((generated) => {
|
|
3006
3904
|
handleRef.current?.clearSelection();
|
|
3007
3905
|
setPropsPopover(null);
|
|
@@ -3096,7 +3994,7 @@ var GeometryEditorPanelInner = forwardRef(
|
|
|
3096
3994
|
"button",
|
|
3097
3995
|
{
|
|
3098
3996
|
type: "button",
|
|
3099
|
-
onClick:
|
|
3997
|
+
onClick: performInsert,
|
|
3100
3998
|
disabled: !ready || !hasContent,
|
|
3101
3999
|
title: !hasContent ? "V\u1EBD \xEDt nh\u1EA5t m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng tr\u01B0\u1EDBc khi ch\xE8n" : void 0,
|
|
3102
4000
|
"data-testid": "geometry-insert-btn-mobile",
|
|
@@ -3183,12 +4081,12 @@ var GeometryEditorPanelInner = forwardRef(
|
|
|
3183
4081
|
onClose: dismissMultiPopover
|
|
3184
4082
|
}
|
|
3185
4083
|
),
|
|
3186
|
-
transformPopover && (transformPopover.tool === "rotate" || transformPopover.tool === "dilate" || transformPopover.tool === "regularPolygon") && /* @__PURE__ */ jsx(
|
|
4084
|
+
transformPopover && (transformPopover.tool === "rotate" || transformPopover.tool === "dilate" || transformPopover.tool === "regularPolygon" || transformPopover.tool === "circleCR") && /* @__PURE__ */ jsx(
|
|
3187
4085
|
TransformParamPopover,
|
|
3188
4086
|
{
|
|
3189
4087
|
kind: transformPopover.tool,
|
|
3190
4088
|
anchor: transformPopover.anchor,
|
|
3191
|
-
defaultValue: transformPopover.tool === "rotate" ? 90 : transformPopover.tool === "dilate" ? 2 : 6,
|
|
4089
|
+
defaultValue: transformPopover.tool === "rotate" ? 90 : transformPopover.tool === "dilate" ? 2 : transformPopover.tool === "circleCR" ? 3 : 6,
|
|
3192
4090
|
isDark,
|
|
3193
4091
|
onConfirm: (v) => {
|
|
3194
4092
|
handleRef.current?.confirmTransformParam(v);
|
|
@@ -3214,7 +4112,7 @@ var GeometryEditorPanelInner = forwardRef(
|
|
|
3214
4112
|
/* @__PURE__ */ jsx(
|
|
3215
4113
|
"button",
|
|
3216
4114
|
{
|
|
3217
|
-
onClick:
|
|
4115
|
+
onClick: performInsert,
|
|
3218
4116
|
disabled: !ready || !hasContent,
|
|
3219
4117
|
title: !hasContent ? "V\u1EBD \xEDt nh\u1EA5t m\u1ED9t \u0111\u1ED1i t\u01B0\u1EE3ng tr\u01B0\u1EDBc khi ch\xE8n" : void 0,
|
|
3220
4118
|
"data-testid": "geometry-insert-btn",
|
|
@@ -3270,7 +4168,7 @@ function parseInitialState(data) {
|
|
|
3270
4168
|
return deserializeBoard(data.jsonState);
|
|
3271
4169
|
}
|
|
3272
4170
|
var GeometryStampHost = forwardRef(
|
|
3273
|
-
function GeometryStampHost2({ api, editingElement, onClose, isDark, generateGeometryFigure }, ref) {
|
|
4171
|
+
function GeometryStampHost2({ api, editingElement, onClose, isDark, generateGeometryFigure, onGeometryDraft }, ref) {
|
|
3274
4172
|
const panelRef = useRef(null);
|
|
3275
4173
|
const { isMobile } = useIsMobile();
|
|
3276
4174
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
@@ -3385,7 +4283,9 @@ var GeometryStampHost = forwardRef(
|
|
|
3385
4283
|
canUndo,
|
|
3386
4284
|
canRedo,
|
|
3387
4285
|
onSelectionChange: setSelectedObjectId,
|
|
3388
|
-
generateGeometryFigure
|
|
4286
|
+
generateGeometryFigure,
|
|
4287
|
+
api,
|
|
4288
|
+
onGeometryDraft
|
|
3389
4289
|
}
|
|
3390
4290
|
)
|
|
3391
4291
|
] });
|
|
@@ -3393,5 +4293,5 @@ var GeometryStampHost = forwardRef(
|
|
|
3393
4293
|
);
|
|
3394
4294
|
|
|
3395
4295
|
export { GeometryStampHost };
|
|
3396
|
-
//# sourceMappingURL=host-
|
|
3397
|
-
//# sourceMappingURL=host-
|
|
4296
|
+
//# sourceMappingURL=host-4P766V4J.mjs.map
|
|
4297
|
+
//# sourceMappingURL=host-4P766V4J.mjs.map
|