@xom11/whiteboard 0.11.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/README.md +67 -0
  2. package/dist/{ExcalidrawWithMenus-EAVPOPJZ.mjs → ExcalidrawWithMenus-KBLDWPM2.mjs} +2 -3
  3. package/dist/ExcalidrawWithMenus-KBLDWPM2.mjs.map +1 -0
  4. package/dist/catalog.json +57 -0
  5. package/dist/{chunk-PWIMZIB6.mjs → chunk-2SKXRBGS.mjs} +7 -8
  6. package/dist/chunk-2SKXRBGS.mjs.map +1 -0
  7. package/dist/chunk-33PEN2WC.mjs +57 -0
  8. package/dist/chunk-33PEN2WC.mjs.map +1 -0
  9. package/dist/chunk-3KBL77M6.mjs +127 -0
  10. package/dist/chunk-3KBL77M6.mjs.map +1 -0
  11. package/dist/chunk-5UTGXHLJ.mjs +57 -0
  12. package/dist/chunk-5UTGXHLJ.mjs.map +1 -0
  13. package/dist/chunk-6XUPIGVD.mjs +467 -0
  14. package/dist/chunk-6XUPIGVD.mjs.map +1 -0
  15. package/dist/chunk-7WG2KDRF.mjs +28 -0
  16. package/dist/chunk-7WG2KDRF.mjs.map +1 -0
  17. package/dist/chunk-FZY33J6Z.mjs +95 -0
  18. package/dist/chunk-FZY33J6Z.mjs.map +1 -0
  19. package/dist/chunk-HNQLZIEP.mjs +78 -0
  20. package/dist/chunk-HNQLZIEP.mjs.map +1 -0
  21. package/dist/chunk-NVJ7K3DK.mjs +29 -0
  22. package/dist/chunk-NVJ7K3DK.mjs.map +1 -0
  23. package/dist/chunk-O4WIZFRQ.mjs +11 -0
  24. package/dist/chunk-O4WIZFRQ.mjs.map +1 -0
  25. package/dist/{chunk-YVJP7NRG.mjs → chunk-O6QTYAKE.mjs} +7 -9
  26. package/dist/chunk-O6QTYAKE.mjs.map +1 -0
  27. package/dist/chunk-R5FL6S7L.mjs +22 -0
  28. package/dist/chunk-R5FL6S7L.mjs.map +1 -0
  29. package/dist/chunk-RBUILBX3.mjs +388 -0
  30. package/dist/chunk-RBUILBX3.mjs.map +1 -0
  31. package/dist/chunk-RD34F5PM.mjs +57 -0
  32. package/dist/chunk-RD34F5PM.mjs.map +1 -0
  33. package/dist/{chunk-7P7SQFOW.mjs → chunk-RXOFO64U.mjs} +3 -3
  34. package/dist/chunk-RXOFO64U.mjs.map +1 -0
  35. package/dist/chunk-TOOHCAWP.mjs +1167 -0
  36. package/dist/chunk-TOOHCAWP.mjs.map +1 -0
  37. package/dist/{chunk-C6SCVOMC.mjs → chunk-TQYQVXNW.mjs} +5 -41
  38. package/dist/chunk-TQYQVXNW.mjs.map +1 -0
  39. package/dist/chunk-VBJLUHCY.mjs +23 -0
  40. package/dist/chunk-VBJLUHCY.mjs.map +1 -0
  41. package/dist/chunk-VRWZILTG.mjs +205 -0
  42. package/dist/chunk-VRWZILTG.mjs.map +1 -0
  43. package/dist/chunk-XVSO7FBM.mjs +61 -0
  44. package/dist/chunk-XVSO7FBM.mjs.map +1 -0
  45. package/dist/geometry-2d.d.mts +3 -6
  46. package/dist/geometry-2d.d.ts +3 -6
  47. package/dist/geometry-2d.js +5069 -2651
  48. package/dist/geometry-2d.js.map +1 -1
  49. package/dist/geometry-2d.mjs +8 -4
  50. package/dist/geometry-3d.d.mts +4 -7
  51. package/dist/geometry-3d.d.ts +4 -7
  52. package/dist/geometry-3d.js +3053 -2150
  53. package/dist/geometry-3d.js.map +1 -1
  54. package/dist/geometry-3d.mjs +7 -4
  55. package/dist/graph-2d.d.mts +4 -7
  56. package/dist/graph-2d.d.ts +4 -7
  57. package/dist/graph-2d.js +3363 -1670
  58. package/dist/graph-2d.js.map +1 -1
  59. package/dist/graph-2d.mjs +10 -3
  60. package/dist/host-3N4E4KJH.mjs +1142 -0
  61. package/dist/host-3N4E4KJH.mjs.map +1 -0
  62. package/dist/{host-Z3TEJKZA.mjs → host-6SNSZ332.mjs} +4 -4
  63. package/dist/{host-Z3TEJKZA.mjs.map → host-6SNSZ332.mjs.map} +1 -1
  64. package/dist/host-EVJT3LIF.mjs +3198 -0
  65. package/dist/host-EVJT3LIF.mjs.map +1 -0
  66. package/dist/host-HN4X3TBC.mjs +2374 -0
  67. package/dist/host-HN4X3TBC.mjs.map +1 -0
  68. package/dist/index.css +4 -1
  69. package/dist/index.css.map +1 -1
  70. package/dist/index.d.mts +659 -19
  71. package/dist/index.d.ts +659 -19
  72. package/dist/index.js +11741 -9420
  73. package/dist/index.js.map +1 -1
  74. package/dist/index.mjs +1467 -336
  75. package/dist/index.mjs.map +1 -1
  76. package/dist/latex.d.mts +3 -4
  77. package/dist/latex.d.ts +3 -4
  78. package/dist/latex.js +33 -18
  79. package/dist/latex.js.map +1 -1
  80. package/dist/latex.mjs +2 -3
  81. package/dist/render-OCVGDKK6.mjs +8 -0
  82. package/dist/render-OCVGDKK6.mjs.map +1 -0
  83. package/dist/serialize-GKN6OVPM.mjs +6 -0
  84. package/dist/serialize-GKN6OVPM.mjs.map +1 -0
  85. package/dist/{types-CinstD7T.d.mts → types-rA4slL08.d.mts} +69 -4
  86. package/dist/{types-CinstD7T.d.ts → types-rA4slL08.d.ts} +69 -4
  87. package/package.json +24 -5
  88. package/dist/ExcalidrawWithMenus-EAVPOPJZ.mjs.map +0 -1
  89. package/dist/chunk-74VEEZBV.mjs +0 -619
  90. package/dist/chunk-74VEEZBV.mjs.map +0 -1
  91. package/dist/chunk-7P7SQFOW.mjs.map +0 -1
  92. package/dist/chunk-BJTO5JO5.mjs +0 -11
  93. package/dist/chunk-BJTO5JO5.mjs.map +0 -1
  94. package/dist/chunk-C6SCVOMC.mjs.map +0 -1
  95. package/dist/chunk-D257NCQW.mjs +0 -58
  96. package/dist/chunk-D257NCQW.mjs.map +0 -1
  97. package/dist/chunk-G7FR3AIV.mjs +0 -193
  98. package/dist/chunk-G7FR3AIV.mjs.map +0 -1
  99. package/dist/chunk-HTBLO5JO.mjs +0 -41
  100. package/dist/chunk-HTBLO5JO.mjs.map +0 -1
  101. package/dist/chunk-PWIMZIB6.mjs.map +0 -1
  102. package/dist/chunk-SBDMF4NQ.mjs +0 -212
  103. package/dist/chunk-SBDMF4NQ.mjs.map +0 -1
  104. package/dist/chunk-WQOABS6N.mjs +0 -197
  105. package/dist/chunk-WQOABS6N.mjs.map +0 -1
  106. package/dist/chunk-YVJP7NRG.mjs.map +0 -1
  107. package/dist/host-N6ACNJKI.mjs +0 -3226
  108. package/dist/host-N6ACNJKI.mjs.map +0 -1
  109. package/dist/host-NKGV6RF2.mjs +0 -1134
  110. package/dist/host-NKGV6RF2.mjs.map +0 -1
  111. package/dist/host-XVK7UCRE.mjs +0 -2908
  112. package/dist/host-XVK7UCRE.mjs.map +0 -1
