@nice2dev/ui-math 1.0.17 → 1.0.18
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/index.cjs +5 -5
- package/dist/index.d.ts +23 -0
- package/dist/index.mjs +998 -725
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,175 @@ var T = Object.defineProperty;
|
|
|
2
2
|
var R = (e, t, r) => t in e ? T(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r;
|
|
3
3
|
var M = (e, t, r) => R(e, typeof t != "symbol" ? t + "" : t, r);
|
|
4
4
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
5
|
-
import { createContext, useContext, useState, useRef, useCallback, useEffect } from "react";
|
|
5
|
+
import React, { createContext, useMemo, useContext, useState, useRef, useCallback, useEffect } from "react";
|
|
6
|
+
const translations = {
|
|
7
|
+
en: {
|
|
8
|
+
"calculator.matrixA": "Matrix A",
|
|
9
|
+
"calculator.matrixB": "Matrix B",
|
|
10
|
+
"calculator.exprPlaceholder": "Enter expression (e.g., x^2, sin(x))",
|
|
11
|
+
"calculator.differentiate": "Differentiate",
|
|
12
|
+
"calculator.integrate": "Integrate",
|
|
13
|
+
"calculator.convert": "Convert",
|
|
14
|
+
"plotter.quickAdd": "Quick add preset...",
|
|
15
|
+
"plotter.noPlots": "No plots added yet",
|
|
16
|
+
"plotter.grid": "Grid",
|
|
17
|
+
"plotter.axes": "Axes",
|
|
18
|
+
"editor.latexPlaceholder": "Enter LaTeX equation...",
|
|
19
|
+
"editor.clear": "Clear",
|
|
20
|
+
"editor.recognize": "Recognize",
|
|
21
|
+
"editor.recognizing": "Recognizing...",
|
|
22
|
+
"editor.handwritingNote": "Draw your equation above. Handwriting recognition requires external API integration."
|
|
23
|
+
},
|
|
24
|
+
pl: {
|
|
25
|
+
"calculator.matrixA": "Macierz A",
|
|
26
|
+
"calculator.matrixB": "Macierz B",
|
|
27
|
+
"calculator.exprPlaceholder": "Wprowadź wyrażenie (np. x^2, sin(x))",
|
|
28
|
+
"calculator.differentiate": "Różniczkuj",
|
|
29
|
+
"calculator.integrate": "Całkuj",
|
|
30
|
+
"calculator.convert": "Przelicz",
|
|
31
|
+
"plotter.quickAdd": "Szybko dodaj ustawienie...",
|
|
32
|
+
"plotter.noPlots": "Nie dodano jeszcze żadnych wykresów",
|
|
33
|
+
"plotter.grid": "Siatka",
|
|
34
|
+
"plotter.axes": "Osie",
|
|
35
|
+
"editor.latexPlaceholder": "Wprowadź równanie LaTeX...",
|
|
36
|
+
"editor.clear": "Wyczyść",
|
|
37
|
+
"editor.recognize": "Rozpoznaj",
|
|
38
|
+
"editor.recognizing": "Rozpoznawanie...",
|
|
39
|
+
"editor.handwritingNote": "Narysuj równanie powyżej. Rozpoznawanie pisma wymaga zewnętrznej integracji API."
|
|
40
|
+
},
|
|
41
|
+
de: {
|
|
42
|
+
"calculator.matrixA": "Matrix A",
|
|
43
|
+
"calculator.matrixB": "Matrix B",
|
|
44
|
+
"calculator.exprPlaceholder": "Ausdruck eingeben (z. B. x^2, sin(x))",
|
|
45
|
+
"calculator.differentiate": "Ableiten",
|
|
46
|
+
"calculator.integrate": "Integrieren",
|
|
47
|
+
"calculator.convert": "Umrechnen",
|
|
48
|
+
"plotter.quickAdd": "Voreinstellung schnell hinzufügen...",
|
|
49
|
+
"plotter.noPlots": "Noch keine Diagramme hinzugefügt",
|
|
50
|
+
"plotter.grid": "Raster",
|
|
51
|
+
"plotter.axes": "Achsen",
|
|
52
|
+
"editor.latexPlaceholder": "LaTeX-Gleichung eingeben...",
|
|
53
|
+
"editor.clear": "Löschen",
|
|
54
|
+
"editor.recognize": "Erkennen",
|
|
55
|
+
"editor.recognizing": "Erkennung läuft...",
|
|
56
|
+
"editor.handwritingNote": "Zeichnen Sie Ihre Gleichung oben. Die Handschrifterkennung erfordert eine externe API-Integration."
|
|
57
|
+
},
|
|
58
|
+
fr: {
|
|
59
|
+
"calculator.matrixA": "Matrice A",
|
|
60
|
+
"calculator.matrixB": "Matrice B",
|
|
61
|
+
"calculator.exprPlaceholder": "Saisir une expression (ex. x^2, sin(x))",
|
|
62
|
+
"calculator.differentiate": "Dériver",
|
|
63
|
+
"calculator.integrate": "Intégrer",
|
|
64
|
+
"calculator.convert": "Convertir",
|
|
65
|
+
"plotter.quickAdd": "Ajouter un préréglage rapide...",
|
|
66
|
+
"plotter.noPlots": "Aucun tracé ajouté pour le moment",
|
|
67
|
+
"plotter.grid": "Grille",
|
|
68
|
+
"plotter.axes": "Axes",
|
|
69
|
+
"editor.latexPlaceholder": "Saisir une équation LaTeX...",
|
|
70
|
+
"editor.clear": "Effacer",
|
|
71
|
+
"editor.recognize": "Reconnaître",
|
|
72
|
+
"editor.recognizing": "Reconnaissance...",
|
|
73
|
+
"editor.handwritingNote": "Dessinez votre équation ci-dessus. La reconnaissance d'écriture nécessite une intégration API externe."
|
|
74
|
+
},
|
|
75
|
+
es: {
|
|
76
|
+
"calculator.matrixA": "Matriz A",
|
|
77
|
+
"calculator.matrixB": "Matriz B",
|
|
78
|
+
"calculator.exprPlaceholder": "Introduce una expresión (p. ej. x^2, sin(x))",
|
|
79
|
+
"calculator.differentiate": "Derivar",
|
|
80
|
+
"calculator.integrate": "Integrar",
|
|
81
|
+
"calculator.convert": "Convertir",
|
|
82
|
+
"plotter.quickAdd": "Añadir preajuste rápido...",
|
|
83
|
+
"plotter.noPlots": "Aún no se ha añadido ningún gráfico",
|
|
84
|
+
"plotter.grid": "Cuadrícula",
|
|
85
|
+
"plotter.axes": "Ejes",
|
|
86
|
+
"editor.latexPlaceholder": "Introduce una ecuación LaTeX...",
|
|
87
|
+
"editor.clear": "Borrar",
|
|
88
|
+
"editor.recognize": "Reconocer",
|
|
89
|
+
"editor.recognizing": "Reconociendo...",
|
|
90
|
+
"editor.handwritingNote": "Dibuja tu ecuación arriba. El reconocimiento de escritura requiere integración con una API externa."
|
|
91
|
+
},
|
|
92
|
+
it: {
|
|
93
|
+
"calculator.matrixA": "Matrice A",
|
|
94
|
+
"calculator.matrixB": "Matrice B",
|
|
95
|
+
"calculator.exprPlaceholder": "Inserisci un’espressione (es. x^2, sin(x))",
|
|
96
|
+
"calculator.differentiate": "Derivare",
|
|
97
|
+
"calculator.integrate": "Integrare",
|
|
98
|
+
"calculator.convert": "Converti",
|
|
99
|
+
"plotter.quickAdd": "Aggiungi un preset rapido...",
|
|
100
|
+
"plotter.noPlots": "Nessun grafico aggiunto",
|
|
101
|
+
"plotter.grid": "Griglia",
|
|
102
|
+
"plotter.axes": "Assi",
|
|
103
|
+
"editor.latexPlaceholder": "Inserisci un’equazione LaTeX...",
|
|
104
|
+
"editor.clear": "Cancella",
|
|
105
|
+
"editor.recognize": "Riconosci",
|
|
106
|
+
"editor.recognizing": "Riconoscimento...",
|
|
107
|
+
"editor.handwritingNote": "Disegna la tua equazione qui sopra. Il riconoscimento della scrittura richiede un’integrazione API esterna."
|
|
108
|
+
},
|
|
109
|
+
pt: {
|
|
110
|
+
"calculator.matrixA": "Matriz A",
|
|
111
|
+
"calculator.matrixB": "Matriz B",
|
|
112
|
+
"calculator.exprPlaceholder": "Introduza uma expressão (p. ex. x^2, sin(x))",
|
|
113
|
+
"calculator.differentiate": "Derivar",
|
|
114
|
+
"calculator.integrate": "Integrar",
|
|
115
|
+
"calculator.convert": "Converter",
|
|
116
|
+
"plotter.quickAdd": "Adicionar predefinição rápida...",
|
|
117
|
+
"plotter.noPlots": "Ainda não foram adicionados gráficos",
|
|
118
|
+
"plotter.grid": "Grelha",
|
|
119
|
+
"plotter.axes": "Eixos",
|
|
120
|
+
"editor.latexPlaceholder": "Introduza uma equação LaTeX...",
|
|
121
|
+
"editor.clear": "Limpar",
|
|
122
|
+
"editor.recognize": "Reconhecer",
|
|
123
|
+
"editor.recognizing": "A reconhecer...",
|
|
124
|
+
"editor.handwritingNote": "Desenhe a sua equação acima. O reconhecimento de escrita requer integração com uma API externa."
|
|
125
|
+
},
|
|
126
|
+
cs: {
|
|
127
|
+
"calculator.matrixA": "Matice A",
|
|
128
|
+
"calculator.matrixB": "Matice B",
|
|
129
|
+
"calculator.exprPlaceholder": "Zadejte výraz (např. x^2, sin(x))",
|
|
130
|
+
"calculator.differentiate": "Derivovat",
|
|
131
|
+
"calculator.integrate": "Integrovat",
|
|
132
|
+
"calculator.convert": "Převést",
|
|
133
|
+
"plotter.quickAdd": "Rychle přidat předvolbu...",
|
|
134
|
+
"plotter.noPlots": "Zatím nebyl přidán žádný graf",
|
|
135
|
+
"plotter.grid": "Mřížka",
|
|
136
|
+
"plotter.axes": "Osy",
|
|
137
|
+
"editor.latexPlaceholder": "Zadejte rovnici v LaTeXu...",
|
|
138
|
+
"editor.clear": "Vymazat",
|
|
139
|
+
"editor.recognize": "Rozpoznat",
|
|
140
|
+
"editor.recognizing": "Rozpoznávání...",
|
|
141
|
+
"editor.handwritingNote": "Nakreslete svou rovnici výše. Rozpoznávání rukopisu vyžaduje externí integraci API."
|
|
142
|
+
},
|
|
143
|
+
uk: {
|
|
144
|
+
"calculator.matrixA": "Матриця A",
|
|
145
|
+
"calculator.matrixB": "Матриця B",
|
|
146
|
+
"calculator.exprPlaceholder": "Введіть вираз (напр. x^2, sin(x))",
|
|
147
|
+
"calculator.differentiate": "Диференціювати",
|
|
148
|
+
"calculator.integrate": "Інтегрувати",
|
|
149
|
+
"calculator.convert": "Перетворити",
|
|
150
|
+
"plotter.quickAdd": "Швидко додати пресет...",
|
|
151
|
+
"plotter.noPlots": "Поки що не додано жодного графіка",
|
|
152
|
+
"plotter.grid": "Сітка",
|
|
153
|
+
"plotter.axes": "Осі",
|
|
154
|
+
"editor.latexPlaceholder": "Введіть рівняння в LaTeX...",
|
|
155
|
+
"editor.clear": "Очистити",
|
|
156
|
+
"editor.recognize": "Розпізнати",
|
|
157
|
+
"editor.recognizing": "Розпізнавання...",
|
|
158
|
+
"editor.handwritingNote": "Намалюйте своє рівняння вище. Розпізнавання рукопису потребує зовнішньої інтеграції з API."
|
|
159
|
+
}
|
|
160
|
+
}, I18nContext = createContext({
|
|
161
|
+
locale: "en",
|
|
162
|
+
t: (e, t) => t ?? e
|
|
163
|
+
});
|
|
164
|
+
function NiceI18nProvider({ locale: e = "en", overrides: t, children: r }) {
|
|
165
|
+
const a = useMemo(() => {
|
|
166
|
+
const o = { ...translations.en, ...translations[e], ...t };
|
|
167
|
+
return { locale: e, t: (s, l) => o[s] ?? l ?? s };
|
|
168
|
+
}, [e, t]);
|
|
169
|
+
return React.createElement(I18nContext.Provider, { value: a }, r);
|
|
170
|
+
}
|
|
171
|
+
function useNiceTranslation() {
|
|
172
|
+
return useContext(I18nContext);
|
|
173
|
+
}
|
|
6
174
|
const MATH_SYMBOLS = [
|
|
7
175
|
// Greek lowercase
|
|
8
176
|
{ name: "alpha", latex: "\\alpha", category: "greek", preview: "α" },
|
|
@@ -267,16 +435,16 @@ class MathEditorService {
|
|
|
267
435
|
}
|
|
268
436
|
/** Update equation */
|
|
269
437
|
updateEquation(t, r, a) {
|
|
270
|
-
const
|
|
271
|
-
if (!
|
|
438
|
+
const o = this.equations.get(t);
|
|
439
|
+
if (!o)
|
|
272
440
|
throw new Error(`Equation not found: ${t}`);
|
|
273
|
-
const
|
|
274
|
-
...
|
|
441
|
+
const n = {
|
|
442
|
+
...o,
|
|
275
443
|
latex: r,
|
|
276
|
-
label: a ??
|
|
444
|
+
label: a ?? o.label,
|
|
277
445
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
278
446
|
};
|
|
279
|
-
return this.equations.set(t,
|
|
447
|
+
return this.equations.set(t, n), n;
|
|
280
448
|
}
|
|
281
449
|
/** Delete equation */
|
|
282
450
|
deleteEquation(t) {
|
|
@@ -310,13 +478,13 @@ class MathEditorService {
|
|
|
310
478
|
/** Parse LaTeX and extract equations */
|
|
311
479
|
parseLatex(t) {
|
|
312
480
|
const r = [], a = /\\\[([\s\S]*?)\\\]/g;
|
|
313
|
-
let
|
|
314
|
-
for (; (
|
|
315
|
-
r.push(this.createEquation(
|
|
316
|
-
const
|
|
317
|
-
for (; (
|
|
318
|
-
const s =
|
|
319
|
-
r.push(this.createEquation(
|
|
481
|
+
let o;
|
|
482
|
+
for (; (o = a.exec(t)) !== null; )
|
|
483
|
+
r.push(this.createEquation(o[1].trim()));
|
|
484
|
+
const n = /\\begin\{equation\}([\s\S]*?)\\end\{equation\}/g;
|
|
485
|
+
for (; (o = n.exec(t)) !== null; ) {
|
|
486
|
+
const s = o[1].match(/\\label\{([^}]+)\}/), l = o[1].replace(/\\label\{[^}]+\}/, "").trim();
|
|
487
|
+
r.push(this.createEquation(l, s == null ? void 0 : s[1]));
|
|
320
488
|
}
|
|
321
489
|
return r;
|
|
322
490
|
}
|
|
@@ -441,62 +609,62 @@ function NiceMathEditor({
|
|
|
441
609
|
initialLatex: t = "",
|
|
442
610
|
onChange: r,
|
|
443
611
|
className: a,
|
|
444
|
-
style:
|
|
612
|
+
style: o
|
|
445
613
|
}) {
|
|
446
|
-
const [
|
|
447
|
-
(
|
|
448
|
-
|
|
614
|
+
const { t: n } = useNiceTranslation(), [s, l] = useState(t), [x, i] = useState("input"), d = useRef(null), g = useCallback(
|
|
615
|
+
(p) => {
|
|
616
|
+
l(p), r == null || r(p);
|
|
449
617
|
},
|
|
450
618
|
[r]
|
|
451
|
-
),
|
|
452
|
-
(
|
|
453
|
-
const
|
|
454
|
-
if (!
|
|
455
|
-
|
|
619
|
+
), y = useCallback(
|
|
620
|
+
(p) => {
|
|
621
|
+
const f = d.current;
|
|
622
|
+
if (!f) {
|
|
623
|
+
g(s + p);
|
|
456
624
|
return;
|
|
457
625
|
}
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
626
|
+
const c = f.selectionStart, u = f.selectionEnd, b = s.slice(0, c) + p + s.slice(u);
|
|
627
|
+
g(b), setTimeout(() => {
|
|
628
|
+
f.focus(), f.selectionStart = f.selectionEnd = c + p.length;
|
|
461
629
|
}, 0);
|
|
462
630
|
},
|
|
463
|
-
[
|
|
631
|
+
[s, g]
|
|
464
632
|
), h = useCallback(
|
|
465
|
-
(
|
|
466
|
-
|
|
633
|
+
(p) => {
|
|
634
|
+
y(p.latex);
|
|
467
635
|
},
|
|
468
|
-
[
|
|
469
|
-
),
|
|
636
|
+
[y]
|
|
637
|
+
), w = {
|
|
470
638
|
service: e,
|
|
471
|
-
latex:
|
|
472
|
-
setLatex:
|
|
473
|
-
insertSymbol:
|
|
639
|
+
latex: s,
|
|
640
|
+
setLatex: g,
|
|
641
|
+
insertSymbol: y,
|
|
474
642
|
insertTemplate: h,
|
|
475
|
-
mode:
|
|
476
|
-
setMode:
|
|
643
|
+
mode: x,
|
|
644
|
+
setMode: i
|
|
477
645
|
};
|
|
478
|
-
return /* @__PURE__ */ jsx(MathEditorContext.Provider, { value:
|
|
646
|
+
return /* @__PURE__ */ jsx(MathEditorContext.Provider, { value: w, children: /* @__PURE__ */ jsxs("div", { className: a, style: { ...styles$3.container, ...o }, children: [
|
|
479
647
|
/* @__PURE__ */ jsx(Toolbar$1, {}),
|
|
480
648
|
/* @__PURE__ */ jsxs("div", { style: styles$3.main, children: [
|
|
481
649
|
/* @__PURE__ */ jsx(SymbolPalette, {}),
|
|
482
|
-
/* @__PURE__ */ jsx("div", { style: styles$3.editor, children:
|
|
650
|
+
/* @__PURE__ */ jsx("div", { style: styles$3.editor, children: x === "input" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
483
651
|
/* @__PURE__ */ jsx(
|
|
484
652
|
"textarea",
|
|
485
653
|
{
|
|
486
|
-
ref:
|
|
487
|
-
value:
|
|
488
|
-
onChange: (
|
|
489
|
-
placeholder: "Enter LaTeX equation...",
|
|
654
|
+
ref: d,
|
|
655
|
+
value: s,
|
|
656
|
+
onChange: (p) => g(p.target.value),
|
|
657
|
+
placeholder: n("editor.latexPlaceholder", "Enter LaTeX equation..."),
|
|
490
658
|
style: styles$3.input
|
|
491
659
|
}
|
|
492
660
|
),
|
|
493
|
-
/* @__PURE__ */ jsx(LatexPreview, { latex:
|
|
661
|
+
/* @__PURE__ */ jsx(LatexPreview, { latex: s })
|
|
494
662
|
] }) : /* @__PURE__ */ jsx(HandwritingCanvas, {}) })
|
|
495
663
|
] })
|
|
496
664
|
] }) });
|
|
497
665
|
}
|
|
498
666
|
function Toolbar$1() {
|
|
499
|
-
const { mode: e, setMode: t, latex: r, insertTemplate: a } = useMathEditor(), [
|
|
667
|
+
const { mode: e, setMode: t, latex: r, insertTemplate: a } = useMathEditor(), [o, n] = useState(!1);
|
|
500
668
|
return /* @__PURE__ */ jsxs("div", { style: styles$3.toolbar, children: [
|
|
501
669
|
/* @__PURE__ */ jsx(
|
|
502
670
|
"button",
|
|
@@ -520,10 +688,19 @@ function Toolbar$1() {
|
|
|
520
688
|
children: "✏️ Handwriting"
|
|
521
689
|
}
|
|
522
690
|
),
|
|
523
|
-
/* @__PURE__ */ jsx(
|
|
691
|
+
/* @__PURE__ */ jsx(
|
|
692
|
+
"div",
|
|
693
|
+
{
|
|
694
|
+
style: {
|
|
695
|
+
borderLeft: "1px solid var(--nice-border, #ccc)",
|
|
696
|
+
height: "24px",
|
|
697
|
+
margin: "0 8px"
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
),
|
|
524
701
|
/* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
|
|
525
|
-
/* @__PURE__ */ jsx("button", { onClick: () =>
|
|
526
|
-
|
|
702
|
+
/* @__PURE__ */ jsx("button", { onClick: () => n(!o), style: styles$3.button, children: "📐 Templates ▾" }),
|
|
703
|
+
o && /* @__PURE__ */ jsx(
|
|
527
704
|
"div",
|
|
528
705
|
{
|
|
529
706
|
style: {
|
|
@@ -542,28 +719,38 @@ function Toolbar$1() {
|
|
|
542
719
|
children: ["basic", "calculus", "algebra", "geometry", "statistics", "physics"].map(
|
|
543
720
|
(s) => /* @__PURE__ */ jsxs("div", { children: [
|
|
544
721
|
/* @__PURE__ */ jsx("div", { style: styles$3.categoryHeader, children: s }),
|
|
545
|
-
EQUATION_TEMPLATES.filter((
|
|
722
|
+
EQUATION_TEMPLATES.filter((l) => l.category === s).map((l) => /* @__PURE__ */ jsxs(
|
|
546
723
|
"div",
|
|
547
724
|
{
|
|
548
725
|
onClick: () => {
|
|
549
|
-
a(
|
|
726
|
+
a(l), n(!1);
|
|
550
727
|
},
|
|
551
728
|
style: {
|
|
552
729
|
padding: "8px 12px",
|
|
553
730
|
cursor: "pointer",
|
|
554
731
|
borderBottom: "1px solid var(--nice-border, #eee)"
|
|
555
732
|
},
|
|
556
|
-
onMouseOver: (
|
|
557
|
-
onMouseOut: (
|
|
733
|
+
onMouseOver: (x) => x.target.style.backgroundColor = "var(--nice-bg-secondary, #f5f5f5)",
|
|
734
|
+
onMouseOut: (x) => x.target.style.backgroundColor = "transparent",
|
|
558
735
|
children: [
|
|
559
|
-
/* @__PURE__ */ jsx("div", { style: { fontWeight: 500 }, children:
|
|
560
|
-
/* @__PURE__ */ jsxs(
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
736
|
+
/* @__PURE__ */ jsx("div", { style: { fontWeight: 500 }, children: l.name }),
|
|
737
|
+
/* @__PURE__ */ jsxs(
|
|
738
|
+
"div",
|
|
739
|
+
{
|
|
740
|
+
style: {
|
|
741
|
+
fontSize: "12px",
|
|
742
|
+
color: "var(--nice-text-secondary, #666)",
|
|
743
|
+
fontFamily: "monospace"
|
|
744
|
+
},
|
|
745
|
+
children: [
|
|
746
|
+
l.latex.substring(0, 40),
|
|
747
|
+
l.latex.length > 40 ? "..." : ""
|
|
748
|
+
]
|
|
749
|
+
}
|
|
750
|
+
)
|
|
564
751
|
]
|
|
565
752
|
},
|
|
566
|
-
|
|
753
|
+
l.name
|
|
567
754
|
))
|
|
568
755
|
] }, s)
|
|
569
756
|
)
|
|
@@ -594,127 +781,143 @@ function SymbolPalette() {
|
|
|
594
781
|
"logic",
|
|
595
782
|
"calculus",
|
|
596
783
|
"matrices"
|
|
597
|
-
],
|
|
784
|
+
], o = getSymbolsByCategory(t);
|
|
598
785
|
return /* @__PURE__ */ jsxs("div", { style: styles$3.sidebar, children: [
|
|
599
|
-
/* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", padding: "8px", gap: "4px" }, children: a.map((
|
|
786
|
+
/* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", padding: "8px", gap: "4px" }, children: a.map((n) => /* @__PURE__ */ jsx(
|
|
600
787
|
"button",
|
|
601
788
|
{
|
|
602
|
-
onClick: () => r(
|
|
789
|
+
onClick: () => r(n),
|
|
603
790
|
style: {
|
|
604
791
|
padding: "4px 8px",
|
|
605
792
|
fontSize: "11px",
|
|
606
793
|
border: "1px solid var(--nice-border, #ddd)",
|
|
607
794
|
borderRadius: "4px",
|
|
608
|
-
backgroundColor: t ===
|
|
609
|
-
color: t ===
|
|
795
|
+
backgroundColor: t === n ? "var(--nice-primary-hover, #1976d2)" : "var(--nice-bg, #fff)",
|
|
796
|
+
color: t === n ? "var(--nice-bg, #fff)" : "var(--nice-text, #333)",
|
|
610
797
|
cursor: "pointer"
|
|
611
798
|
},
|
|
612
|
-
children:
|
|
799
|
+
children: n
|
|
613
800
|
},
|
|
614
|
-
|
|
801
|
+
n
|
|
615
802
|
)) }),
|
|
616
|
-
/* @__PURE__ */ jsx("div", { style: styles$3.symbolGrid, children:
|
|
803
|
+
/* @__PURE__ */ jsx("div", { style: styles$3.symbolGrid, children: o.map((n) => /* @__PURE__ */ jsx(
|
|
617
804
|
"button",
|
|
618
805
|
{
|
|
619
|
-
onClick: () => e(
|
|
806
|
+
onClick: () => e(n.latex),
|
|
620
807
|
style: styles$3.symbolButton,
|
|
621
|
-
title: `${
|
|
622
|
-
children:
|
|
808
|
+
title: `${n.name}: ${n.latex}`,
|
|
809
|
+
children: n.preview || n.latex
|
|
623
810
|
},
|
|
624
|
-
|
|
811
|
+
n.name
|
|
625
812
|
)) })
|
|
626
813
|
] });
|
|
627
814
|
}
|
|
628
815
|
function LatexPreview({ latex: e }) {
|
|
629
816
|
return /* @__PURE__ */ jsxs("div", { style: styles$3.preview, children: [
|
|
630
|
-
/* @__PURE__ */ jsx(
|
|
817
|
+
/* @__PURE__ */ jsx(
|
|
818
|
+
"div",
|
|
819
|
+
{
|
|
820
|
+
style: { fontSize: "12px", color: "var(--nice-text-secondary, #666)", marginBottom: "8px" },
|
|
821
|
+
children: "Preview (requires KaTeX/MathJax integration):"
|
|
822
|
+
}
|
|
823
|
+
),
|
|
631
824
|
/* @__PURE__ */ jsx("code", { style: { fontSize: "16px" }, children: e || "(empty)" })
|
|
632
825
|
] });
|
|
633
826
|
}
|
|
634
827
|
function HandwritingCanvas() {
|
|
635
|
-
const {
|
|
636
|
-
const
|
|
637
|
-
if (!
|
|
828
|
+
const { t: e } = useNiceTranslation(), { setLatex: t, setMode: r } = useMathEditor(), a = useRef(null), [o, n] = useState([]), [s, l] = useState([]), [x, i] = useState(!1), [d, g] = useState(!1), y = (c) => {
|
|
829
|
+
const u = a.current;
|
|
830
|
+
if (!u)
|
|
638
831
|
return;
|
|
639
|
-
|
|
640
|
-
const
|
|
641
|
-
|
|
832
|
+
i(!0);
|
|
833
|
+
const b = u.getBoundingClientRect();
|
|
834
|
+
l([
|
|
642
835
|
{
|
|
643
|
-
x:
|
|
644
|
-
y:
|
|
645
|
-
pressure:
|
|
836
|
+
x: c.clientX - b.left,
|
|
837
|
+
y: c.clientY - b.top,
|
|
838
|
+
pressure: c.pressure,
|
|
646
839
|
timestamp: Date.now()
|
|
647
840
|
}
|
|
648
841
|
]);
|
|
649
|
-
}, h = (
|
|
650
|
-
if (!
|
|
842
|
+
}, h = (c) => {
|
|
843
|
+
if (!x)
|
|
651
844
|
return;
|
|
652
|
-
const
|
|
653
|
-
if (!
|
|
845
|
+
const u = a.current, b = u == null ? void 0 : u.getContext("2d");
|
|
846
|
+
if (!u || !b)
|
|
654
847
|
return;
|
|
655
|
-
const
|
|
656
|
-
x:
|
|
657
|
-
y:
|
|
658
|
-
pressure:
|
|
848
|
+
const k = u.getBoundingClientRect(), m = {
|
|
849
|
+
x: c.clientX - k.left,
|
|
850
|
+
y: c.clientY - k.top,
|
|
851
|
+
pressure: c.pressure,
|
|
659
852
|
timestamp: Date.now()
|
|
660
853
|
};
|
|
661
|
-
if (
|
|
662
|
-
const
|
|
663
|
-
|
|
854
|
+
if (l((v) => [...v, m]), s.length > 0) {
|
|
855
|
+
const v = s[s.length - 1];
|
|
856
|
+
b.beginPath(), b.moveTo(v.x, v.y), b.lineTo(m.x, m.y), b.strokeStyle = "var(--nice-text, #000)", b.lineWidth = 2, b.lineCap = "round", b.stroke();
|
|
664
857
|
}
|
|
665
|
-
},
|
|
666
|
-
if (
|
|
667
|
-
if (
|
|
668
|
-
const
|
|
858
|
+
}, w = () => {
|
|
859
|
+
if (x) {
|
|
860
|
+
if (i(!1), s.length > 1) {
|
|
861
|
+
const c = {
|
|
669
862
|
id: Date.now().toString(),
|
|
670
|
-
points: [...
|
|
863
|
+
points: [...s]
|
|
671
864
|
};
|
|
672
|
-
n((
|
|
865
|
+
n((u) => [...u, c]);
|
|
673
866
|
}
|
|
674
|
-
|
|
867
|
+
l([]);
|
|
675
868
|
}
|
|
676
|
-
},
|
|
677
|
-
const
|
|
678
|
-
|
|
679
|
-
},
|
|
680
|
-
if (
|
|
681
|
-
|
|
869
|
+
}, p = () => {
|
|
870
|
+
const c = a.current, u = c == null ? void 0 : c.getContext("2d");
|
|
871
|
+
u && c && u.clearRect(0, 0, c.width, c.height), n([]);
|
|
872
|
+
}, f = async () => {
|
|
873
|
+
if (o.length !== 0) {
|
|
874
|
+
g(!0);
|
|
682
875
|
try {
|
|
683
|
-
const
|
|
684
|
-
|
|
876
|
+
const c = await recognizeHandwriting(o);
|
|
877
|
+
t(c.latex), r("input");
|
|
685
878
|
} finally {
|
|
686
|
-
|
|
879
|
+
g(!1);
|
|
687
880
|
}
|
|
688
881
|
}
|
|
689
882
|
};
|
|
690
883
|
return useEffect(() => {
|
|
691
|
-
const
|
|
692
|
-
|
|
884
|
+
const c = a.current;
|
|
885
|
+
c && (c.width = c.offsetWidth, c.height = 200);
|
|
693
886
|
}, []), /* @__PURE__ */ jsxs("div", { children: [
|
|
694
887
|
/* @__PURE__ */ jsx(
|
|
695
888
|
"canvas",
|
|
696
889
|
{
|
|
697
|
-
ref:
|
|
890
|
+
ref: a,
|
|
698
891
|
style: styles$3.canvas,
|
|
699
|
-
onPointerDown:
|
|
892
|
+
onPointerDown: y,
|
|
700
893
|
onPointerMove: h,
|
|
701
|
-
onPointerUp:
|
|
702
|
-
onPointerLeave:
|
|
894
|
+
onPointerUp: w,
|
|
895
|
+
onPointerLeave: w
|
|
703
896
|
}
|
|
704
897
|
),
|
|
705
898
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px", marginTop: "12px" }, children: [
|
|
706
|
-
/* @__PURE__ */ jsx("button", { onClick: y, style: { ...styles$3.button, backgroundColor: "var(--nice-danger, #f44336)" }, children: "Clear" }),
|
|
707
899
|
/* @__PURE__ */ jsx(
|
|
708
900
|
"button",
|
|
709
901
|
{
|
|
710
|
-
onClick:
|
|
711
|
-
|
|
902
|
+
onClick: p,
|
|
903
|
+
style: { ...styles$3.button, backgroundColor: "var(--nice-danger, #f44336)" },
|
|
904
|
+
children: e("editor.clear", "Clear")
|
|
905
|
+
}
|
|
906
|
+
),
|
|
907
|
+
/* @__PURE__ */ jsx(
|
|
908
|
+
"button",
|
|
909
|
+
{
|
|
910
|
+
onClick: f,
|
|
911
|
+
disabled: o.length === 0 || d,
|
|
712
912
|
style: styles$3.button,
|
|
713
|
-
children:
|
|
913
|
+
children: d ? e("editor.recognizing", "Recognizing...") : e("editor.recognize", "Recognize")
|
|
714
914
|
}
|
|
715
915
|
)
|
|
716
916
|
] }),
|
|
717
|
-
/* @__PURE__ */ jsx("p", { style: { fontSize: "12px", color: "var(--nice-text-secondary, #666)", marginTop: "8px" }, children:
|
|
917
|
+
/* @__PURE__ */ jsx("p", { style: { fontSize: "12px", color: "var(--nice-text-secondary, #666)", marginTop: "8px" }, children: e(
|
|
918
|
+
"editor.handwritingNote",
|
|
919
|
+
"Draw your equation above. Handwriting recognition requires external API integration."
|
|
920
|
+
) })
|
|
718
921
|
] });
|
|
719
922
|
}
|
|
720
923
|
const mathConstants = {
|
|
@@ -816,10 +1019,10 @@ class ExpressionParser {
|
|
|
816
1019
|
const a = r[1];
|
|
817
1020
|
if (this.pos += a.length, this.expr[this.pos] === "(") {
|
|
818
1021
|
this.pos++;
|
|
819
|
-
const
|
|
1022
|
+
const o = [];
|
|
820
1023
|
for (; this.expr[this.pos] !== ")" && this.pos < this.expr.length; )
|
|
821
|
-
|
|
822
|
-
return this.expr[this.pos] === ")" && this.pos++, { type: "call", name: a, args:
|
|
1024
|
+
o.push(this.parseAddSub()), this.expr[this.pos] === "," && this.pos++;
|
|
1025
|
+
return this.expr[this.pos] === ")" && this.pos++, { type: "call", name: a, args: o };
|
|
823
1026
|
}
|
|
824
1027
|
return { type: "variable", name: a };
|
|
825
1028
|
}
|
|
@@ -836,18 +1039,18 @@ class ExpressionParser {
|
|
|
836
1039
|
case "unary":
|
|
837
1040
|
return t.op === "-" ? -this.evaluate(t.arg, r) : this.evaluate(t.arg, r);
|
|
838
1041
|
case "binary": {
|
|
839
|
-
const a = this.evaluate(t.left, r),
|
|
1042
|
+
const a = this.evaluate(t.left, r), o = this.evaluate(t.right, r);
|
|
840
1043
|
switch (t.op) {
|
|
841
1044
|
case "+":
|
|
842
|
-
return a +
|
|
1045
|
+
return a + o;
|
|
843
1046
|
case "-":
|
|
844
|
-
return a -
|
|
1047
|
+
return a - o;
|
|
845
1048
|
case "*":
|
|
846
|
-
return a *
|
|
1049
|
+
return a * o;
|
|
847
1050
|
case "/":
|
|
848
|
-
return a /
|
|
1051
|
+
return a / o;
|
|
849
1052
|
case "^":
|
|
850
|
-
return Math.pow(a,
|
|
1053
|
+
return Math.pow(a, o);
|
|
851
1054
|
default:
|
|
852
1055
|
return 0;
|
|
853
1056
|
}
|
|
@@ -856,8 +1059,8 @@ class ExpressionParser {
|
|
|
856
1059
|
const a = mathFunctions[t.name];
|
|
857
1060
|
if (!a)
|
|
858
1061
|
throw new Error(`Unknown function: ${t.name}`);
|
|
859
|
-
const
|
|
860
|
-
return a(...
|
|
1062
|
+
const o = t.args.map((n) => this.evaluate(n, r));
|
|
1063
|
+
return a(...o);
|
|
861
1064
|
}
|
|
862
1065
|
default:
|
|
863
1066
|
return 0;
|
|
@@ -897,8 +1100,8 @@ class GraphService {
|
|
|
897
1100
|
const a = this.plots.get(t);
|
|
898
1101
|
if (!a)
|
|
899
1102
|
throw new Error(`Plot not found: ${t}`);
|
|
900
|
-
const
|
|
901
|
-
return this.plots.set(t,
|
|
1103
|
+
const o = { ...a, ...r, id: a.id };
|
|
1104
|
+
return this.plots.set(t, o), o;
|
|
902
1105
|
}
|
|
903
1106
|
/** Remove a plot */
|
|
904
1107
|
removePlot(t) {
|
|
@@ -913,12 +1116,12 @@ class GraphService {
|
|
|
913
1116
|
return this.plots.get(t);
|
|
914
1117
|
}
|
|
915
1118
|
/** Compute 2D function points */
|
|
916
|
-
compute2DFunction(t, r, a,
|
|
917
|
-
const
|
|
918
|
-
for (let
|
|
919
|
-
const i = r.min +
|
|
1119
|
+
compute2DFunction(t, r, a, o) {
|
|
1120
|
+
const n = this.parser.parse(t), s = [], l = (r.max - r.min) / a;
|
|
1121
|
+
for (let x = 0; x <= a; x++) {
|
|
1122
|
+
const i = r.min + x * l;
|
|
920
1123
|
try {
|
|
921
|
-
const d =
|
|
1124
|
+
const d = n({ x: i, ...o });
|
|
922
1125
|
Number.isFinite(d) && s.push({ x: i, y: d });
|
|
923
1126
|
} catch {
|
|
924
1127
|
}
|
|
@@ -926,28 +1129,28 @@ class GraphService {
|
|
|
926
1129
|
return s;
|
|
927
1130
|
}
|
|
928
1131
|
/** Compute 2D parametric curve */
|
|
929
|
-
compute2DParametric(t, r, a,
|
|
930
|
-
const s = this.parser.parse(t),
|
|
931
|
-
for (let d = 0; d <=
|
|
932
|
-
const
|
|
1132
|
+
compute2DParametric(t, r, a, o, n) {
|
|
1133
|
+
const s = this.parser.parse(t), l = this.parser.parse(r), x = [], i = (a.max - a.min) / o;
|
|
1134
|
+
for (let d = 0; d <= o; d++) {
|
|
1135
|
+
const g = a.min + d * i;
|
|
933
1136
|
try {
|
|
934
|
-
const
|
|
935
|
-
Number.isFinite(
|
|
1137
|
+
const y = s({ t: g, ...n }), h = l({ t: g, ...n });
|
|
1138
|
+
Number.isFinite(y) && Number.isFinite(h) && x.push({ x: y, y: h });
|
|
936
1139
|
} catch {
|
|
937
1140
|
}
|
|
938
1141
|
}
|
|
939
|
-
return
|
|
1142
|
+
return x;
|
|
940
1143
|
}
|
|
941
1144
|
/** Compute 2D polar curve */
|
|
942
|
-
compute2DPolar(t, r, a,
|
|
943
|
-
const
|
|
944
|
-
for (let
|
|
945
|
-
const i = r.min +
|
|
1145
|
+
compute2DPolar(t, r, a, o) {
|
|
1146
|
+
const n = this.parser.parse(t), s = [], l = (r.max - r.min) / a;
|
|
1147
|
+
for (let x = 0; x <= a; x++) {
|
|
1148
|
+
const i = r.min + x * l;
|
|
946
1149
|
try {
|
|
947
|
-
const d =
|
|
1150
|
+
const d = n({ theta: i, t: i, ...o });
|
|
948
1151
|
if (Number.isFinite(d)) {
|
|
949
|
-
const
|
|
950
|
-
s.push({ x:
|
|
1152
|
+
const g = d * Math.cos(i), y = d * Math.sin(i);
|
|
1153
|
+
s.push({ x: g, y });
|
|
951
1154
|
}
|
|
952
1155
|
} catch {
|
|
953
1156
|
}
|
|
@@ -955,78 +1158,78 @@ class GraphService {
|
|
|
955
1158
|
return s;
|
|
956
1159
|
}
|
|
957
1160
|
/** Compute 3D surface points */
|
|
958
|
-
compute3DSurface(t, r, a,
|
|
959
|
-
const s = this.parser.parse(t),
|
|
960
|
-
for (let d = 0; d <=
|
|
961
|
-
const
|
|
962
|
-
for (let
|
|
963
|
-
const
|
|
1161
|
+
compute3DSurface(t, r, a, o, n) {
|
|
1162
|
+
const s = this.parser.parse(t), l = [], x = (r.max - r.min) / o, i = (a.max - a.min) / o;
|
|
1163
|
+
for (let d = 0; d <= o; d++) {
|
|
1164
|
+
const g = [], y = r.min + d * x;
|
|
1165
|
+
for (let h = 0; h <= o; h++) {
|
|
1166
|
+
const w = a.min + h * i;
|
|
964
1167
|
try {
|
|
965
|
-
const
|
|
966
|
-
Number.isFinite(
|
|
1168
|
+
const p = s({ x: y, y: w, ...n });
|
|
1169
|
+
Number.isFinite(p) ? g.push({ x: y, y: w, z: p }) : g.push({ x: y, y: w, z: 0 });
|
|
967
1170
|
} catch {
|
|
968
|
-
|
|
1171
|
+
g.push({ x: y, y: w, z: 0 });
|
|
969
1172
|
}
|
|
970
1173
|
}
|
|
971
|
-
|
|
1174
|
+
l.push(g);
|
|
972
1175
|
}
|
|
973
|
-
return
|
|
1176
|
+
return l;
|
|
974
1177
|
}
|
|
975
1178
|
/** Export plot data as SVG */
|
|
976
|
-
exportSVG(t, r, a,
|
|
977
|
-
const s = a - 80,
|
|
978
|
-
`<svg xmlns="http://www.w3.org/2000/svg" width="${a}" height="${
|
|
1179
|
+
exportSVG(t, r, a, o) {
|
|
1180
|
+
const s = a - 80, l = o - 40 * 2, x = (g) => 40 + (g - r.xRange.min) / (r.xRange.max - r.xRange.min) * s, i = (g) => o - 40 - (g - r.yRange.min) / (r.yRange.max - r.yRange.min) * l, d = [
|
|
1181
|
+
`<svg xmlns="http://www.w3.org/2000/svg" width="${a}" height="${o}">`,
|
|
979
1182
|
`<rect width="100%" height="100%" fill="rgb(${r.backgroundColor.r},${r.backgroundColor.g},${r.backgroundColor.b})"/>`
|
|
980
1183
|
];
|
|
981
1184
|
if (r.showGrid) {
|
|
982
1185
|
d.push('<g stroke="var(--nice-border, #ddd)" stroke-width="0.5">');
|
|
983
|
-
for (let
|
|
984
|
-
const
|
|
985
|
-
d.push(`<line x1="${
|
|
1186
|
+
for (let g = Math.ceil(r.xRange.min); g <= r.xRange.max; g++) {
|
|
1187
|
+
const y = x(g);
|
|
1188
|
+
d.push(`<line x1="${y}" y1="40" x2="${y}" y2="${o - 40}"/>`);
|
|
986
1189
|
}
|
|
987
|
-
for (let
|
|
988
|
-
const
|
|
989
|
-
d.push(`<line x1="40" y1="${
|
|
1190
|
+
for (let g = Math.ceil(r.yRange.min); g <= r.yRange.max; g++) {
|
|
1191
|
+
const y = i(g);
|
|
1192
|
+
d.push(`<line x1="40" y1="${y}" x2="${a - 40}" y2="${y}"/>`);
|
|
990
1193
|
}
|
|
991
1194
|
d.push("</g>");
|
|
992
1195
|
}
|
|
993
1196
|
if (r.showAxes) {
|
|
994
1197
|
d.push('<g stroke="var(--nice-text, #333)" stroke-width="1">');
|
|
995
|
-
const
|
|
996
|
-
d.push(`<line x1="40" y1="${
|
|
1198
|
+
const g = i(0), y = x(0);
|
|
1199
|
+
d.push(`<line x1="40" y1="${g}" x2="${a - 40}" y2="${g}"/>`), d.push(`<line x1="${y}" y1="40" x2="${y}" y2="${o - 40}"/>`), d.push("</g>");
|
|
997
1200
|
}
|
|
998
|
-
for (const
|
|
999
|
-
if (
|
|
1000
|
-
const
|
|
1001
|
-
if (
|
|
1002
|
-
const
|
|
1201
|
+
for (const g of t.filter((y) => y.visible))
|
|
1202
|
+
if (g.type === "2d-function") {
|
|
1203
|
+
const y = this.compute2DFunction(g.expression, r.xRange, g.resolution || 200);
|
|
1204
|
+
if (y.length > 0) {
|
|
1205
|
+
const h = y.map((w, p) => `${p === 0 ? "M" : "L"}${x(w.x)},${i(w.y)}`).join(" ");
|
|
1003
1206
|
d.push(
|
|
1004
|
-
`<path d="${
|
|
1207
|
+
`<path d="${h}" fill="none" stroke="rgb(${g.color.r},${g.color.g},${g.color.b})" stroke-width="${g.lineWidth}"/>`
|
|
1005
1208
|
);
|
|
1006
1209
|
}
|
|
1007
1210
|
}
|
|
1008
1211
|
return r.showLabels && (d.push(
|
|
1009
|
-
`<text x="${a / 2}" y="${
|
|
1212
|
+
`<text x="${a / 2}" y="${o - 10}" text-anchor="middle" font-size="12">x</text>`
|
|
1010
1213
|
), d.push(
|
|
1011
|
-
`<text x="10" y="${
|
|
1214
|
+
`<text x="10" y="${o / 2}" text-anchor="middle" font-size="12" transform="rotate(-90,10,${o / 2})">y</text>`
|
|
1012
1215
|
)), d.push("</svg>"), d.join(`
|
|
1013
1216
|
`);
|
|
1014
1217
|
}
|
|
1015
1218
|
/** Export as PNG (data URL) */
|
|
1016
|
-
async exportPNG(t, r, a,
|
|
1017
|
-
const
|
|
1018
|
-
return new Promise((s,
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1219
|
+
async exportPNG(t, r, a, o) {
|
|
1220
|
+
const n = this.exportSVG(t, r, a, o);
|
|
1221
|
+
return new Promise((s, l) => {
|
|
1222
|
+
const x = new Image();
|
|
1223
|
+
x.onload = () => {
|
|
1021
1224
|
const i = document.createElement("canvas");
|
|
1022
|
-
i.width = a, i.height =
|
|
1225
|
+
i.width = a, i.height = o;
|
|
1023
1226
|
const d = i.getContext("2d");
|
|
1024
1227
|
if (!d) {
|
|
1025
|
-
|
|
1228
|
+
l(new Error("Could not get canvas context"));
|
|
1026
1229
|
return;
|
|
1027
1230
|
}
|
|
1028
|
-
d.drawImage(
|
|
1029
|
-
},
|
|
1231
|
+
d.drawImage(x, 0, 0), s(i.toDataURL("image/png"));
|
|
1232
|
+
}, x.onerror = l, x.src = "data:image/svg+xml;base64," + btoa(n);
|
|
1030
1233
|
});
|
|
1031
1234
|
}
|
|
1032
1235
|
generateId() {
|
|
@@ -1190,9 +1393,9 @@ function NiceGraphPlotter({
|
|
|
1190
1393
|
width: t = 800,
|
|
1191
1394
|
height: r = 600,
|
|
1192
1395
|
className: a,
|
|
1193
|
-
style:
|
|
1396
|
+
style: o
|
|
1194
1397
|
}) {
|
|
1195
|
-
const [
|
|
1398
|
+
const [n, s] = useState([]), [l, x] = useState({
|
|
1196
1399
|
xRange: { min: -10, max: 10 },
|
|
1197
1400
|
yRange: { min: -10, max: 10 },
|
|
1198
1401
|
zRange: { min: -5, max: 5 },
|
|
@@ -1210,40 +1413,40 @@ function NiceGraphPlotter({
|
|
|
1210
1413
|
minValue: -5,
|
|
1211
1414
|
maxValue: 5,
|
|
1212
1415
|
speed: 1
|
|
1213
|
-
}),
|
|
1416
|
+
}), g = useCallback(() => {
|
|
1214
1417
|
s(e.getPlots());
|
|
1215
|
-
}, [e]),
|
|
1216
|
-
|
|
1217
|
-
}, []),
|
|
1218
|
-
d((
|
|
1219
|
-
}, []),
|
|
1220
|
-
(
|
|
1221
|
-
e.addPlot(
|
|
1418
|
+
}, [e]), y = useCallback((u) => {
|
|
1419
|
+
x((b) => ({ ...b, ...u }));
|
|
1420
|
+
}, []), h = useCallback((u) => {
|
|
1421
|
+
d((b) => ({ ...b, ...u }));
|
|
1422
|
+
}, []), w = useCallback(
|
|
1423
|
+
(u) => {
|
|
1424
|
+
e.addPlot(u), g();
|
|
1222
1425
|
},
|
|
1223
|
-
[e,
|
|
1224
|
-
),
|
|
1225
|
-
(
|
|
1226
|
-
e.removePlot(
|
|
1426
|
+
[e, g]
|
|
1427
|
+
), p = useCallback(
|
|
1428
|
+
(u) => {
|
|
1429
|
+
e.removePlot(u), g();
|
|
1227
1430
|
},
|
|
1228
|
-
[e,
|
|
1229
|
-
),
|
|
1230
|
-
(
|
|
1231
|
-
e.updatePlot(
|
|
1431
|
+
[e, g]
|
|
1432
|
+
), f = useCallback(
|
|
1433
|
+
(u, b) => {
|
|
1434
|
+
e.updatePlot(u, b), g();
|
|
1232
1435
|
},
|
|
1233
|
-
[e,
|
|
1234
|
-
),
|
|
1436
|
+
[e, g]
|
|
1437
|
+
), c = {
|
|
1235
1438
|
service: e,
|
|
1236
|
-
plots:
|
|
1237
|
-
view:
|
|
1238
|
-
setView:
|
|
1239
|
-
addPlot:
|
|
1240
|
-
removePlot:
|
|
1241
|
-
updatePlot:
|
|
1439
|
+
plots: n,
|
|
1440
|
+
view: l,
|
|
1441
|
+
setView: y,
|
|
1442
|
+
addPlot: w,
|
|
1443
|
+
removePlot: p,
|
|
1444
|
+
updatePlot: f,
|
|
1242
1445
|
animation: i,
|
|
1243
|
-
setAnimation:
|
|
1244
|
-
refresh:
|
|
1446
|
+
setAnimation: h,
|
|
1447
|
+
refresh: g
|
|
1245
1448
|
};
|
|
1246
|
-
return /* @__PURE__ */ jsx(GraphContext.Provider, { value:
|
|
1449
|
+
return /* @__PURE__ */ jsx(GraphContext.Provider, { value: c, children: /* @__PURE__ */ jsxs("div", { className: a, style: { ...styles$2.container, ...o }, children: [
|
|
1247
1450
|
/* @__PURE__ */ jsxs("div", { style: styles$2.sidebar, children: [
|
|
1248
1451
|
/* @__PURE__ */ jsx(PlotControls, {}),
|
|
1249
1452
|
/* @__PURE__ */ jsx(PlotList, {})
|
|
@@ -1255,11 +1458,11 @@ function NiceGraphPlotter({
|
|
|
1255
1458
|
] }) });
|
|
1256
1459
|
}
|
|
1257
1460
|
function PlotControls() {
|
|
1258
|
-
const {
|
|
1259
|
-
|
|
1260
|
-
type:
|
|
1261
|
-
expression:
|
|
1262
|
-
expressionY:
|
|
1461
|
+
const { t: e } = useNiceTranslation(), { addPlot: t } = useGraph(), [r, a] = useState("sin(x)"), [o, n] = useState("2d-function"), [s, l] = useState("cos(x)"), x = () => {
|
|
1462
|
+
t({
|
|
1463
|
+
type: o,
|
|
1464
|
+
expression: r,
|
|
1465
|
+
expressionY: o === "2d-parametric" ? s : void 0,
|
|
1263
1466
|
color: {
|
|
1264
1467
|
r: Math.floor(Math.random() * 200),
|
|
1265
1468
|
g: Math.floor(Math.random() * 200),
|
|
@@ -1271,8 +1474,8 @@ function PlotControls() {
|
|
|
1271
1474
|
/* @__PURE__ */ jsx("div", { style: { marginBottom: "8px" }, children: /* @__PURE__ */ jsxs(
|
|
1272
1475
|
"select",
|
|
1273
1476
|
{
|
|
1274
|
-
value:
|
|
1275
|
-
onChange: (
|
|
1477
|
+
value: o,
|
|
1478
|
+
onChange: (i) => n(i.target.value),
|
|
1276
1479
|
style: { ...styles$2.input, width: "100%" },
|
|
1277
1480
|
children: [
|
|
1278
1481
|
/* @__PURE__ */ jsx("option", { value: "2d-function", children: "y = f(x)" }),
|
|
@@ -1286,33 +1489,33 @@ function PlotControls() {
|
|
|
1286
1489
|
"input",
|
|
1287
1490
|
{
|
|
1288
1491
|
type: "text",
|
|
1289
|
-
value:
|
|
1290
|
-
onChange: (
|
|
1291
|
-
placeholder:
|
|
1492
|
+
value: r,
|
|
1493
|
+
onChange: (i) => a(i.target.value),
|
|
1494
|
+
placeholder: o === "2d-parametric" ? "x(t) = ..." : "Expression...",
|
|
1292
1495
|
style: { ...styles$2.input, width: "100%" }
|
|
1293
1496
|
}
|
|
1294
1497
|
) }),
|
|
1295
|
-
|
|
1498
|
+
o === "2d-parametric" && /* @__PURE__ */ jsx("div", { style: { marginBottom: "8px" }, children: /* @__PURE__ */ jsx(
|
|
1296
1499
|
"input",
|
|
1297
1500
|
{
|
|
1298
1501
|
type: "text",
|
|
1299
|
-
value:
|
|
1300
|
-
onChange: (
|
|
1502
|
+
value: s,
|
|
1503
|
+
onChange: (i) => l(i.target.value),
|
|
1301
1504
|
placeholder: "y(t) = ...",
|
|
1302
1505
|
style: { ...styles$2.input, width: "100%" }
|
|
1303
1506
|
}
|
|
1304
1507
|
) }),
|
|
1305
|
-
/* @__PURE__ */ jsx("button", { onClick:
|
|
1508
|
+
/* @__PURE__ */ jsx("button", { onClick: x, style: { ...styles$2.button, width: "100%" }, children: "+ Add Plot" }),
|
|
1306
1509
|
/* @__PURE__ */ jsxs(
|
|
1307
1510
|
"select",
|
|
1308
1511
|
{
|
|
1309
|
-
onChange: (
|
|
1310
|
-
const
|
|
1311
|
-
|
|
1312
|
-
type:
|
|
1313
|
-
expression:
|
|
1314
|
-
expressionY:
|
|
1315
|
-
label:
|
|
1512
|
+
onChange: (i) => {
|
|
1513
|
+
const d = FUNCTION_PRESETS.find((g) => g.name === i.target.value);
|
|
1514
|
+
d && t({
|
|
1515
|
+
type: d.type,
|
|
1516
|
+
expression: d.expression,
|
|
1517
|
+
expressionY: d.expressionY,
|
|
1518
|
+
label: d.name,
|
|
1316
1519
|
color: {
|
|
1317
1520
|
r: Math.floor(Math.random() * 200),
|
|
1318
1521
|
g: Math.floor(Math.random() * 200),
|
|
@@ -1323,29 +1526,39 @@ function PlotControls() {
|
|
|
1323
1526
|
style: { ...styles$2.input, width: "100%", marginTop: "8px" },
|
|
1324
1527
|
value: "",
|
|
1325
1528
|
children: [
|
|
1326
|
-
/* @__PURE__ */ jsx("option", { value: "", disabled: !0, children: "Quick add preset..." }),
|
|
1327
|
-
["basic", "trigonometric", "exponential", "parametric", "polar", "3d"].map((
|
|
1328
|
-
|
|
1529
|
+
/* @__PURE__ */ jsx("option", { value: "", disabled: !0, children: e("plotter.quickAdd", "Quick add preset...") }),
|
|
1530
|
+
["basic", "trigonometric", "exponential", "parametric", "polar", "3d"].map((i) => /* @__PURE__ */ jsx("optgroup", { label: i.charAt(0).toUpperCase() + i.slice(1), children: FUNCTION_PRESETS.filter((d) => d.category === i).map((d) => /* @__PURE__ */ jsxs("option", { value: d.name, children: [
|
|
1531
|
+
d.name,
|
|
1329
1532
|
": ",
|
|
1330
|
-
|
|
1331
|
-
] },
|
|
1533
|
+
d.expression
|
|
1534
|
+
] }, d.name)) }, i))
|
|
1332
1535
|
]
|
|
1333
1536
|
}
|
|
1334
1537
|
)
|
|
1335
1538
|
] });
|
|
1336
1539
|
}
|
|
1337
1540
|
function PlotList() {
|
|
1338
|
-
const {
|
|
1541
|
+
const { t: e } = useNiceTranslation(), { plots: t, removePlot: r, updatePlot: a } = useGraph();
|
|
1339
1542
|
return /* @__PURE__ */ jsxs("div", { style: styles$2.plotList, children: [
|
|
1340
|
-
|
|
1341
|
-
|
|
1543
|
+
t.length === 0 && /* @__PURE__ */ jsx(
|
|
1544
|
+
"div",
|
|
1545
|
+
{
|
|
1546
|
+
style: {
|
|
1547
|
+
color: "var(--nice-text-secondary, #666)",
|
|
1548
|
+
textAlign: "center",
|
|
1549
|
+
padding: "16px"
|
|
1550
|
+
},
|
|
1551
|
+
children: e("plotter.noPlots", "No plots added yet")
|
|
1552
|
+
}
|
|
1553
|
+
),
|
|
1554
|
+
t.map((o) => /* @__PURE__ */ jsxs("div", { style: styles$2.plotItem, children: [
|
|
1342
1555
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px", marginBottom: "8px" }, children: [
|
|
1343
1556
|
/* @__PURE__ */ jsx(
|
|
1344
1557
|
"input",
|
|
1345
1558
|
{
|
|
1346
1559
|
type: "checkbox",
|
|
1347
|
-
checked:
|
|
1348
|
-
onChange: (n) =>
|
|
1560
|
+
checked: o.visible,
|
|
1561
|
+
onChange: (n) => a(o.id, { visible: n.target.checked })
|
|
1349
1562
|
}
|
|
1350
1563
|
),
|
|
1351
1564
|
/* @__PURE__ */ jsx(
|
|
@@ -1355,15 +1568,15 @@ function PlotList() {
|
|
|
1355
1568
|
width: "16px",
|
|
1356
1569
|
height: "16px",
|
|
1357
1570
|
borderRadius: "3px",
|
|
1358
|
-
backgroundColor: `rgb(${
|
|
1571
|
+
backgroundColor: `rgb(${o.color.r},${o.color.g},${o.color.b})`
|
|
1359
1572
|
}
|
|
1360
1573
|
}
|
|
1361
1574
|
),
|
|
1362
|
-
/* @__PURE__ */ jsx("span", { style: { flex: 1, fontWeight: 500 }, children:
|
|
1575
|
+
/* @__PURE__ */ jsx("span", { style: { flex: 1, fontWeight: 500 }, children: o.label || o.expression }),
|
|
1363
1576
|
/* @__PURE__ */ jsx(
|
|
1364
1577
|
"button",
|
|
1365
1578
|
{
|
|
1366
|
-
onClick: () =>
|
|
1579
|
+
onClick: () => r(o.id),
|
|
1367
1580
|
style: {
|
|
1368
1581
|
padding: "2px 6px",
|
|
1369
1582
|
backgroundColor: "var(--nice-danger, #f44336)",
|
|
@@ -1377,23 +1590,33 @@ function PlotList() {
|
|
|
1377
1590
|
}
|
|
1378
1591
|
)
|
|
1379
1592
|
] }),
|
|
1380
|
-
/* @__PURE__ */ jsx(
|
|
1381
|
-
"
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1593
|
+
/* @__PURE__ */ jsx(
|
|
1594
|
+
"div",
|
|
1595
|
+
{
|
|
1596
|
+
style: {
|
|
1597
|
+
fontSize: "12px",
|
|
1598
|
+
color: "var(--nice-text-secondary, #666)",
|
|
1599
|
+
fontFamily: "monospace"
|
|
1600
|
+
},
|
|
1601
|
+
children: o.type === "2d-parametric" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1602
|
+
"x(t) = ",
|
|
1603
|
+
o.expression,
|
|
1604
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
1605
|
+
"y(t) = ",
|
|
1606
|
+
o.expressionY
|
|
1607
|
+
] }) : o.expression
|
|
1608
|
+
}
|
|
1609
|
+
)
|
|
1610
|
+
] }, o.id))
|
|
1388
1611
|
] });
|
|
1389
1612
|
}
|
|
1390
1613
|
function GraphToolbar({ width: e, height: t }) {
|
|
1391
|
-
const {
|
|
1392
|
-
const
|
|
1393
|
-
|
|
1394
|
-
},
|
|
1395
|
-
const
|
|
1396
|
-
|
|
1614
|
+
const { t: r } = useNiceTranslation(), { service: a, plots: o, view: n, setView: s } = useGraph(), l = () => {
|
|
1615
|
+
const i = a.exportSVG(o, n, e, t), d = new Blob([i], { type: "image/svg+xml" }), g = URL.createObjectURL(d), y = document.createElement("a");
|
|
1616
|
+
y.href = g, y.download = "graph.svg", y.click(), URL.revokeObjectURL(g);
|
|
1617
|
+
}, x = async () => {
|
|
1618
|
+
const i = await a.exportPNG(o, n, e, t), d = document.createElement("a");
|
|
1619
|
+
d.href = i, d.download = "graph.png", d.click();
|
|
1397
1620
|
};
|
|
1398
1621
|
return /* @__PURE__ */ jsxs("div", { style: styles$2.toolbar, children: [
|
|
1399
1622
|
/* @__PURE__ */ jsxs("label", { style: { fontSize: "12px" }, children: [
|
|
@@ -1404,7 +1627,7 @@ function GraphToolbar({ width: e, height: t }) {
|
|
|
1404
1627
|
{
|
|
1405
1628
|
type: "number",
|
|
1406
1629
|
value: n.xRange.min,
|
|
1407
|
-
onChange: (
|
|
1630
|
+
onChange: (i) => s({ xRange: { ...n.xRange, min: Number(i.target.value) } }),
|
|
1408
1631
|
style: { ...styles$2.input, width: "50px" }
|
|
1409
1632
|
}
|
|
1410
1633
|
),
|
|
@@ -1415,7 +1638,7 @@ function GraphToolbar({ width: e, height: t }) {
|
|
|
1415
1638
|
{
|
|
1416
1639
|
type: "number",
|
|
1417
1640
|
value: n.xRange.max,
|
|
1418
|
-
onChange: (
|
|
1641
|
+
onChange: (i) => s({ xRange: { ...n.xRange, max: Number(i.target.value) } }),
|
|
1419
1642
|
style: { ...styles$2.input, width: "50px" }
|
|
1420
1643
|
}
|
|
1421
1644
|
),
|
|
@@ -1429,7 +1652,7 @@ function GraphToolbar({ width: e, height: t }) {
|
|
|
1429
1652
|
{
|
|
1430
1653
|
type: "number",
|
|
1431
1654
|
value: n.yRange.min,
|
|
1432
|
-
onChange: (
|
|
1655
|
+
onChange: (i) => s({ yRange: { ...n.yRange, min: Number(i.target.value) } }),
|
|
1433
1656
|
style: { ...styles$2.input, width: "50px" }
|
|
1434
1657
|
}
|
|
1435
1658
|
),
|
|
@@ -1440,7 +1663,7 @@ function GraphToolbar({ width: e, height: t }) {
|
|
|
1440
1663
|
{
|
|
1441
1664
|
type: "number",
|
|
1442
1665
|
value: n.yRange.max,
|
|
1443
|
-
onChange: (
|
|
1666
|
+
onChange: (i) => s({ yRange: { ...n.yRange, max: Number(i.target.value) } }),
|
|
1444
1667
|
style: { ...styles$2.input, width: "50px" }
|
|
1445
1668
|
}
|
|
1446
1669
|
),
|
|
@@ -1452,10 +1675,10 @@ function GraphToolbar({ width: e, height: t }) {
|
|
|
1452
1675
|
{
|
|
1453
1676
|
type: "checkbox",
|
|
1454
1677
|
checked: n.showGrid,
|
|
1455
|
-
onChange: (
|
|
1678
|
+
onChange: (i) => s({ showGrid: i.target.checked })
|
|
1456
1679
|
}
|
|
1457
1680
|
),
|
|
1458
|
-
"Grid"
|
|
1681
|
+
r("plotter.grid", "Grid")
|
|
1459
1682
|
] }),
|
|
1460
1683
|
/* @__PURE__ */ jsxs("label", { style: { fontSize: "12px" }, children: [
|
|
1461
1684
|
/* @__PURE__ */ jsx(
|
|
@@ -1463,95 +1686,109 @@ function GraphToolbar({ width: e, height: t }) {
|
|
|
1463
1686
|
{
|
|
1464
1687
|
type: "checkbox",
|
|
1465
1688
|
checked: n.showAxes,
|
|
1466
|
-
onChange: (
|
|
1689
|
+
onChange: (i) => s({ showAxes: i.target.checked })
|
|
1467
1690
|
}
|
|
1468
1691
|
),
|
|
1469
|
-
"Axes"
|
|
1692
|
+
r("plotter.axes", "Axes")
|
|
1470
1693
|
] }),
|
|
1471
1694
|
/* @__PURE__ */ jsx("div", { style: { flex: 1 } }),
|
|
1472
|
-
/* @__PURE__ */ jsx(
|
|
1473
|
-
|
|
1695
|
+
/* @__PURE__ */ jsx(
|
|
1696
|
+
"button",
|
|
1697
|
+
{
|
|
1698
|
+
onClick: l,
|
|
1699
|
+
style: { ...styles$2.button, backgroundColor: "var(--nice-success, #4caf50)" },
|
|
1700
|
+
children: "📥 SVG"
|
|
1701
|
+
}
|
|
1702
|
+
),
|
|
1703
|
+
/* @__PURE__ */ jsx(
|
|
1704
|
+
"button",
|
|
1705
|
+
{
|
|
1706
|
+
onClick: x,
|
|
1707
|
+
style: { ...styles$2.button, backgroundColor: "var(--nice-warning, #ff9800)" },
|
|
1708
|
+
children: "📥 PNG"
|
|
1709
|
+
}
|
|
1710
|
+
)
|
|
1474
1711
|
] });
|
|
1475
1712
|
}
|
|
1476
1713
|
function Canvas2D({ width: e, height: t }) {
|
|
1477
|
-
const r = useRef(null), { service: a, plots:
|
|
1714
|
+
const r = useRef(null), { service: a, plots: o, view: n, animation: s } = useGraph(), l = 40;
|
|
1478
1715
|
return useEffect(() => {
|
|
1479
|
-
const
|
|
1480
|
-
if (!
|
|
1716
|
+
const x = r.current, i = x == null ? void 0 : x.getContext("2d");
|
|
1717
|
+
if (!x || !i)
|
|
1481
1718
|
return;
|
|
1482
|
-
i.fillStyle = `rgb(${
|
|
1483
|
-
const d = e -
|
|
1484
|
-
if (
|
|
1719
|
+
i.fillStyle = `rgb(${n.backgroundColor.r},${n.backgroundColor.g},${n.backgroundColor.b})`, i.fillRect(0, 0, e, t);
|
|
1720
|
+
const d = e - l * 2, g = t - l * 2, y = (p) => l + (p - n.xRange.min) / (n.xRange.max - n.xRange.min) * d, h = (p) => t - l - (p - n.yRange.min) / (n.yRange.max - n.yRange.min) * g;
|
|
1721
|
+
if (n.showGrid) {
|
|
1485
1722
|
i.strokeStyle = "var(--nice-border, #e0e0e0)", i.lineWidth = 0.5;
|
|
1486
|
-
for (let
|
|
1487
|
-
const
|
|
1488
|
-
i.beginPath(), i.moveTo(
|
|
1723
|
+
for (let p = Math.ceil(n.xRange.min); p <= n.xRange.max; p++) {
|
|
1724
|
+
const f = y(p);
|
|
1725
|
+
i.beginPath(), i.moveTo(f, l), i.lineTo(f, t - l), i.stroke();
|
|
1489
1726
|
}
|
|
1490
|
-
for (let
|
|
1491
|
-
const
|
|
1492
|
-
i.beginPath(), i.moveTo(
|
|
1727
|
+
for (let p = Math.ceil(n.yRange.min); p <= n.yRange.max; p++) {
|
|
1728
|
+
const f = h(p);
|
|
1729
|
+
i.beginPath(), i.moveTo(l, f), i.lineTo(e - l, f), i.stroke();
|
|
1493
1730
|
}
|
|
1494
1731
|
}
|
|
1495
|
-
if (
|
|
1732
|
+
if (n.showAxes) {
|
|
1496
1733
|
i.strokeStyle = "var(--nice-text, #333)", i.lineWidth = 1;
|
|
1497
|
-
const
|
|
1498
|
-
i.beginPath(), i.moveTo(
|
|
1499
|
-
const
|
|
1500
|
-
if (i.beginPath(), i.moveTo(
|
|
1501
|
-
i.fillStyle = "var(--nice-text, #333)", i.font = "12px sans-serif", i.textAlign = "center", i.fillText("x", e -
|
|
1502
|
-
for (let
|
|
1503
|
-
|
|
1504
|
-
for (let
|
|
1505
|
-
|
|
1734
|
+
const p = h(0);
|
|
1735
|
+
i.beginPath(), i.moveTo(l, p), i.lineTo(e - l, p), i.stroke();
|
|
1736
|
+
const f = y(0);
|
|
1737
|
+
if (i.beginPath(), i.moveTo(f, l), i.lineTo(f, t - l), i.stroke(), n.showLabels) {
|
|
1738
|
+
i.fillStyle = "var(--nice-text, #333)", i.font = "12px sans-serif", i.textAlign = "center", i.fillText("x", e - l + 15, p + 4), i.fillText("y", f, l - 10), i.font = "10px sans-serif";
|
|
1739
|
+
for (let c = Math.ceil(n.xRange.min); c <= n.xRange.max; c++)
|
|
1740
|
+
c !== 0 && i.fillText(String(c), y(c), p + 15);
|
|
1741
|
+
for (let c = Math.ceil(n.yRange.min); c <= n.yRange.max; c++)
|
|
1742
|
+
c !== 0 && (i.textAlign = "right", i.fillText(String(c), f - 5, h(c) + 4));
|
|
1506
1743
|
}
|
|
1507
1744
|
}
|
|
1508
|
-
const
|
|
1509
|
-
for (const
|
|
1510
|
-
const
|
|
1511
|
-
i.strokeStyle =
|
|
1512
|
-
let
|
|
1513
|
-
switch (
|
|
1745
|
+
const w = s.isPlaying ? { [s.parameter]: s.currentValue } : {};
|
|
1746
|
+
for (const p of o.filter((f) => f.visible)) {
|
|
1747
|
+
const f = `rgb(${p.color.r},${p.color.g},${p.color.b})`;
|
|
1748
|
+
i.strokeStyle = f, i.lineWidth = p.lineWidth || 2;
|
|
1749
|
+
let c = [];
|
|
1750
|
+
switch (p.type) {
|
|
1514
1751
|
case "2d-function":
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1752
|
+
c = a.compute2DFunction(
|
|
1753
|
+
p.expression,
|
|
1754
|
+
n.xRange,
|
|
1755
|
+
p.resolution || 200,
|
|
1756
|
+
w
|
|
1520
1757
|
);
|
|
1521
1758
|
break;
|
|
1522
1759
|
case "2d-parametric":
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1760
|
+
c = a.compute2DParametric(
|
|
1761
|
+
p.expression,
|
|
1762
|
+
p.expressionY || "sin(t)",
|
|
1763
|
+
p.parameterRange || { min: 0, max: 2 * Math.PI },
|
|
1764
|
+
p.resolution || 200,
|
|
1765
|
+
w
|
|
1529
1766
|
);
|
|
1530
1767
|
break;
|
|
1531
1768
|
case "2d-polar":
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1769
|
+
c = a.compute2DPolar(
|
|
1770
|
+
p.expression,
|
|
1771
|
+
p.parameterRange || { min: 0, max: 2 * Math.PI },
|
|
1772
|
+
p.resolution || 200,
|
|
1773
|
+
w
|
|
1537
1774
|
);
|
|
1538
1775
|
break;
|
|
1539
1776
|
}
|
|
1540
|
-
if (
|
|
1777
|
+
if (c.length > 0) {
|
|
1541
1778
|
i.beginPath();
|
|
1542
|
-
let
|
|
1543
|
-
for (const
|
|
1544
|
-
const k =
|
|
1545
|
-
if (
|
|
1546
|
-
|
|
1779
|
+
let u = !1;
|
|
1780
|
+
for (const b of c) {
|
|
1781
|
+
const k = y(b.x), m = h(b.y);
|
|
1782
|
+
if (m < 0 || m > t) {
|
|
1783
|
+
u = !1;
|
|
1547
1784
|
continue;
|
|
1548
1785
|
}
|
|
1549
|
-
|
|
1786
|
+
u ? i.lineTo(k, m) : (i.moveTo(k, m), u = !0);
|
|
1550
1787
|
}
|
|
1551
1788
|
i.stroke();
|
|
1552
1789
|
}
|
|
1553
1790
|
}
|
|
1554
|
-
}, [
|
|
1791
|
+
}, [o, n, s, a, e, t]), /* @__PURE__ */ jsx("canvas", { ref: r, width: e, height: t, style: styles$2.canvas });
|
|
1555
1792
|
}
|
|
1556
1793
|
const UNITS = [
|
|
1557
1794
|
// Length
|
|
@@ -1826,7 +2063,7 @@ const UNITS = [
|
|
|
1826
2063
|
], MatrixOps = {
|
|
1827
2064
|
create(e, t, r = 0) {
|
|
1828
2065
|
const a = [];
|
|
1829
|
-
for (let
|
|
2066
|
+
for (let o = 0; o < e; o++)
|
|
1830
2067
|
a.push(new Array(t).fill(r));
|
|
1831
2068
|
return { rows: e, cols: t, data: a };
|
|
1832
2069
|
},
|
|
@@ -1841,8 +2078,8 @@ const UNITS = [
|
|
|
1841
2078
|
throw new Error("Matrix dimensions must match for addition");
|
|
1842
2079
|
const r = this.create(e.rows, e.cols);
|
|
1843
2080
|
for (let a = 0; a < e.rows; a++)
|
|
1844
|
-
for (let
|
|
1845
|
-
r.data[a][
|
|
2081
|
+
for (let o = 0; o < e.cols; o++)
|
|
2082
|
+
r.data[a][o] = e.data[a][o] + t.data[a][o];
|
|
1846
2083
|
return r;
|
|
1847
2084
|
},
|
|
1848
2085
|
subtract(e, t) {
|
|
@@ -1850,8 +2087,8 @@ const UNITS = [
|
|
|
1850
2087
|
throw new Error("Matrix dimensions must match for subtraction");
|
|
1851
2088
|
const r = this.create(e.rows, e.cols);
|
|
1852
2089
|
for (let a = 0; a < e.rows; a++)
|
|
1853
|
-
for (let
|
|
1854
|
-
r.data[a][
|
|
2090
|
+
for (let o = 0; o < e.cols; o++)
|
|
2091
|
+
r.data[a][o] = e.data[a][o] - t.data[a][o];
|
|
1855
2092
|
return r;
|
|
1856
2093
|
},
|
|
1857
2094
|
multiply(e, t) {
|
|
@@ -1859,19 +2096,19 @@ const UNITS = [
|
|
|
1859
2096
|
throw new Error("Matrix A columns must equal Matrix B rows for multiplication");
|
|
1860
2097
|
const r = this.create(e.rows, t.cols);
|
|
1861
2098
|
for (let a = 0; a < e.rows; a++)
|
|
1862
|
-
for (let
|
|
1863
|
-
let
|
|
2099
|
+
for (let o = 0; o < t.cols; o++) {
|
|
2100
|
+
let n = 0;
|
|
1864
2101
|
for (let s = 0; s < e.cols; s++)
|
|
1865
|
-
|
|
1866
|
-
r.data[a][
|
|
2102
|
+
n += e.data[a][s] * t.data[s][o];
|
|
2103
|
+
r.data[a][o] = n;
|
|
1867
2104
|
}
|
|
1868
2105
|
return r;
|
|
1869
2106
|
},
|
|
1870
2107
|
scalar(e, t) {
|
|
1871
2108
|
const r = this.create(e.rows, e.cols);
|
|
1872
2109
|
for (let a = 0; a < e.rows; a++)
|
|
1873
|
-
for (let
|
|
1874
|
-
r.data[a][
|
|
2110
|
+
for (let o = 0; o < e.cols; o++)
|
|
2111
|
+
r.data[a][o] = e.data[a][o] * t;
|
|
1875
2112
|
return r;
|
|
1876
2113
|
},
|
|
1877
2114
|
transpose(e) {
|
|
@@ -1895,14 +2132,14 @@ const UNITS = [
|
|
|
1895
2132
|
},
|
|
1896
2133
|
minor(e, t, r) {
|
|
1897
2134
|
const a = this.create(e.rows - 1, e.cols - 1);
|
|
1898
|
-
let
|
|
1899
|
-
for (let
|
|
1900
|
-
if (
|
|
2135
|
+
let o = 0;
|
|
2136
|
+
for (let n = 0; n < e.rows; n++) {
|
|
2137
|
+
if (n === t)
|
|
1901
2138
|
continue;
|
|
1902
2139
|
let s = 0;
|
|
1903
|
-
for (let
|
|
1904
|
-
|
|
1905
|
-
|
|
2140
|
+
for (let l = 0; l < e.cols; l++)
|
|
2141
|
+
l !== r && (a.data[o][s] = e.data[n][l], s++);
|
|
2142
|
+
o++;
|
|
1906
2143
|
}
|
|
1907
2144
|
return a;
|
|
1908
2145
|
},
|
|
@@ -1911,10 +2148,10 @@ const UNITS = [
|
|
|
1911
2148
|
if (t === 0)
|
|
1912
2149
|
throw new Error("Matrix is singular, cannot compute inverse");
|
|
1913
2150
|
const r = e.rows, a = this.create(r, r);
|
|
1914
|
-
for (let
|
|
1915
|
-
for (let
|
|
1916
|
-
const s = Math.pow(-1,
|
|
1917
|
-
a.data[
|
|
2151
|
+
for (let o = 0; o < r; o++)
|
|
2152
|
+
for (let n = 0; n < r; n++) {
|
|
2153
|
+
const s = Math.pow(-1, o + n), l = this.determinant(this.minor(e, o, n));
|
|
2154
|
+
a.data[n][o] = s * l;
|
|
1918
2155
|
}
|
|
1919
2156
|
return this.scalar(a, 1 / t);
|
|
1920
2157
|
},
|
|
@@ -1929,11 +2166,11 @@ const UNITS = [
|
|
|
1929
2166
|
eigenvalues2x2(e) {
|
|
1930
2167
|
if (e.rows !== 2 || e.cols !== 2)
|
|
1931
2168
|
throw new Error("Only 2x2 matrices supported for eigenvalues");
|
|
1932
|
-
const t = e.data[0][0], r = e.data[0][1], a = e.data[1][0],
|
|
1933
|
-
if (
|
|
2169
|
+
const t = e.data[0][0], r = e.data[0][1], a = e.data[1][0], o = e.data[1][1], n = t + o, s = t * o - r * a, l = n * n - 4 * s;
|
|
2170
|
+
if (l < 0)
|
|
1934
2171
|
throw new Error("Complex eigenvalues not supported");
|
|
1935
|
-
const
|
|
1936
|
-
return [(
|
|
2172
|
+
const x = Math.sqrt(l);
|
|
2173
|
+
return [(n + x) / 2, (n - x) / 2];
|
|
1937
2174
|
},
|
|
1938
2175
|
toString(e, t = 4) {
|
|
1939
2176
|
return e.data.map((r) => "[ " + r.map((a) => a.toFixed(t).padStart(10)).join(" ") + " ]").join(`
|
|
@@ -1951,25 +2188,25 @@ const UNITS = [
|
|
|
1951
2188
|
/** Numerical integration using Simpson's rule */
|
|
1952
2189
|
integrate(e, t, r, a = 1e3) {
|
|
1953
2190
|
a % 2 !== 0 && a++;
|
|
1954
|
-
const
|
|
1955
|
-
let
|
|
2191
|
+
const o = (r - t) / a;
|
|
2192
|
+
let n = e(t) + e(r);
|
|
1956
2193
|
for (let s = 1; s < a; s++) {
|
|
1957
|
-
const
|
|
1958
|
-
|
|
2194
|
+
const l = t + s * o;
|
|
2195
|
+
n += s % 2 === 0 ? 2 * e(l) : 4 * e(l);
|
|
1959
2196
|
}
|
|
1960
|
-
return
|
|
2197
|
+
return o / 3 * n;
|
|
1961
2198
|
},
|
|
1962
2199
|
/** Find root using Newton-Raphson method */
|
|
1963
2200
|
findRoot(e, t, r = 1e-10, a = 100) {
|
|
1964
|
-
let
|
|
1965
|
-
for (let
|
|
1966
|
-
const s = e(
|
|
2201
|
+
let o = t;
|
|
2202
|
+
for (let n = 0; n < a; n++) {
|
|
2203
|
+
const s = e(o);
|
|
1967
2204
|
if (Math.abs(s) < r)
|
|
1968
|
-
return
|
|
1969
|
-
const
|
|
1970
|
-
if (
|
|
2205
|
+
return o;
|
|
2206
|
+
const l = this.derivative(e, o);
|
|
2207
|
+
if (l === 0)
|
|
1971
2208
|
throw new Error("Derivative is zero, Newton-Raphson failed");
|
|
1972
|
-
|
|
2209
|
+
o = o - s / l;
|
|
1973
2210
|
}
|
|
1974
2211
|
throw new Error("Newton-Raphson did not converge");
|
|
1975
2212
|
},
|
|
@@ -1986,8 +2223,8 @@ const UNITS = [
|
|
|
1986
2223
|
}
|
|
1987
2224
|
const r = e.match(/^(-?\d+\.?\d*)\*x\^(\d+)$/);
|
|
1988
2225
|
if (r) {
|
|
1989
|
-
const a = parseFloat(r[1]),
|
|
1990
|
-
return
|
|
2226
|
+
const a = parseFloat(r[1]), o = parseInt(r[2], 10), n = a * o;
|
|
2227
|
+
return o === 1 ? String(n) : o === 2 ? `${n}*x` : `${n}*x^${o - 1}`;
|
|
1991
2228
|
}
|
|
1992
2229
|
return e === "sin(x)" ? "cos(x)" : e === "cos(x)" ? "-sin(x)" : e === "e^x" || e === "exp(x)" ? "e^x" : e === "ln(x)" ? "1/x" : `d/dx(${e})`;
|
|
1993
2230
|
},
|
|
@@ -2011,8 +2248,8 @@ const UNITS = [
|
|
|
2011
2248
|
throw new Error("No real solutions (complex roots)");
|
|
2012
2249
|
if (a === 0)
|
|
2013
2250
|
return [-t / (2 * e)];
|
|
2014
|
-
const
|
|
2015
|
-
return [(-t +
|
|
2251
|
+
const o = Math.sqrt(a);
|
|
2252
|
+
return [(-t + o) / (2 * e), (-t - o) / (2 * e)];
|
|
2016
2253
|
}
|
|
2017
2254
|
};
|
|
2018
2255
|
class CalculatorService {
|
|
@@ -2080,20 +2317,20 @@ class CalculatorService {
|
|
|
2080
2317
|
}
|
|
2081
2318
|
/** Convert between number bases */
|
|
2082
2319
|
convertBase(e, t, r) {
|
|
2083
|
-
const a = { BIN: 2, OCT: 8, DEC: 10, HEX: 16 },
|
|
2084
|
-
if (isNaN(
|
|
2320
|
+
const a = { BIN: 2, OCT: 8, DEC: 10, HEX: 16 }, o = parseInt(e, a[t]);
|
|
2321
|
+
if (isNaN(o))
|
|
2085
2322
|
throw new Error("Invalid number for base conversion");
|
|
2086
|
-
return
|
|
2323
|
+
return o.toString(a[r]).toUpperCase();
|
|
2087
2324
|
}
|
|
2088
2325
|
/** Convert units */
|
|
2089
2326
|
convertUnits(e, t, r) {
|
|
2090
|
-
const a = UNITS.find((s) => s.symbol === t),
|
|
2091
|
-
if (!a || !
|
|
2327
|
+
const a = UNITS.find((s) => s.symbol === t), o = UNITS.find((s) => s.symbol === r);
|
|
2328
|
+
if (!a || !o)
|
|
2092
2329
|
throw new Error("Unknown unit");
|
|
2093
|
-
if (a.category !==
|
|
2330
|
+
if (a.category !== o.category)
|
|
2094
2331
|
throw new Error("Cannot convert between different unit categories");
|
|
2095
|
-
const
|
|
2096
|
-
return
|
|
2332
|
+
const n = a.toBase(e);
|
|
2333
|
+
return o.fromBase(n);
|
|
2097
2334
|
}
|
|
2098
2335
|
/** Get units by category */
|
|
2099
2336
|
getUnitsByCategory(e) {
|
|
@@ -2182,40 +2419,40 @@ function NiceCalculator({
|
|
|
2182
2419
|
className: t,
|
|
2183
2420
|
style: r
|
|
2184
2421
|
}) {
|
|
2185
|
-
const [a,
|
|
2186
|
-
(
|
|
2187
|
-
i && /[0-9.]/.test(
|
|
2422
|
+
const [a, o] = useState("0"), [n, s] = useState("standard"), [l, x] = useState([]), [i, d] = useState(!1), g = useCallback(
|
|
2423
|
+
(p) => {
|
|
2424
|
+
i && /[0-9.]/.test(p) ? (o(p), d(!1)) : (o((f) => f === "0" && p !== "." ? p : f + p), d(!1));
|
|
2188
2425
|
},
|
|
2189
2426
|
[i]
|
|
2190
|
-
),
|
|
2191
|
-
|
|
2192
|
-
}, []),
|
|
2427
|
+
), y = useCallback(() => {
|
|
2428
|
+
o("0"), d(!1);
|
|
2429
|
+
}, []), h = useCallback(() => {
|
|
2193
2430
|
try {
|
|
2194
|
-
const
|
|
2195
|
-
e.addHistory(a,
|
|
2431
|
+
const p = e.evaluate(a), f = e.format(p);
|
|
2432
|
+
e.addHistory(a, f, n), x(e.getHistory()), o(f), d(!0);
|
|
2196
2433
|
} catch {
|
|
2197
|
-
|
|
2434
|
+
o("Error"), d(!0);
|
|
2198
2435
|
}
|
|
2199
|
-
}, [a,
|
|
2436
|
+
}, [a, n, e]), w = {
|
|
2200
2437
|
service: e,
|
|
2201
2438
|
display: a,
|
|
2202
|
-
setDisplay:
|
|
2203
|
-
mode:
|
|
2439
|
+
setDisplay: o,
|
|
2440
|
+
mode: n,
|
|
2204
2441
|
setMode: s,
|
|
2205
|
-
history:
|
|
2206
|
-
calculate:
|
|
2207
|
-
appendDigit:
|
|
2208
|
-
clear:
|
|
2442
|
+
history: l,
|
|
2443
|
+
calculate: h,
|
|
2444
|
+
appendDigit: g,
|
|
2445
|
+
clear: y
|
|
2209
2446
|
};
|
|
2210
|
-
return /* @__PURE__ */ jsx(CalculatorContext.Provider, { value:
|
|
2447
|
+
return /* @__PURE__ */ jsx(CalculatorContext.Provider, { value: w, children: /* @__PURE__ */ jsxs("div", { className: t, style: { ...styles$1.container, ...r }, children: [
|
|
2211
2448
|
/* @__PURE__ */ jsx(ModeSelector, {}),
|
|
2212
2449
|
/* @__PURE__ */ jsx(Display, {}),
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2450
|
+
n === "standard" && /* @__PURE__ */ jsx(StandardKeypad, {}),
|
|
2451
|
+
n === "scientific" && /* @__PURE__ */ jsx(ScientificKeypad, {}),
|
|
2452
|
+
n === "programmer" && /* @__PURE__ */ jsx(ProgrammerKeypad, {}),
|
|
2453
|
+
n === "matrix" && /* @__PURE__ */ jsx(MatrixMode, {}),
|
|
2454
|
+
n === "calculus" && /* @__PURE__ */ jsx(CalculusMode, {}),
|
|
2455
|
+
n === "units" && /* @__PURE__ */ jsx(UnitsMode, {})
|
|
2219
2456
|
] }) });
|
|
2220
2457
|
}
|
|
2221
2458
|
function ModeSelector() {
|
|
@@ -2246,11 +2483,11 @@ function Display() {
|
|
|
2246
2483
|
return /* @__PURE__ */ jsx("div", { style: styles$1.display, children: /* @__PURE__ */ jsx("div", { style: styles$1.displayText, children: e }) });
|
|
2247
2484
|
}
|
|
2248
2485
|
function StandardKeypad() {
|
|
2249
|
-
const { appendDigit: e, clear: t, calculate: r, setDisplay: a, display:
|
|
2486
|
+
const { appendDigit: e, clear: t, calculate: r, setDisplay: a, display: o } = useCalculator(), n = [
|
|
2250
2487
|
{ label: "AC", action: t, style: styles$1.keyFunction },
|
|
2251
2488
|
{
|
|
2252
2489
|
label: "±",
|
|
2253
|
-
action: () => a(
|
|
2490
|
+
action: () => a(o.startsWith("-") ? o.slice(1) : "-" + o),
|
|
2254
2491
|
style: styles$1.keyFunction
|
|
2255
2492
|
},
|
|
2256
2493
|
{ label: "%", action: () => e("/100"), style: styles$1.keyFunction },
|
|
@@ -2269,13 +2506,19 @@ function StandardKeypad() {
|
|
|
2269
2506
|
{ label: "+", action: () => e("+"), style: styles$1.keyOperator },
|
|
2270
2507
|
{ label: "0", action: () => e("0") },
|
|
2271
2508
|
{ label: ".", action: () => e(".") },
|
|
2272
|
-
{ label: "⌫", action: () => a(
|
|
2509
|
+
{ label: "⌫", action: () => a(o.length > 1 ? o.slice(0, -1) : "0") },
|
|
2273
2510
|
{ label: "=", action: r, style: styles$1.keyOperator }
|
|
2274
2511
|
];
|
|
2275
|
-
return /* @__PURE__ */ jsx("div", { style: styles$1.keypad, children:
|
|
2512
|
+
return /* @__PURE__ */ jsx("div", { style: styles$1.keypad, children: n.map((s, l) => /* @__PURE__ */ jsx("button", { onClick: s.action, style: { ...styles$1.key, ...s.style || {} }, children: s.label }, l)) });
|
|
2276
2513
|
}
|
|
2277
2514
|
function ScientificKeypad() {
|
|
2278
|
-
const {
|
|
2515
|
+
const {
|
|
2516
|
+
appendDigit: e,
|
|
2517
|
+
clear: t,
|
|
2518
|
+
calculate: r,
|
|
2519
|
+
setDisplay: a,
|
|
2520
|
+
display: o
|
|
2521
|
+
} = useCalculator(), n = [
|
|
2279
2522
|
{ label: "sin", action: () => e("sin(") },
|
|
2280
2523
|
{ label: "cos", action: () => e("cos(") },
|
|
2281
2524
|
{ label: "tan", action: () => e("tan(") },
|
|
@@ -2287,7 +2530,7 @@ function ScientificKeypad() {
|
|
|
2287
2530
|
{ label: "xʸ", action: () => e("^") },
|
|
2288
2531
|
{ label: "√", action: () => e("sqrt(") },
|
|
2289
2532
|
{ label: "eˣ", action: () => e("exp(") }
|
|
2290
|
-
],
|
|
2533
|
+
], l = [
|
|
2291
2534
|
{ label: "(", action: () => e("(") },
|
|
2292
2535
|
{ label: ")", action: () => e(")") },
|
|
2293
2536
|
{ label: "π", action: () => e("π") },
|
|
@@ -2295,12 +2538,12 @@ function ScientificKeypad() {
|
|
|
2295
2538
|
{ label: "AC", action: t }
|
|
2296
2539
|
];
|
|
2297
2540
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
2298
|
-
/* @__PURE__ */ jsx("div", { style: { ...styles$1.keypad, gridTemplateColumns: "repeat(5, 1fr)" }, children: [...
|
|
2541
|
+
/* @__PURE__ */ jsx("div", { style: { ...styles$1.keypad, gridTemplateColumns: "repeat(5, 1fr)" }, children: [...n, ...s, ...l].map((x, i) => /* @__PURE__ */ jsx(
|
|
2299
2542
|
"button",
|
|
2300
2543
|
{
|
|
2301
|
-
onClick:
|
|
2544
|
+
onClick: x.action,
|
|
2302
2545
|
style: { ...styles$1.key, ...styles$1.keyFunction, padding: "12px" },
|
|
2303
|
-
children:
|
|
2546
|
+
children: x.label
|
|
2304
2547
|
},
|
|
2305
2548
|
i
|
|
2306
2549
|
)) }),
|
|
@@ -2308,40 +2551,51 @@ function ScientificKeypad() {
|
|
|
2308
2551
|
] });
|
|
2309
2552
|
}
|
|
2310
2553
|
function ProgrammerKeypad() {
|
|
2311
|
-
const { display: e, setDisplay: t, service: r } = useCalculator(), [a,
|
|
2554
|
+
const { display: e, setDisplay: t, service: r } = useCalculator(), [a, o] = useState("DEC"), n = (l) => {
|
|
2312
2555
|
try {
|
|
2313
|
-
const
|
|
2314
|
-
t(
|
|
2556
|
+
const x = r.convertBase(e, a, l);
|
|
2557
|
+
t(x), o(l);
|
|
2315
2558
|
} catch {
|
|
2316
2559
|
t("Error");
|
|
2317
2560
|
}
|
|
2318
2561
|
}, s = a === "HEX" ? ["A", "B", "C", "D", "E", "F"] : [];
|
|
2319
2562
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
2320
|
-
/* @__PURE__ */ jsx(
|
|
2321
|
-
"
|
|
2563
|
+
/* @__PURE__ */ jsx(
|
|
2564
|
+
"div",
|
|
2322
2565
|
{
|
|
2323
|
-
onClick: () => o(c),
|
|
2324
2566
|
style: {
|
|
2325
|
-
|
|
2567
|
+
display: "flex",
|
|
2568
|
+
gap: "4px",
|
|
2326
2569
|
padding: "8px",
|
|
2327
|
-
|
|
2328
|
-
borderRadius: "4px",
|
|
2329
|
-
backgroundColor: a === c ? "var(--nice-primary-hover, #1976d2)" : "var(--nice-border, #3c3c3c)",
|
|
2330
|
-
color: "var(--nice-bg, #fff)",
|
|
2331
|
-
cursor: "pointer"
|
|
2570
|
+
backgroundColor: "var(--nice-bg-secondary, #252525)"
|
|
2332
2571
|
},
|
|
2333
|
-
children:
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2572
|
+
children: ["BIN", "OCT", "DEC", "HEX"].map((l) => /* @__PURE__ */ jsx(
|
|
2573
|
+
"button",
|
|
2574
|
+
{
|
|
2575
|
+
onClick: () => n(l),
|
|
2576
|
+
style: {
|
|
2577
|
+
flex: 1,
|
|
2578
|
+
padding: "8px",
|
|
2579
|
+
border: "none",
|
|
2580
|
+
borderRadius: "4px",
|
|
2581
|
+
backgroundColor: a === l ? "var(--nice-primary-hover, #1976d2)" : "var(--nice-border, #3c3c3c)",
|
|
2582
|
+
color: "var(--nice-bg, #fff)",
|
|
2583
|
+
cursor: "pointer"
|
|
2584
|
+
},
|
|
2585
|
+
children: l
|
|
2586
|
+
},
|
|
2587
|
+
l
|
|
2588
|
+
))
|
|
2589
|
+
}
|
|
2590
|
+
),
|
|
2591
|
+
s.length > 0 && /* @__PURE__ */ jsx("div", { style: { ...styles$1.keypad, gridTemplateColumns: "repeat(6, 1fr)" }, children: s.map((l) => /* @__PURE__ */ jsx(
|
|
2338
2592
|
"button",
|
|
2339
2593
|
{
|
|
2340
|
-
onClick: () => t(e === "0" ?
|
|
2594
|
+
onClick: () => t(e === "0" ? l : e + l),
|
|
2341
2595
|
style: styles$1.key,
|
|
2342
|
-
children:
|
|
2596
|
+
children: l
|
|
2343
2597
|
},
|
|
2344
|
-
|
|
2598
|
+
l
|
|
2345
2599
|
)) }),
|
|
2346
2600
|
/* @__PURE__ */ jsx("div", { style: { ...styles$1.keypad, gridTemplateColumns: "repeat(4, 1fr)" }, children: [
|
|
2347
2601
|
"7",
|
|
@@ -2360,63 +2614,63 @@ function ProgrammerKeypad() {
|
|
|
2360
2614
|
"NOT",
|
|
2361
2615
|
"<<",
|
|
2362
2616
|
">>"
|
|
2363
|
-
].map((
|
|
2364
|
-
const
|
|
2617
|
+
].map((l) => {
|
|
2618
|
+
const x = ["AND", "OR", "XOR", "NOT", "<<", ">>"].includes(l);
|
|
2365
2619
|
return /* @__PURE__ */ jsx(
|
|
2366
2620
|
"button",
|
|
2367
2621
|
{
|
|
2368
|
-
onClick: () => t(e === "0" ?
|
|
2369
|
-
style: { ...styles$1.key, ...
|
|
2370
|
-
children:
|
|
2622
|
+
onClick: () => t(e === "0" ? l : e + l),
|
|
2623
|
+
style: { ...styles$1.key, ...x ? styles$1.keyFunction : {} },
|
|
2624
|
+
children: l
|
|
2371
2625
|
},
|
|
2372
|
-
|
|
2626
|
+
l
|
|
2373
2627
|
);
|
|
2374
2628
|
}) })
|
|
2375
2629
|
] });
|
|
2376
2630
|
}
|
|
2377
2631
|
function MatrixMode() {
|
|
2378
|
-
const
|
|
2379
|
-
const
|
|
2380
|
-
|
|
2381
|
-
}
|
|
2632
|
+
const { t: e } = useNiceTranslation(), [t, r] = useState(MatrixOps.create(2, 2)), [a, o] = useState(MatrixOps.create(2, 2)), [n, s] = useState(""), l = (i, d, g, y, h) => {
|
|
2633
|
+
const w = { ...i, data: i.data.map((p) => [...p]) };
|
|
2634
|
+
w.data[g][y] = parseFloat(h) || 0, d(w);
|
|
2635
|
+
}, x = [
|
|
2636
|
+
{ label: "A + B", fn: () => MatrixOps.toString(MatrixOps.add(t, a)) },
|
|
2637
|
+
{ label: "A - B", fn: () => MatrixOps.toString(MatrixOps.subtract(t, a)) },
|
|
2638
|
+
{ label: "A × B", fn: () => MatrixOps.toString(MatrixOps.multiply(t, a)) },
|
|
2639
|
+
{ label: "det(A)", fn: () => MatrixOps.determinant(t).toFixed(4) },
|
|
2640
|
+
{ label: "Aᵀ", fn: () => MatrixOps.toString(MatrixOps.transpose(t)) },
|
|
2641
|
+
{ label: "A⁻¹", fn: () => MatrixOps.toString(MatrixOps.inverse(t)) }
|
|
2642
|
+
];
|
|
2382
2643
|
return /* @__PURE__ */ jsxs("div", { style: { padding: "12px", backgroundColor: "var(--nice-bg-secondary, #252525)" }, children: [
|
|
2383
2644
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "12px", marginBottom: "12px" }, children: [
|
|
2384
2645
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
2385
|
-
/* @__PURE__ */ jsx("div", { style: { color: "var(--nice-text-secondary, #888)", marginBottom: "4px" }, children: "Matrix A" }),
|
|
2646
|
+
/* @__PURE__ */ jsx("div", { style: { color: "var(--nice-text-secondary, #888)", marginBottom: "4px" }, children: e("calculator.matrixA", "Matrix A") }),
|
|
2386
2647
|
/* @__PURE__ */ jsx(
|
|
2387
2648
|
MatrixInput,
|
|
2388
2649
|
{
|
|
2389
|
-
matrix:
|
|
2390
|
-
onChange: (
|
|
2650
|
+
matrix: t,
|
|
2651
|
+
onChange: (i, d, g) => l(t, r, i, d, g)
|
|
2391
2652
|
}
|
|
2392
2653
|
)
|
|
2393
2654
|
] }),
|
|
2394
2655
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
2395
|
-
/* @__PURE__ */ jsx("div", { style: { color: "var(--nice-text-secondary, #888)", marginBottom: "4px" }, children: "Matrix B" }),
|
|
2656
|
+
/* @__PURE__ */ jsx("div", { style: { color: "var(--nice-text-secondary, #888)", marginBottom: "4px" }, children: e("calculator.matrixB", "Matrix B") }),
|
|
2396
2657
|
/* @__PURE__ */ jsx(
|
|
2397
2658
|
MatrixInput,
|
|
2398
2659
|
{
|
|
2399
|
-
matrix:
|
|
2400
|
-
onChange: (
|
|
2660
|
+
matrix: a,
|
|
2661
|
+
onChange: (i, d, g) => l(a, o, i, d, g)
|
|
2401
2662
|
}
|
|
2402
2663
|
)
|
|
2403
2664
|
] })
|
|
2404
2665
|
] }),
|
|
2405
|
-
/* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "4px", flexWrap: "wrap", marginBottom: "12px" }, children:
|
|
2406
|
-
{ label: "A + B", fn: () => MatrixOps.toString(MatrixOps.add(e, r)) },
|
|
2407
|
-
{ label: "A - B", fn: () => MatrixOps.toString(MatrixOps.subtract(e, r)) },
|
|
2408
|
-
{ label: "A × B", fn: () => MatrixOps.toString(MatrixOps.multiply(e, r)) },
|
|
2409
|
-
{ label: "det(A)", fn: () => MatrixOps.determinant(e).toFixed(4) },
|
|
2410
|
-
{ label: "Aᵀ", fn: () => MatrixOps.toString(MatrixOps.transpose(e)) },
|
|
2411
|
-
{ label: "A⁻¹", fn: () => MatrixOps.toString(MatrixOps.inverse(e)) }
|
|
2412
|
-
].map((l) => /* @__PURE__ */ jsx(
|
|
2666
|
+
/* @__PURE__ */ jsx("div", { style: { display: "flex", gap: "4px", flexWrap: "wrap", marginBottom: "12px" }, children: x.map((i) => /* @__PURE__ */ jsx(
|
|
2413
2667
|
"button",
|
|
2414
2668
|
{
|
|
2415
2669
|
onClick: () => {
|
|
2416
2670
|
try {
|
|
2417
|
-
|
|
2418
|
-
} catch (
|
|
2419
|
-
|
|
2671
|
+
s(i.fn());
|
|
2672
|
+
} catch (d) {
|
|
2673
|
+
s(d.message);
|
|
2420
2674
|
}
|
|
2421
2675
|
},
|
|
2422
2676
|
style: {
|
|
@@ -2427,11 +2681,17 @@ function MatrixMode() {
|
|
|
2427
2681
|
borderRadius: "4px",
|
|
2428
2682
|
cursor: "pointer"
|
|
2429
2683
|
},
|
|
2430
|
-
children:
|
|
2684
|
+
children: i.label
|
|
2431
2685
|
},
|
|
2432
|
-
|
|
2686
|
+
i.label
|
|
2433
2687
|
)) }),
|
|
2434
|
-
n && /* @__PURE__ */ jsx(
|
|
2688
|
+
n && /* @__PURE__ */ jsx(
|
|
2689
|
+
"pre",
|
|
2690
|
+
{
|
|
2691
|
+
style: { color: "var(--nice-bg, #fff)", fontFamily: "monospace", whiteSpace: "pre-wrap" },
|
|
2692
|
+
children: n
|
|
2693
|
+
}
|
|
2694
|
+
)
|
|
2435
2695
|
] });
|
|
2436
2696
|
}
|
|
2437
2697
|
function MatrixInput({
|
|
@@ -2443,12 +2703,12 @@ function MatrixInput({
|
|
|
2443
2703
|
{
|
|
2444
2704
|
style: { display: "grid", gridTemplateColumns: `repeat(${e.cols}, 50px)`, gap: "4px" },
|
|
2445
2705
|
children: e.data.map(
|
|
2446
|
-
(r, a) => r.map((
|
|
2706
|
+
(r, a) => r.map((o, n) => /* @__PURE__ */ jsx(
|
|
2447
2707
|
"input",
|
|
2448
2708
|
{
|
|
2449
2709
|
type: "number",
|
|
2450
|
-
value:
|
|
2451
|
-
onChange: (s) => t(a,
|
|
2710
|
+
value: o,
|
|
2711
|
+
onChange: (s) => t(a, n, s.target.value),
|
|
2452
2712
|
style: {
|
|
2453
2713
|
width: "100%",
|
|
2454
2714
|
padding: "4px",
|
|
@@ -2459,22 +2719,22 @@ function MatrixInput({
|
|
|
2459
2719
|
borderRadius: "4px"
|
|
2460
2720
|
}
|
|
2461
2721
|
},
|
|
2462
|
-
`${a}-${
|
|
2722
|
+
`${a}-${n}`
|
|
2463
2723
|
))
|
|
2464
2724
|
)
|
|
2465
2725
|
}
|
|
2466
2726
|
);
|
|
2467
2727
|
}
|
|
2468
2728
|
function CalculusMode() {
|
|
2469
|
-
const
|
|
2729
|
+
const { t: e } = useNiceTranslation(), [t, r] = useState("x^2"), [a, o] = useState("");
|
|
2470
2730
|
return /* @__PURE__ */ jsxs("div", { style: { padding: "12px", backgroundColor: "var(--nice-bg-secondary, #252525)" }, children: [
|
|
2471
2731
|
/* @__PURE__ */ jsx(
|
|
2472
2732
|
"input",
|
|
2473
2733
|
{
|
|
2474
2734
|
type: "text",
|
|
2475
|
-
value:
|
|
2476
|
-
onChange: (n) =>
|
|
2477
|
-
placeholder: "Enter expression (e.g., x^2, sin(x))",
|
|
2735
|
+
value: t,
|
|
2736
|
+
onChange: (n) => r(n.target.value),
|
|
2737
|
+
placeholder: e("calculator.exprPlaceholder", "Enter expression (e.g., x^2, sin(x))"),
|
|
2478
2738
|
style: {
|
|
2479
2739
|
width: "100%",
|
|
2480
2740
|
padding: "8px",
|
|
@@ -2490,7 +2750,7 @@ function CalculusMode() {
|
|
|
2490
2750
|
/* @__PURE__ */ jsx(
|
|
2491
2751
|
"button",
|
|
2492
2752
|
{
|
|
2493
|
-
onClick: () =>
|
|
2753
|
+
onClick: () => o(`d/dx(${t}) = ${CalculusOps.symbolicDerivative(t)}`),
|
|
2494
2754
|
style: {
|
|
2495
2755
|
padding: "8px 12px",
|
|
2496
2756
|
backgroundColor: "var(--nice-success, #4caf50)",
|
|
@@ -2499,13 +2759,13 @@ function CalculusMode() {
|
|
|
2499
2759
|
borderRadius: "4px",
|
|
2500
2760
|
cursor: "pointer"
|
|
2501
2761
|
},
|
|
2502
|
-
children: "Differentiate"
|
|
2762
|
+
children: e("calculator.differentiate", "Differentiate")
|
|
2503
2763
|
}
|
|
2504
2764
|
),
|
|
2505
2765
|
/* @__PURE__ */ jsx(
|
|
2506
2766
|
"button",
|
|
2507
2767
|
{
|
|
2508
|
-
onClick: () =>
|
|
2768
|
+
onClick: () => o(`∫(${t})dx = ${CalculusOps.symbolicIntegral(t)}`),
|
|
2509
2769
|
style: {
|
|
2510
2770
|
padding: "8px 12px",
|
|
2511
2771
|
backgroundColor: "var(--nice-primary, #2196f3)",
|
|
@@ -2514,11 +2774,11 @@ function CalculusMode() {
|
|
|
2514
2774
|
borderRadius: "4px",
|
|
2515
2775
|
cursor: "pointer"
|
|
2516
2776
|
},
|
|
2517
|
-
children: "Integrate"
|
|
2777
|
+
children: e("calculator.integrate", "Integrate")
|
|
2518
2778
|
}
|
|
2519
2779
|
)
|
|
2520
2780
|
] }),
|
|
2521
|
-
|
|
2781
|
+
a && /* @__PURE__ */ jsx(
|
|
2522
2782
|
"div",
|
|
2523
2783
|
{
|
|
2524
2784
|
style: {
|
|
@@ -2528,29 +2788,29 @@ function CalculusMode() {
|
|
|
2528
2788
|
backgroundColor: "var(--nice-bg, #1e1e1e)",
|
|
2529
2789
|
borderRadius: "4px"
|
|
2530
2790
|
},
|
|
2531
|
-
children:
|
|
2791
|
+
children: a
|
|
2532
2792
|
}
|
|
2533
2793
|
)
|
|
2534
2794
|
] });
|
|
2535
2795
|
}
|
|
2536
2796
|
function UnitsMode() {
|
|
2537
|
-
const {
|
|
2797
|
+
const { t: e } = useNiceTranslation(), { service: t } = useCalculator(), [r, a] = useState("1"), [o, n] = useState("m"), [s, l] = useState("ft"), [x, i] = useState("Length"), [d, g] = useState(""), y = t.getUnitCategories(), h = t.getUnitsByCategory(x), w = () => {
|
|
2538
2798
|
try {
|
|
2539
|
-
const
|
|
2540
|
-
|
|
2541
|
-
} catch (
|
|
2542
|
-
|
|
2799
|
+
const p = t.convertUnits(parseFloat(r), o, s);
|
|
2800
|
+
g(`${r} ${o} = ${t.format(p)} ${s}`);
|
|
2801
|
+
} catch (p) {
|
|
2802
|
+
g(p.message);
|
|
2543
2803
|
}
|
|
2544
2804
|
};
|
|
2545
2805
|
return /* @__PURE__ */ jsxs("div", { style: { padding: "12px", backgroundColor: "var(--nice-bg-secondary, #252525)" }, children: [
|
|
2546
2806
|
/* @__PURE__ */ jsx(
|
|
2547
2807
|
"select",
|
|
2548
2808
|
{
|
|
2549
|
-
value:
|
|
2550
|
-
onChange: (
|
|
2551
|
-
|
|
2552
|
-
const
|
|
2553
|
-
|
|
2809
|
+
value: x,
|
|
2810
|
+
onChange: (p) => {
|
|
2811
|
+
i(p.target.value);
|
|
2812
|
+
const f = t.getUnitsByCategory(p.target.value);
|
|
2813
|
+
f.length >= 2 && (n(f[0].symbol), l(f[1].symbol));
|
|
2554
2814
|
},
|
|
2555
2815
|
style: {
|
|
2556
2816
|
width: "100%",
|
|
@@ -2561,7 +2821,7 @@ function UnitsMode() {
|
|
|
2561
2821
|
border: "1px solid var(--nice-text-secondary, #555)",
|
|
2562
2822
|
borderRadius: "4px"
|
|
2563
2823
|
},
|
|
2564
|
-
children:
|
|
2824
|
+
children: y.map((p) => /* @__PURE__ */ jsx("option", { value: p, children: p }, p))
|
|
2565
2825
|
}
|
|
2566
2826
|
),
|
|
2567
2827
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px", alignItems: "center", marginBottom: "12px" }, children: [
|
|
@@ -2569,8 +2829,8 @@ function UnitsMode() {
|
|
|
2569
2829
|
"input",
|
|
2570
2830
|
{
|
|
2571
2831
|
type: "number",
|
|
2572
|
-
value:
|
|
2573
|
-
onChange: (
|
|
2832
|
+
value: r,
|
|
2833
|
+
onChange: (p) => a(p.target.value),
|
|
2574
2834
|
style: {
|
|
2575
2835
|
flex: 1,
|
|
2576
2836
|
padding: "8px",
|
|
@@ -2584,8 +2844,8 @@ function UnitsMode() {
|
|
|
2584
2844
|
/* @__PURE__ */ jsx(
|
|
2585
2845
|
"select",
|
|
2586
2846
|
{
|
|
2587
|
-
value:
|
|
2588
|
-
onChange: (
|
|
2847
|
+
value: o,
|
|
2848
|
+
onChange: (p) => n(p.target.value),
|
|
2589
2849
|
style: {
|
|
2590
2850
|
padding: "8px",
|
|
2591
2851
|
backgroundColor: "var(--nice-border, #3c3c3c)",
|
|
@@ -2593,12 +2853,12 @@ function UnitsMode() {
|
|
|
2593
2853
|
border: "1px solid var(--nice-text-secondary, #555)",
|
|
2594
2854
|
borderRadius: "4px"
|
|
2595
2855
|
},
|
|
2596
|
-
children: h.map((
|
|
2597
|
-
|
|
2856
|
+
children: h.map((p) => /* @__PURE__ */ jsxs("option", { value: p.symbol, children: [
|
|
2857
|
+
p.name,
|
|
2598
2858
|
" (",
|
|
2599
|
-
|
|
2859
|
+
p.symbol,
|
|
2600
2860
|
")"
|
|
2601
|
-
] },
|
|
2861
|
+
] }, p.symbol))
|
|
2602
2862
|
}
|
|
2603
2863
|
)
|
|
2604
2864
|
] }),
|
|
@@ -2607,8 +2867,8 @@ function UnitsMode() {
|
|
|
2607
2867
|
/* @__PURE__ */ jsx(
|
|
2608
2868
|
"select",
|
|
2609
2869
|
{
|
|
2610
|
-
value:
|
|
2611
|
-
onChange: (
|
|
2870
|
+
value: s,
|
|
2871
|
+
onChange: (p) => l(p.target.value),
|
|
2612
2872
|
style: {
|
|
2613
2873
|
flex: 1,
|
|
2614
2874
|
padding: "8px",
|
|
@@ -2617,19 +2877,19 @@ function UnitsMode() {
|
|
|
2617
2877
|
border: "1px solid var(--nice-text-secondary, #555)",
|
|
2618
2878
|
borderRadius: "4px"
|
|
2619
2879
|
},
|
|
2620
|
-
children: h.map((
|
|
2621
|
-
|
|
2880
|
+
children: h.map((p) => /* @__PURE__ */ jsxs("option", { value: p.symbol, children: [
|
|
2881
|
+
p.name,
|
|
2622
2882
|
" (",
|
|
2623
|
-
|
|
2883
|
+
p.symbol,
|
|
2624
2884
|
")"
|
|
2625
|
-
] },
|
|
2885
|
+
] }, p.symbol))
|
|
2626
2886
|
}
|
|
2627
2887
|
)
|
|
2628
2888
|
] }),
|
|
2629
2889
|
/* @__PURE__ */ jsx(
|
|
2630
2890
|
"button",
|
|
2631
2891
|
{
|
|
2632
|
-
onClick:
|
|
2892
|
+
onClick: w,
|
|
2633
2893
|
style: {
|
|
2634
2894
|
width: "100%",
|
|
2635
2895
|
padding: "12px",
|
|
@@ -2640,10 +2900,21 @@ function UnitsMode() {
|
|
|
2640
2900
|
fontSize: "16px",
|
|
2641
2901
|
cursor: "pointer"
|
|
2642
2902
|
},
|
|
2643
|
-
children: "Convert"
|
|
2903
|
+
children: e("calculator.convert", "Convert")
|
|
2644
2904
|
}
|
|
2645
2905
|
),
|
|
2646
|
-
|
|
2906
|
+
d && /* @__PURE__ */ jsx(
|
|
2907
|
+
"div",
|
|
2908
|
+
{
|
|
2909
|
+
style: {
|
|
2910
|
+
marginTop: "12px",
|
|
2911
|
+
color: "var(--nice-bg, #fff)",
|
|
2912
|
+
fontSize: "18px",
|
|
2913
|
+
textAlign: "center"
|
|
2914
|
+
},
|
|
2915
|
+
children: d
|
|
2916
|
+
}
|
|
2917
|
+
)
|
|
2647
2918
|
] });
|
|
2648
2919
|
}
|
|
2649
2920
|
const GeoMath = {
|
|
@@ -2658,8 +2929,8 @@ const GeoMath = {
|
|
|
2658
2929
|
/** Angle between three points (vertex at p2) */
|
|
2659
2930
|
angle(e, t, r) {
|
|
2660
2931
|
const a = Math.atan2(e.y - t.y, e.x - t.x);
|
|
2661
|
-
let
|
|
2662
|
-
return
|
|
2932
|
+
let n = Math.atan2(r.y - t.y, r.x - t.x) - a;
|
|
2933
|
+
return n < 0 && (n += 2 * Math.PI), n;
|
|
2663
2934
|
},
|
|
2664
2935
|
/** Angle in degrees */
|
|
2665
2936
|
angleDegrees(e, t, r) {
|
|
@@ -2667,63 +2938,63 @@ const GeoMath = {
|
|
|
2667
2938
|
},
|
|
2668
2939
|
/** Point on line closest to given point */
|
|
2669
2940
|
closestPointOnLine(e, t, r) {
|
|
2670
|
-
const a = t.x - e.x,
|
|
2671
|
-
return { x: e.x +
|
|
2941
|
+
const a = t.x - e.x, o = t.y - e.y, n = ((r.x - e.x) * a + (r.y - e.y) * o) / (a * a + o * o);
|
|
2942
|
+
return { x: e.x + n * a, y: e.y + n * o };
|
|
2672
2943
|
},
|
|
2673
2944
|
/** Line-line intersection */
|
|
2674
2945
|
lineIntersection(e, t, r, a) {
|
|
2675
|
-
const
|
|
2676
|
-
if (Math.abs(
|
|
2946
|
+
const o = (e.x - t.x) * (r.y - a.y) - (e.y - t.y) * (r.x - a.x);
|
|
2947
|
+
if (Math.abs(o) < 1e-10)
|
|
2677
2948
|
return null;
|
|
2678
|
-
const
|
|
2949
|
+
const n = ((e.x - r.x) * (r.y - a.y) - (e.y - r.y) * (r.x - a.x)) / o;
|
|
2679
2950
|
return {
|
|
2680
|
-
x: e.x +
|
|
2681
|
-
y: e.y +
|
|
2951
|
+
x: e.x + n * (t.x - e.x),
|
|
2952
|
+
y: e.y + n * (t.y - e.y)
|
|
2682
2953
|
};
|
|
2683
2954
|
},
|
|
2684
2955
|
/** Circle-line intersection */
|
|
2685
2956
|
circleLineIntersection(e, t, r, a) {
|
|
2686
|
-
const
|
|
2687
|
-
if (
|
|
2957
|
+
const o = a.x - r.x, n = a.y - r.y, s = r.x - e.x, l = r.y - e.y, x = o * o + n * n, i = 2 * (s * o + l * n), d = s * s + l * l - t * t, g = i * i - 4 * x * d;
|
|
2958
|
+
if (g < 0)
|
|
2688
2959
|
return [];
|
|
2689
|
-
const
|
|
2690
|
-
return
|
|
2960
|
+
const y = Math.sqrt(g), h = (-i - y) / (2 * x), w = (-i + y) / (2 * x), p = [];
|
|
2961
|
+
return h >= 0 && h <= 1 && p.push({ x: r.x + h * o, y: r.y + h * n }), w >= 0 && w <= 1 && Math.abs(w - h) > 1e-10 && p.push({ x: r.x + w * o, y: r.y + w * n }), p;
|
|
2691
2962
|
},
|
|
2692
2963
|
/** Circle-circle intersection */
|
|
2693
2964
|
circleCircleIntersection(e, t, r, a) {
|
|
2694
|
-
const
|
|
2695
|
-
if (
|
|
2965
|
+
const o = this.distance(e, r);
|
|
2966
|
+
if (o > t + a || o < Math.abs(t - a) || o === 0)
|
|
2696
2967
|
return [];
|
|
2697
|
-
const
|
|
2968
|
+
const n = (t * t - a * a + o * o) / (2 * o), s = Math.sqrt(t * t - n * n), l = e.x + n * (r.x - e.x) / o, x = e.y + n * (r.y - e.y) / o;
|
|
2698
2969
|
return [
|
|
2699
|
-
{ x:
|
|
2700
|
-
{ x:
|
|
2970
|
+
{ x: l + s * (r.y - e.y) / o, y: x - s * (r.x - e.x) / o },
|
|
2971
|
+
{ x: l - s * (r.y - e.y) / o, y: x + s * (r.x - e.x) / o }
|
|
2701
2972
|
];
|
|
2702
2973
|
},
|
|
2703
2974
|
/** Perpendicular line through point */
|
|
2704
2975
|
perpendicularThrough(e, t, r) {
|
|
2705
2976
|
this.closestPointOnLine(e, t, r);
|
|
2706
|
-
const a = t.x - e.x,
|
|
2977
|
+
const a = t.x - e.x, o = t.y - e.y;
|
|
2707
2978
|
return {
|
|
2708
2979
|
p1: r,
|
|
2709
|
-
p2: { x: r.x -
|
|
2980
|
+
p2: { x: r.x - o, y: r.y + a }
|
|
2710
2981
|
};
|
|
2711
2982
|
},
|
|
2712
2983
|
/** Parallel line through point */
|
|
2713
2984
|
parallelThrough(e, t, r) {
|
|
2714
|
-
const a = t.x - e.x,
|
|
2985
|
+
const a = t.x - e.x, o = t.y - e.y;
|
|
2715
2986
|
return {
|
|
2716
2987
|
p1: r,
|
|
2717
|
-
p2: { x: r.x + a, y: r.y +
|
|
2988
|
+
p2: { x: r.x + a, y: r.y + o }
|
|
2718
2989
|
};
|
|
2719
2990
|
},
|
|
2720
2991
|
/** Angle bisector */
|
|
2721
2992
|
angleBisector(e, t, r) {
|
|
2722
|
-
const a = Math.atan2(e.y - t.y, e.x - t.x),
|
|
2723
|
-
let
|
|
2724
|
-
return Math.abs(
|
|
2725
|
-
x: t.x + Math.cos(
|
|
2726
|
-
y: t.y + Math.sin(
|
|
2993
|
+
const a = Math.atan2(e.y - t.y, e.x - t.x), o = Math.atan2(r.y - t.y, r.x - t.x);
|
|
2994
|
+
let n = (a + o) / 2;
|
|
2995
|
+
return Math.abs(o - a) > Math.PI && (n += Math.PI), {
|
|
2996
|
+
x: t.x + Math.cos(n),
|
|
2997
|
+
y: t.y + Math.sin(n)
|
|
2727
2998
|
};
|
|
2728
2999
|
},
|
|
2729
3000
|
/** Reflect point over line */
|
|
@@ -2736,10 +3007,10 @@ const GeoMath = {
|
|
|
2736
3007
|
},
|
|
2737
3008
|
/** Rotate point around center */
|
|
2738
3009
|
rotate(e, t, r) {
|
|
2739
|
-
const a = Math.cos(r),
|
|
3010
|
+
const a = Math.cos(r), o = Math.sin(r), n = e.x - t.x, s = e.y - t.y;
|
|
2740
3011
|
return {
|
|
2741
|
-
x: t.x +
|
|
2742
|
-
y: t.y +
|
|
3012
|
+
x: t.x + n * a - s * o,
|
|
3013
|
+
y: t.y + n * o + s * a
|
|
2743
3014
|
};
|
|
2744
3015
|
},
|
|
2745
3016
|
/** Translate point */
|
|
@@ -2758,8 +3029,8 @@ const GeoMath = {
|
|
|
2758
3029
|
let t = 0;
|
|
2759
3030
|
const r = e.length;
|
|
2760
3031
|
for (let a = 0; a < r; a++) {
|
|
2761
|
-
const
|
|
2762
|
-
t += e[a].x * e[
|
|
3032
|
+
const o = (a + 1) % r;
|
|
3033
|
+
t += e[a].x * e[o].y, t -= e[o].x * e[a].y;
|
|
2763
3034
|
}
|
|
2764
3035
|
return Math.abs(t) / 2;
|
|
2765
3036
|
},
|
|
@@ -2768,22 +3039,22 @@ const GeoMath = {
|
|
|
2768
3039
|
let t = 0;
|
|
2769
3040
|
const r = e.length;
|
|
2770
3041
|
for (let a = 0; a < r; a++) {
|
|
2771
|
-
const
|
|
2772
|
-
t += this.distance(e[a], e[
|
|
3042
|
+
const o = (a + 1) % r;
|
|
3043
|
+
t += this.distance(e[a], e[o]);
|
|
2773
3044
|
}
|
|
2774
3045
|
return t;
|
|
2775
3046
|
},
|
|
2776
3047
|
/** Triangle circumcenter */
|
|
2777
3048
|
circumcenter(e, t, r) {
|
|
2778
|
-
const a = 2 * (e.x * (t.y - r.y) + t.x * (r.y - e.y) + r.x * (e.y - t.y)),
|
|
2779
|
-
return { x:
|
|
3049
|
+
const a = 2 * (e.x * (t.y - r.y) + t.x * (r.y - e.y) + r.x * (e.y - t.y)), o = ((e.x ** 2 + e.y ** 2) * (t.y - r.y) + (t.x ** 2 + t.y ** 2) * (r.y - e.y) + (r.x ** 2 + r.y ** 2) * (e.y - t.y)) / a, n = ((e.x ** 2 + e.y ** 2) * (r.x - t.x) + (t.x ** 2 + t.y ** 2) * (e.x - r.x) + (r.x ** 2 + r.y ** 2) * (t.x - e.x)) / a;
|
|
3050
|
+
return { x: o, y: n };
|
|
2780
3051
|
},
|
|
2781
3052
|
/** Triangle incenter */
|
|
2782
3053
|
incenter(e, t, r) {
|
|
2783
|
-
const a = this.distance(e, t),
|
|
3054
|
+
const a = this.distance(e, t), o = this.distance(t, r), n = this.distance(r, e), s = a + o + n;
|
|
2784
3055
|
return {
|
|
2785
|
-
x: (
|
|
2786
|
-
y: (
|
|
3056
|
+
x: (o * e.x + n * t.x + a * r.x) / s,
|
|
3057
|
+
y: (o * e.y + n * t.y + a * r.y) / s
|
|
2787
3058
|
};
|
|
2788
3059
|
},
|
|
2789
3060
|
/** Triangle centroid */
|
|
@@ -2822,8 +3093,8 @@ class GeometryService {
|
|
|
2822
3093
|
deleteElement(t) {
|
|
2823
3094
|
var r;
|
|
2824
3095
|
this.elements.delete(t);
|
|
2825
|
-
for (const [a,
|
|
2826
|
-
(r =
|
|
3096
|
+
for (const [a, o] of this.elements)
|
|
3097
|
+
(r = o.dependencies) != null && r.includes(t) && this.deleteElement(a);
|
|
2827
3098
|
}
|
|
2828
3099
|
/** Get all elements */
|
|
2829
3100
|
getAllElements() {
|
|
@@ -2928,91 +3199,91 @@ class GeometryService {
|
|
|
2928
3199
|
}
|
|
2929
3200
|
/** Create midpoint */
|
|
2930
3201
|
createMidpoint(t, r) {
|
|
2931
|
-
const a = this.getPoint(t),
|
|
2932
|
-
if (!a || !
|
|
3202
|
+
const a = this.getPoint(t), o = this.getPoint(r);
|
|
3203
|
+
if (!a || !o)
|
|
2933
3204
|
return null;
|
|
2934
|
-
const
|
|
3205
|
+
const n = GeoMath.midpoint(a, o), s = this.createPoint(n.x, n.y);
|
|
2935
3206
|
return s.dependencies = [t, r], s.color = { r: 0, g: 150, b: 0 }, s;
|
|
2936
3207
|
}
|
|
2937
3208
|
/** Create perpendicular */
|
|
2938
3209
|
createPerpendicular(t, r, a) {
|
|
2939
|
-
const
|
|
2940
|
-
if (!
|
|
3210
|
+
const o = this.getPoint(t), n = this.getPoint(r), s = this.getPoint(a);
|
|
3211
|
+
if (!o || !n || !s)
|
|
2941
3212
|
return null;
|
|
2942
|
-
const
|
|
3213
|
+
const l = GeoMath.perpendicularThrough(o, n, s), x = this.createPoint(l.p2.x, l.p2.y), i = this.createLine(a, x.id);
|
|
2943
3214
|
return i.dependencies = [t, r, a], i;
|
|
2944
3215
|
}
|
|
2945
3216
|
/** Create parallel */
|
|
2946
3217
|
createParallel(t, r, a) {
|
|
2947
|
-
const
|
|
2948
|
-
if (!
|
|
3218
|
+
const o = this.getPoint(t), n = this.getPoint(r), s = this.getPoint(a);
|
|
3219
|
+
if (!o || !n || !s)
|
|
2949
3220
|
return null;
|
|
2950
|
-
const
|
|
3221
|
+
const l = GeoMath.parallelThrough(o, n, s), x = this.createPoint(l.p2.x, l.p2.y), i = this.createLine(a, x.id);
|
|
2951
3222
|
return i.dependencies = [t, r, a], i;
|
|
2952
3223
|
}
|
|
2953
3224
|
/** Create intersection point */
|
|
2954
3225
|
createIntersection(t, r) {
|
|
2955
|
-
const a = this.elements.get(t),
|
|
2956
|
-
if (!a || !
|
|
3226
|
+
const a = this.elements.get(t), o = this.elements.get(r);
|
|
3227
|
+
if (!a || !o)
|
|
2957
3228
|
return [];
|
|
2958
|
-
const
|
|
3229
|
+
const n = [], s = (i) => {
|
|
2959
3230
|
if (i.type === "line" || i.type === "segment" || i.type === "ray") {
|
|
2960
|
-
const d = i,
|
|
2961
|
-
if (
|
|
2962
|
-
return [
|
|
3231
|
+
const d = i, g = this.getPoint(d.point1Id ?? d.originId), y = this.getPoint(d.point2Id ?? d.throughId);
|
|
3232
|
+
if (g && y)
|
|
3233
|
+
return [g, y];
|
|
2963
3234
|
}
|
|
2964
3235
|
return null;
|
|
2965
|
-
},
|
|
2966
|
-
if (
|
|
2967
|
-
const i = GeoMath.lineIntersection(
|
|
3236
|
+
}, l = s(a), x = s(o);
|
|
3237
|
+
if (l && x) {
|
|
3238
|
+
const i = GeoMath.lineIntersection(l[0], l[1], x[0], x[1]);
|
|
2968
3239
|
if (i) {
|
|
2969
3240
|
const d = this.createPoint(i.x, i.y);
|
|
2970
|
-
d.dependencies = [t, r], d.color = { r: 255, g: 0, b: 0 },
|
|
3241
|
+
d.dependencies = [t, r], d.color = { r: 255, g: 0, b: 0 }, n.push(d);
|
|
2971
3242
|
}
|
|
2972
3243
|
}
|
|
2973
|
-
if (a.type === "circle" &&
|
|
3244
|
+
if (a.type === "circle" && x) {
|
|
2974
3245
|
const i = a, d = this.getPoint(i.centerId);
|
|
2975
3246
|
if (d) {
|
|
2976
|
-
const
|
|
2977
|
-
for (const
|
|
2978
|
-
const
|
|
2979
|
-
|
|
3247
|
+
const g = GeoMath.circleLineIntersection(d, i.radius, x[0], x[1]);
|
|
3248
|
+
for (const y of g) {
|
|
3249
|
+
const h = this.createPoint(y.x, y.y);
|
|
3250
|
+
h.dependencies = [t, r], h.color = { r: 255, g: 0, b: 0 }, n.push(h);
|
|
2980
3251
|
}
|
|
2981
3252
|
}
|
|
2982
3253
|
}
|
|
2983
|
-
if (a.type === "circle" &&
|
|
2984
|
-
const i = a, d =
|
|
2985
|
-
if (
|
|
2986
|
-
const
|
|
2987
|
-
for (const
|
|
2988
|
-
const
|
|
2989
|
-
|
|
3254
|
+
if (a.type === "circle" && o.type === "circle") {
|
|
3255
|
+
const i = a, d = o, g = this.getPoint(i.centerId), y = this.getPoint(d.centerId);
|
|
3256
|
+
if (g && y) {
|
|
3257
|
+
const h = GeoMath.circleCircleIntersection(g, i.radius, y, d.radius);
|
|
3258
|
+
for (const w of h) {
|
|
3259
|
+
const p = this.createPoint(w.x, w.y);
|
|
3260
|
+
p.dependencies = [t, r], p.color = { r: 255, g: 0, b: 0 }, n.push(p);
|
|
2990
3261
|
}
|
|
2991
3262
|
}
|
|
2992
3263
|
}
|
|
2993
|
-
return
|
|
3264
|
+
return n;
|
|
2994
3265
|
}
|
|
2995
3266
|
/** Calculate measurement */
|
|
2996
3267
|
calculateMeasurement(t, r) {
|
|
2997
3268
|
switch (t) {
|
|
2998
3269
|
case "distance": {
|
|
2999
|
-
const a = this.getPoint(r[0]),
|
|
3000
|
-
if (a &&
|
|
3001
|
-
return GeoMath.distance(a,
|
|
3270
|
+
const a = this.getPoint(r[0]), o = this.getPoint(r[1]);
|
|
3271
|
+
if (a && o)
|
|
3272
|
+
return GeoMath.distance(a, o);
|
|
3002
3273
|
break;
|
|
3003
3274
|
}
|
|
3004
3275
|
case "angle": {
|
|
3005
|
-
const a = this.getPoint(r[0]),
|
|
3006
|
-
if (a &&
|
|
3007
|
-
return GeoMath.angleDegrees(a,
|
|
3276
|
+
const a = this.getPoint(r[0]), o = this.getPoint(r[1]), n = this.getPoint(r[2]);
|
|
3277
|
+
if (a && o && n)
|
|
3278
|
+
return GeoMath.angleDegrees(a, o, n);
|
|
3008
3279
|
break;
|
|
3009
3280
|
}
|
|
3010
3281
|
case "area":
|
|
3011
3282
|
case "perimeter": {
|
|
3012
3283
|
const a = this.elements.get(r[0]);
|
|
3013
3284
|
if ((a == null ? void 0 : a.type) === "polygon") {
|
|
3014
|
-
const
|
|
3015
|
-
return t === "area" ? GeoMath.polygonArea(
|
|
3285
|
+
const o = a.pointIds.map((n) => this.getPoint(n)).filter(Boolean);
|
|
3286
|
+
return t === "area" ? GeoMath.polygonArea(o) : GeoMath.polygonPerimeter(o);
|
|
3016
3287
|
}
|
|
3017
3288
|
break;
|
|
3018
3289
|
}
|
|
@@ -3021,75 +3292,75 @@ class GeometryService {
|
|
|
3021
3292
|
}
|
|
3022
3293
|
/** Export to SVG */
|
|
3023
3294
|
exportSVG(t, r, a) {
|
|
3024
|
-
const
|
|
3295
|
+
const o = [
|
|
3025
3296
|
`<svg xmlns="http://www.w3.org/2000/svg" width="${t}" height="${r}" viewBox="0 0 ${t} ${r}">`
|
|
3026
3297
|
];
|
|
3027
|
-
|
|
3298
|
+
o.push(
|
|
3028
3299
|
`<rect width="100%" height="100%" fill="rgb(${a.backgroundColor.r},${a.backgroundColor.g},${a.backgroundColor.b})"/>`
|
|
3029
3300
|
);
|
|
3030
|
-
const
|
|
3301
|
+
const n = (s, l) => ({
|
|
3031
3302
|
x: t / 2 + (s - a.centerX) * a.scale,
|
|
3032
|
-
y: r / 2 - (
|
|
3303
|
+
y: r / 2 - (l - a.centerY) * a.scale
|
|
3033
3304
|
});
|
|
3034
3305
|
if (a.showGrid) {
|
|
3035
|
-
|
|
3306
|
+
o.push('<g stroke="var(--nice-border, #ddd)" stroke-width="0.5">');
|
|
3036
3307
|
for (let s = Math.floor(-t / 2 / a.scale + a.centerX); s <= t / 2 / a.scale + a.centerX; s += a.gridSpacing) {
|
|
3037
|
-
const
|
|
3038
|
-
|
|
3308
|
+
const l = n(s, 0);
|
|
3309
|
+
o.push(`<line x1="${l.x}" y1="0" x2="${l.x}" y2="${r}"/>`);
|
|
3039
3310
|
}
|
|
3040
3311
|
for (let s = Math.floor(-r / 2 / a.scale + a.centerY); s <= r / 2 / a.scale + a.centerY; s += a.gridSpacing) {
|
|
3041
|
-
const
|
|
3042
|
-
|
|
3312
|
+
const l = n(0, s);
|
|
3313
|
+
o.push(`<line x1="0" y1="${l.y}" x2="${t}" y2="${l.y}"/>`);
|
|
3043
3314
|
}
|
|
3044
|
-
|
|
3315
|
+
o.push("</g>");
|
|
3045
3316
|
}
|
|
3046
3317
|
if (a.showAxes) {
|
|
3047
|
-
const s =
|
|
3048
|
-
|
|
3318
|
+
const s = n(0, 0);
|
|
3319
|
+
o.push('<g stroke="var(--nice-text, #333)" stroke-width="1">'), o.push(`<line x1="0" y1="${s.y}" x2="${t}" y2="${s.y}"/>`), o.push(`<line x1="${s.x}" y1="0" x2="${s.x}" y2="${r}"/>`), o.push("</g>");
|
|
3049
3320
|
}
|
|
3050
3321
|
for (const s of this.getAllElements()) {
|
|
3051
3322
|
if (!s.visible)
|
|
3052
3323
|
continue;
|
|
3053
|
-
const
|
|
3324
|
+
const l = `rgb(${s.color.r},${s.color.g},${s.color.b})`;
|
|
3054
3325
|
switch (s.type) {
|
|
3055
3326
|
case "point": {
|
|
3056
|
-
const
|
|
3057
|
-
|
|
3327
|
+
const x = n(s.x, s.y);
|
|
3328
|
+
o.push(`<circle cx="${x.x}" cy="${x.y}" r="${s.size}" fill="${l}"/>`), s.label && o.push(`<text x="${x.x + 8}" y="${x.y - 8}" font-size="12">${s.label}</text>`);
|
|
3058
3329
|
break;
|
|
3059
3330
|
}
|
|
3060
3331
|
case "segment": {
|
|
3061
|
-
const
|
|
3062
|
-
if (
|
|
3063
|
-
const d =
|
|
3064
|
-
|
|
3065
|
-
`<line x1="${d.x}" y1="${d.y}" x2="${
|
|
3332
|
+
const x = this.getPoint(s.point1Id), i = this.getPoint(s.point2Id);
|
|
3333
|
+
if (x && i) {
|
|
3334
|
+
const d = n(x.x, x.y), g = n(i.x, i.y);
|
|
3335
|
+
o.push(
|
|
3336
|
+
`<line x1="${d.x}" y1="${d.y}" x2="${g.x}" y2="${g.y}" stroke="${l}" stroke-width="${s.width}"/>`
|
|
3066
3337
|
);
|
|
3067
3338
|
}
|
|
3068
3339
|
break;
|
|
3069
3340
|
}
|
|
3070
3341
|
case "circle": {
|
|
3071
|
-
const
|
|
3072
|
-
if (
|
|
3073
|
-
const i =
|
|
3074
|
-
|
|
3075
|
-
`<circle cx="${i.x}" cy="${i.y}" r="${d}" fill="${
|
|
3342
|
+
const x = this.getPoint(s.centerId);
|
|
3343
|
+
if (x) {
|
|
3344
|
+
const i = n(x.x, x.y), d = s.radius * a.scale, g = s.fill ? `rgba(${s.fill.r},${s.fill.g},${s.fill.b},${s.fill.a ?? 0.2})` : "none";
|
|
3345
|
+
o.push(
|
|
3346
|
+
`<circle cx="${i.x}" cy="${i.y}" r="${d}" fill="${g}" stroke="${l}" stroke-width="${s.width}"/>`
|
|
3076
3347
|
);
|
|
3077
3348
|
}
|
|
3078
3349
|
break;
|
|
3079
3350
|
}
|
|
3080
3351
|
case "polygon": {
|
|
3081
|
-
const
|
|
3082
|
-
if (
|
|
3083
|
-
const d =
|
|
3084
|
-
|
|
3085
|
-
`<path d="${d}" fill="${
|
|
3352
|
+
const x = s.pointIds.map((i) => this.getPoint(i)).filter(Boolean);
|
|
3353
|
+
if (x.length > 0) {
|
|
3354
|
+
const d = x.map((y) => n(y.x, y.y)).map((y, h) => `${h === 0 ? "M" : "L"}${y.x},${y.y}`).join(" ") + " Z", g = s.fill ? `rgba(${s.fill.r},${s.fill.g},${s.fill.b},${s.fill.a ?? 0.2})` : "none";
|
|
3355
|
+
o.push(
|
|
3356
|
+
`<path d="${d}" fill="${g}" stroke="${l}" stroke-width="${s.width}"/>`
|
|
3086
3357
|
);
|
|
3087
3358
|
}
|
|
3088
3359
|
break;
|
|
3089
3360
|
}
|
|
3090
3361
|
}
|
|
3091
3362
|
}
|
|
3092
|
-
return
|
|
3363
|
+
return o.push("</svg>"), o.join(`
|
|
3093
3364
|
`);
|
|
3094
3365
|
}
|
|
3095
3366
|
generateId() {
|
|
@@ -3164,9 +3435,9 @@ function NiceGeometry({
|
|
|
3164
3435
|
width: t = 800,
|
|
3165
3436
|
height: r = 600,
|
|
3166
3437
|
className: a,
|
|
3167
|
-
style:
|
|
3438
|
+
style: o
|
|
3168
3439
|
}) {
|
|
3169
|
-
const [
|
|
3440
|
+
const [n, s] = useState([]), [l, x] = useState("point"), [i, d] = useState([]), [g, y] = useState({
|
|
3170
3441
|
centerX: 0,
|
|
3171
3442
|
centerY: 0,
|
|
3172
3443
|
scale: 40,
|
|
@@ -3176,26 +3447,26 @@ function NiceGeometry({
|
|
|
3176
3447
|
snapToGrid: !0,
|
|
3177
3448
|
snapToPoints: !0,
|
|
3178
3449
|
backgroundColor: { r: 255, g: 255, b: 255 }
|
|
3179
|
-
}),
|
|
3450
|
+
}), h = useCallback(() => {
|
|
3180
3451
|
s(e.getAllElements());
|
|
3181
|
-
}, [e]),
|
|
3182
|
-
|
|
3452
|
+
}, [e]), w = useCallback((f) => {
|
|
3453
|
+
y((c) => ({ ...c, ...f }));
|
|
3183
3454
|
}, []);
|
|
3184
3455
|
useEffect(() => {
|
|
3185
|
-
|
|
3186
|
-
}, [
|
|
3187
|
-
const
|
|
3456
|
+
h();
|
|
3457
|
+
}, [h]);
|
|
3458
|
+
const p = {
|
|
3188
3459
|
service: e,
|
|
3189
|
-
elements:
|
|
3190
|
-
tool:
|
|
3191
|
-
setTool:
|
|
3192
|
-
view:
|
|
3193
|
-
setView:
|
|
3460
|
+
elements: n,
|
|
3461
|
+
tool: l,
|
|
3462
|
+
setTool: x,
|
|
3463
|
+
view: g,
|
|
3464
|
+
setView: w,
|
|
3194
3465
|
selectedIds: i,
|
|
3195
3466
|
setSelectedIds: d,
|
|
3196
|
-
refresh:
|
|
3467
|
+
refresh: h
|
|
3197
3468
|
};
|
|
3198
|
-
return /* @__PURE__ */ jsx(GeometryContext.Provider, { value:
|
|
3469
|
+
return /* @__PURE__ */ jsx(GeometryContext.Provider, { value: p, children: /* @__PURE__ */ jsxs("div", { className: a, style: { ...styles.container, ...o }, children: [
|
|
3199
3470
|
/* @__PURE__ */ jsx(Toolbar, {}),
|
|
3200
3471
|
/* @__PURE__ */ jsxs("div", { style: styles.main, children: [
|
|
3201
3472
|
/* @__PURE__ */ jsx(Canvas, { width: t, height: r }),
|
|
@@ -3237,186 +3508,186 @@ function Toolbar() {
|
|
|
3237
3508
|
)) });
|
|
3238
3509
|
}
|
|
3239
3510
|
function Canvas({ width: e, height: t }) {
|
|
3240
|
-
const r = useRef(null), { service: a, elements:
|
|
3241
|
-
(
|
|
3242
|
-
const
|
|
3511
|
+
const r = useRef(null), { service: a, elements: o, tool: n, view: s, selectedIds: l, setSelectedIds: x, refresh: i } = useGeometry(), [d, g] = useState([]), y = useCallback(
|
|
3512
|
+
(f, c) => {
|
|
3513
|
+
const u = (f - e / 2) / s.scale + s.centerX, b = -(c - t / 2) / s.scale + s.centerY;
|
|
3243
3514
|
return s.snapToGrid ? {
|
|
3244
|
-
x: Math.round(
|
|
3245
|
-
y: Math.round(
|
|
3246
|
-
} : { x, y:
|
|
3515
|
+
x: Math.round(u / s.gridSpacing) * s.gridSpacing,
|
|
3516
|
+
y: Math.round(b / s.gridSpacing) * s.gridSpacing
|
|
3517
|
+
} : { x: u, y: b };
|
|
3247
3518
|
},
|
|
3248
3519
|
[e, t, s]
|
|
3249
|
-
),
|
|
3250
|
-
(
|
|
3251
|
-
x: e / 2 + (
|
|
3252
|
-
y: t / 2 - (
|
|
3520
|
+
), h = useCallback(
|
|
3521
|
+
(f, c) => ({
|
|
3522
|
+
x: e / 2 + (f - s.centerX) * s.scale,
|
|
3523
|
+
y: t / 2 - (c - s.centerY) * s.scale
|
|
3253
3524
|
}),
|
|
3254
3525
|
[e, t, s]
|
|
3255
|
-
),
|
|
3256
|
-
(
|
|
3257
|
-
var
|
|
3258
|
-
const
|
|
3259
|
-
if (!
|
|
3526
|
+
), w = useCallback(
|
|
3527
|
+
(f) => {
|
|
3528
|
+
var b;
|
|
3529
|
+
const c = (b = r.current) == null ? void 0 : b.getBoundingClientRect();
|
|
3530
|
+
if (!c)
|
|
3260
3531
|
return;
|
|
3261
|
-
const
|
|
3262
|
-
switch (
|
|
3532
|
+
const u = y(f.clientX - c.left, f.clientY - c.top);
|
|
3533
|
+
switch (n) {
|
|
3263
3534
|
case "point": {
|
|
3264
|
-
a.createPoint(
|
|
3535
|
+
a.createPoint(u.x, u.y), i();
|
|
3265
3536
|
break;
|
|
3266
3537
|
}
|
|
3267
3538
|
case "segment":
|
|
3268
3539
|
case "line":
|
|
3269
3540
|
case "ray": {
|
|
3270
|
-
const k =
|
|
3541
|
+
const k = p(u);
|
|
3271
3542
|
if (k)
|
|
3272
|
-
|
|
3273
|
-
const v = [...
|
|
3274
|
-
return v.length === 2 ? (
|
|
3543
|
+
g((m) => {
|
|
3544
|
+
const v = [...m, k.id];
|
|
3545
|
+
return v.length === 2 ? (n === "segment" ? a.createSegment(v[0], v[1]) : n === "line" ? a.createLine(v[0], v[1]) : n === "ray" && a.createRay(v[0], v[1]), i(), []) : v;
|
|
3275
3546
|
});
|
|
3276
3547
|
else {
|
|
3277
|
-
const
|
|
3278
|
-
|
|
3279
|
-
const C = [...v,
|
|
3280
|
-
return C.length === 2 ? (
|
|
3548
|
+
const m = a.createPoint(u.x, u.y);
|
|
3549
|
+
g((v) => {
|
|
3550
|
+
const C = [...v, m.id];
|
|
3551
|
+
return C.length === 2 ? (n === "segment" ? a.createSegment(C[0], C[1]) : n === "line" ? a.createLine(C[0], C[1]) : n === "ray" && a.createRay(C[0], C[1]), i(), []) : C;
|
|
3281
3552
|
}), i();
|
|
3282
3553
|
}
|
|
3283
3554
|
break;
|
|
3284
3555
|
}
|
|
3285
3556
|
case "circle": {
|
|
3286
|
-
const k =
|
|
3557
|
+
const k = p(u);
|
|
3287
3558
|
if (k)
|
|
3288
|
-
|
|
3289
|
-
if (
|
|
3559
|
+
g((m) => {
|
|
3560
|
+
if (m.length === 0)
|
|
3290
3561
|
return [k.id];
|
|
3291
|
-
const v = a.getPoint(
|
|
3562
|
+
const v = a.getPoint(m[0]);
|
|
3292
3563
|
if (v) {
|
|
3293
|
-
const C = GeoMath.distance(v,
|
|
3294
|
-
a.createCircle(
|
|
3564
|
+
const C = GeoMath.distance(v, u);
|
|
3565
|
+
a.createCircle(m[0], C), i();
|
|
3295
3566
|
}
|
|
3296
3567
|
return [];
|
|
3297
3568
|
});
|
|
3298
3569
|
else if (d.length === 1) {
|
|
3299
|
-
const
|
|
3300
|
-
if (
|
|
3301
|
-
const v = GeoMath.distance(
|
|
3302
|
-
a.createCircle(d[0], v), i(),
|
|
3570
|
+
const m = a.getPoint(d[0]);
|
|
3571
|
+
if (m) {
|
|
3572
|
+
const v = GeoMath.distance(m, u);
|
|
3573
|
+
a.createCircle(d[0], v), i(), g([]);
|
|
3303
3574
|
}
|
|
3304
3575
|
} else {
|
|
3305
|
-
const
|
|
3306
|
-
|
|
3576
|
+
const m = a.createPoint(u.x, u.y);
|
|
3577
|
+
g([m.id]), i();
|
|
3307
3578
|
}
|
|
3308
3579
|
break;
|
|
3309
3580
|
}
|
|
3310
3581
|
case "midpoint": {
|
|
3311
|
-
const k =
|
|
3312
|
-
k &&
|
|
3313
|
-
const v = [...
|
|
3582
|
+
const k = p(u);
|
|
3583
|
+
k && g((m) => {
|
|
3584
|
+
const v = [...m, k.id];
|
|
3314
3585
|
return v.length === 2 ? (a.createMidpoint(v[0], v[1]), i(), []) : v;
|
|
3315
3586
|
});
|
|
3316
3587
|
break;
|
|
3317
3588
|
}
|
|
3318
3589
|
case "select": {
|
|
3319
|
-
const k =
|
|
3320
|
-
|
|
3590
|
+
const k = p(u);
|
|
3591
|
+
x(k ? [k.id] : []);
|
|
3321
3592
|
break;
|
|
3322
3593
|
}
|
|
3323
3594
|
}
|
|
3324
3595
|
},
|
|
3325
|
-
[
|
|
3326
|
-
),
|
|
3327
|
-
for (const
|
|
3328
|
-
if (
|
|
3329
|
-
return
|
|
3596
|
+
[n, a, i, y, d, x]
|
|
3597
|
+
), p = (f) => {
|
|
3598
|
+
for (const u of o)
|
|
3599
|
+
if (u.type === "point" && GeoMath.distance(u, f) < 0.5)
|
|
3600
|
+
return u;
|
|
3330
3601
|
};
|
|
3331
3602
|
return useEffect(() => {
|
|
3332
|
-
const
|
|
3333
|
-
if (!(!
|
|
3334
|
-
if (
|
|
3335
|
-
|
|
3336
|
-
const
|
|
3337
|
-
for (let
|
|
3338
|
-
|
|
3339
|
-
for (let
|
|
3340
|
-
|
|
3603
|
+
const f = r.current, c = f == null ? void 0 : f.getContext("2d");
|
|
3604
|
+
if (!(!f || !c)) {
|
|
3605
|
+
if (c.fillStyle = `rgb(${s.backgroundColor.r},${s.backgroundColor.g},${s.backgroundColor.b})`, c.fillRect(0, 0, e, t), s.showGrid) {
|
|
3606
|
+
c.strokeStyle = "var(--nice-border, #e0e0e0)", c.lineWidth = 0.5;
|
|
3607
|
+
const u = s.gridSpacing * s.scale, b = (e / 2 - s.centerX * s.scale) % u, k = (t / 2 + s.centerY * s.scale) % u;
|
|
3608
|
+
for (let m = b; m < e; m += u)
|
|
3609
|
+
c.beginPath(), c.moveTo(m, 0), c.lineTo(m, t), c.stroke();
|
|
3610
|
+
for (let m = k; m < t; m += u)
|
|
3611
|
+
c.beginPath(), c.moveTo(0, m), c.lineTo(e, m), c.stroke();
|
|
3341
3612
|
}
|
|
3342
3613
|
if (s.showAxes) {
|
|
3343
|
-
const
|
|
3344
|
-
|
|
3614
|
+
const u = h(0, 0);
|
|
3615
|
+
c.strokeStyle = "var(--nice-text, #333)", c.lineWidth = 1, c.beginPath(), c.moveTo(0, u.y), c.lineTo(e, u.y), c.stroke(), c.beginPath(), c.moveTo(u.x, 0), c.lineTo(u.x, t), c.stroke();
|
|
3345
3616
|
}
|
|
3346
|
-
for (const
|
|
3347
|
-
if (!
|
|
3617
|
+
for (const u of o) {
|
|
3618
|
+
if (!u.visible)
|
|
3348
3619
|
continue;
|
|
3349
|
-
const
|
|
3350
|
-
switch (
|
|
3620
|
+
const b = `rgb(${u.color.r},${u.color.g},${u.color.b})`, k = l.includes(u.id);
|
|
3621
|
+
switch (u.type) {
|
|
3351
3622
|
case "point": {
|
|
3352
|
-
const
|
|
3353
|
-
|
|
3623
|
+
const m = h(u.x, u.y);
|
|
3624
|
+
c.beginPath(), c.arc(m.x, m.y, k ? u.size + 2 : u.size, 0, 2 * Math.PI), c.fillStyle = k ? "var(--nice-primary-hover, #1976d2)" : b, c.fill(), u.label && (c.fillStyle = "var(--nice-text, #333)", c.font = "12px sans-serif", c.fillText(u.label, m.x + 8, m.y - 8));
|
|
3354
3625
|
break;
|
|
3355
3626
|
}
|
|
3356
3627
|
case "segment": {
|
|
3357
|
-
const
|
|
3358
|
-
if (
|
|
3359
|
-
const C =
|
|
3360
|
-
|
|
3628
|
+
const m = a.getPoint(u.point1Id), v = a.getPoint(u.point2Id);
|
|
3629
|
+
if (m && v) {
|
|
3630
|
+
const C = h(m.x, m.y), S = h(v.x, v.y);
|
|
3631
|
+
c.strokeStyle = b, c.lineWidth = u.width, c.beginPath(), c.moveTo(C.x, C.y), c.lineTo(S.x, S.y), c.stroke();
|
|
3361
3632
|
}
|
|
3362
3633
|
break;
|
|
3363
3634
|
}
|
|
3364
3635
|
case "line": {
|
|
3365
|
-
const
|
|
3366
|
-
if (
|
|
3367
|
-
const C = v.x -
|
|
3368
|
-
|
|
3636
|
+
const m = a.getPoint(u.point1Id), v = a.getPoint(u.point2Id);
|
|
3637
|
+
if (m && v) {
|
|
3638
|
+
const C = v.x - m.x, S = v.y - m.y, P = -1e3, $ = 1e3, j = h(m.x + P * C, m.y + P * S), B = h(m.x + $ * C, m.y + $ * S);
|
|
3639
|
+
c.strokeStyle = b, c.lineWidth = u.width, c.beginPath(), c.moveTo(j.x, j.y), c.lineTo(B.x, B.y), c.stroke();
|
|
3369
3640
|
}
|
|
3370
3641
|
break;
|
|
3371
3642
|
}
|
|
3372
3643
|
case "circle": {
|
|
3373
|
-
const
|
|
3374
|
-
if (
|
|
3375
|
-
const v =
|
|
3376
|
-
|
|
3644
|
+
const m = a.getPoint(u.centerId);
|
|
3645
|
+
if (m) {
|
|
3646
|
+
const v = h(m.x, m.y), C = u.radius * s.scale;
|
|
3647
|
+
c.beginPath(), c.arc(v.x, v.y, C, 0, 2 * Math.PI), u.fill && (c.fillStyle = `rgba(${u.fill.r},${u.fill.g},${u.fill.b},${u.fill.a ?? 0.2})`, c.fill()), c.strokeStyle = b, c.lineWidth = u.width, c.stroke();
|
|
3377
3648
|
}
|
|
3378
3649
|
break;
|
|
3379
3650
|
}
|
|
3380
3651
|
case "polygon": {
|
|
3381
|
-
const
|
|
3382
|
-
if (
|
|
3383
|
-
|
|
3384
|
-
const v =
|
|
3385
|
-
|
|
3386
|
-
for (let C = 1; C <
|
|
3387
|
-
const S =
|
|
3388
|
-
|
|
3652
|
+
const m = u.pointIds.map((v) => a.getPoint(v)).filter(Boolean);
|
|
3653
|
+
if (m.length > 0) {
|
|
3654
|
+
c.beginPath();
|
|
3655
|
+
const v = h(m[0].x, m[0].y);
|
|
3656
|
+
c.moveTo(v.x, v.y);
|
|
3657
|
+
for (let C = 1; C < m.length; C++) {
|
|
3658
|
+
const S = h(m[C].x, m[C].y);
|
|
3659
|
+
c.lineTo(S.x, S.y);
|
|
3389
3660
|
}
|
|
3390
|
-
|
|
3661
|
+
c.closePath(), u.fill && (c.fillStyle = `rgba(${u.fill.r},${u.fill.g},${u.fill.b},${u.fill.a ?? 0.2})`, c.fill()), c.strokeStyle = b, c.lineWidth = u.width, c.stroke();
|
|
3391
3662
|
}
|
|
3392
3663
|
break;
|
|
3393
3664
|
}
|
|
3394
3665
|
}
|
|
3395
3666
|
}
|
|
3396
3667
|
if (d.length > 0) {
|
|
3397
|
-
|
|
3398
|
-
for (const
|
|
3399
|
-
const
|
|
3400
|
-
if (
|
|
3401
|
-
const k = b
|
|
3402
|
-
|
|
3668
|
+
c.fillStyle = "rgba(25, 118, 210, 0.3)";
|
|
3669
|
+
for (const u of d) {
|
|
3670
|
+
const b = a.getPoint(u);
|
|
3671
|
+
if (b) {
|
|
3672
|
+
const k = h(b.x, b.y);
|
|
3673
|
+
c.beginPath(), c.arc(k.x, k.y, 12, 0, 2 * Math.PI), c.fill();
|
|
3403
3674
|
}
|
|
3404
3675
|
}
|
|
3405
3676
|
}
|
|
3406
3677
|
}
|
|
3407
|
-
}, [
|
|
3678
|
+
}, [o, s, e, t, l, d, a, h]), /* @__PURE__ */ jsx(
|
|
3408
3679
|
"canvas",
|
|
3409
3680
|
{
|
|
3410
3681
|
ref: r,
|
|
3411
3682
|
width: e,
|
|
3412
3683
|
height: t,
|
|
3413
3684
|
style: styles.canvas,
|
|
3414
|
-
onClick:
|
|
3685
|
+
onClick: w
|
|
3415
3686
|
}
|
|
3416
3687
|
);
|
|
3417
3688
|
}
|
|
3418
3689
|
function StatusBar({ width: e, height: t }) {
|
|
3419
|
-
const { tool: r, elements: a, view:
|
|
3690
|
+
const { tool: r, elements: a, view: o, service: n } = useGeometry(), [s, l] = useState(null), x = {
|
|
3420
3691
|
select: "Select",
|
|
3421
3692
|
point: "Point",
|
|
3422
3693
|
line: "Line",
|
|
@@ -3442,7 +3713,7 @@ function StatusBar({ width: e, height: t }) {
|
|
|
3442
3713
|
return /* @__PURE__ */ jsxs("div", { style: styles.statusBar, children: [
|
|
3443
3714
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
3444
3715
|
"Tool: ",
|
|
3445
|
-
|
|
3716
|
+
x[r]
|
|
3446
3717
|
] }),
|
|
3447
3718
|
/* @__PURE__ */ jsx("span", { style: { margin: "0 16px" }, children: "|" }),
|
|
3448
3719
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
@@ -3457,7 +3728,7 @@ function StatusBar({ width: e, height: t }) {
|
|
|
3457
3728
|
/* @__PURE__ */ jsx("span", { style: { margin: "0 16px" }, children: "|" }),
|
|
3458
3729
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
3459
3730
|
"Scale: ",
|
|
3460
|
-
|
|
3731
|
+
o.scale.toFixed(1),
|
|
3461
3732
|
"x"
|
|
3462
3733
|
] })
|
|
3463
3734
|
] });
|
|
@@ -3477,6 +3748,7 @@ export {
|
|
|
3477
3748
|
NiceCalculator,
|
|
3478
3749
|
NiceGeometry,
|
|
3479
3750
|
NiceGraphPlotter,
|
|
3751
|
+
NiceI18nProvider,
|
|
3480
3752
|
NiceMathEditor,
|
|
3481
3753
|
createCalculatorService,
|
|
3482
3754
|
createGeometryService,
|
|
@@ -3487,5 +3759,6 @@ export {
|
|
|
3487
3759
|
useCalculator,
|
|
3488
3760
|
useGeometry,
|
|
3489
3761
|
useGraph,
|
|
3490
|
-
useMathEditor
|
|
3762
|
+
useMathEditor,
|
|
3763
|
+
useNiceTranslation
|
|
3491
3764
|
};
|