@@ -1,619 +0,0 @@
1
- "use client";
2
- // src/stamps/graph-2d/serialize.ts
3
- var EMPTY_GRAPH = {
4
- version: 1,
5
- view: { xMin: -10, xMax: 10, yMin: -10, yMax: 10, showAxis: true, showGrid: true },
6
- functions: [],
7
- parameters: [],
8
- points: [],
9
- intersections: [],
10
- tangents: []
11
- };
12
- function stringifySerializedGraph(graph) {
13
- return JSON.stringify(graph);
14
- }
15
- function parseSerializedGraph(jsonState) {
16
- let raw;
17
- try {
18
- raw = JSON.parse(jsonState);
19
- } catch {
20
- return null;
21
- }
22
- if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
23
- const r = raw;
24
- if (r.version !== 1) return null;
25
- if (!r.view || typeof r.view !== "object") return null;
26
- const v = r.view;
27
- if (typeof v.xMin !== "number" || typeof v.xMax !== "number" || typeof v.yMin !== "number" || typeof v.yMax !== "number" || typeof v.showAxis !== "boolean" || typeof v.showGrid !== "boolean") {
28
- return null;
29
- }
30
- for (const key of ["functions", "parameters", "points", "intersections", "tangents"]) {
31
- if (!Array.isArray(r[key])) return null;
32
- }
33
- return raw;
34
- }
35
-
36
- // src/stamps/graph-2d/evaluator.ts
37
- var ALLOWED_FUNCTIONS = {
38
- sin: Math.sin,
39
- cos: Math.cos,
40
- tan: Math.tan,
41
- asin: Math.asin,
42
- acos: Math.acos,
43
- atan: Math.atan,
44
- log: Math.log10,
45
- // log = log10 (khớp với rewriteToJs)
46
- ln: Math.log,
47
- // ln = log tự nhiên
48
- exp: Math.exp,
49
- sqrt: Math.sqrt,
50
- abs: Math.abs,
51
- floor: Math.floor,
52
- ceil: Math.ceil,
53
- round: Math.round
54
- };
55
- var ALLOWED_CONSTANTS = {
56
- pi: Math.PI,
57
- e: Math.E
58
- };
59
- var OPERATORS = /* @__PURE__ */ new Set(["+", "-", "*", "/", "^"]);
60
- function tokenize(src) {
61
- const tokens = [];
62
- let i = 0;
63
- while (i < src.length) {
64
- const ch = src[i];
65
- if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
66
- i++;
67
- continue;
68
- }
69
- if (ch >= "0" && ch <= "9" || ch === ".") {
70
- let j = i;
71
- let hasDot = false;
72
- let hasExp = false;
73
- while (j < src.length) {
74
- const c = src[j];
75
- if (c >= "0" && c <= "9") {
76
- j++;
77
- } else if (c === "." && !hasDot && !hasExp) {
78
- hasDot = true;
79
- j++;
80
- } else if ((c === "e" || c === "E") && !hasExp) {
81
- hasExp = true;
82
- j++;
83
- if (src[j] === "+" || src[j] === "-") j++;
84
- } else {
85
- break;
86
- }
87
- }
88
- const raw = src.slice(i, j);
89
- if (!/[0-9]/.test(raw)) {
90
- throw new Error(`S\u1ED1 kh\xF4ng h\u1EE3p l\u1EC7 t\u1EA1i v\u1ECB tr\xED ${i}: "${raw}"`);
91
- }
92
- tokens.push({ type: "NUMBER", value: raw, pos: i });
93
- i = j;
94
- continue;
95
- }
96
- if (ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z") {
97
- let j = i;
98
- while (j < src.length) {
99
- const c = src[j];
100
- if (c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c >= "0" && c <= "9" || c === "_") {
101
- j++;
102
- } else {
103
- break;
104
- }
105
- }
106
- tokens.push({ type: "IDENT", value: src.slice(i, j), pos: i });
107
- i = j;
108
- continue;
109
- }
110
- if (OPERATORS.has(ch)) {
111
- tokens.push({ type: "OP", value: ch, pos: i });
112
- i++;
113
- continue;
114
- }
115
- if (ch === "(") {
116
- tokens.push({ type: "LPAREN", value: ch, pos: i });
117
- i++;
118
- continue;
119
- }
120
- if (ch === ")") {
121
- tokens.push({ type: "RPAREN", value: ch, pos: i });
122
- i++;
123
- continue;
124
- }
125
- if (ch === ",") {
126
- tokens.push({ type: "COMMA", value: ch, pos: i });
127
- i++;
128
- continue;
129
- }
130
- throw new Error(`K\xFD t\u1EF1 kh\xF4ng h\u1EE3p l\u1EC7 t\u1EA1i v\u1ECB tr\xED ${i}: "${ch}"`);
131
- }
132
- return tokens;
133
- }
134
- var Parser = class {
135
- constructor(tokens) {
136
- this.tokens = tokens;
137
- this.pos = 0;
138
- }
139
- peek() {
140
- return this.tokens[this.pos];
141
- }
142
- consume() {
143
- const t = this.tokens[this.pos++];
144
- if (!t) throw new Error("C\xFA ph\xE1p: h\u1EBFt token s\u1EDBm");
145
- return t;
146
- }
147
- parseExpression() {
148
- const node = this.parseAddSub();
149
- if (this.pos < this.tokens.length) {
150
- const t = this.tokens[this.pos];
151
- throw new Error(`C\xFA ph\xE1p: token th\u1EEBa "${t.value}" t\u1EA1i v\u1ECB tr\xED ${t.pos}`);
152
- }
153
- return node;
154
- }
155
- // + - (left assoc)
156
- parseAddSub() {
157
- let lhs = this.parseMulDiv();
158
- while (true) {
159
- const t = this.peek();
160
- if (t && t.type === "OP" && (t.value === "+" || t.value === "-")) {
161
- this.consume();
162
- const rhs = this.parseMulDiv();
163
- lhs = { kind: "binary", op: t.value, lhs, rhs };
164
- } else {
165
- break;
166
- }
167
- }
168
- return lhs;
169
- }
170
- // * / (left assoc)
171
- parseMulDiv() {
172
- let lhs = this.parseUnary();
173
- while (true) {
174
- const t = this.peek();
175
- if (t && t.type === "OP" && (t.value === "*" || t.value === "/")) {
176
- this.consume();
177
- const rhs = this.parseUnary();
178
- lhs = { kind: "binary", op: t.value, lhs, rhs };
179
- } else {
180
- break;
181
- }
182
- }
183
- return lhs;
184
- }
185
- // unary + - (right assoc) sau đó parsePow
186
- parseUnary() {
187
- const t = this.peek();
188
- if (t && t.type === "OP" && (t.value === "+" || t.value === "-")) {
189
- this.consume();
190
- const arg = this.parseUnary();
191
- return { kind: "unary", op: t.value, arg };
192
- }
193
- return this.parsePow();
194
- }
195
- // ^ (right assoc)
196
- parsePow() {
197
- const lhs = this.parsePrimary();
198
- const t = this.peek();
199
- if (t && t.type === "OP" && t.value === "^") {
200
- this.consume();
201
- const rhs = this.parseUnary();
202
- return { kind: "binary", op: "^", lhs, rhs };
203
- }
204
- return lhs;
205
- }
206
- parsePrimary() {
207
- const t = this.peek();
208
- if (!t) throw new Error("C\xFA ph\xE1p: thi\u1EBFu bi\u1EC3u th\u1EE9c");
209
- if (t.type === "NUMBER") {
210
- this.consume();
211
- const v = Number(t.value);
212
- return { kind: "num", value: v };
213
- }
214
- if (t.type === "IDENT") {
215
- this.consume();
216
- const next = this.peek();
217
- if (next && next.type === "LPAREN") {
218
- this.consume();
219
- const args = [];
220
- const lookahead = this.peek();
221
- if (!lookahead || lookahead.type !== "RPAREN") {
222
- args.push(this.parseAddSub());
223
- while (true) {
224
- const nx = this.peek();
225
- if (nx && nx.type === "COMMA") {
226
- this.consume();
227
- args.push(this.parseAddSub());
228
- } else {
229
- break;
230
- }
231
- }
232
- }
233
- const close = this.peek();
234
- if (!close || close.type !== "RPAREN") {
235
- throw new Error(`C\xFA ph\xE1p: thi\u1EBFu ")" sau h\xE0m "${t.value}"`);
236
- }
237
- this.consume();
238
- return { kind: "call", name: t.value, args };
239
- }
240
- return { kind: "ident", name: t.value };
241
- }
242
- if (t.type === "LPAREN") {
243
- this.consume();
244
- const inner = this.parseAddSub();
245
- const close = this.peek();
246
- if (!close || close.type !== "RPAREN") {
247
- throw new Error('C\xFA ph\xE1p: thi\u1EBFu ")"');
248
- }
249
- this.consume();
250
- return inner;
251
- }
252
- throw new Error(`C\xFA ph\xE1p: token b\u1EA5t ng\u1EDD "${t.value}" t\u1EA1i v\u1ECB tr\xED ${t.pos}`);
253
- }
254
- };
255
- function parseAst(src) {
256
- const tokens = tokenize(src);
257
- if (tokens.length === 0) throw new Error("Bi\u1EC3u th\u1EE9c r\u1ED7ng");
258
- const p = new Parser(tokens);
259
- return p.parseExpression();
260
- }
261
- function evaluate(node, env) {
262
- switch (node.kind) {
263
- case "num":
264
- return node.value;
265
- case "ident": {
266
- const name = node.name;
267
- if (name === "x") return env.x;
268
- if (Object.prototype.hasOwnProperty.call(ALLOWED_CONSTANTS, name)) {
269
- return ALLOWED_CONSTANTS[name];
270
- }
271
- if (name.length === 1 && Object.prototype.hasOwnProperty.call(env.params, name)) {
272
- return env.params[name];
273
- }
274
- throw new Error(`Identifier kh\xF4ng h\u1EE3p l\u1EC7: "${name}"`);
275
- }
276
- case "unary": {
277
- const v = evaluate(node.arg, env);
278
- return node.op === "-" ? -v : +v;
279
- }
280
- case "binary": {
281
- const a = evaluate(node.lhs, env);
282
- const b = evaluate(node.rhs, env);
283
- switch (node.op) {
284
- case "+":
285
- return a + b;
286
- case "-":
287
- return a - b;
288
- case "*":
289
- return a * b;
290
- case "/":
291
- return a / b;
292
- // có thể trả Infinity/NaN — đúng theo IEEE 754
293
- case "^":
294
- return Math.pow(a, b);
295
- }
296
- throw new Error(`To\xE1n t\u1EED kh\xF4ng h\u1ED7 tr\u1EE3: "${node.op}"`);
297
- }
298
- case "call": {
299
- const fn = ALLOWED_FUNCTIONS[node.name];
300
- if (typeof fn !== "function") {
301
- throw new Error(`H\xE0m kh\xF4ng h\u1EE3p l\u1EC7: "${node.name}"`);
302
- }
303
- const args = node.args.map((a) => evaluate(a, env));
304
- return fn(...args);
305
- }
306
- }
307
- }
308
- function collectFreeVars(node, out = /* @__PURE__ */ new Set()) {
309
- switch (node.kind) {
310
- case "num":
311
- return out;
312
- case "ident": {
313
- const name = node.name;
314
- if (name === "x") return out;
315
- if (Object.prototype.hasOwnProperty.call(ALLOWED_CONSTANTS, name)) return out;
316
- if (name.length === 1) out.add(name);
317
- return out;
318
- }
319
- case "unary":
320
- return collectFreeVars(node.arg, out);
321
- case "binary":
322
- collectFreeVars(node.lhs, out);
323
- collectFreeVars(node.rhs, out);
324
- return out;
325
- case "call":
326
- for (const a of node.args) collectFreeVars(a, out);
327
- return out;
328
- }
329
- }
330
- function checkIdentifiers(node) {
331
- switch (node.kind) {
332
- case "num":
333
- return null;
334
- case "ident": {
335
- const name = node.name;
336
- if (name === "x") return null;
337
- if (Object.prototype.hasOwnProperty.call(ALLOWED_CONSTANTS, name)) return null;
338
- if (name.length === 1) return null;
339
- return `T\xEAn kh\xF4ng h\u1EE3p l\u1EC7: "${name}"`;
340
- }
341
- case "unary":
342
- return checkIdentifiers(node.arg);
343
- case "binary":
344
- return checkIdentifiers(node.lhs) ?? checkIdentifiers(node.rhs);
345
- case "call": {
346
- if (!Object.prototype.hasOwnProperty.call(ALLOWED_FUNCTIONS, node.name)) {
347
- return `T\xEAn h\xE0m kh\xF4ng h\u1EE3p l\u1EC7: "${node.name}"`;
348
- }
349
- for (const a of node.args) {
350
- const e = checkIdentifiers(a);
351
- if (e) return e;
352
- }
353
- return null;
354
- }
355
- }
356
- }
357
- var ALLOWED_FUNCTION_NAMES = new Set(Object.keys(ALLOWED_FUNCTIONS));
358
- new Set(Object.keys(ALLOWED_CONSTANTS));
359
-
360
- // src/stamps/graph-2d/parser.ts
361
- var ALLOWED_FUNCTIONS2 = ALLOWED_FUNCTION_NAMES;
362
- var ALLOWED_CHARS = /^[a-zA-Z0-9_.+\-*/^()\s,]+$/;
363
- var SUGGESTIONS = {
364
- tg: "tan",
365
- arcsin: "asin",
366
- arccos: "acos",
367
- arctan: "atan"
368
- };
369
- function errResult(message) {
370
- return { ok: false, error: message, freeVars: /* @__PURE__ */ new Set() };
371
- }
372
- function validate(expr) {
373
- const trimmed = expr.trim();
374
- if (!trimmed) return errResult("Bi\u1EC3u th\u1EE9c r\u1ED7ng");
375
- if (!ALLOWED_CHARS.test(trimmed)) return errResult("K\xFD t\u1EF1 kh\xF4ng h\u1EE3p l\u1EC7");
376
- let tokens;
377
- try {
378
- tokens = tokenize(trimmed);
379
- } catch {
380
- return errResult("L\u1ED7i c\xFA ph\xE1p");
381
- }
382
- const earlyFree = /* @__PURE__ */ new Set();
383
- for (const tok of tokens) {
384
- if (tok.type !== "IDENT") continue;
385
- const id = tok.value;
386
- if (id === "x" || id === "pi" || id === "e") continue;
387
- if (ALLOWED_FUNCTIONS2.has(id)) continue;
388
- if (id.length === 1) {
389
- earlyFree.add(id);
390
- continue;
391
- }
392
- const hint = SUGGESTIONS[id];
393
- return errResult(
394
- hint ? `T\xEAn h\xE0m kh\xF4ng h\u1EE3p l\u1EC7: "${id}". B\u1EA1n c\xF3 \xFD l\xE0 "${hint}" kh\xF4ng?` : `T\xEAn kh\xF4ng h\u1EE3p l\u1EC7: "${id}"`
395
- );
396
- }
397
- let ast;
398
- try {
399
- ast = parseAst(trimmed);
400
- } catch {
401
- return errResult("L\u1ED7i c\xFA ph\xE1p");
402
- }
403
- const idErr = checkIdentifiers(ast);
404
- if (idErr) return errResult(idErr);
405
- const freeVars = collectFreeVars(ast);
406
- for (const v of earlyFree) freeVars.add(v);
407
- return { ok: true, freeVars };
408
- }
409
- function compile(expr, paramValues) {
410
- const v = validate(expr);
411
- if (!v.ok) return { error: v.error ?? "Invalid" };
412
- let ast;
413
- try {
414
- ast = parseAst(expr.trim());
415
- } catch (err) {
416
- return { error: err instanceof Error ? err.message : String(err) };
417
- }
418
- return (x) => {
419
- try {
420
- const y = evaluate(ast, { x, params: paramValues });
421
- return typeof y === "number" ? y : NaN;
422
- } catch {
423
- return NaN;
424
- }
425
- };
426
- }
427
-
428
- // src/stamps/graph-2d/editor/handlers.ts
429
- function addPointOnCurve(graph, ctx, idFactory) {
430
- if (!ctx.functionId) return graph;
431
- const point = {
432
- id: idFactory(),
433
- functionId: ctx.functionId,
434
- x: ctx.x
435
- };
436
- return { ...graph, points: [...graph.points, point] };
437
- }
438
- function addIntersection(graph, functionIdA, functionIdB, idFactory) {
439
- if (functionIdA === functionIdB) return graph;
440
- const exists = graph.intersections.some(
441
- (i) => i.functionIdA === functionIdA && i.functionIdB === functionIdB || i.functionIdA === functionIdB && i.functionIdB === functionIdA
442
- );
443
- if (exists) return graph;
444
- const intersection = {
445
- id: idFactory(),
446
- functionIdA,
447
- functionIdB
448
- };
449
- return { ...graph, intersections: [...graph.intersections, intersection] };
450
- }
451
- function numericalDerivative(expression, paramValues, x, h = 1e-4) {
452
- const fn = compile(expression, paramValues);
453
- if (typeof fn !== "function") return NaN;
454
- const y1 = fn(x - h);
455
- const y2 = fn(x + h);
456
- return (y2 - y1) / (2 * h);
457
- }
458
-
459
- // src/stamps/graph-2d/renderObjects.ts
460
- function renderGraphObjects(board, graph) {
461
- const paramMap = {};
462
- for (const p of graph.parameters) paramMap[p.name] = p.value;
463
- for (const f of graph.functions) {
464
- if (!f.visible) continue;
465
- const compiled = compile(f.expression, paramMap);
466
- if (typeof compiled !== "function") continue;
467
- const domain = f.domain ?? { min: graph.view.xMin, max: graph.view.xMax };
468
- board.create("functiongraph", [compiled, domain.min, domain.max], {
469
- strokeColor: f.color,
470
- strokeWidth: 2,
471
- name: f.name,
472
- withLabel: false,
473
- highlight: false
474
- });
475
- }
476
- for (const point of graph.points) {
477
- const fn = graph.functions.find((f) => f.id === point.functionId);
478
- if (!fn || !fn.visible) continue;
479
- const compiled = compile(fn.expression, paramMap);
480
- if (typeof compiled !== "function") continue;
481
- const y = compiled(point.x);
482
- board.create("point", [point.x, y], {
483
- name: point.label ?? "",
484
- size: 3,
485
- fillColor: fn.color,
486
- strokeColor: fn.color,
487
- withLabel: !!point.label
488
- });
489
- }
490
- for (const inter of graph.intersections) {
491
- const fa = graph.functions.find((f) => f.id === inter.functionIdA);
492
- const fb = graph.functions.find((f) => f.id === inter.functionIdB);
493
- if (!fa || !fb || !fa.visible || !fb.visible) continue;
494
- const cfa = compile(fa.expression, paramMap);
495
- const cfb = compile(fb.expression, paramMap);
496
- if (typeof cfa !== "function" || typeof cfb !== "function") continue;
497
- const roots = scanRoots((x) => cfa(x) - cfb(x), graph.view.xMin, graph.view.xMax);
498
- for (const x of roots) {
499
- board.create("point", [x, cfa(x)], {
500
- size: 3,
501
- fillColor: "#000",
502
- strokeColor: "#000"
503
- });
504
- }
505
- }
506
- for (const tan of graph.tangents) {
507
- const pt = graph.points.find((p) => p.id === tan.pointId);
508
- if (!pt) continue;
509
- const fn = graph.functions.find((f) => f.id === pt.functionId);
510
- if (!fn || !fn.visible) continue;
511
- const slope = numericalDerivative(fn.expression, paramMap, pt.x);
512
- const cfn = compile(fn.expression, paramMap);
513
- if (typeof cfn !== "function" || !Number.isFinite(slope)) continue;
514
- const y0 = cfn(pt.x);
515
- const x1 = graph.view.xMin;
516
- const x2 = graph.view.xMax;
517
- board.create(
518
- "line",
519
- [
520
- [x1, slope * (x1 - pt.x) + y0],
521
- [x2, slope * (x2 - pt.x) + y0]
522
- ],
523
- {
524
- strokeColor: fn.color,
525
- strokeWidth: 1,
526
- dash: 2,
527
- straightFirst: false,
528
- straightLast: false
529
- }
530
- );
531
- }
532
- }
533
- function scanRoots(fn, xMin, xMax, samples = 200) {
534
- const roots = [];
535
- const step = (xMax - xMin) / samples;
536
- let prevX = xMin;
537
- let prevY = fn(prevX);
538
- for (let i = 1; i <= samples; i++) {
539
- const x = xMin + i * step;
540
- const y = fn(x);
541
- if (Number.isFinite(prevY) && Number.isFinite(y) && prevY * y < 0) {
542
- let a = prevX;
543
- let b = x;
544
- let ya = prevY;
545
- for (let j = 0; j < 30; j++) {
546
- const m = (a + b) / 2;
547
- const ym = fn(m);
548
- if (Math.abs(ym) < 1e-6) {
549
- a = b = m;
550
- break;
551
- }
552
- if (ya * ym < 0) {
553
- b = m;
554
- } else {
555
- a = m;
556
- ya = ym;
557
- }
558
- }
559
- roots.push((a + b) / 2);
560
- }
561
- prevX = x;
562
- prevY = y;
563
- }
564
- return roots;
565
- }
566
-
567
- // src/stamps/graph-2d/render.ts
568
- async function renderGraph2dSvgFromState(jsonState) {
569
- const parsed = parseSerializedGraph(jsonState);
570
- if (!parsed) throw new Error("renderGraph2dSvgFromState: jsonState corrupt");
571
- const JXG = (await import('jsxgraph')).default;
572
- const opts = JXG.Options;
573
- if (opts) {
574
- opts.text = opts.text || {};
575
- opts.text.display = "internal";
576
- opts.text.useASCIIMathML = false;
577
- opts.text.useMathJax = false;
578
- opts.text.useKatex = false;
579
- opts.label = opts.label || {};
580
- opts.label.display = "internal";
581
- }
582
- const container = document.createElement("div");
583
- container.id = `jxg_graph2d_off_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
584
- container.style.cssText = "position:absolute;top:-99999px;left:-99999px;width:600px;height:400px;visibility:hidden;pointer-events:none;";
585
- document.body.appendChild(container);
586
- let board = null;
587
- try {
588
- board = JXG.JSXGraph.initBoard(container.id, {
589
- boundingbox: [parsed.view.xMin, parsed.view.yMax, parsed.view.xMax, parsed.view.yMin],
590
- axis: parsed.view.showAxis,
591
- grid: parsed.view.showGrid,
592
- showCopyright: false,
593
- showNavigation: false,
594
- keepAspectRatio: false
595
- });
596
- renderGraphObjects(board, parsed);
597
- board.update();
598
- const svgEl = container.querySelector("svg");
599
- if (!svgEl) throw new Error("renderGraph2dSvgFromState: no svg generated");
600
- return svgEl.outerHTML;
601
- } finally {
602
- try {
603
- if (board) JXG.JSXGraph.freeBoard(board);
604
- } catch {
605
- }
606
- if (container.parentNode) container.parentNode.removeChild(container);
607
- }
608
- }
609
-
610
- // src/stamps/graph-2d/types.ts
611
- function isGraph2DCustomData(data) {
612
- if (!data || typeof data !== "object") return false;
613
- const d = data;
614
- return d.kind === "graph2d" && d.version === 1 && typeof d.jsonState === "string";
615
- }
616
-
617
- export { EMPTY_GRAPH, addIntersection, addPointOnCurve, compile, isGraph2DCustomData, numericalDerivative, parseSerializedGraph, renderGraph2dSvgFromState, stringifySerializedGraph, validate };
618
- //# sourceMappingURL=chunk-74VEEZBV.mjs.map
619
- //# sourceMappingURL=chunk-74VEEZBV.mjs.map