@codefrydev/svg-engine 0.1.0 → 0.1.2

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/editor.js CHANGED
@@ -2,13 +2,64 @@ import {
2
2
  Renderer,
3
3
  getMouseCoords,
4
4
  snap
5
- } from "./chunk-KRCGD3CL.js";
5
+ } from "./chunk-VCDC4W3X.js";
6
6
 
7
7
  // src/editor/Editor.tsx
8
8
  import { forwardRef, useEffect as useEffect2, useImperativeHandle, useMemo as useMemo2, useState as useState2 } from "react";
9
9
 
10
10
  // src/editor/hooks/useDrawing.ts
11
11
  import { useEffect, useMemo, useRef, useState } from "react";
12
+ var MIN_DRAW_DISTANCE = 10;
13
+ var snapshotOriginal = (element) => {
14
+ const base = {
15
+ x1: element.x1,
16
+ y1: element.y1,
17
+ x2: element.x2,
18
+ y2: element.y2
19
+ };
20
+ const raw = element.cps;
21
+ if (raw?.length) {
22
+ return { ...base, cps: raw.map((p) => ({ x: p.x, y: p.y })) };
23
+ }
24
+ return base;
25
+ };
26
+ var applyDragDelta = (prev, draggingId, dx, dy, orig) => {
27
+ const str = String(draggingId);
28
+ if (str.endsWith("-start")) {
29
+ const rawId = str.slice(0, -"-start".length);
30
+ return prev.map(
31
+ (el) => String(el.id) === rawId ? { ...el, x1: orig.x1 + dx, y1: orig.y1 + dy } : el
32
+ );
33
+ }
34
+ if (str.endsWith("-end")) {
35
+ const rawId = str.slice(0, -"-end".length);
36
+ return prev.map(
37
+ (el) => String(el.id) === rawId ? { ...el, x2: orig.x2 + dx, y2: orig.y2 + dy } : el
38
+ );
39
+ }
40
+ const cpMatch = str.match(/^(.*)-cp-(\d+)$/);
41
+ if (cpMatch && orig.cps) {
42
+ const rawId = cpMatch[1];
43
+ const cpIndex = Number(cpMatch[2]);
44
+ return prev.map((el) => {
45
+ if (String(el.id) !== rawId) return el;
46
+ const newCps = orig.cps.map(
47
+ (p, i) => i === cpIndex ? { x: p.x + dx, y: p.y + dy } : { ...p }
48
+ );
49
+ return { ...el, cps: newCps };
50
+ });
51
+ }
52
+ return prev.map(
53
+ (el) => el.id === draggingId || String(el.id) === str ? {
54
+ ...el,
55
+ x1: orig.x1 + dx,
56
+ y1: orig.y1 + dy,
57
+ x2: orig.x2 + dx,
58
+ y2: orig.y2 + dy,
59
+ ...orig.cps ? { cps: orig.cps.map((p) => ({ x: p.x + dx, y: p.y + dy })) } : {}
60
+ } : el
61
+ );
62
+ };
12
63
  var waveOscillationDefaults = (tool) => {
13
64
  let defaultColor = "#0f172a";
14
65
  if (["spring", "sine_wave", "wavefronts", "shm_graph"].includes(tool)) defaultColor = "#3b82f6";
@@ -74,10 +125,10 @@ var toolCreateDefaults = (tool, presetKey) => {
74
125
  case "ray":
75
126
  case "photon":
76
127
  if (presetKey === "graph") return { color: "#0f172a", strokeWidth: 2 };
77
- if (presetKey === "mechanics" || presetKey === "mechanical") return { color: "#0f172a", strokeWidth: 2 };
128
+ if (presetKey === "mechanics") return { color: "#0f172a", strokeWidth: 2 };
78
129
  if (presetKey === "rotation") return { color: "#ef4444", strokeWidth: 2 };
79
- if (presetKey === "optical" && (tool === "ray" || tool === "photon")) return { color: "#ef4444", strokeWidth: 2 };
80
- if (presetKey === "optical") return { color: "#0f172a", strokeWidth: 2 };
130
+ if (presetKey === "light" && (tool === "ray" || tool === "photon")) return { color: "#ef4444", strokeWidth: 2 };
131
+ if (presetKey === "light") return { color: "#0f172a", strokeWidth: 2 };
81
132
  return { color: "#ef4444", strokeWidth: 2 };
82
133
  case "lens_convex":
83
134
  case "lens_concave":
@@ -92,7 +143,7 @@ var toolCreateDefaults = (tool, presetKey) => {
92
143
  case "slit_single":
93
144
  return { color: "#0f172a", strokeWidth: 4 };
94
145
  case "nucleus":
95
- if (presetKey === "optical") return { color: "#3b82f6", strokeWidth: 2, fontSize: 18 };
146
+ if (presetKey === "light") return { color: "#3b82f6", strokeWidth: 2, fontSize: 18 };
96
147
  return { color: "#0f172a", strokeWidth: 2, fontSize: 18 };
97
148
  case "energy_level":
98
149
  return { color: "#0f172a", strokeWidth: 2, label: "n=1", energy: "-13.6 eV" };
@@ -100,13 +151,13 @@ var toolCreateDefaults = (tool, presetKey) => {
100
151
  return { color: "#0f172a", strokeWidth: 2, lineStyle: "dashed" };
101
152
  case "point":
102
153
  if (presetKey === "graph") return { color: "#0f172a", strokeWidth: 2, fontSize: 16, label: "P" };
103
- if (presetKey === "light" || presetKey === "optical") return { color: "#0f172a", strokeWidth: 2, fontSize: 18, label: "P" };
154
+ if (presetKey === "light") return { color: "#0f172a", strokeWidth: 2, fontSize: 18, label: "P" };
104
155
  if (presetKey === "thermo") return { color: "#0f172a", strokeWidth: 2, fontSize: 18, label: "P" };
105
156
  return {};
106
157
  case "text":
107
158
  if (presetKey === "graph") return { color: "#0f172a", strokeWidth: 2, fontSize: 18, label: "Label" };
108
- if (presetKey === "light" || presetKey === "optical") return { color: "#0f172a", strokeWidth: 2, fontSize: 18, label: "Text" };
109
- if (presetKey === "mechanics" || presetKey === "mechanical") return { color: "#0f172a", strokeWidth: 2, fontSize: 20, label: "Label" };
159
+ if (presetKey === "light") return { color: "#0f172a", strokeWidth: 2, fontSize: 18, label: "Text" };
160
+ if (presetKey === "mechanics") return { color: "#0f172a", strokeWidth: 2, fontSize: 20, label: "Label" };
110
161
  if (presetKey === "rotation") return { color: "#1e293b", strokeWidth: 2, fontSize: 20, label: "Label" };
111
162
  if (presetKey === "thermo") return { color: "#1e293b", strokeWidth: 2, fontSize: 18, label: "Equation/Label" };
112
163
  return {};
@@ -141,15 +192,15 @@ var toolCreateDefaults = (tool, presetKey) => {
141
192
  return {};
142
193
  case "spring":
143
194
  if (presetKey === "rotation") return { color: "#0f172a", strokeWidth: 2, coils: 6, lineStyle: "solid" };
144
- if (presetKey === "mechanics" || presetKey === "mechanical") return { color: "#0f172a", strokeWidth: 2 };
195
+ if (presetKey === "mechanics") return { color: "#0f172a", strokeWidth: 2 };
145
196
  return {};
146
197
  case "pivot":
147
198
  if (presetKey === "rotation") return { color: "#64748b", strokeWidth: 2, size: 20, fontSize: 18 };
148
- if (presetKey === "mechanics" || presetKey === "mechanical") return { color: "#0f172a", strokeWidth: 2 };
199
+ if (presetKey === "mechanics") return { color: "#0f172a", strokeWidth: 2 };
149
200
  return {};
150
201
  case "pulley":
151
202
  if (presetKey === "rotation") return { color: "#64748b", strokeWidth: 2, fontSize: 18 };
152
- if (presetKey === "mechanics" || presetKey === "mechanical") return { color: "#0f172a", strokeWidth: 2 };
203
+ if (presetKey === "mechanics") return { color: "#0f172a", strokeWidth: 2 };
153
204
  return {};
154
205
  case "point_mass":
155
206
  return { color: "#ef4444", strokeWidth: 2, size: 15, fontSize: 18 };
@@ -248,18 +299,18 @@ var toolCreateDefaults = (tool, presetKey) => {
248
299
  case "disk":
249
300
  case "particle":
250
301
  case "com":
251
- if (presetKey === "mechanics" || presetKey === "mechanical") return { color: "#0f172a", strokeWidth: 2 };
302
+ if (presetKey === "mechanics") return { color: "#0f172a", strokeWidth: 2 };
252
303
  return {};
253
304
  case "block":
254
305
  case "cart":
255
- if (presetKey === "mechanics" || presetKey === "mechanical") return { color: "#0f172a", strokeWidth: 2, rotation: 0 };
306
+ if (presetKey === "mechanics") return { color: "#0f172a", strokeWidth: 2, rotation: 0 };
256
307
  return {};
257
308
  default:
258
309
  return {};
259
310
  }
260
311
  };
261
- var useDrawing = (initialElements2, presetKey) => {
262
- const [elements, setElements] = useState(initialElements2);
312
+ var useDrawing = (initialElements, presetKey) => {
313
+ const [elements, setElements] = useState(initialElements);
263
314
  const [tool, setTool] = useState("select");
264
315
  const [drawing, setDrawing] = useState(null);
265
316
  const [selectedId, setSelectedId] = useState(null);
@@ -272,10 +323,17 @@ var useDrawing = (initialElements2, presetKey) => {
272
323
  const dragStart = useRef({ x: 0, y: 0 });
273
324
  const originalPos = useRef(null);
274
325
  useEffect(() => {
275
- setElements(initialElements2);
276
- }, [initialElements2]);
326
+ setElements(initialElements);
327
+ setSelectedId(null);
328
+ setShowProperties(false);
329
+ setDrawing(null);
330
+ }, [initialElements]);
277
331
  useEffect(() => {
278
332
  const handleKeyDown = (e) => {
333
+ const tgt = e.target;
334
+ if (tgt && (tgt.tagName === "INPUT" || tgt.tagName === "TEXTAREA" || tgt.isContentEditable)) {
335
+ return;
336
+ }
279
337
  if ((e.key === "Delete" || e.key === "Backspace") && selectedId) {
280
338
  setElements((prev) => prev.filter((el) => el.id !== selectedId));
281
339
  setSelectedId(null);
@@ -284,6 +342,7 @@ var useDrawing = (initialElements2, presetKey) => {
284
342
  if (e.key === "Escape") {
285
343
  setDrawing(null);
286
344
  setTool("select");
345
+ setShowProperties(false);
287
346
  }
288
347
  };
289
348
  window.addEventListener("keydown", handleKeyDown);
@@ -301,18 +360,61 @@ var useDrawing = (initialElements2, presetKey) => {
301
360
  setSelectedId(element.id);
302
361
  setDraggingId(element.id);
303
362
  dragStart.current = m;
304
- originalPos.current = { x1: element.x1, y1: element.y1, x2: element.x2, y2: element.y2 };
363
+ originalPos.current = snapshotOriginal(element);
364
+ };
365
+ const onHandlePointerDown = (e, element, kind, cpIndex) => {
366
+ if (tool !== "select") return;
367
+ e.stopPropagation();
368
+ if (!svgRef.current) return;
369
+ const m = getMouseCoords(svgRef.current, e);
370
+ setSelectedId(element.id);
371
+ dragStart.current = m;
372
+ if (kind === "start") {
373
+ setDraggingId(`${element.id}-start`);
374
+ originalPos.current = {
375
+ x1: element.x1,
376
+ y1: element.y1,
377
+ x2: element.x2,
378
+ y2: element.y2
379
+ };
380
+ return;
381
+ }
382
+ if (kind === "end") {
383
+ setDraggingId(`${element.id}-end`);
384
+ originalPos.current = {
385
+ x1: element.x1,
386
+ y1: element.y1,
387
+ x2: element.x2,
388
+ y2: element.y2
389
+ };
390
+ return;
391
+ }
392
+ if (kind === "cp" && cpIndex !== void 0) {
393
+ const raw = element.cps;
394
+ const cps = raw?.length && raw.length > 0 ? raw.map((p) => ({ x: p.x, y: p.y })) : [{ x: (element.x1 + element.x2) / 2, y: Math.min(element.y1, element.y2) - 50 }];
395
+ setDraggingId(`${element.id}-cp-${cpIndex}`);
396
+ originalPos.current = {
397
+ x1: element.x1,
398
+ y1: element.y1,
399
+ x2: element.x2,
400
+ y2: element.y2,
401
+ cps
402
+ };
403
+ }
305
404
  };
306
405
  const handleMouseDown = (e) => {
307
406
  if (!svgRef.current) return;
308
407
  if (tool === "select") {
309
- setSelectedId(null);
408
+ if (e.target === svgRef.current) {
409
+ setSelectedId(null);
410
+ setShowProperties(false);
411
+ }
310
412
  return;
311
413
  }
312
414
  const p = getMouseCoords(svgRef.current, e);
313
415
  const x = snap(p.x);
314
416
  const y = snap(p.y);
315
- const mechanicsSingle = (presetKey === "mechanics" || presetKey === "mechanical") && ["pivot", "com", "axes"].includes(tool);
417
+ const mechanicsSingle = presetKey === "mechanics" && ["pivot", "com", "axes"].includes(tool);
316
418
  const rotationSingle = presetKey === "rotation" && ["point_mass", "com_indicator", "pivot"].includes(tool);
317
419
  const waveOscSingle = presetKey === "waveOscillation" && ["tuning_fork", "speaker", "wavefronts", "mass_box"].includes(tool);
318
420
  const single = mechanicsSingle || rotationSingle || waveOscSingle || [
@@ -354,7 +456,7 @@ var useDrawing = (initialElements2, presetKey) => {
354
456
  return;
355
457
  }
356
458
  const defaults = toolCreateDefaults(tool, presetKey);
357
- const rotationExtra = (presetKey === "mechanics" || presetKey === "mechanical") && (tool === "block" || tool === "cart") ? { rotation: defaults.rotation ?? 0 } : {};
459
+ const rotationExtra = presetKey === "mechanics" && (tool === "block" || tool === "cart") ? { rotation: defaults.rotation ?? 0 } : {};
358
460
  setDrawing({
359
461
  ...defaults,
360
462
  ...rotationExtra,
@@ -370,32 +472,24 @@ var useDrawing = (initialElements2, presetKey) => {
370
472
  const handleMouseMove = (e) => {
371
473
  if (!svgRef.current) return;
372
474
  const p = getMouseCoords(svgRef.current, e);
373
- if (draggingId && originalPos.current) {
475
+ if (draggingId != null && originalPos.current) {
374
476
  const dx = snap(p.x - dragStart.current.x);
375
477
  const dy = snap(p.y - dragStart.current.y);
376
- setElements(
377
- (prev) => prev.map(
378
- (el) => el.id === draggingId ? {
379
- ...el,
380
- x1: originalPos.current.x1 + dx,
381
- y1: originalPos.current.y1 + dy,
382
- x2: originalPos.current.x2 + dx,
383
- y2: originalPos.current.y2 + dy
384
- } : el
385
- )
386
- );
478
+ const orig = originalPos.current;
479
+ setElements((prev) => applyDragDelta(prev, draggingId, dx, dy, orig));
387
480
  return;
388
481
  }
389
482
  if (!drawing) return;
390
483
  setDrawing({ ...drawing, x2: snap(p.x), y2: snap(p.y) });
391
484
  };
392
485
  const handleMouseUp = () => {
393
- if (draggingId) {
486
+ if (draggingId != null) {
394
487
  setDraggingId(null);
488
+ originalPos.current = null;
395
489
  return;
396
490
  }
397
491
  if (!drawing) return;
398
- if (drawing.x1 === drawing.x2 && drawing.y1 === drawing.y2) {
492
+ if (Math.abs(drawing.x1 - drawing.x2) <= MIN_DRAW_DISTANCE && Math.abs(drawing.y1 - drawing.y2) <= MIN_DRAW_DISTANCE) {
399
493
  setDrawing(null);
400
494
  setTool("select");
401
495
  return;
@@ -428,6 +522,7 @@ var useDrawing = (initialElements2, presetKey) => {
428
522
  selectedElement,
429
523
  svgRef,
430
524
  onElementMouseDown,
525
+ onHandlePointerDown,
431
526
  handleMouseDown,
432
527
  handleMouseMove,
433
528
  handleMouseUp
@@ -605,9 +700,10 @@ var Icons = {
605
700
  /* @__PURE__ */ jsx("line", { x1: "8", y1: "16", x2: "16", y2: "16", stroke: "currentColor", strokeWidth: "2" })
606
701
  ] }),
607
702
  torsion_pendulum: /* @__PURE__ */ jsxs(Fragment, { children: [
608
- /* @__PURE__ */ jsx("line", { x1: "12", y1: "2", x2: "12", y2: "16", stroke: "currentColor", strokeWidth: "2" }),
609
- /* @__PURE__ */ jsx("ellipse", { cx: "12", cy: "16", rx: "8", ry: "3", fill: "currentColor" }),
610
- /* @__PURE__ */ jsx("line", { x1: "12", y1: "16", x2: "18", y2: "18", stroke: "currentColor", strokeWidth: "1" })
703
+ /* @__PURE__ */ jsx("rect", { x: "5", y: "2", width: "14", height: "4", rx: "1", fill: "currentColor", opacity: "0.35" }),
704
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "6", x2: "12", y2: "15", stroke: "currentColor", strokeWidth: "1.5" }),
705
+ /* @__PURE__ */ jsx("rect", { x: "8", y: "15", width: "8", height: "6", rx: "2", fill: "none", stroke: "currentColor", strokeWidth: "2" }),
706
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "15", x2: "14", y2: "13", stroke: "currentColor", strokeWidth: "1.2" })
611
707
  ] }),
612
708
  standing_wave: /* @__PURE__ */ jsxs(Fragment, { children: [
613
709
  /* @__PURE__ */ jsx("path", { d: "M 2 12 Q 7 2 12 12 T 22 12", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }),
@@ -730,7 +826,16 @@ var Icons = {
730
826
  /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3", fill: "#ef4444" })
731
827
  ] }),
732
828
  orbit: /* @__PURE__ */ jsx("ellipse", { cx: "12", cy: "12", rx: "8", ry: "4.5", fill: "none", stroke: "currentColor", strokeWidth: "2" }),
733
- resistor: /* @__PURE__ */ jsx("path", { d: "M 2 12 L 6 12 L 8 6 L 12 18 L 16 6 L 18 12 L 22 12", fill: "none", stroke: "currentColor", strokeWidth: "2" }),
829
+ resistor: /* @__PURE__ */ jsx(
830
+ "path",
831
+ {
832
+ d: "M 2 12 L 6 12 L 8 6 L 12 18 L 16 6 L 18 12 L 22 12",
833
+ fill: "none",
834
+ stroke: "#b45309",
835
+ strokeWidth: "2.2",
836
+ strokeLinejoin: "round"
837
+ }
838
+ ),
734
839
  capacitor: /* @__PURE__ */ jsxs(Fragment, { children: [
735
840
  /* @__PURE__ */ jsx("line", { x1: "3", y1: "12", x2: "9", y2: "12", stroke: "currentColor", strokeWidth: "2" }),
736
841
  /* @__PURE__ */ jsx("line", { x1: "9", y1: "6", x2: "9", y2: "18", stroke: "currentColor", strokeWidth: "2.2" }),
@@ -740,22 +845,24 @@ var Icons = {
740
845
  inductor: /* @__PURE__ */ jsx("path", { d: "M 3 12 C 5 8, 7 8, 9 12 C 11 8, 13 8, 15 12 C 17 8, 19 8, 21 12", fill: "none", stroke: "currentColor", strokeWidth: "2" }),
741
846
  diode: /* @__PURE__ */ jsxs(Fragment, { children: [
742
847
  /* @__PURE__ */ jsx("line", { x1: "3", y1: "12", x2: "8", y2: "12", stroke: "currentColor", strokeWidth: "2" }),
743
- /* @__PURE__ */ jsx("polygon", { points: "8,7 8,17 14,12", fill: "none", stroke: "currentColor", strokeWidth: "2" }),
848
+ /* @__PURE__ */ jsx("polygon", { points: "8,7 8,17 14,12", fill: "#fcd34d", fillOpacity: "0.85", stroke: "currentColor", strokeWidth: "2" }),
744
849
  /* @__PURE__ */ jsx("line", { x1: "15.5", y1: "7", x2: "15.5", y2: "17", stroke: "currentColor", strokeWidth: "2" }),
745
850
  /* @__PURE__ */ jsx("line", { x1: "15.5", y1: "12", x2: "21", y2: "12", stroke: "currentColor", strokeWidth: "2" })
746
851
  ] }),
747
852
  battery: /* @__PURE__ */ jsxs(Fragment, { children: [
748
- /* @__PURE__ */ jsx("path", { d: "M 2 12 L 10 12 M 10 4 L 10 20 M 14 8 L 14 16 M 14 12 L 22 12", fill: "none", stroke: "currentColor", strokeWidth: "2" }),
749
- /* @__PURE__ */ jsx("line", { x1: "14", y1: "8", x2: "14", y2: "16", stroke: "currentColor", strokeWidth: "4" })
853
+ /* @__PURE__ */ jsx("path", { d: "M 2 12 L 7 12", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }),
854
+ /* @__PURE__ */ jsx("rect", { x: "7", y: "4", width: "5", height: "16", rx: "0.5", fill: "#dc2626", stroke: "currentColor", strokeWidth: "1" }),
855
+ /* @__PURE__ */ jsx("rect", { x: "12", y: "7", width: "5", height: "10", rx: "0.5", fill: "#2563eb", stroke: "currentColor", strokeWidth: "1" }),
856
+ /* @__PURE__ */ jsx("path", { d: "M 17 12 L 22 12", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })
750
857
  ] }),
751
858
  ground: /* @__PURE__ */ jsx("path", { d: "M 12 4 L 12 12 M 6 12 L 18 12 M 8 16 L 16 16 M 10 20 L 14 20", fill: "none", stroke: "currentColor", strokeWidth: "2" }),
752
859
  meter_v: /* @__PURE__ */ jsxs(Fragment, { children: [
753
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "7", fill: "none", stroke: "currentColor", strokeWidth: "2" }),
754
- /* @__PURE__ */ jsx("text", { x: "12", y: "15", fontSize: "8", textAnchor: "middle", fill: "currentColor", children: "V" })
860
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "8", fill: "#e0f2fe", stroke: "#0369a1", strokeWidth: "2" }),
861
+ /* @__PURE__ */ jsx("text", { x: "12", y: "15", fontSize: "9", textAnchor: "middle", fill: "#0369a1", fontWeight: "bold", children: "V" })
755
862
  ] }),
756
863
  meter_a: /* @__PURE__ */ jsxs(Fragment, { children: [
757
- /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "7", fill: "none", stroke: "currentColor", strokeWidth: "2" }),
758
- /* @__PURE__ */ jsx("text", { x: "12", y: "15", fontSize: "8", textAnchor: "middle", fill: "currentColor", children: "A" })
864
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "8", fill: "#fef3c7", stroke: "#b45309", strokeWidth: "2" }),
865
+ /* @__PURE__ */ jsx("text", { x: "12", y: "15", fontSize: "9", textAnchor: "middle", fill: "#b45309", fontWeight: "bold", children: "A" })
759
866
  ] }),
760
867
  piston_cylinder: /* @__PURE__ */ jsxs(Fragment, { children: [
761
868
  /* @__PURE__ */ jsx("path", { d: "M 4 4 L 4 20 L 20 20 L 20 4", fill: "none", stroke: "currentColor", strokeWidth: "2" }),
@@ -837,7 +944,7 @@ var Icons = {
837
944
  lens_convex: /* @__PURE__ */ jsx(
838
945
  "path",
839
946
  {
840
- d: "M 12 2 Q 18 12 12 22 Q 6 12 12 2 Z",
947
+ d: "M 4 12 Q 12 4 20 12 Q 12 20 4 12 Z",
841
948
  fill: "#38bdf8",
842
949
  fillOpacity: "0.4",
843
950
  stroke: "currentColor",
@@ -847,7 +954,7 @@ var Icons = {
847
954
  lens_concave: /* @__PURE__ */ jsx(
848
955
  "path",
849
956
  {
850
- d: "M 8 2 L 16 2 Q 12 12 16 22 L 8 22 Q 12 12 8 2 Z",
957
+ d: "M 4 8 L 4 16 Q 12 14 20 16 L 20 8 Q 12 10 4 8 Z",
851
958
  fill: "#38bdf8",
852
959
  fillOpacity: "0.4",
853
960
  stroke: "currentColor",
@@ -855,16 +962,16 @@ var Icons = {
855
962
  }
856
963
  ),
857
964
  mirror_concave: /* @__PURE__ */ jsxs(Fragment, { children: [
858
- /* @__PURE__ */ jsx("path", { d: "M 16 2 Q 8 12 16 22", fill: "none", stroke: "currentColor", strokeWidth: "2" }),
859
- /* @__PURE__ */ jsx("line", { x1: "11", y1: "6", x2: "6", y2: "8", stroke: "currentColor" }),
860
- /* @__PURE__ */ jsx("line", { x1: "8", y1: "12", x2: "3", y2: "12", stroke: "currentColor" }),
861
- /* @__PURE__ */ jsx("line", { x1: "11", y1: "18", x2: "6", y2: "16", stroke: "currentColor" })
965
+ /* @__PURE__ */ jsx("path", { d: "M 4 12 Q 12 18 20 12", fill: "none", stroke: "currentColor", strokeWidth: "2" }),
966
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "14", x2: "5", y2: "17", stroke: "currentColor" }),
967
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "15", x2: "9", y2: "18", stroke: "currentColor" }),
968
+ /* @__PURE__ */ jsx("line", { x1: "16", y1: "14", x2: "13", y2: "17", stroke: "currentColor" })
862
969
  ] }),
863
970
  mirror_convex: /* @__PURE__ */ jsxs(Fragment, { children: [
864
- /* @__PURE__ */ jsx("path", { d: "M 8 4 Q 16 12 8 20", fill: "none", stroke: "currentColor", strokeWidth: "2" }),
865
- /* @__PURE__ */ jsx("line", { x1: "13", y1: "6", x2: "18", y2: "8", stroke: "currentColor" }),
866
- /* @__PURE__ */ jsx("line", { x1: "16", y1: "12", x2: "21", y2: "12", stroke: "currentColor" }),
867
- /* @__PURE__ */ jsx("line", { x1: "13", y1: "18", x2: "18", y2: "16", stroke: "currentColor" })
971
+ /* @__PURE__ */ jsx("path", { d: "M 4 12 Q 12 6 20 12", fill: "none", stroke: "currentColor", strokeWidth: "2" }),
972
+ /* @__PURE__ */ jsx("line", { x1: "8", y1: "10", x2: "5", y2: "7", stroke: "currentColor" }),
973
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "9", x2: "9", y2: "6", stroke: "currentColor" }),
974
+ /* @__PURE__ */ jsx("line", { x1: "16", y1: "10", x2: "13", y2: "7", stroke: "currentColor" })
868
975
  ] }),
869
976
  prism: /* @__PURE__ */ jsx("polygon", { points: "12,4 2,20 22,20", fill: "#38bdf8", fillOpacity: "0.4", stroke: "currentColor", strokeWidth: "2" }),
870
977
  glass_slab: /* @__PURE__ */ jsx("rect", { x: "4", y: "6", width: "16", height: "12", fill: "#38bdf8", fillOpacity: "0.4", stroke: "currentColor", strokeWidth: "2" }),
@@ -1004,22 +1111,303 @@ var Icons = {
1004
1111
  ] })
1005
1112
  };
1006
1113
 
1114
+ // src/editor/toolbarToolTheme.ts
1115
+ var tones = {
1116
+ teal: {
1117
+ idle: "text-teal-600 border-transparent hover:bg-teal-50 hover:border-teal-200",
1118
+ active: "bg-teal-50 text-teal-700 border-teal-200 shadow-sm ring-1 ring-teal-100"
1119
+ },
1120
+ indigo: {
1121
+ idle: "text-indigo-600 border-transparent hover:bg-indigo-50 hover:border-indigo-200",
1122
+ active: "bg-indigo-50 text-indigo-700 border-indigo-200 shadow-sm ring-1 ring-indigo-100"
1123
+ },
1124
+ violet: {
1125
+ idle: "text-violet-600 border-transparent hover:bg-violet-50 hover:border-violet-200",
1126
+ active: "bg-violet-50 text-violet-700 border-violet-200 shadow-sm ring-1 ring-violet-100"
1127
+ },
1128
+ purple: {
1129
+ idle: "text-purple-600 border-transparent hover:bg-purple-50 hover:border-purple-200",
1130
+ active: "bg-purple-50 text-purple-700 border-purple-200 shadow-sm ring-1 ring-purple-100"
1131
+ },
1132
+ amber: {
1133
+ idle: "text-amber-600 border-transparent hover:bg-amber-50 hover:border-amber-200",
1134
+ active: "bg-amber-50 text-amber-700 border-amber-200 shadow-sm ring-1 ring-amber-100"
1135
+ },
1136
+ orange: {
1137
+ idle: "text-orange-600 border-transparent hover:bg-orange-50 hover:border-orange-200",
1138
+ active: "bg-orange-50 text-orange-700 border-orange-200 shadow-sm ring-1 ring-orange-100"
1139
+ },
1140
+ rose: {
1141
+ idle: "text-rose-600 border-transparent hover:bg-rose-50 hover:border-rose-200",
1142
+ active: "bg-rose-50 text-rose-700 border-rose-200 shadow-sm ring-1 ring-rose-100"
1143
+ },
1144
+ sky: {
1145
+ idle: "text-sky-600 border-transparent hover:bg-sky-50 hover:border-sky-200",
1146
+ active: "bg-sky-50 text-sky-700 border-sky-200 shadow-sm ring-1 ring-sky-100"
1147
+ },
1148
+ cyan: {
1149
+ idle: "text-cyan-600 border-transparent hover:bg-cyan-50 hover:border-cyan-200",
1150
+ active: "bg-cyan-50 text-cyan-700 border-cyan-200 shadow-sm ring-1 ring-cyan-100"
1151
+ },
1152
+ fuchsia: {
1153
+ idle: "text-fuchsia-600 border-transparent hover:bg-fuchsia-50 hover:border-fuchsia-200",
1154
+ active: "bg-fuchsia-50 text-fuchsia-700 border-fuchsia-200 shadow-sm ring-1 ring-fuchsia-100"
1155
+ },
1156
+ emerald: {
1157
+ idle: "text-emerald-600 border-transparent hover:bg-emerald-50 hover:border-emerald-200",
1158
+ active: "bg-emerald-50 text-emerald-700 border-emerald-200 shadow-sm ring-1 ring-emerald-100"
1159
+ },
1160
+ lime: {
1161
+ idle: "text-lime-600 border-transparent hover:bg-lime-50 hover:border-lime-200",
1162
+ active: "bg-lime-50 text-lime-700 border-lime-200 shadow-sm ring-1 ring-lime-100"
1163
+ },
1164
+ green: {
1165
+ idle: "text-green-600 border-transparent hover:bg-green-50 hover:border-green-200",
1166
+ active: "bg-green-50 text-green-700 border-green-200 shadow-sm ring-1 ring-green-100"
1167
+ },
1168
+ blue: {
1169
+ idle: "text-blue-600 border-transparent hover:bg-blue-50 hover:border-blue-200",
1170
+ active: "bg-blue-50 text-blue-700 border-blue-200 shadow-sm ring-1 ring-blue-100"
1171
+ },
1172
+ pink: {
1173
+ idle: "text-pink-600 border-transparent hover:bg-pink-50 hover:border-pink-200",
1174
+ active: "bg-pink-50 text-pink-700 border-pink-200 shadow-sm ring-1 ring-pink-100"
1175
+ },
1176
+ slate: {
1177
+ idle: "text-slate-600 border-transparent hover:bg-slate-100 hover:border-slate-200",
1178
+ active: "bg-slate-100 text-slate-800 border-slate-300 shadow-sm ring-1 ring-slate-200"
1179
+ },
1180
+ red: {
1181
+ idle: "text-red-600 border-transparent hover:bg-red-50 hover:border-red-200",
1182
+ active: "bg-red-50 text-red-700 border-red-200 shadow-sm ring-1 ring-red-100"
1183
+ },
1184
+ yellow: {
1185
+ idle: "text-yellow-600 border-transparent hover:bg-yellow-50 hover:border-yellow-200",
1186
+ active: "bg-yellow-50 text-yellow-800 border-yellow-200 shadow-sm ring-1 ring-yellow-100"
1187
+ },
1188
+ stone: {
1189
+ idle: "text-stone-600 border-transparent hover:bg-stone-50 hover:border-stone-200",
1190
+ active: "bg-stone-50 text-stone-800 border-stone-200 shadow-sm ring-1 ring-stone-100"
1191
+ }
1192
+ };
1193
+ var bondCycle = ["indigo", "violet", "purple", "amber", "orange"];
1194
+ var arrowCycle = ["rose", "sky", "cyan", "fuchsia"];
1195
+ var fallbackCycle = [
1196
+ "indigo",
1197
+ "sky",
1198
+ "violet",
1199
+ "emerald",
1200
+ "rose",
1201
+ "amber",
1202
+ "cyan",
1203
+ "fuchsia",
1204
+ "lime",
1205
+ "orange",
1206
+ "blue",
1207
+ "pink"
1208
+ ];
1209
+ function hashId(id) {
1210
+ let h = 2166136261;
1211
+ for (let i = 0; i < id.length; i++) {
1212
+ h ^= id.charCodeAt(i);
1213
+ h = Math.imul(h, 16777619);
1214
+ }
1215
+ return Math.abs(h);
1216
+ }
1217
+ var curated = {
1218
+ select: "teal",
1219
+ single_bond: "indigo",
1220
+ double_bond: "violet",
1221
+ triple_bond: "purple",
1222
+ wedge_bond: "amber",
1223
+ dash_bond: "orange",
1224
+ curved_arrow: "rose",
1225
+ reaction_arrow: "sky",
1226
+ equilibrium_arrow: "cyan",
1227
+ resonance_arrow: "fuchsia",
1228
+ benzene: "emerald",
1229
+ cyclohexane: "lime",
1230
+ cyclopentane: "green",
1231
+ cyclobutane: "teal",
1232
+ cyclopropane: "cyan",
1233
+ atom: "indigo",
1234
+ charge_plus: "rose",
1235
+ charge_minus: "sky",
1236
+ charge_pos: "rose",
1237
+ charge_neg: "sky",
1238
+ radical: "orange",
1239
+ lone_pair: "violet",
1240
+ text: "slate",
1241
+ point: "blue",
1242
+ line: "indigo",
1243
+ string: "indigo",
1244
+ dashed: "stone",
1245
+ dashed_line: "stone",
1246
+ vector: "sky",
1247
+ ray: "amber",
1248
+ axes: "slate",
1249
+ axes_3d: "slate",
1250
+ angle: "violet",
1251
+ arc: "violet",
1252
+ curve_arrow: "fuchsia",
1253
+ wire: "slate",
1254
+ resistor: "amber",
1255
+ capacitor: "blue",
1256
+ inductor: "violet",
1257
+ diode: "purple",
1258
+ battery: "green",
1259
+ meter_v: "cyan",
1260
+ meter_a: "cyan",
1261
+ meter: "cyan",
1262
+ ground: "stone",
1263
+ ac_source: "orange",
1264
+ spring: "lime",
1265
+ block: "amber",
1266
+ wedge: "amber",
1267
+ cart: "orange",
1268
+ particle: "blue",
1269
+ disk: "violet",
1270
+ rod: "stone",
1271
+ pulley: "slate",
1272
+ pivot: "red",
1273
+ com: "pink",
1274
+ surface: "stone",
1275
+ dimension: "cyan",
1276
+ point_mass: "blue",
1277
+ block_mass: "amber",
1278
+ com_indicator: "pink",
1279
+ system_boundary: "fuchsia",
1280
+ dashed_path: "stone",
1281
+ rocket: "orange",
1282
+ uniform_rod: "lime",
1283
+ solid_disk: "violet",
1284
+ hoop_ring: "purple",
1285
+ rolling_body: "rose",
1286
+ inclined_wedge: "amber",
1287
+ rectangle: "indigo",
1288
+ circle: "blue",
1289
+ ellipse: "violet",
1290
+ parabola_v: "emerald",
1291
+ parabola_h: "emerald",
1292
+ hyperbola: "purple",
1293
+ modulus: "orange",
1294
+ step_function: "stone",
1295
+ shaded_area: "cyan",
1296
+ exponential: "green",
1297
+ logarithmic: "teal",
1298
+ sine_wave: "sky",
1299
+ standing_wave: "cyan",
1300
+ wave_pulse: "blue",
1301
+ shm_graph: "violet",
1302
+ energy_graph: "amber",
1303
+ strobe_shm: "fuchsia",
1304
+ wall: "slate",
1305
+ speaker: "orange",
1306
+ tuning_fork: "amber",
1307
+ wavefronts: "sky",
1308
+ mach_cone: "purple",
1309
+ beats_graph: "pink",
1310
+ mass_box: "amber",
1311
+ pendulum: "indigo",
1312
+ torsion_pendulum: "violet",
1313
+ vane_liquid: "cyan",
1314
+ phasor: "fuchsia",
1315
+ damped_wave: "rose",
1316
+ dipole: "fuchsia",
1317
+ charged_plate_pos: "rose",
1318
+ charged_plate_neg: "sky",
1319
+ gaussian_sphere: "stone",
1320
+ gaussian_cylinder: "slate",
1321
+ lens_convex: "amber",
1322
+ lens_concave: "orange",
1323
+ mirror_plane: "sky",
1324
+ mirror_concave: "cyan",
1325
+ mirror_convex: "teal",
1326
+ prism: "violet",
1327
+ glass_slab: "blue",
1328
+ photon: "yellow",
1329
+ slit_double: "pink",
1330
+ slit_single: "rose",
1331
+ energy_level: "emerald",
1332
+ nucleus: "orange",
1333
+ b_field_line: "blue",
1334
+ b_field_curve: "indigo",
1335
+ bezier: "violet",
1336
+ pole_piece: "amber",
1337
+ b_region_in: "sky",
1338
+ b_region_out: "cyan",
1339
+ current_wire: "orange",
1340
+ bar_magnet: "red",
1341
+ coil: "purple",
1342
+ piston_cylinder: "sky",
1343
+ heat_engine: "orange",
1344
+ conduction_rod: "amber",
1345
+ pv_axes: "indigo",
1346
+ thermo_process: "violet",
1347
+ carnot_cycle: "fuchsia",
1348
+ maxwell_boltzmann: "emerald",
1349
+ diatomic_gas: "lime",
1350
+ random_walk: "stone"
1351
+ };
1352
+ var circuitIds = /* @__PURE__ */ new Set([
1353
+ "wire",
1354
+ "resistor",
1355
+ "capacitor",
1356
+ "inductor",
1357
+ "diode",
1358
+ "battery",
1359
+ "meter_v",
1360
+ "meter_a",
1361
+ "meter",
1362
+ "ground",
1363
+ "ac_source"
1364
+ ]);
1365
+ function resolveToneKey(toolId) {
1366
+ const c = curated[toolId];
1367
+ if (c) return c;
1368
+ if (toolId.endsWith("_bond")) {
1369
+ return bondCycle[hashId(toolId) % bondCycle.length];
1370
+ }
1371
+ if (toolId.endsWith("_arrow")) {
1372
+ return arrowCycle[hashId(toolId) % arrowCycle.length];
1373
+ }
1374
+ const low = toolId.toLowerCase();
1375
+ if (low.includes("charge") && (low.includes("pos") || low.includes("plus") || low.includes("_p") || low.endsWith("pos"))) {
1376
+ return "rose";
1377
+ }
1378
+ if (low.includes("charge") && (low.includes("neg") || low.includes("minus"))) {
1379
+ return "sky";
1380
+ }
1381
+ if (toolId.startsWith("meter_")) return "violet";
1382
+ if (circuitIds.has(toolId)) {
1383
+ const circuitCycle = ["slate", "amber", "blue", "violet", "green"];
1384
+ return circuitCycle[hashId(toolId) % circuitCycle.length];
1385
+ }
1386
+ if (low.includes("lens") || low.includes("mirror") || low === "ray" || low.includes("photon")) {
1387
+ const opticsCycle = ["amber", "sky", "cyan", "violet", "yellow"];
1388
+ return opticsCycle[hashId(toolId) % opticsCycle.length];
1389
+ }
1390
+ return fallbackCycle[hashId(toolId) % fallbackCycle.length];
1391
+ }
1392
+ function getToolbarToolClasses(toolId, selected) {
1393
+ const key = resolveToneKey(toolId);
1394
+ return selected ? tones[key].active : tones[key].idle;
1395
+ }
1396
+
1007
1397
  // src/editor/Toolbar.tsx
1008
1398
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1009
1399
  var Toolbar = ({ categories, tool, onSelect }) => {
1010
- const isChemistryCategory = (name) => ["chemistry", "arrows", "rings", "symbols"].includes(name.toLowerCase());
1011
1400
  return /* @__PURE__ */ jsx2("aside", { className: "w-[108px] border-r border-slate-200 bg-gradient-to-b from-slate-50 to-white flex flex-col items-center py-3 gap-4 overflow-y-auto shrink-0", children: categories.map((category) => /* @__PURE__ */ jsxs2("section", { className: "flex flex-col items-center w-full px-2", children: [
1012
1401
  /* @__PURE__ */ jsx2("div", { className: "text-[10px] font-semibold uppercase tracking-[0.20em] text-slate-400 mb-2 w-full text-center border-b border-slate-200 pb-1.5", children: category.name }),
1013
1402
  /* @__PURE__ */ jsx2("div", { className: "flex flex-col gap-1.5 w-full items-center", children: category.tools.map((t) => {
1014
- const chemistryTone = isChemistryCategory(category.name);
1015
- const activeClass = chemistryTone ? "bg-teal-50 text-teal-700 border-teal-200 shadow-sm ring-1 ring-teal-100" : "bg-blue-50 text-blue-700 border-blue-200 shadow-sm ring-1 ring-blue-100";
1403
+ const toneClasses = getToolbarToolClasses(t.id, tool === t.id);
1016
1404
  return /* @__PURE__ */ jsxs2(
1017
1405
  "button",
1018
1406
  {
1019
1407
  type: "button",
1020
1408
  onClick: () => onSelect(t.id),
1021
1409
  title: t.name,
1022
- className: `p-2 rounded-xl border flex flex-col items-center gap-1 transition-all w-full ${tool === t.id ? activeClass : "text-slate-600 border-transparent hover:bg-slate-100 hover:border-slate-200"}`,
1410
+ className: `p-2 rounded-xl border flex flex-col items-center gap-1 transition-all w-full ${toneClasses}`,
1023
1411
  children: [
1024
1412
  /* @__PURE__ */ jsx2("svg", { className: "w-5 h-5", viewBox: "0 0 24 24", children: Icons[t.id] ?? Icons.line }),
1025
1413
  /* @__PURE__ */ jsx2("span", { className: "text-[11px] font-medium tracking-tight leading-tight text-center", children: t.name })
@@ -1034,6 +1422,7 @@ var Toolbar = ({ categories, tool, onSelect }) => {
1034
1422
  // src/editor/PropertiesPanel.tsx
1035
1423
  import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1036
1424
  var needsCurveHeight = (t) => ["gaussian_cylinder", "lens_convex", "lens_concave", "mirror_concave", "mirror_convex", "b_field_curve"].includes(t);
1425
+ var opticsCurveHeightAuto = (t) => ["lens_convex", "lens_concave", "mirror_concave", "mirror_convex"].includes(t);
1037
1426
  var needsFontSize = (t) => ["text", "point", "charge_pos", "charge_neg", "point_mass", "com_indicator", "diatomic_gas", "mass_box"].includes(t);
1038
1427
  var needsRotationSize = (t) => ["point_mass", "com_indicator", "pivot"].includes(t);
1039
1428
  var needsWaveOscSize = (t) => ["pendulum", "mass_box"].includes(t);
@@ -1118,13 +1507,32 @@ var PropertiesPanel = ({ element, onChange, onDelete, onClose }) => {
1118
1507
  ] }),
1119
1508
  needsCurveHeight(String(element.type)) && /* @__PURE__ */ jsxs3("label", { className: "block space-y-1", children: [
1120
1509
  /* @__PURE__ */ jsx3("span", { className: "text-xs font-medium text-slate-600", children: element.type === "gaussian_cylinder" ? "Cylinder half-height (curveHeight)" : "Curve height" }),
1121
- /* @__PURE__ */ jsx3(
1510
+ opticsCurveHeightAuto(String(element.type)) ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
1511
+ /* @__PURE__ */ jsx3(
1512
+ "input",
1513
+ {
1514
+ type: "number",
1515
+ className: "w-full px-3 py-2 border border-slate-300 rounded-lg text-sm",
1516
+ placeholder: "Auto",
1517
+ value: element.curveHeight === void 0 || element.curveHeight === null ? "" : Number(element.curveHeight),
1518
+ onChange: (e) => {
1519
+ const raw = e.target.value;
1520
+ if (raw === "") onChange("curveHeight", void 0);
1521
+ else {
1522
+ const n = Number(raw);
1523
+ onChange("curveHeight", Number.isFinite(n) ? n : void 0);
1524
+ }
1525
+ }
1526
+ }
1527
+ ),
1528
+ /* @__PURE__ */ jsx3("p", { className: "text-[11px] text-slate-500 leading-snug", children: "Empty = auto (scales with chord length: lenses ~15% / 4% sagitta, mirrors ~20%)." })
1529
+ ] }) : /* @__PURE__ */ jsx3(
1122
1530
  "input",
1123
1531
  {
1124
1532
  type: "number",
1125
1533
  className: "w-full px-3 py-2 border border-slate-300 rounded-lg text-sm",
1126
1534
  value: Number(
1127
- element.curveHeight ?? (element.type === "b_field_curve" ? 50 : element.type === "gaussian_cylinder" || element.type === "mirror_concave" || element.type === "mirror_convex" ? 40 : 20)
1535
+ element.curveHeight ?? (element.type === "b_field_curve" ? 50 : element.type === "gaussian_cylinder" ? 40 : 20)
1128
1536
  ),
1129
1537
  onChange: (e) => onChange("curveHeight", Number(e.target.value))
1130
1538
  }
@@ -1620,8 +2028,8 @@ var JsonModal = ({ open, jsonText, setJsonText, onClose, onLoad }) => {
1620
2028
  };
1621
2029
 
1622
2030
  // src/editor/presets/mechanics.ts
1623
- var mechanics = {
1624
- name: "Mechanics",
2031
+ var classicIncline = {
2032
+ name: "Classic incline FBD",
1625
2033
  elements: [
1626
2034
  { id: 1, type: "wedge", x1: 100, y1: 400, x2: 500, y2: 100, label: "M", value: "", color: "#0f172a", strokeWidth: 2 },
1627
2035
  { id: 2, type: "block", x1: 220, y1: 260, x2: 300, y2: 300, label: "m", value: "", color: "#0f172a", strokeWidth: 2, rotation: -37 },
@@ -1633,7 +2041,60 @@ var mechanics = {
1633
2041
  { id: 8, type: "dashed", x1: 260, y1: 280, x2: 340, y2: 220, label: "", value: "", color: "#0f172a", strokeWidth: 2 },
1634
2042
  { id: 9, type: "dimension", x1: 100, y1: 440, x2: 500, y2: 440, label: "L", value: "", color: "#0f172a", strokeWidth: 2 },
1635
2043
  { id: 10, type: "axes", x1: 60, y1: 100, x2: 60, y2: 100, label: "", value: "", color: "#0f172a", strokeWidth: 2 }
1636
- ],
2044
+ ]
2045
+ };
2046
+ var rigidBodiesShowcase = {
2047
+ name: "Rigid bodies & fluids showcase",
2048
+ elements: [
2049
+ { id: 1, type: "text", x1: 200, y1: 40, x2: 200, y2: 40, label: "3D rigid bodies (moment of inertia)", color: "#64748b", fontSize: 18 },
2050
+ { id: 2, type: "cylinder", x1: 60, y1: 140, x2: 220, y2: 180, color: "#38bdf8", strokeWidth: 2, fillOpacity: 0.4, curveHeight: 30, label: "M, R" },
2051
+ { id: 3, type: "line", x1: 20, y1: 160, x2: 280, y2: 160, color: "#94a3b8", strokeWidth: 2, lineStyle: "dashed" },
2052
+ { id: 4, type: "arc_arrow", x1: 260, y1: 160, x2: 300, y2: 160, color: "#0f172a", strokeWidth: 2, label: "\u03C9" },
2053
+ { id: 5, type: "text", x1: 140, y1: 240, x2: 140, y2: 240, label: "I = MR\xB2/2", color: "#0f172a", fontSize: 16 },
2054
+ { id: 6, type: "slab", x1: 340, y1: 120, x2: 500, y2: 180, color: "#38bdf8", strokeWidth: 2, fillOpacity: 0.4, curveHeight: 30, label: "M" },
2055
+ { id: 7, type: "line", x1: 420, y1: 60, x2: 420, y2: 260, color: "#94a3b8", strokeWidth: 2, lineStyle: "dashed", label: "z" },
2056
+ { id: 8, type: "arc_arrow", x1: 420, y1: 80, x2: 460, y2: 80, color: "#0f172a", strokeWidth: 2 },
2057
+ { id: 9, type: "text", x1: 420, y1: 240, x2: 420, y2: 240, label: "I = M(l\xB2+b\xB2)/12", color: "#0f172a", fontSize: 16 },
2058
+ { id: 10, type: "text", x1: 700, y1: 40, x2: 700, y2: 40, label: "Fluids & gravitation", color: "#64748b", fontSize: 18 },
2059
+ { id: 11, type: "pipe", x1: 600, y1: 80, x2: 800, y2: 140, color: "#3b82f6", strokeWidth: 2, fillOpacity: 0.2, curveHeight: 20 },
2060
+ { id: 12, type: "vector", x1: 620, y1: 110, x2: 680, y2: 110, color: "#0f172a", strokeWidth: 2, label: "v\u2081" },
2061
+ { id: 13, type: "vector", x1: 720, y1: 110, x2: 780, y2: 110, color: "#0f172a", strokeWidth: 2, label: "v\u2082" },
2062
+ { id: 14, type: "shell", x1: 700, y1: 240, x2: 760, y2: 240, color: "#8b5cf6", strokeWidth: 2, fillOpacity: 0.3, curveHeight: 15, label: "M" },
2063
+ { id: 15, type: "point", x1: 700, y1: 240, x2: 700, y2: 240, color: "#0f172a", strokeWidth: 2, label: "O" },
2064
+ { id: 16, type: "point", x1: 820, y1: 240, x2: 820, y2: 240, color: "#0f172a", strokeWidth: 2, label: "m" },
2065
+ { id: 17, type: "vector", x1: 820, y1: 240, x2: 780, y2: 240, color: "#ef4444", strokeWidth: 2, label: "F" },
2066
+ { id: 18, type: "text", x1: 220, y1: 320, x2: 220, y2: 320, label: "Ladders & pulleys", color: "#64748b", fontSize: 18 },
2067
+ { id: 19, type: "table_edge", x1: 40, y1: 420, x2: 240, y2: 560, color: "#475569", strokeWidth: 2 },
2068
+ { id: 20, type: "block", x1: 100, y1: 380, x2: 160, y2: 420, color: "#38bdf8", strokeWidth: 2, fillOpacity: 0.4, label: "m\u2081" },
2069
+ { id: 21, type: "pulley", x1: 240, y1: 420, x2: 260, y2: 420, color: "#0f172a", strokeWidth: 2, fillOpacity: 0.2 },
2070
+ { id: 22, type: "block", x1: 240, y1: 480, x2: 280, y2: 540, color: "#38bdf8", strokeWidth: 2, fillOpacity: 0.4, label: "m\u2082" },
2071
+ { id: 23, type: "line", x1: 160, y1: 400, x2: 240, y2: 400, color: "#0f172a", strokeWidth: 2 },
2072
+ { id: 24, type: "line", x1: 260, y1: 420, x2: 260, y2: 480, color: "#0f172a", strokeWidth: 2 },
2073
+ { id: 25, type: "table_edge", x1: 400, y1: 360, x2: 560, y2: 560, color: "#475569", strokeWidth: 2 },
2074
+ { id: 26, type: "ladder", x1: 460, y1: 560, x2: 560, y2: 420, color: "#f59e0b", strokeWidth: 2, curveHeight: 12 },
2075
+ { id: 27, type: "vector", x1: 460, y1: 560, x2: 460, y2: 500, color: "#10b981", strokeWidth: 2, label: "N\u2081" },
2076
+ { id: 28, type: "vector", x1: 560, y1: 420, x2: 500, y2: 420, color: "#10b981", strokeWidth: 2, label: "N\u2082" },
2077
+ { id: 29, type: "vector", x1: 510, y1: 490, x2: 510, y2: 540, color: "#ef4444", strokeWidth: 2, label: "Mg" }
2078
+ ]
2079
+ };
2080
+ var verticalSpringMass = {
2081
+ name: "Vertical spring\u2013mass",
2082
+ elements: [
2083
+ { id: 1, type: "text", x1: 380, y1: 40, x2: 380, y2: 40, label: "Equilibrium along a vertical spring", color: "#64748b", fontSize: 18 },
2084
+ { id: 2, type: "line", x1: 260, y1: 100, x2: 500, y2: 100, color: "#475569", strokeWidth: 3, label: "Ceiling" },
2085
+ { id: 3, type: "spring", x1: 380, y1: 100, x2: 380, y2: 220, color: "#2563eb", strokeWidth: 2, coils: 8 },
2086
+ { id: 4, type: "block", x1: 340, y1: 220, x2: 420, y2: 280, color: "#38bdf8", strokeWidth: 2, fillOpacity: 0.35, label: "m" },
2087
+ { id: 5, type: "vector", x1: 380, y1: 280, x2: 380, y2: 380, color: "#ef4444", strokeWidth: 2.5, label: "mg" },
2088
+ { id: 6, type: "vector", x1: 380, y1: 250, x2: 380, y2: 150, color: "#10b981", strokeWidth: 2.5, label: "F_s" },
2089
+ { id: 7, type: "dashed", x1: 380, y1: 280, x2: 460, y2: 280, color: "#94a3b8", strokeWidth: 1.5, label: "x = 0" },
2090
+ { id: 8, type: "text", x1: 480, y1: 260, x2: 480, y2: 260, label: "mg = kx\u2080", color: "#0f172a", fontSize: 16 }
2091
+ ]
2092
+ };
2093
+ var mechanicsExamples = [classicIncline, rigidBodiesShowcase, verticalSpringMass];
2094
+ var mechanics = {
2095
+ name: "Mechanics",
2096
+ elements: mechanicsExamples[0].elements,
2097
+ examples: mechanicsExamples,
1637
2098
  toolCategories: [
1638
2099
  {
1639
2100
  name: "Sketch and forces",
@@ -1673,24 +2134,59 @@ var mechanics = {
1673
2134
  };
1674
2135
 
1675
2136
  // src/editor/presets/circuit.ts
2137
+ var wheatstoneBridge = {
2138
+ name: "Wheatstone bridge",
2139
+ elements: [
2140
+ { id: 1, type: "resistor", x1: 200, y1: 300, x2: 400, y2: 200, label: "R1", value: "60\u03A9", color: "#b45309", strokeWidth: 2.5 },
2141
+ { id: 2, type: "resistor", x1: 200, y1: 300, x2: 400, y2: 400, label: "R3", value: "300\u03A9", color: "#b45309", strokeWidth: 2.5 },
2142
+ { id: 3, type: "resistor", x1: 400, y1: 200, x2: 600, y2: 300, label: "R2", value: "100\u03A9", color: "#b45309", strokeWidth: 2.5 },
2143
+ { id: 4, type: "resistor", x1: 400, y1: 400, x2: 600, y2: 300, label: "R4", value: "500\u03A9", color: "#b45309", strokeWidth: 2.5 },
2144
+ { id: 5, type: "battery", x1: 400, y1: 400, x2: 400, y2: 200, label: "50V", value: "", color: "#1d4ed8", strokeWidth: 2.5 },
2145
+ { id: 6, type: "wire", x1: 200, y1: 300, x2: 200, y2: 100, label: "", value: "", color: "#334155", strokeWidth: 3 },
2146
+ { id: 7, type: "wire", x1: 200, y1: 100, x2: 340, y2: 100, label: "", value: "", color: "#334155", strokeWidth: 3 },
2147
+ { id: 8, type: "meter_v", x1: 340, y1: 100, x2: 460, y2: 100, label: "", value: "", color: "#0369a1", strokeWidth: 2.5 },
2148
+ { id: 9, type: "wire", x1: 460, y1: 100, x2: 600, y2: 100, label: "", value: "", color: "#334155", strokeWidth: 3 },
2149
+ { id: 10, type: "wire", x1: 600, y1: 100, x2: 600, y2: 300, label: "", value: "", color: "#334155", strokeWidth: 3 },
2150
+ { id: 11, type: "text", x1: 180, y1: 300, x2: 180, y2: 300, label: "S", value: "", fontFamily: "sans-serif", fontWeight: "bold", fontSize: 18, color: "#0f172a" },
2151
+ { id: 12, type: "text", x1: 400, y1: 180, x2: 400, y2: 180, label: "P", value: "", fontFamily: "sans-serif", fontWeight: "bold", fontSize: 18, color: "#0f172a" },
2152
+ { id: 13, type: "text", x1: 400, y1: 420, x2: 400, y2: 420, label: "Q", value: "", fontFamily: "sans-serif", fontWeight: "bold", fontSize: 18, color: "#0f172a" },
2153
+ { id: 14, type: "text", x1: 620, y1: 300, x2: 620, y2: 300, label: "T", value: "", fontFamily: "sans-serif", fontWeight: "bold", fontSize: 18, color: "#0f172a" }
2154
+ ]
2155
+ };
2156
+ var seriesRC = {
2157
+ name: "Series RC (charging)",
2158
+ elements: [
2159
+ { id: 1, type: "text", x1: 320, y1: 40, x2: 320, y2: 40, label: "Single-loop RC after switch closes", color: "#475569", fontSize: 16 },
2160
+ { id: 2, type: "battery", x1: 120, y1: 360, x2: 120, y2: 220, label: "\u03B5", value: "12 V", color: "#1d4ed8", strokeWidth: 2.5 },
2161
+ { id: 3, type: "wire", x1: 120, y1: 220, x2: 220, y2: 220, color: "#334155", strokeWidth: 3 },
2162
+ { id: 4, type: "resistor", x1: 220, y1: 220, x2: 380, y2: 220, label: "R", value: "2.2 k\u03A9", color: "#b45309", strokeWidth: 2.5 },
2163
+ { id: 5, type: "wire", x1: 380, y1: 220, x2: 480, y2: 220, color: "#334155", strokeWidth: 3 },
2164
+ { id: 6, type: "capacitor", x1: 480, y1: 220, x2: 620, y2: 220, label: "C", value: "47 \u03BCF", color: "#0f172a", strokeWidth: 2.5 },
2165
+ { id: 7, type: "wire", x1: 620, y1: 220, x2: 620, y2: 360, color: "#334155", strokeWidth: 3 },
2166
+ { id: 8, type: "wire", x1: 620, y1: 360, x2: 120, y2: 360, color: "#334155", strokeWidth: 3 },
2167
+ { id: 9, type: "text", x1: 360, y1: 120, x2: 360, y2: 120, label: "\u03C4 = RC", color: "#0f172a", fontSize: 18 }
2168
+ ]
2169
+ };
2170
+ var parallelResistors = {
2171
+ name: "Parallel resistors",
2172
+ elements: [
2173
+ { id: 1, type: "text", x1: 300, y1: 40, x2: 300, y2: 40, label: "Same voltage across each branch", color: "#475569", fontSize: 16 },
2174
+ { id: 2, type: "battery", x1: 120, y1: 380, x2: 120, y2: 120, label: "V", value: "9 V", color: "#1d4ed8", strokeWidth: 2.5 },
2175
+ { id: 3, type: "wire", x1: 120, y1: 120, x2: 520, y2: 120, color: "#334155", strokeWidth: 3 },
2176
+ { id: 4, type: "resistor", x1: 260, y1: 120, x2: 260, y2: 300, label: "R\u2081", value: "300 \u03A9", color: "#b45309", strokeWidth: 2.5 },
2177
+ { id: 5, type: "resistor", x1: 400, y1: 120, x2: 400, y2: 300, label: "R\u2082", value: "600 \u03A9", color: "#b45309", strokeWidth: 2.5 },
2178
+ { id: 6, type: "wire", x1: 260, y1: 300, x2: 400, y2: 300, color: "#334155", strokeWidth: 3 },
2179
+ { id: 7, type: "wire", x1: 260, y1: 300, x2: 220, y2: 300, color: "#334155", strokeWidth: 3 },
2180
+ { id: 8, type: "meter_a", x1: 220, y1: 300, x2: 120, y2: 300, label: "I", value: "", color: "#b45309", strokeWidth: 2.5 },
2181
+ { id: 9, type: "wire", x1: 120, y1: 300, x2: 120, y2: 380, color: "#334155", strokeWidth: 3 },
2182
+ { id: 10, type: "text", x1: 280, y1: 340, x2: 280, y2: 340, label: "1/R_eq = 1/R\u2081 + 1/R\u2082", color: "#0f172a", fontSize: 15 }
2183
+ ]
2184
+ };
2185
+ var circuitExamples = [wheatstoneBridge, seriesRC, parallelResistors];
1676
2186
  var circuit = {
1677
2187
  name: "Circuit",
1678
- elements: [
1679
- { id: 1, type: "resistor", x1: 200, y1: 300, x2: 400, y2: 200, label: "R1", value: "60\u03A9" },
1680
- { id: 2, type: "resistor", x1: 200, y1: 300, x2: 400, y2: 400, label: "R3", value: "300\u03A9" },
1681
- { id: 3, type: "resistor", x1: 400, y1: 200, x2: 600, y2: 300, label: "R2", value: "100\u03A9" },
1682
- { id: 4, type: "resistor", x1: 400, y1: 400, x2: 600, y2: 300, label: "R4", value: "500\u03A9" },
1683
- { id: 5, type: "battery", x1: 400, y1: 400, x2: 400, y2: 200, label: "50V", value: "" },
1684
- { id: 6, type: "wire", x1: 200, y1: 300, x2: 200, y2: 100, label: "", value: "" },
1685
- { id: 7, type: "wire", x1: 200, y1: 100, x2: 340, y2: 100, label: "", value: "" },
1686
- { id: 8, type: "meter_v", x1: 340, y1: 100, x2: 460, y2: 100, label: "", value: "" },
1687
- { id: 9, type: "wire", x1: 460, y1: 100, x2: 600, y2: 100, label: "", value: "" },
1688
- { id: 10, type: "wire", x1: 600, y1: 100, x2: 600, y2: 300, label: "", value: "" },
1689
- { id: 11, type: "text", x1: 180, y1: 300, x2: 180, y2: 300, label: "S", value: "", fontFamily: "sans-serif", fontWeight: "bold", fontSize: 18 },
1690
- { id: 12, type: "text", x1: 400, y1: 180, x2: 400, y2: 180, label: "P", value: "", fontFamily: "sans-serif", fontWeight: "bold", fontSize: 18 },
1691
- { id: 13, type: "text", x1: 400, y1: 420, x2: 400, y2: 420, label: "Q", value: "", fontFamily: "sans-serif", fontWeight: "bold", fontSize: 18 },
1692
- { id: 14, type: "text", x1: 620, y1: 300, x2: 620, y2: 300, label: "T", value: "", fontFamily: "sans-serif", fontWeight: "bold", fontSize: 18 }
1693
- ],
2188
+ elements: circuitExamples[0].elements,
2189
+ examples: circuitExamples,
1694
2190
  toolCategories: [
1695
2191
  {
1696
2192
  name: "Circuit",
@@ -1712,91 +2208,130 @@ var circuit = {
1712
2208
  };
1713
2209
 
1714
2210
  // src/editor/presets/thermo.ts
1715
- var thermo = {
1716
- name: "Thermodynamics",
2211
+ var pistonAndGas = {
2212
+ name: "Piston, heat & first law",
1717
2213
  elements: [
1718
- { id: 101, type: "text", x1: 50, y1: 40, x2: 50, y2: 40, label: "1. First Law (Piston/Cylinder)", fontSize: 20, color: "#1e293b" },
2214
+ { id: 1, type: "text", x1: 120, y1: 40, x2: 120, y2: 40, label: "Gas in a cylinder (work, heat, \u0394U)", color: "#475569", fontSize: 18 },
1719
2215
  {
1720
- id: 1,
2216
+ id: 2,
1721
2217
  type: "piston_cylinder",
1722
- x1: 80,
1723
- y1: 100,
1724
- x2: 240,
1725
- y2: 260,
1726
- compression: 0.6,
2218
+ x1: 120,
2219
+ y1: 120,
2220
+ x2: 320,
2221
+ y2: 320,
2222
+ compression: 0.55,
1727
2223
  showHeat: true,
1728
2224
  color: "#0ea5e9",
1729
2225
  strokeWidth: 2,
1730
- label: "Gas Expansion"
2226
+ label: "Expansion stroke"
1731
2227
  },
1732
- { id: 102, type: "text", x1: 380, y1: 40, x2: 380, y2: 40, label: "2. Heat Engine & 2nd Law", fontSize: 20, color: "#1e293b" },
2228
+ {
2229
+ id: 3,
2230
+ type: "thermo_process",
2231
+ x1: 400,
2232
+ y1: 200,
2233
+ x2: 560,
2234
+ y2: 120,
2235
+ processType: "isobaric",
2236
+ color: "#f59e0b",
2237
+ strokeWidth: 2,
2238
+ label: "Isobaric leg"
2239
+ },
2240
+ { id: 4, type: "text", x1: 400, y1: 280, x2: 400, y2: 280, label: "Q = \u0394U + W", color: "#0f172a", fontSize: 18 }
2241
+ ]
2242
+ };
2243
+ var engineAndPV = {
2244
+ name: "Heat engine & P\u2013V cycle",
2245
+ elements: [
2246
+ { id: 1, type: "text", x1: 80, y1: 40, x2: 80, y2: 40, label: "Carnot engine between two reservoirs", color: "#475569", fontSize: 18 },
1733
2247
  {
1734
2248
  id: 2,
1735
2249
  type: "heat_engine",
1736
- x1: 440,
1737
- y1: 80,
1738
- x2: 540,
1739
- y2: 260,
2250
+ x1: 120,
2251
+ y1: 100,
2252
+ x2: 260,
2253
+ y2: 300,
1740
2254
  engineType: "engine",
1741
- efficiency: 0.4,
2255
+ efficiency: 0.35,
1742
2256
  color: "#10b981",
1743
2257
  strokeWidth: 2,
1744
- label: "Carnot Engine"
2258
+ label: "\u03B7 < 1"
1745
2259
  },
1746
- { id: 103, type: "text", x1: 700, y1: 40, x2: 700, y2: 40, label: "3. P-V Diagrams", fontSize: 20, color: "#1e293b" },
1747
2260
  {
1748
2261
  id: 3,
1749
2262
  type: "pv_axes",
1750
- x1: 720,
1751
- y1: 260,
1752
- x2: 920,
1753
- y2: 80,
2263
+ x1: 360,
2264
+ y1: 300,
2265
+ x2: 620,
2266
+ y2: 100,
1754
2267
  color: "#1e293b",
1755
2268
  strokeWidth: 2,
1756
- label: "P vs V",
2269
+ label: "State space",
1757
2270
  xLabel: "V",
1758
2271
  yLabel: "P"
1759
2272
  },
1760
- { id: 4, type: "carnot_cycle", x1: 760, y1: 120, x2: 880, y2: 220, color: "#ec4899", strokeWidth: 2 },
1761
- { id: 104, type: "text", x1: 50, y1: 340, x2: 50, y2: 340, label: "4. Kinetic Theory & Speeds", fontSize: 20, color: "#1e293b" },
2273
+ { id: 4, type: "carnot_cycle", x1: 400, y1: 140, x2: 560, y2: 240, color: "#ec4899", strokeWidth: 2 },
2274
+ { id: 5, type: "text", x1: 640, y1: 180, x2: 640, y2: 180, label: "Isotherms + adiabats", color: "#64748b", fontSize: 15 }
2275
+ ]
2276
+ };
2277
+ var kineticTheory = {
2278
+ name: "Kinetic theory & conduction",
2279
+ elements: [
2280
+ { id: 1, type: "text", x1: 80, y1: 40, x2: 80, y2: 40, label: "Speed distribution & thermal transport", color: "#475569", fontSize: 18 },
1762
2281
  {
1763
- id: 5,
2282
+ id: 2,
1764
2283
  type: "maxwell_boltzmann",
1765
2284
  x1: 80,
1766
- y1: 520,
1767
- x2: 320,
1768
- y2: 380,
2285
+ y1: 320,
2286
+ x2: 340,
2287
+ y2: 160,
1769
2288
  t1: 300,
1770
2289
  t2: 600,
1771
2290
  color: "#8b5cf6",
1772
2291
  strokeWidth: 2,
1773
- label: "f(v) vs v"
2292
+ label: "f(v) at T\u2081, T\u2082"
1774
2293
  },
1775
2294
  {
1776
- id: 6,
2295
+ id: 3,
1777
2296
  type: "diatomic_gas",
1778
- x1: 400,
1779
- y1: 440,
1780
- x2: 500,
1781
- y2: 440,
2297
+ x1: 420,
2298
+ y1: 220,
2299
+ x2: 520,
2300
+ y2: 220,
1782
2301
  color: "#f59e0b",
1783
2302
  strokeWidth: 2,
1784
- label: "Diatomic (5 DOF)",
2303
+ label: "5 DOF (trans + rot)",
1785
2304
  showRotations: true
1786
2305
  },
1787
- { id: 105, type: "text", x1: 700, y1: 340, x2: 700, y2: 340, label: "5. Thermal Conduction", fontSize: 20, color: "#1e293b" },
1788
2306
  {
1789
- id: 7,
2307
+ id: 4,
1790
2308
  type: "conduction_rod",
1791
- x1: 700,
1792
- y1: 420,
1793
- x2: 900,
1794
- y2: 460,
2309
+ x1: 420,
2310
+ y1: 320,
2311
+ x2: 720,
2312
+ y2: 360,
1795
2313
  color: "#3b82f6",
1796
2314
  strokeWidth: 2,
1797
- label: "L, k, A"
2315
+ label: "Q\u0307 = kA\u0394T/L"
2316
+ },
2317
+ {
2318
+ id: 5,
2319
+ type: "random_walk",
2320
+ x1: 600,
2321
+ y1: 120,
2322
+ x2: 720,
2323
+ y2: 80,
2324
+ color: "#64748b",
2325
+ strokeWidth: 2,
2326
+ label: "Diffusion"
1798
2327
  }
1799
- ],
2328
+ ]
2329
+ };
2330
+ var thermoExamples = [pistonAndGas, engineAndPV, kineticTheory];
2331
+ var thermo = {
2332
+ name: "Thermodynamics",
2333
+ elements: thermoExamples[0].elements,
2334
+ examples: thermoExamples,
1800
2335
  toolCategories: [
1801
2336
  {
1802
2337
  name: "Basics",
@@ -1834,81 +2369,55 @@ var thermo = {
1834
2369
  ]
1835
2370
  };
1836
2371
 
1837
- // src/editor/presets/optical.ts
1838
- var optical = {
1839
- name: "Optical",
2372
+ // src/editor/presets/magnetism.ts
2373
+ var electromagnetGap = {
2374
+ name: "Electromagnet & uniform gap",
1840
2375
  elements: [
1841
- { id: 1, type: "line", x1: 50, y1: 300, x2: 750, y2: 300, lineStyle: "dashed", color: "#94a3b8", strokeWidth: 2 },
1842
- { id: 2, type: "lens_convex", x1: 400, y1: 150, x2: 400, y2: 450, color: "#38bdf8", strokeWidth: 2 },
1843
- { id: 3, type: "point", x1: 250, y1: 300, x2: 250, y2: 300, label: "2F\u2081", color: "#64748b" },
1844
- { id: 4, type: "point", x1: 325, y1: 300, x2: 325, y2: 300, label: "F\u2081", color: "#64748b" },
1845
- { id: 5, type: "point", x1: 475, y1: 300, x2: 475, y2: 300, label: "F\u2082", color: "#64748b" },
1846
- { id: 6, type: "point", x1: 550, y1: 300, x2: 550, y2: 300, label: "2F\u2082", color: "#64748b" },
1847
- { id: 7, type: "vector", x1: 250, y1: 300, x2: 250, y2: 200, label: "O", color: "#10b981", strokeWidth: 3 },
1848
- { id: 8, type: "ray", x1: 250, y1: 200, x2: 400, y2: 200, color: "#ef4444", strokeWidth: 2 },
1849
- { id: 9, type: "ray", x1: 400, y1: 200, x2: 625, y2: 500, color: "#ef4444", strokeWidth: 2 },
1850
- { id: 10, type: "ray", x1: 250, y1: 200, x2: 625, y2: 450, color: "#ef4444", strokeWidth: 2 },
1851
- { id: 11, type: "vector", x1: 550, y1: 300, x2: 550, y2: 400, label: "I", color: "#f59e0b", strokeWidth: 3, lineStyle: "dashed" },
1852
- { id: 12, type: "nucleus", x1: 120, y1: 500, x2: 120, y2: 500, label: "Atom", color: "#3b82f6", strokeWidth: 2 },
1853
- { id: 13, type: "photon", x1: 160, y1: 500, x2: 280, y2: 460, label: "\u03B3", color: "#8b5cf6", strokeWidth: 2 },
1854
- { id: 14, type: "slit_double", x1: 600, y1: 460, x2: 600, y2: 580, color: "#0f172a", strokeWidth: 4 },
1855
- { id: 15, type: "photon", x1: 510, y1: 520, x2: 590, y2: 520, label: "\u03BB", color: "#eab308", strokeWidth: 2 }
1856
- ],
1857
- toolCategories: [
1858
- {
1859
- name: "Basics",
1860
- tools: [
1861
- { id: "select", name: "Select/Move" },
1862
- { id: "point", name: "Point/Node" },
1863
- { id: "line", name: "Solid Line" },
1864
- { id: "vector", name: "Vector/Object" },
1865
- { id: "text", name: "Text Label" }
1866
- ]
1867
- },
1868
- {
1869
- name: "Optics",
1870
- tools: [
1871
- { id: "ray", name: "Light Ray" },
1872
- { id: "lens_convex", name: "Convex Lens" },
1873
- { id: "lens_concave", name: "Concave Lens" },
1874
- { id: "mirror_plane", name: "Plane Mirror" },
1875
- { id: "mirror_concave", name: "Concave Mirror" },
1876
- { id: "mirror_convex", name: "Convex Mirror" },
1877
- { id: "prism", name: "Glass Prism" },
1878
- { id: "glass_slab", name: "Glass Slab" }
1879
- ]
1880
- },
1881
- {
1882
- name: "Modern Physics",
1883
- tools: [
1884
- { id: "photon", name: "Photon (Wavy)" },
1885
- { id: "slit_double", name: "Double Slit" },
1886
- { id: "slit_single", name: "Single Slit" },
1887
- { id: "energy_level", name: "Energy Level" },
1888
- { id: "nucleus", name: "Atom/Nucleus" }
1889
- ]
1890
- }
2376
+ { id: 1, type: "text", x1: 280, y1: 36, x2: 280, y2: 36, label: "Iron pole pieces + straight B in the gap", color: "#475569", fontSize: 16 },
2377
+ { id: 2, type: "pole_piece", x1: 100, y1: 80, x2: 200, y2: 300, label: "N", color: "#0f172a", strokeWidth: 2 },
2378
+ { id: 3, type: "pole_piece", x1: 500, y1: 80, x2: 600, y2: 300, label: "S", color: "#0f172a", strokeWidth: 2 },
2379
+ { id: 4, type: "b_field_line", x1: 200, y1: 120, x2: 500, y2: 120, label: "", color: "#10b981", strokeWidth: 2 },
2380
+ { id: 5, type: "b_field_line", x1: 200, y1: 190, x2: 500, y2: 190, label: "B", color: "#10b981", strokeWidth: 2, fontSize: 18 },
2381
+ { id: 6, type: "b_field_line", x1: 200, y1: 260, x2: 500, y2: 260, label: "", color: "#10b981", strokeWidth: 2 },
2382
+ { id: 7, type: "b_field_curve", x1: 280, y1: 80, x2: 420, y2: 80, curveHeight: 50, label: "", color: "#10b981", strokeWidth: 2 },
2383
+ { id: 8, type: "b_field_curve", x1: 250, y1: 80, x2: 450, y2: 80, curveHeight: 110, label: "", color: "#10b981", strokeWidth: 2 },
2384
+ { id: 9, type: "axes_3d", x1: 200, y1: 540, x2: 300, y2: 540, label: "", color: "#64748b", strokeWidth: 2, fontSize: 16 },
2385
+ { id: 10, type: "line", x1: 200, y1: 540, x2: 200, y2: 400, label: "", color: "#0ea5e9", strokeWidth: 3 },
2386
+ { id: 11, type: "line", x1: 200, y1: 400, x2: 320, y2: 400, label: "i", color: "#0ea5e9", strokeWidth: 3, fontSize: 16 },
2387
+ { id: 12, type: "line", x1: 320, y1: 400, x2: 320, y2: 540, label: "", color: "#0ea5e9", strokeWidth: 3 },
2388
+ { id: 13, type: "line", x1: 320, y1: 540, x2: 200, y2: 540, label: "", color: "#0ea5e9", strokeWidth: 3 },
2389
+ { id: 14, type: "vector", x1: 200, y1: 540, x2: 320, y2: 620, label: "B", color: "#10b981", strokeWidth: 2.5, fontSize: 18 }
1891
2390
  ]
1892
2391
  };
1893
-
1894
- // src/editor/presets/magnetism.ts
2392
+ var barMagnetField = {
2393
+ name: "Bar magnet & field lines",
2394
+ elements: [
2395
+ { id: 1, type: "text", x1: 260, y1: 36, x2: 260, y2: 36, label: "Dipole field outside the magnet", color: "#475569", fontSize: 16 },
2396
+ { id: 2, type: "bar_magnet", x1: 200, y1: 260, x2: 520, y2: 260, label: "Permanent magnet", color: "#0f172a", strokeWidth: 2 },
2397
+ { id: 3, type: "b_field_curve", x1: 220, y1: 120, x2: 480, y2: 120, curveHeight: 80, color: "#10b981", strokeWidth: 2 },
2398
+ { id: 4, type: "b_field_curve", x1: 200, y1: 400, x2: 500, y2: 400, curveHeight: -90, color: "#10b981", strokeWidth: 2 },
2399
+ { id: 5, type: "b_field_line", x1: 360, y1: 140, x2: 360, y2: 220, color: "#10b981", strokeWidth: 2 },
2400
+ { id: 6, type: "b_field_line", x1: 360, y1: 300, x2: 360, y2: 380, color: "#10b981", strokeWidth: 2 },
2401
+ { id: 7, type: "text", x1: 600, y1: 240, x2: 600, y2: 240, label: "N \u2192 S outside", color: "#0f172a", fontSize: 15 }
2402
+ ]
2403
+ };
2404
+ var longStraightWire = {
2405
+ name: "Long straight wire (Amp\xE8re)",
2406
+ elements: [
2407
+ { id: 1, type: "text", x1: 240, y1: 36, x2: 240, y2: 36, label: "Current into page \u2297 \u2014 circular B around the wire", color: "#475569", fontSize: 15 },
2408
+ { id: 2, type: "current_wire", x1: 400, y1: 120, x2: 400, y2: 420, label: "I", color: "#dc2626", strokeWidth: 3 },
2409
+ { id: 3, type: "b_field_curve", x1: 520, y1: 200, x2: 280, y2: 200, curveHeight: 120, color: "#10b981", strokeWidth: 2 },
2410
+ { id: 4, type: "b_field_curve", x1: 540, y1: 280, x2: 260, y2: 280, curveHeight: 140, color: "#10b981", strokeWidth: 2 },
2411
+ { id: 5, type: "b_field_curve", x1: 500, y1: 360, x2: 300, y2: 360, curveHeight: 100, color: "#10b981", strokeWidth: 2 },
2412
+ { id: 6, type: "b_region_in", x1: 360, y1: 240, x2: 440, y2: 320, color: "#6366f1", strokeWidth: 1.5 },
2413
+ { id: 7, type: "text", x1: 400, y1: 460, x2: 400, y2: 460, label: "\u222E B\xB7dl = \u03BC\u2080I", color: "#0f172a", fontSize: 17 }
2414
+ ]
2415
+ };
2416
+ var magnetismExamples = [electromagnetGap, barMagnetField, longStraightWire];
1895
2417
  var magnetism = {
1896
2418
  name: "Magnetism",
1897
- elements: [
1898
- { id: 1, type: "pole_piece", x1: 100, y1: 80, x2: 200, y2: 300, label: "N", color: "#0f172a", strokeWidth: 2 },
1899
- { id: 2, type: "pole_piece", x1: 500, y1: 80, x2: 600, y2: 300, label: "S", color: "#0f172a", strokeWidth: 2 },
1900
- { id: 3, type: "b_field_line", x1: 200, y1: 120, x2: 500, y2: 120, label: "", color: "#10b981", strokeWidth: 2 },
1901
- { id: 4, type: "b_field_line", x1: 200, y1: 190, x2: 500, y2: 190, label: "B", color: "#10b981", strokeWidth: 2, fontSize: 18 },
1902
- { id: 5, type: "b_field_line", x1: 200, y1: 260, x2: 500, y2: 260, label: "", color: "#10b981", strokeWidth: 2 },
1903
- { id: 6, type: "b_field_curve", x1: 280, y1: 80, x2: 420, y2: 80, curveHeight: 50, label: "", color: "#10b981", strokeWidth: 2 },
1904
- { id: 7, type: "b_field_curve", x1: 250, y1: 80, x2: 450, y2: 80, curveHeight: 110, label: "", color: "#10b981", strokeWidth: 2 },
1905
- { id: 8, type: "axes_3d", x1: 200, y1: 540, x2: 300, y2: 540, label: "", color: "#64748b", strokeWidth: 2, fontSize: 16 },
1906
- { id: 9, type: "line", x1: 200, y1: 540, x2: 200, y2: 400, label: "", color: "#0ea5e9", strokeWidth: 3 },
1907
- { id: 10, type: "line", x1: 200, y1: 400, x2: 320, y2: 400, label: "i", color: "#0ea5e9", strokeWidth: 3, fontSize: 16 },
1908
- { id: 11, type: "line", x1: 320, y1: 400, x2: 320, y2: 540, label: "", color: "#0ea5e9", strokeWidth: 3 },
1909
- { id: 12, type: "line", x1: 320, y1: 540, x2: 200, y2: 540, label: "", color: "#0ea5e9", strokeWidth: 3 },
1910
- { id: 13, type: "vector", x1: 200, y1: 540, x2: 320, y2: 620, label: "B", color: "#10b981", strokeWidth: 2.5, fontSize: 18 }
1911
- ],
2419
+ elements: magnetismExamples[0].elements,
2420
+ examples: magnetismExamples,
1912
2421
  toolCategories: [
1913
2422
  {
1914
2423
  name: "Basics",
@@ -1958,10 +2467,10 @@ var magnetism = {
1958
2467
  ]
1959
2468
  };
1960
2469
 
1961
- // src/editor/presets/lightTopics.ts
1962
- var LIGHT_TOPICS = [
2470
+ // src/editor/presets/light.ts
2471
+ var lightExamples = [
1963
2472
  {
1964
- name: "Ch 34: Lens Combination (JEE Classic)",
2473
+ name: "Lens combination",
1965
2474
  elements: [
1966
2475
  { id: 1, type: "line", x1: 50, y1: 300, x2: 850, y2: 300, lineStyle: "dashed", color: "#94a3b8", strokeWidth: 2 },
1967
2476
  { id: 2, type: "lens_convex", x1: 300, y1: 150, x2: 300, y2: 450, color: "#38bdf8", strokeWidth: 2, label: "f\u2081 = +30cm" },
@@ -1974,11 +2483,11 @@ var LIGHT_TOPICS = [
1974
2483
  { id: 9, type: "ray", x1: 500, y1: 334, x2: 800, y2: 334, color: "#ef4444", strokeWidth: 2 },
1975
2484
  { id: 10, type: "line", x1: 500, y1: 266, x2: 600, y2: 300, lineStyle: "dashed", color: "#ef4444", strokeWidth: 2 },
1976
2485
  { id: 11, type: "line", x1: 500, y1: 334, x2: 600, y2: 300, lineStyle: "dashed", color: "#ef4444", strokeWidth: 2 },
1977
- { id: 12, type: "point", x1: 600, y1: 300, x2: 600, y2: 300, label: "Virtual Object", color: "#64748b" }
2486
+ { id: 12, type: "point", x1: 600, y1: 300, x2: 600, y2: 300, label: "Virtual object", color: "#64748b" }
1978
2487
  ]
1979
2488
  },
1980
2489
  {
1981
- name: "Ch 34: Concave Mirror (Real Image)",
2490
+ name: "Concave mirror (real image)",
1982
2491
  elements: [
1983
2492
  { id: 1, type: "line", x1: 50, y1: 300, x2: 700, y2: 300, lineStyle: "dashed", color: "#94a3b8", strokeWidth: 2 },
1984
2493
  { id: 2, type: "mirror_concave", x1: 600, y1: 150, x2: 600, y2: 450, curveHeight: 40, color: "#0f172a", strokeWidth: 2 },
@@ -1994,28 +2503,28 @@ var LIGHT_TOPICS = [
1994
2503
  ]
1995
2504
  },
1996
2505
  {
1997
- name: "Ch 34: Apparent Depth (Refraction)",
2506
+ name: "Apparent depth (refraction)",
1998
2507
  elements: [
1999
2508
  { id: 1, type: "glass_slab", x1: 50, y1: 300, x2: 850, y2: 600, color: "#38bdf8", label: "Water (\u03BC = 4/3)", strokeWidth: 2 },
2000
- { id: 2, type: "point", x1: 450, y1: 500, x2: 450, y2: 500, label: "Real Object", color: "#0f172a" },
2509
+ { id: 2, type: "point", x1: 450, y1: 500, x2: 450, y2: 500, label: "Real object", color: "#0f172a" },
2001
2510
  { id: 3, type: "ray", x1: 450, y1: 500, x2: 450, y2: 300, color: "#ef4444", strokeWidth: 2 },
2002
2511
  { id: 4, type: "ray", x1: 450, y1: 300, x2: 450, y2: 100, color: "#ef4444", strokeWidth: 2 },
2003
2512
  { id: 5, type: "ray", x1: 450, y1: 500, x2: 550, y2: 300, color: "#ef4444", strokeWidth: 2 },
2004
2513
  { id: 6, type: "ray", x1: 550, y1: 300, x2: 700, y2: 150, color: "#ef4444", strokeWidth: 2 },
2005
2514
  { id: 7, type: "line", x1: 550, y1: 300, x2: 450, y2: 400, lineStyle: "dashed", color: "#ef4444", strokeWidth: 2 },
2006
- { id: 8, type: "point", x1: 450, y1: 400, x2: 450, y2: 400, label: "Virtual Image", color: "#f59e0b" },
2515
+ { id: 8, type: "point", x1: 450, y1: 400, x2: 450, y2: 400, label: "Virtual image", color: "#f59e0b" },
2007
2516
  { id: 9, type: "line", x1: 550, y1: 200, x2: 550, y2: 400, lineStyle: "dashed", color: "#94a3b8", strokeWidth: 1 }
2008
2517
  ]
2009
2518
  },
2010
2519
  {
2011
- name: "Ch 35: Young's Double Slit (YDSE)",
2520
+ name: "Double-slit interference",
2012
2521
  elements: [
2013
2522
  { id: 1, type: "slit_double", x1: 200, y1: 200, x2: 200, y2: 400, color: "#0f172a", strokeWidth: 4 },
2014
2523
  { id: 2, type: "line", x1: 700, y1: 100, x2: 700, y2: 500, color: "#0f172a", strokeWidth: 4, label: "Screen" },
2015
2524
  { id: 3, type: "line", x1: 200, y1: 300, x2: 700, y2: 300, lineStyle: "dashed", color: "#94a3b8", strokeWidth: 2 },
2016
2525
  { id: 4, type: "point", x1: 200, y1: 250, x2: 200, y2: 250, label: "S\u2081", color: "#0f172a" },
2017
2526
  { id: 5, type: "point", x1: 200, y1: 350, x2: 200, y2: 350, label: "S\u2082", color: "#0f172a" },
2018
- { id: 6, type: "point", x1: 700, y1: 150, x2: 700, y2: 150, label: "P (Maxima/Minima)", color: "#0f172a" },
2527
+ { id: 6, type: "point", x1: 700, y1: 150, x2: 700, y2: 150, label: "P (max/min)", color: "#0f172a" },
2019
2528
  { id: 7, type: "photon", x1: 200, y1: 250, x2: 700, y2: 150, color: "#eab308", strokeWidth: 2 },
2020
2529
  { id: 8, type: "photon", x1: 200, y1: 350, x2: 700, y2: 150, color: "#eab308", strokeWidth: 2 },
2021
2530
  { id: 9, type: "line", x1: 200, y1: 250, x2: 240, y2: 340, lineStyle: "dashed", color: "#ef4444", strokeWidth: 2 },
@@ -2026,7 +2535,7 @@ var LIGHT_TOPICS = [
2026
2535
  ]
2027
2536
  },
2028
2537
  {
2029
- name: "Ch 36: Single Slit Diffraction",
2538
+ name: "Single-slit diffraction",
2030
2539
  elements: [
2031
2540
  { id: 1, type: "slit_single", x1: 300, y1: 200, x2: 300, y2: 400, color: "#0f172a", strokeWidth: 5, label: "Width a" },
2032
2541
  { id: 2, type: "line", x1: 700, y1: 100, x2: 700, y2: 500, color: "#0f172a", strokeWidth: 4 },
@@ -2037,89 +2546,77 @@ var LIGHT_TOPICS = [
2037
2546
  { id: 7, type: "ray", x1: 300, y1: 380, x2: 700, y2: 300, color: "#ef4444", strokeWidth: 2 },
2038
2547
  { id: 8, type: "ray", x1: 300, y1: 220, x2: 700, y2: 150, color: "#3b82f6", strokeWidth: 2 },
2039
2548
  { id: 9, type: "ray", x1: 300, y1: 380, x2: 700, y2: 150, color: "#3b82f6", strokeWidth: 2 },
2040
- { id: 10, type: "point", x1: 700, y1: 300, x2: 700, y2: 300, label: "Central Max", color: "#ef4444" },
2041
- { id: 11, type: "point", x1: 700, y1: 150, x2: 700, y2: 150, label: "1st Min (a sin \u03B8 = \u03BB)", color: "#3b82f6" }
2549
+ { id: 10, type: "point", x1: 700, y1: 300, x2: 700, y2: 300, label: "Central max", color: "#ef4444" },
2550
+ { id: 11, type: "point", x1: 700, y1: 150, x2: 700, y2: 150, label: "1st min (a sin \u03B8 = \u03BB)", color: "#3b82f6" }
2042
2551
  ]
2043
2552
  }
2044
2553
  ];
2045
-
2046
- // src/editor/presets/light.ts
2047
2554
  var light = {
2048
- name: "Light",
2049
- elements: LIGHT_TOPICS[0].elements,
2555
+ name: "Light & optics",
2556
+ elements: lightExamples[0].elements,
2557
+ examples: lightExamples,
2050
2558
  toolCategories: [
2051
2559
  {
2052
2560
  name: "Basics",
2053
2561
  tools: [
2054
2562
  { id: "select", name: "Select/Move" },
2055
2563
  { id: "point", name: "Point/Node" },
2056
- { id: "line", name: "Solid Line" },
2564
+ { id: "line", name: "Solid line" },
2057
2565
  { id: "vector", name: "Vector/Object" },
2058
- { id: "text", name: "Text Label" }
2566
+ { id: "text", name: "Text label" }
2059
2567
  ]
2060
2568
  },
2061
2569
  {
2062
2570
  name: "Optics",
2063
2571
  tools: [
2064
- { id: "ray", name: "Light Ray" },
2065
- { id: "lens_convex", name: "Convex Lens" },
2066
- { id: "lens_concave", name: "Concave Lens" },
2067
- { id: "mirror_plane", name: "Plane Mirror" },
2068
- { id: "mirror_concave", name: "Concave Mirror" },
2069
- { id: "mirror_convex", name: "Convex Mirror" },
2070
- { id: "prism", name: "Glass Prism" },
2071
- { id: "glass_slab", name: "Glass Slab" }
2572
+ { id: "ray", name: "Light ray" },
2573
+ { id: "lens_convex", name: "Convex lens" },
2574
+ { id: "lens_concave", name: "Concave lens" },
2575
+ { id: "mirror_plane", name: "Plane mirror" },
2576
+ { id: "mirror_concave", name: "Concave mirror" },
2577
+ { id: "mirror_convex", name: "Convex mirror" },
2578
+ { id: "prism", name: "Glass prism" },
2579
+ { id: "glass_slab", name: "Glass slab" }
2072
2580
  ]
2073
2581
  },
2074
2582
  {
2075
- name: "Modern Physics",
2583
+ name: "Modern physics",
2076
2584
  tools: [
2077
- { id: "photon", name: "Photon (Wavy)" },
2078
- { id: "slit_double", name: "Double Slit" },
2079
- { id: "slit_single", name: "Single Slit" },
2080
- { id: "energy_level", name: "Energy Level" },
2585
+ { id: "photon", name: "Photon (wavy)" },
2586
+ { id: "slit_double", name: "Double slit" },
2587
+ { id: "slit_single", name: "Single slit" },
2588
+ { id: "energy_level", name: "Energy level" },
2081
2589
  { id: "nucleus", name: "Atom/Nucleus" }
2082
2590
  ]
2083
2591
  }
2084
2592
  ]
2085
2593
  };
2086
2594
 
2087
- // src/editor/presets/waves.ts
2088
- var waves = {
2089
- name: "Waves",
2090
- elements: [
2091
- { id: 1, type: "line", x1: 100, y1: 300, x2: 800, y2: 300, color: "#64748b", strokeWidth: 2 },
2092
- { id: 2, type: "slit_double", x1: 200, y1: 200, x2: 200, y2: 400, color: "#0f172a", strokeWidth: 4 },
2093
- { id: 3, type: "photon", x1: 200, y1: 250, x2: 700, y2: 150, color: "#eab308", strokeWidth: 2 }
2094
- ],
2095
- toolCategories: [{ name: "Waves", tools: [{ id: "select", name: "Select" }, { id: "line", name: "Axis" }, { id: "slit_double", name: "Double Slit" }, { id: "photon", name: "Wave Ray" }, { id: "text", name: "Text" }] }]
2096
- };
2097
-
2098
2595
  // src/editor/presets/waveOscillation.ts
2099
- var waveOscillation = {
2100
- name: "Oscillations & Waves",
2596
+ var shmKinematics = {
2597
+ name: "SHM kinematics",
2101
2598
  elements: [
2102
- { id: 101, type: "text", x1: 50, y1: 40, x2: 50, y2: 40, label: "1. SHM Kinematics & Freeze-Frames", fontSize: 20, color: "#1e293b" },
2599
+ { id: 1, type: "text", x1: 120, y1: 36, x2: 120, y2: 36, label: "Motion along x vs time graphs (x, v, a)", color: "#475569", fontSize: 17 },
2103
2600
  {
2104
- id: 1,
2601
+ id: 2,
2105
2602
  type: "strobe_shm",
2106
2603
  x1: 120,
2107
- y1: 80,
2604
+ y1: 100,
2108
2605
  x2: 120,
2109
- y2: 280,
2606
+ y2: 300,
2110
2607
  amplitude: 60,
2111
2608
  loops: 1,
2112
2609
  color: "#f59e0b",
2113
2610
  strokeWidth: 2,
2114
- label: "Motion"
2611
+ label: "Mass on spring"
2115
2612
  },
2116
2613
  {
2117
- id: 2,
2614
+ id: 3,
2118
2615
  type: "shm_graph",
2119
2616
  x1: 260,
2120
- y1: 180,
2617
+ y1: 200,
2121
2618
  x2: 460,
2122
- y2: 180,
2619
+ y2: 200,
2123
2620
  amplitude: 60,
2124
2621
  loops: 1,
2125
2622
  graphType: "all",
@@ -2127,14 +2624,20 @@ var waveOscillation = {
2127
2624
  strokeWidth: 2,
2128
2625
  label: "x(t), v(t), a(t)"
2129
2626
  },
2130
- { id: 102, type: "text", x1: 540, y1: 40, x2: 540, y2: 40, label: "2. Oscillators & Projections", fontSize: 20, color: "#1e293b" },
2627
+ { id: 4, type: "text", x1: 520, y1: 200, x2: 520, y2: 200, label: "\u03C9\xB2 = k/m", color: "#0f172a", fontSize: 16 }
2628
+ ]
2629
+ };
2630
+ var pendulumAndPhasor = {
2631
+ name: "Pendulum & phasor",
2632
+ elements: [
2633
+ { id: 1, type: "text", x1: 140, y1: 36, x2: 140, y2: 36, label: "Small oscillations + rotating phasor picture", color: "#475569", fontSize: 17 },
2131
2634
  {
2132
- id: 3,
2635
+ id: 2,
2133
2636
  type: "pendulum",
2134
- x1: 600,
2135
- y1: 80,
2136
- x2: 660,
2137
- y2: 220,
2637
+ x1: 200,
2638
+ y1: 100,
2639
+ x2: 260,
2640
+ y2: 240,
2138
2641
  label: "m",
2139
2642
  color: "#0ea5e9",
2140
2643
  strokeWidth: 2,
@@ -2142,56 +2645,105 @@ var waveOscillation = {
2142
2645
  showForces: true
2143
2646
  },
2144
2647
  {
2145
- id: 4,
2648
+ id: 3,
2146
2649
  type: "phasor",
2147
- x1: 800,
2148
- y1: 150,
2149
- x2: 860,
2650
+ x1: 420,
2651
+ y1: 170,
2652
+ x2: 500,
2150
2653
  y2: 90,
2151
2654
  label: "\u03C9t",
2152
2655
  color: "#8b5cf6",
2153
2656
  strokeWidth: 2,
2154
2657
  showProjection: true
2155
2658
  },
2156
- { id: 103, type: "text", x1: 50, y1: 340, x2: 50, y2: 340, label: "3. Energy & Damped Systems", fontSize: 20, color: "#1e293b" },
2659
+ {
2660
+ id: 4,
2661
+ type: "spring",
2662
+ x1: 620,
2663
+ y1: 120,
2664
+ x2: 620,
2665
+ y2: 240,
2666
+ color: "#3b82f6",
2667
+ strokeWidth: 2,
2668
+ coils: 7
2669
+ },
2157
2670
  {
2158
2671
  id: 5,
2672
+ type: "mass_box",
2673
+ x1: 600,
2674
+ y1: 240,
2675
+ x2: 640,
2676
+ y2: 280,
2677
+ color: "#ef4444",
2678
+ strokeWidth: 2,
2679
+ label: "m"
2680
+ },
2681
+ { id: 6, type: "text", x1: 620, y1: 320, x2: 620, y2: 320, label: "Same \u03C9 for linear & torsional SHM", color: "#64748b", fontSize: 14 }
2682
+ ]
2683
+ };
2684
+ var dampingAndEnergy = {
2685
+ name: "Damping & energy",
2686
+ elements: [
2687
+ { id: 1, type: "text", x1: 120, y1: 36, x2: 120, y2: 36, label: "Lossy oscillator: energy swap + torsion mode", color: "#475569", fontSize: 17 },
2688
+ {
2689
+ id: 2,
2159
2690
  type: "energy_graph",
2160
- x1: 80,
2161
- y1: 460,
2162
- x2: 280,
2163
- y2: 460,
2691
+ x1: 100,
2692
+ y1: 280,
2693
+ x2: 300,
2694
+ y2: 280,
2164
2695
  amplitude: 80,
2165
2696
  domain: "time",
2166
2697
  color: "#1e293b",
2167
2698
  strokeWidth: 2,
2168
- label: "U(t) & K(t)"
2699
+ label: "U(t), K(t)"
2169
2700
  },
2170
2701
  {
2171
- id: 6,
2702
+ id: 3,
2172
2703
  type: "vane_liquid",
2173
- x1: 400,
2174
- y1: 380,
2175
- x2: 480,
2176
- y2: 380,
2704
+ x1: 380,
2705
+ y1: 200,
2706
+ x2: 460,
2707
+ y2: 200,
2177
2708
  color: "#64748b",
2178
2709
  strokeWidth: 2,
2179
- label: "Damping (b)"
2710
+ label: "Drag \u221D v"
2711
+ },
2712
+ { id: 4, type: "spring", x1: 420, y1: 80, x2: 420, y2: 200, color: "#3b82f6", strokeWidth: 2, coils: 6 },
2713
+ {
2714
+ id: 5,
2715
+ type: "damped_wave",
2716
+ x1: 520,
2717
+ y1: 240,
2718
+ x2: 720,
2719
+ y2: 240,
2720
+ amplitude: 45,
2721
+ damping: 0.02,
2722
+ frequency: 12,
2723
+ color: "#ec4899",
2724
+ strokeWidth: 2,
2725
+ label: "x(t) envelope"
2180
2726
  },
2181
- { id: 7, type: "spring", x1: 440, y1: 260, x2: 440, y2: 380, color: "#3b82f6", strokeWidth: 2, coils: 6 },
2182
- { id: 104, type: "text", x1: 440, y1: 240, x2: 440, y2: 240, label: "Rigid Support", fontSize: 14, color: "#64748b" },
2183
2727
  {
2184
- id: 8,
2728
+ id: 6,
2185
2729
  type: "torsion_pendulum",
2186
- x1: 640,
2187
- y1: 320,
2188
- x2: 640,
2189
- y2: 480,
2730
+ x1: 320,
2731
+ y1: 380,
2732
+ x2: 320,
2733
+ y2: 520,
2190
2734
  color: "#ec4899",
2191
2735
  strokeWidth: 2,
2192
- label: "Torsion Pendulum"
2736
+ size: 44,
2737
+ rotation: 22,
2738
+ label: "Disk + fiber"
2193
2739
  }
2194
- ],
2740
+ ]
2741
+ };
2742
+ var waveExamples = [shmKinematics, pendulumAndPhasor, dampingAndEnergy];
2743
+ var waveOscillation = {
2744
+ name: "Oscillations & Waves",
2745
+ elements: waveExamples[0].elements,
2746
+ examples: waveExamples,
2195
2747
  toolCategories: [
2196
2748
  {
2197
2749
  name: "Basics",
@@ -2241,47 +2793,63 @@ var waveOscillation = {
2241
2793
  };
2242
2794
 
2243
2795
  // src/editor/presets/rotation.ts
2244
- var rotation = {
2245
- name: "Rotation",
2796
+ var collisionCom = {
2797
+ name: "Collision & center of mass",
2246
2798
  elements: [
2247
- { id: 101, type: "text", x1: 60, y1: 40, x2: 60, y2: 40, label: "1. Center of Mass & 2D Collision", fontSize: 20, color: "#1e293b" },
2248
- { id: 1, type: "point_mass", x1: 80, y1: 140, x2: 80, y2: 140, size: 25, color: "#ef4444", label: "m\u2081" },
2249
- { id: 2, type: "vector", x1: 80, y1: 140, x2: 180, y2: 140, color: "#ef4444", label: "u\u2081", strokeWidth: 2 },
2250
- { id: 3, type: "point_mass", x1: 240, y1: 100, x2: 240, y2: 100, size: 15, color: "#3b82f6", label: "m\u2082" },
2251
- { id: 4, type: "dashed_path", x1: 180, y1: 140, x2: 320, y2: 80, color: "#ef4444", strokeWidth: 1.5 },
2252
- { id: 5, type: "dashed_path", x1: 240, y1: 100, x2: 340, y2: 180, color: "#3b82f6", strokeWidth: 1.5 },
2253
- { id: 6, type: "com_indicator", x1: 160, y1: 120, x2: 160, y2: 120, color: "#10b981", size: 15, label: "COM" },
2254
- { id: 7, type: "system_boundary", x1: 40, y1: 60, x2: 360, y2: 220, color: "#94a3b8", strokeWidth: 1.5 },
2255
- { id: 102, type: "text", x1: 560, y1: 40, x2: 560, y2: 40, label: "2. Rigid Body Rotation & Torque", fontSize: 20, color: "#1e293b" },
2256
- { id: 8, type: "pivot", x1: 480, y1: 140, x2: 480, y2: 140, color: "#64748b", size: 25 },
2257
- { id: 9, type: "uniform_rod", x1: 480, y1: 140, x2: 740, y2: 180, color: "#f59e0b", strokeWidth: 12, label: "L, M" },
2258
- { id: 10, type: "vector", x1: 740, y1: 180, x2: 740, y2: 260, color: "#ef4444", label: "mg", strokeWidth: 2 },
2259
- { id: 11, type: "curve_arrow", x1: 480, y1: 140, x2: 540, y2: 200, color: "#8b5cf6", strokeWidth: 2, label: "\u03B1" },
2260
- { id: 103, type: "text", x1: 60, y1: 300, x2: 60, y2: 300, label: "3. Rolling on Inclined Plane", fontSize: 20, color: "#1e293b" },
2261
- { id: 12, type: "inclined_wedge", x1: 60, y1: 360, x2: 360, y2: 520, color: "#64748b", strokeWidth: 2, label: "\u03B8" },
2262
- {
2263
- id: 13,
2799
+ { id: 1, type: "text", x1: 120, y1: 36, x2: 120, y2: 36, label: "Two bodies before/after contact \u2014 COM frame", color: "#475569", fontSize: 17 },
2800
+ { id: 2, type: "point_mass", x1: 80, y1: 140, x2: 80, y2: 140, size: 25, color: "#ef4444", label: "m\u2081" },
2801
+ { id: 3, type: "vector", x1: 80, y1: 140, x2: 180, y2: 140, color: "#ef4444", label: "u\u2081", strokeWidth: 2 },
2802
+ { id: 4, type: "point_mass", x1: 240, y1: 100, x2: 240, y2: 100, size: 15, color: "#3b82f6", label: "m\u2082" },
2803
+ { id: 5, type: "dashed_path", x1: 180, y1: 140, x2: 320, y2: 80, color: "#ef4444", strokeWidth: 1.5 },
2804
+ { id: 6, type: "dashed_path", x1: 240, y1: 100, x2: 340, y2: 180, color: "#3b82f6", strokeWidth: 1.5 },
2805
+ { id: 7, type: "com_indicator", x1: 160, y1: 120, x2: 160, y2: 120, color: "#10b981", size: 15, label: "COM" },
2806
+ { id: 8, type: "system_boundary", x1: 40, y1: 60, x2: 360, y2: 220, color: "#94a3b8", strokeWidth: 1.5 }
2807
+ ]
2808
+ };
2809
+ var rigidRodTorque = {
2810
+ name: "Rigid rod & torque",
2811
+ elements: [
2812
+ { id: 1, type: "text", x1: 140, y1: 36, x2: 140, y2: 36, label: "Hinged rod: weight, pivot reaction, angular accel", color: "#475569", fontSize: 17 },
2813
+ { id: 2, type: "pivot", x1: 120, y1: 160, x2: 120, y2: 160, color: "#64748b", size: 25 },
2814
+ { id: 3, type: "uniform_rod", x1: 120, y1: 160, x2: 420, y2: 200, color: "#f59e0b", strokeWidth: 12, label: "L, M" },
2815
+ { id: 4, type: "vector", x1: 420, y1: 200, x2: 420, y2: 300, color: "#ef4444", label: "mg", strokeWidth: 2 },
2816
+ { id: 5, type: "curve_arrow", x1: 120, y1: 160, x2: 200, y2: 240, color: "#8b5cf6", strokeWidth: 2, label: "\u03B1" },
2817
+ { id: 6, type: "text", x1: 480, y1: 200, x2: 480, y2: 200, label: "\u03C4 = I\u03B1 about pivot", color: "#0f172a", fontSize: 15 }
2818
+ ]
2819
+ };
2820
+ var rollingPulleyRocket = {
2821
+ name: "Rolling, pulleys & rocket",
2822
+ elements: [
2823
+ { id: 1, type: "text", x1: 100, y1: 36, x2: 100, y2: 36, label: "Rolling on incline \xB7 Atwood \xB7 variable-mass rocket", color: "#475569", fontSize: 16 },
2824
+ { id: 2, type: "inclined_wedge", x1: 60, y1: 200, x2: 360, y2: 360, color: "#64748b", strokeWidth: 2, label: "\u03B8" },
2825
+ {
2826
+ id: 3,
2264
2827
  type: "rolling_body",
2265
2828
  x1: 160,
2266
- y1: 390,
2829
+ y1: 230,
2267
2830
  x2: 200,
2268
- y2: 390,
2831
+ y2: 230,
2269
2832
  color: "#3b82f6",
2270
2833
  strokeWidth: 2,
2271
2834
  label: "M, R",
2272
2835
  showVelocity: true,
2273
2836
  showOmega: true
2274
2837
  },
2275
- { id: 14, type: "vector", x1: 160, y1: 410, x2: 110, y2: 385, color: "#ef4444", label: "f_s", strokeWidth: 2 },
2276
- { id: 104, type: "text", x1: 560, y1: 300, x2: 560, y2: 300, label: "4. Systems & Angular Momentum", fontSize: 20, color: "#1e293b" },
2277
- { id: 15, type: "rocket", x1: 500, y1: 500, x2: 500, y2: 380, color: "#3b82f6", strokeWidth: 2, label: "v(t)" },
2278
- { id: 16, type: "vector", x1: 500, y1: 500, x2: 500, y2: 560, color: "#f97316", strokeWidth: 2, label: "u_rel dm/dt" },
2279
- { id: 17, type: "pulley", x1: 720, y1: 360, x2: 760, y2: 360, color: "#64748b", strokeWidth: 2 },
2280
- { id: 18, type: "block_mass", x1: 660, y1: 460, x2: 700, y2: 500, color: "#0ea5e9", label: "m\u2081" },
2281
- { id: 19, type: "block_mass", x1: 740, y1: 400, x2: 780, y2: 440, color: "#0ea5e9", label: "m\u2082" },
2282
- { id: 20, type: "line", x1: 680, y1: 460, x2: 680, y2: 360, color: "#1e293b", strokeWidth: 1.5 },
2283
- { id: 21, type: "line", x1: 760, y1: 400, x2: 760, y2: 360, color: "#1e293b", strokeWidth: 1.5 }
2284
- ],
2838
+ { id: 4, type: "vector", x1: 160, y1: 250, x2: 110, y2: 225, color: "#ef4444", label: "f_s", strokeWidth: 2 },
2839
+ { id: 5, type: "pulley", x1: 420, y1: 200, x2: 460, y2: 200, color: "#64748b", strokeWidth: 2 },
2840
+ { id: 6, type: "block_mass", x1: 360, y1: 300, x2: 400, y2: 340, color: "#0ea5e9", label: "m\u2081" },
2841
+ { id: 7, type: "block_mass", x1: 500, y1: 240, x2: 540, y2: 280, color: "#0ea5e9", label: "m\u2082" },
2842
+ { id: 8, type: "line", x1: 380, y1: 300, x2: 380, y2: 200, color: "#1e293b", strokeWidth: 1.5 },
2843
+ { id: 9, type: "line", x1: 520, y1: 260, x2: 520, y2: 200, color: "#1e293b", strokeWidth: 1.5 },
2844
+ { id: 10, type: "rocket", x1: 620, y1: 340, x2: 620, y2: 220, color: "#3b82f6", strokeWidth: 2, label: "v(t)" },
2845
+ { id: 11, type: "vector", x1: 620, y1: 340, x2: 620, y2: 400, color: "#f97316", strokeWidth: 2, label: "u_rel dm/dt" }
2846
+ ]
2847
+ };
2848
+ var rotationExamples = [collisionCom, rigidRodTorque, rollingPulleyRocket];
2849
+ var rotation = {
2850
+ name: "Rotation",
2851
+ elements: rotationExamples[0].elements,
2852
+ examples: rotationExamples,
2285
2853
  toolCategories: [
2286
2854
  {
2287
2855
  name: "Basics",
@@ -2326,57 +2894,83 @@ var rotation = {
2326
2894
  };
2327
2895
 
2328
2896
  // src/editor/presets/graph.ts
2329
- var initialElements = [
2330
- { id: 1, type: "capacitor", x1: 260, y1: 200, x2: 460, y2: 200, label: "C = 10\u03BCF", color: "#0f172a", strokeWidth: 3, fontSize: 18 },
2331
- { id: 2, type: "line", x1: 260, y1: 200, x2: 260, y2: 400, color: "#0f172a", strokeWidth: 2 },
2332
- { id: 3, type: "line", x1: 460, y1: 200, x2: 460, y2: 400, color: "#0f172a", strokeWidth: 2 },
2333
- { id: 4, type: "capacitor", x1: 260, y1: 400, x2: 460, y2: 400, label: "V = 12V", color: "#0f172a", strokeWidth: 3, fontSize: 18 },
2334
- { id: 5, type: "ground", x1: 460, y1: 400, x2: 460, y2: 460, label: "", color: "#0f172a", strokeWidth: 2 },
2335
- { id: 6, type: "charge_plus", x1: 340, y1: 180, x2: 340, y2: 180, label: "+Q", color: "#ef4444", strokeWidth: 2, fontSize: 16 },
2336
- { id: 7, type: "charge_minus", x1: 380, y1: 180, x2: 380, y2: 180, label: "-Q", color: "#3b82f6", strokeWidth: 2, fontSize: 16 },
2337
- {
2338
- id: 8,
2339
- type: "vector",
2340
- x1: 345,
2341
- y1: 190,
2342
- x2: 375,
2343
- y2: 190,
2344
- label: "",
2345
- color: "#ef4444",
2346
- strokeWidth: 1.5,
2347
- lineStyle: "dashed"
2348
- },
2349
- {
2350
- id: 9,
2351
- type: "vector",
2352
- x1: 345,
2353
- y1: 210,
2354
- x2: 375,
2355
- y2: 210,
2356
- label: "",
2357
- color: "#ef4444",
2358
- strokeWidth: 1.5,
2359
- lineStyle: "dashed"
2360
- },
2361
- { id: 10, type: "charge_plus", x1: 600, y1: 300, x2: 600, y2: 300, label: "+q", color: "#ef4444", strokeWidth: 2, fontSize: 18 },
2362
- { id: 11, type: "charge_minus", x1: 800, y1: 300, x2: 800, y2: 300, label: "-q", color: "#3b82f6", strokeWidth: 2, fontSize: 18 },
2363
- { id: 12, type: "vector", x1: 620, y1: 300, x2: 780, y2: 300, label: "E", color: "#94a3b8", strokeWidth: 2, fontSize: 18 },
2364
- {
2365
- id: 13,
2366
- type: "circle",
2367
- x1: 600,
2368
- y1: 300,
2369
- x2: 660,
2370
- y2: 300,
2371
- label: "",
2372
- color: "#ef4444",
2373
- strokeWidth: 1.5,
2374
- lineStyle: "dashed"
2375
- }
2376
- ];
2897
+ var chargesAndRC = {
2898
+ name: "Charges, capacitor & field",
2899
+ elements: [
2900
+ { id: 1, type: "text", x1: 320, y1: 24, x2: 320, y2: 24, label: "RC branch + point charges in an external E", color: "#475569", fontSize: 16 },
2901
+ { id: 2, type: "capacitor", x1: 260, y1: 200, x2: 460, y2: 200, label: "C = 10\u03BCF", color: "#0f172a", strokeWidth: 3, fontSize: 18 },
2902
+ { id: 3, type: "line", x1: 260, y1: 200, x2: 260, y2: 400, color: "#0f172a", strokeWidth: 2 },
2903
+ { id: 4, type: "line", x1: 460, y1: 200, x2: 460, y2: 400, color: "#0f172a", strokeWidth: 2 },
2904
+ { id: 5, type: "capacitor", x1: 260, y1: 400, x2: 460, y2: 400, label: "V = 12V", color: "#0f172a", strokeWidth: 3, fontSize: 18 },
2905
+ { id: 6, type: "ground", x1: 460, y1: 400, x2: 460, y2: 460, label: "", color: "#0f172a", strokeWidth: 2 },
2906
+ { id: 7, type: "charge_plus", x1: 340, y1: 180, x2: 340, y2: 180, label: "+Q", color: "#ef4444", strokeWidth: 2, fontSize: 16 },
2907
+ { id: 8, type: "charge_minus", x1: 380, y1: 180, x2: 380, y2: 180, label: "-Q", color: "#3b82f6", strokeWidth: 2, fontSize: 16 },
2908
+ {
2909
+ id: 9,
2910
+ type: "vector",
2911
+ x1: 345,
2912
+ y1: 190,
2913
+ x2: 375,
2914
+ y2: 190,
2915
+ label: "",
2916
+ color: "#ef4444",
2917
+ strokeWidth: 1.5,
2918
+ lineStyle: "dashed"
2919
+ },
2920
+ {
2921
+ id: 10,
2922
+ type: "vector",
2923
+ x1: 345,
2924
+ y1: 210,
2925
+ x2: 375,
2926
+ y2: 210,
2927
+ label: "",
2928
+ color: "#ef4444",
2929
+ strokeWidth: 1.5,
2930
+ lineStyle: "dashed"
2931
+ },
2932
+ { id: 11, type: "charge_plus", x1: 600, y1: 300, x2: 600, y2: 300, label: "+q", color: "#ef4444", strokeWidth: 2, fontSize: 18 },
2933
+ { id: 12, type: "charge_minus", x1: 800, y1: 300, x2: 800, y2: 300, label: "-q", color: "#3b82f6", strokeWidth: 2, fontSize: 18 },
2934
+ { id: 13, type: "vector", x1: 620, y1: 300, x2: 780, y2: 300, label: "E", color: "#94a3b8", strokeWidth: 2, fontSize: 18 },
2935
+ {
2936
+ id: 14,
2937
+ type: "circle",
2938
+ x1: 600,
2939
+ y1: 300,
2940
+ x2: 660,
2941
+ y2: 300,
2942
+ label: "",
2943
+ color: "#ef4444",
2944
+ strokeWidth: 1.5,
2945
+ lineStyle: "dashed"
2946
+ }
2947
+ ]
2948
+ };
2949
+ var coordinateClassics = {
2950
+ name: "Coordinate plane: parabola & sine",
2951
+ elements: [
2952
+ { id: 1, type: "text", x1: 280, y1: 24, x2: 280, y2: 24, label: "Standard graphs in x\u2013y", color: "#475569", fontSize: 16 },
2953
+ { id: 2, type: "axes", x1: 120, y1: 380, x2: 520, y2: 80, color: "#0f172a", strokeWidth: 2 },
2954
+ { id: 3, type: "parabola_v", x1: 200, y1: 200, x2: 440, y2: 340, color: "#2563eb", strokeWidth: 2.5, label: "y \u221D x\xB2" },
2955
+ { id: 4, type: "sine_wave", x1: 140, y1: 260, x2: 500, y2: 260, color: "#db2777", strokeWidth: 2.5, label: "sin x", amplitude: 40, loops: 2 },
2956
+ { id: 5, type: "text", x1: 560, y1: 120, x2: 560, y2: 120, label: "Use axes bbox for domain", color: "#64748b", fontSize: 14 }
2957
+ ]
2958
+ };
2959
+ var hyperbolaAndModulus = {
2960
+ name: "Hyperbola & modulus",
2961
+ elements: [
2962
+ { id: 1, type: "text", x1: 260, y1: 24, x2: 260, y2: 24, label: "Rational-style curves & piecewise linear", color: "#475569", fontSize: 16 },
2963
+ { id: 2, type: "axes", x1: 100, y1: 400, x2: 540, y2: 60, color: "#0f172a", strokeWidth: 2 },
2964
+ { id: 3, type: "hyperbola", x1: 320, y1: 100, x2: 480, y2: 360, color: "#7c3aed", strokeWidth: 2.5, label: "xy = const" },
2965
+ { id: 4, type: "modulus", x1: 160, y1: 320, x2: 280, y2: 180, color: "#ea580c", strokeWidth: 2.5, label: "|x - a|" },
2966
+ { id: 5, type: "text", x1: 560, y1: 280, x2: 560, y2: 280, label: "Asymptotes & kinks", color: "#64748b", fontSize: 14 }
2967
+ ]
2968
+ };
2969
+ var graphExamples = [chargesAndRC, coordinateClassics, hyperbolaAndModulus];
2377
2970
  var graph = {
2378
2971
  name: "Graph",
2379
- elements: initialElements,
2972
+ elements: graphExamples[0].elements,
2973
+ examples: graphExamples,
2380
2974
  toolCategories: [
2381
2975
  {
2382
2976
  name: "Basic",
@@ -2430,30 +3024,50 @@ var graph = {
2430
3024
  };
2431
3025
 
2432
3026
  // src/editor/presets/electrostatics.ts
3027
+ var pointChargeGauss = {
3028
+ name: "Point charge & Gaussian sphere",
3029
+ elements: [
3030
+ { id: 1, type: "text", x1: 160, y1: 36, x2: 160, y2: 36, label: "Spherical symmetry: E \u221D 1/r\xB2 outside", color: "#475569", fontSize: 16 },
3031
+ { id: 2, type: "charge_pos", x1: 160, y1: 180, x2: 160, y2: 180, label: "+q", color: "#ef4444", strokeWidth: 2, fontSize: 18 },
3032
+ { id: 3, type: "gaussian_sphere", x1: 160, y1: 180, x2: 280, y2: 180, color: "#10b981", strokeWidth: 2, label: "Gaussian surface" },
3033
+ { id: 4, type: "vector", x1: 160, y1: 180, x2: 300, y2: 180, color: "#ef4444", strokeWidth: 2, label: "E" },
3034
+ { id: 5, type: "vector", x1: 160, y1: 180, x2: 160, y2: 60, color: "#ef4444", strokeWidth: 2 },
3035
+ { id: 6, type: "vector", x1: 160, y1: 180, x2: 40, y2: 180, color: "#ef4444", strokeWidth: 2 },
3036
+ { id: 7, type: "vector", x1: 160, y1: 180, x2: 160, y2: 300, color: "#ef4444", strokeWidth: 2 },
3037
+ { id: 8, type: "text", x1: 160, y1: 340, x2: 160, y2: 340, label: "\u222E E\xB7dA = Q/\u03B5\u2080", color: "#0f172a", fontSize: 17 }
3038
+ ]
3039
+ };
3040
+ var lineChargeCylinder = {
3041
+ name: "Line charge & Gaussian cylinder",
3042
+ elements: [
3043
+ { id: 1, type: "text", x1: 500, y1: 36, x2: 500, y2: 36, label: "Infinite line \u03BB \u2014 use a coaxial cylinder", color: "#475569", fontSize: 15 },
3044
+ { id: 2, type: "charged_plate_pos", x1: 480, y1: 80, x2: 480, y2: 360, color: "#ef4444", strokeWidth: 2, label: "+\u03BB" },
3045
+ { id: 3, type: "gaussian_cylinder", x1: 480, y1: 140, x2: 480, y2: 300, color: "#10b981", strokeWidth: 2, curveHeight: 60, label: "Coaxial S" },
3046
+ { id: 4, type: "vector", x1: 480, y1: 210, x2: 600, y2: 210, color: "#ef4444", strokeWidth: 2, label: "E" },
3047
+ { id: 5, type: "vector", x1: 480, y1: 210, x2: 360, y2: 210, color: "#ef4444", strokeWidth: 2 },
3048
+ { id: 6, type: "text", x1: 480, y1: 400, x2: 480, y2: 400, label: "E = \u03BB / (2\u03C0\u03B5\u2080r)", color: "#0f172a", fontSize: 16 }
3049
+ ]
3050
+ };
3051
+ var platesAndDipole = {
3052
+ name: "Parallel plates & dipole",
3053
+ elements: [
3054
+ { id: 1, type: "text", x1: 720, y1: 36, x2: 720, y2: 36, label: "Uniform field + torque on p in E", color: "#475569", fontSize: 15 },
3055
+ { id: 2, type: "charged_plate_pos", x1: 600, y1: 100, x2: 820, y2: 100, color: "#ef4444", strokeWidth: 2 },
3056
+ { id: 3, type: "charged_plate_neg", x1: 600, y1: 300, x2: 820, y2: 300, color: "#3b82f6", strokeWidth: 2 },
3057
+ { id: 4, type: "vector", x1: 640, y1: 100, x2: 640, y2: 300, color: "#ef4444", strokeWidth: 2 },
3058
+ { id: 5, type: "vector", x1: 700, y1: 100, x2: 700, y2: 300, color: "#ef4444", strokeWidth: 2 },
3059
+ { id: 6, type: "vector", x1: 780, y1: 100, x2: 780, y2: 300, color: "#ef4444", strokeWidth: 2, label: "E" },
3060
+ { id: 7, type: "text", x1: 720, y1: 340, x2: 720, y2: 340, label: "\u0394V = E d (parallel)", color: "#0f172a", fontSize: 15 },
3061
+ { id: 8, type: "dipole", x1: 120, y1: 480, x2: 280, y2: 480, color: "#64748b", strokeWidth: 2, label: "p" },
3062
+ { id: 9, type: "vector", x1: 120, y1: 480, x2: 280, y2: 480, color: "#f59e0b", strokeWidth: 2, label: "p" },
3063
+ { id: 10, type: "text", x1: 320, y1: 480, x2: 320, y2: 480, label: "\u03C4 = p \xD7 E", color: "#0f172a", fontSize: 16 }
3064
+ ]
3065
+ };
3066
+ var electrostaticsExamples = [pointChargeGauss, lineChargeCylinder, platesAndDipole];
2433
3067
  var electrostatics = {
2434
3068
  name: "Electrostatics",
2435
- elements: [
2436
- { id: 1, type: "charge_pos", x1: 160, y1: 160, x2: 160, y2: 160, label: "+q", color: "#ef4444", strokeWidth: 2, fontSize: 18 },
2437
- { id: 2, type: "gaussian_sphere", x1: 160, y1: 160, x2: 260, y2: 160, color: "#10b981", strokeWidth: 2, label: "Gaussian Sphere" },
2438
- { id: 3, type: "vector", x1: 160, y1: 160, x2: 280, y2: 160, color: "#ef4444", strokeWidth: 2, label: "E" },
2439
- { id: 4, type: "vector", x1: 160, y1: 160, x2: 160, y2: 40, color: "#ef4444", strokeWidth: 2 },
2440
- { id: 5, type: "vector", x1: 160, y1: 160, x2: 40, y2: 160, color: "#ef4444", strokeWidth: 2 },
2441
- { id: 6, type: "vector", x1: 160, y1: 160, x2: 160, y2: 280, color: "#ef4444", strokeWidth: 2 },
2442
- { id: 7, type: "text", x1: 160, y1: 320, x2: 160, y2: 320, label: "Point Charge Field", color: "#334155", fontSize: 18 },
2443
- { id: 8, type: "charged_plate_pos", x1: 500, y1: 60, x2: 500, y2: 360, color: "#ef4444", strokeWidth: 2, label: "+\u03BB (Line Charge)" },
2444
- { id: 9, type: "gaussian_cylinder", x1: 500, y1: 120, x2: 500, y2: 300, color: "#10b981", strokeWidth: 2, curveHeight: 60, label: "Gaussian Cylinder" },
2445
- { id: 10, type: "vector", x1: 500, y1: 210, x2: 620, y2: 210, color: "#ef4444", strokeWidth: 2, label: "E" },
2446
- { id: 11, type: "vector", x1: 500, y1: 210, x2: 380, y2: 210, color: "#ef4444", strokeWidth: 2 },
2447
- { id: 12, type: "text", x1: 500, y1: 400, x2: 500, y2: 400, label: "Cylindrical Symmetry", color: "#334155", fontSize: 18 },
2448
- { id: 13, type: "charged_plate_pos", x1: 760, y1: 100, x2: 960, y2: 100, color: "#ef4444", strokeWidth: 2 },
2449
- { id: 14, type: "charged_plate_neg", x1: 760, y1: 300, x2: 960, y2: 300, color: "#3b82f6", strokeWidth: 2 },
2450
- { id: 15, type: "vector", x1: 800, y1: 100, x2: 800, y2: 300, color: "#ef4444", strokeWidth: 2 },
2451
- { id: 16, type: "vector", x1: 860, y1: 100, x2: 860, y2: 300, color: "#ef4444", strokeWidth: 2, label: "Uniform E-Field" },
2452
- { id: 17, type: "vector", x1: 920, y1: 100, x2: 920, y2: 300, color: "#ef4444", strokeWidth: 2 },
2453
- { id: 18, type: "text", x1: 860, y1: 340, x2: 860, y2: 340, label: "Parallel Plates", color: "#334155", fontSize: 18 },
2454
- { id: 19, type: "dipole", x1: 100, y1: 500, x2: 260, y2: 500, color: "#64748b", strokeWidth: 2, label: "Electric Dipole (p)" },
2455
- { id: 20, type: "vector", x1: 100, y1: 500, x2: 260, y2: 500, color: "#f59e0b", strokeWidth: 2, label: "p" }
2456
- ],
3069
+ elements: electrostaticsExamples[0].elements,
3070
+ examples: electrostaticsExamples,
2457
3071
  toolCategories: [
2458
3072
  {
2459
3073
  name: "Basics",
@@ -2498,21 +3112,51 @@ var electrostatics = {
2498
3112
  };
2499
3113
 
2500
3114
  // src/editor/presets/chemistry.ts
3115
+ var aromaticHalogenation = {
3116
+ name: "Aromatic halogenation",
3117
+ elements: [
3118
+ { id: 1, type: "text", x1: 420, y1: 40, x2: 420, y2: 40, label: "Lewis acid catalyzed chlorination (schematic)", color: "#64748b", fontSize: 15 },
3119
+ { id: 2, type: "benzene", x1: 150, y1: 210, x2: 150, y2: 210, label: "", value: "" },
3120
+ { id: 3, type: "text", x1: 240, y1: 210, x2: 240, y2: 210, label: "+", value: "" },
3121
+ { id: 4, type: "atom", x1: 285, y1: 210, x2: 285, y2: 210, label: "Cl", value: "" },
3122
+ { id: 5, type: "single_bond", x1: 300, y1: 210, x2: 330, y2: 210, label: "", value: "" },
3123
+ { id: 6, type: "atom", x1: 345, y1: 210, x2: 345, y2: 210, label: "Cl", value: "" },
3124
+ { id: 7, type: "reaction_arrow", x1: 400, y1: 210, x2: 520, y2: 210, label: "FeCl\u2083", value: "cat." },
3125
+ { id: 8, type: "benzene", x1: 600, y1: 210, x2: 600, y2: 210, label: "", value: "" },
3126
+ { id: 9, type: "single_bond", x1: 600, y1: 180, x2: 600, y2: 120, label: "", value: "" },
3127
+ { id: 10, type: "atom", x1: 600, y1: 105, x2: 600, y2: 105, label: "Cl", value: "" },
3128
+ { id: 11, type: "text", x1: 690, y1: 210, x2: 690, y2: 210, label: "+", value: "" },
3129
+ { id: 12, type: "atom", x1: 750, y1: 210, x2: 750, y2: 210, label: "HCl", value: "" }
3130
+ ]
3131
+ };
3132
+ var ringEquilibrium = {
3133
+ name: "Ring conformers",
3134
+ elements: [
3135
+ { id: 1, type: "text", x1: 400, y1: 50, x2: 400, y2: 50, label: "Six-membered ring: two low-energy chairs", color: "#64748b", fontSize: 15 },
3136
+ { id: 2, type: "cyclohexane", x1: 220, y1: 240, x2: 220, y2: 240, label: "", value: "" },
3137
+ { id: 3, type: "equilibrium_arrow", x1: 300, y1: 240, x2: 420, y2: 240, label: "", value: "" },
3138
+ { id: 4, type: "cyclohexane", x1: 520, y1: 240, x2: 520, y2: 240, label: "", value: "" },
3139
+ { id: 5, type: "text", x1: 400, y1: 360, x2: 400, y2: 360, label: "Flip interconverts axial / equatorial substituents", color: "#334155", fontSize: 14 },
3140
+ { id: 6, type: "wedge_bond", x1: 220, y1: 200, x2: 200, y2: 170, label: "", value: "" },
3141
+ { id: 7, type: "atom", x1: 188, y1: 158, x2: 188, y2: 158, label: "R", value: "" }
3142
+ ]
3143
+ };
3144
+ var resonanceStructures = {
3145
+ name: "Benzene resonance",
3146
+ elements: [
3147
+ { id: 1, type: "text", x1: 400, y1: 50, x2: 400, y2: 50, label: "\u03C0 system: two major Kekul\xE9 contributors", color: "#64748b", fontSize: 15 },
3148
+ { id: 2, type: "benzene", x1: 200, y1: 220, x2: 200, y2: 220, label: "", value: "" },
3149
+ { id: 3, type: "resonance_arrow", x1: 280, y1: 220, x2: 360, y2: 220, label: "", value: "" },
3150
+ { id: 4, type: "benzene", x1: 440, y1: 220, x2: 440, y2: 220, label: "", value: "" },
3151
+ { id: 5, type: "double_bond", x1: 440, y1: 200, x2: 470, y2: 180, label: "", value: "" },
3152
+ { id: 6, type: "text", x1: 400, y1: 340, x2: 400, y2: 340, label: "Real molecule = hybrid (D\u2086h symmetry)", color: "#334155", fontSize: 14 }
3153
+ ]
3154
+ };
3155
+ var chemistryExamples = [aromaticHalogenation, ringEquilibrium, resonanceStructures];
2501
3156
  var chemistry = {
2502
3157
  name: "Chemistry",
2503
- elements: [
2504
- { id: 1, type: "benzene", x1: 150, y1: 210, x2: 150, y2: 210, label: "", value: "" },
2505
- { id: 2, type: "text", x1: 240, y1: 210, x2: 240, y2: 210, label: "+", value: "" },
2506
- { id: 3, type: "atom", x1: 285, y1: 210, x2: 285, y2: 210, label: "Cl", value: "" },
2507
- { id: 4, type: "single_bond", x1: 300, y1: 210, x2: 330, y2: 210, label: "", value: "" },
2508
- { id: 5, type: "atom", x1: 345, y1: 210, x2: 345, y2: 210, label: "Cl", value: "" },
2509
- { id: 6, type: "reaction_arrow", x1: 400, y1: 210, x2: 520, y2: 210, label: "FeCl3", value: "Dark" },
2510
- { id: 7, type: "benzene", x1: 600, y1: 210, x2: 600, y2: 210, label: "", value: "" },
2511
- { id: 8, type: "single_bond", x1: 600, y1: 180, x2: 600, y2: 120, label: "", value: "" },
2512
- { id: 9, type: "atom", x1: 600, y1: 105, x2: 600, y2: 105, label: "Cl", value: "" },
2513
- { id: 10, type: "text", x1: 690, y1: 210, x2: 690, y2: 210, label: "+", value: "" },
2514
- { id: 11, type: "atom", x1: 750, y1: 210, x2: 750, y2: 210, label: "HCl", value: "" }
2515
- ],
3158
+ elements: chemistryExamples[0].elements,
3159
+ examples: chemistryExamples,
2516
3160
  toolCategories: [
2517
3161
  {
2518
3162
  name: "Chemistry",
@@ -2558,39 +3202,25 @@ var chemistry = {
2558
3202
  ]
2559
3203
  };
2560
3204
 
2561
- // src/editor/presets/mechanical.ts
2562
- var mechanical = {
2563
- name: "Mechanical",
2564
- elements: [
2565
- { id: 1, type: "wedge", x1: 100, y1: 400, x2: 500, y2: 100, label: "M" },
2566
- { id: 2, type: "block", x1: 220, y1: 260, x2: 300, y2: 300, label: "m" },
2567
- { id: 3, type: "arc", x1: 100, y1: 400, x2: 260, y2: 280, label: "\u03B8" }
2568
- ],
2569
- toolCategories: [{ name: "Mechanical", tools: [{ id: "select", name: "Select" }, { id: "wedge", name: "Wedge" }, { id: "block", name: "Block" }, { id: "vector", name: "Vector" }, { id: "arc", name: "Arc" }, { id: "dashed", name: "Dashed" }, { id: "dimension", name: "Dimension" }, { id: "axes", name: "Axes" }] }]
2570
- };
2571
-
2572
3205
  // src/editor/presets/index.ts
2573
3206
  var presets = {
2574
3207
  mechanics,
2575
3208
  circuit,
2576
3209
  thermo,
2577
- optical,
2578
3210
  magnetism,
2579
3211
  light,
2580
- waves,
2581
3212
  waveOscillation,
2582
3213
  rotation,
2583
3214
  graph,
2584
3215
  electrostatics,
2585
- chemistry,
2586
- mechanical
3216
+ chemistry
2587
3217
  };
2588
3218
 
2589
3219
  // src/editor/Editor.tsx
2590
3220
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
2591
3221
  var defaultPreset = presets.mechanics;
2592
3222
  var Editor = forwardRef(function Editor2({
2593
- initialElements: initialElements2,
3223
+ initialElements,
2594
3224
  toolCategories,
2595
3225
  preset,
2596
3226
  enablePresetSwitch = true,
@@ -2603,6 +3233,7 @@ var Editor = forwardRef(function Editor2({
2603
3233
  onExport
2604
3234
  }, ref) {
2605
3235
  const [selectedPreset, setSelectedPreset] = useState2(preset ?? "mechanics");
3236
+ const [exampleIndex, setExampleIndex] = useState2(0);
2606
3237
  const isPresetExternallyControlled = preset !== void 0 && typeof onPresetChange === "function";
2607
3238
  useEffect2(() => {
2608
3239
  if (isPresetExternallyControlled && preset) {
@@ -2611,23 +3242,21 @@ var Editor = forwardRef(function Editor2({
2611
3242
  }, [preset, isPresetExternallyControlled]);
2612
3243
  const activePresetKey = isPresetExternallyControlled ? preset ?? selectedPreset : selectedPreset;
2613
3244
  const activePreset = presets[activePresetKey] ?? defaultPreset;
2614
- const [lightTopicIndex, setLightTopicIndex] = useState2(0);
2615
3245
  useEffect2(() => {
2616
- setLightTopicIndex(0);
3246
+ setExampleIndex(0);
2617
3247
  }, [activePresetKey]);
2618
3248
  const resolvedInitialElements = useMemo2(() => {
2619
- if (initialElements2 != null) return initialElements2;
2620
- if (activePresetKey === "light") return LIGHT_TOPICS[lightTopicIndex].elements;
2621
- return activePreset.elements;
2622
- }, [initialElements2, activePresetKey, lightTopicIndex, activePreset.elements]);
3249
+ if (initialElements != null) return initialElements;
3250
+ const source = activePreset.examples?.[exampleIndex]?.elements ?? activePreset.elements;
3251
+ return source.slice();
3252
+ }, [initialElements, activePreset, exampleIndex]);
2623
3253
  const state = useDrawing(resolvedInitialElements, activePresetKey);
2624
- const showLightTopicPicker = activePresetKey === "light" && initialElements2 == null;
2625
3254
  useEffect2(() => {
2626
3255
  onChange?.(state.elements);
2627
3256
  }, [state.elements, onChange]);
2628
3257
  useEffect2(() => {
2629
3258
  if (typeof viewMode === "boolean") state.setIsViewMode(viewMode);
2630
- }, [viewMode, state]);
3259
+ }, [viewMode, state.setIsViewMode]);
2631
3260
  useEffect2(() => {
2632
3261
  if (!state.selectedId) {
2633
3262
  state.setShowProperties(false);
@@ -2642,6 +3271,7 @@ var Editor = forwardRef(function Editor2({
2642
3271
  clear: () => state.setElements([])
2643
3272
  }),
2644
3273
  [state.elements, state.setElements]
3274
+ // eslint-disable-line react-hooks/exhaustive-deps
2645
3275
  );
2646
3276
  const displayTitle = documentTitle ?? `${activePreset.name} Editor`;
2647
3277
  const handleCopyJson = () => {
@@ -2652,7 +3282,17 @@ var Editor = forwardRef(function Editor2({
2652
3282
  }
2653
3283
  };
2654
3284
  const updateSelected = (key, value) => {
2655
- state.setElements((els) => els.map((el) => el.id === state.selectedId ? { ...el, [key]: value } : el));
3285
+ state.setElements(
3286
+ (els) => els.map((el) => {
3287
+ if (el.id !== state.selectedId) return el;
3288
+ if (value === void 0) {
3289
+ const copy = { ...el };
3290
+ delete copy[key];
3291
+ return copy;
3292
+ }
3293
+ return { ...el, [key]: value };
3294
+ })
3295
+ );
2656
3296
  };
2657
3297
  const deleteSelected = () => {
2658
3298
  if (!state.selectedId) return;
@@ -2684,24 +3324,18 @@ var Editor = forwardRef(function Editor2({
2684
3324
  ),
2685
3325
  /* @__PURE__ */ jsx5("span", { className: "pointer-events-none absolute inset-y-0 right-2 flex items-center text-slate-500 text-xs", children: "\u25BC" })
2686
3326
  ] }),
2687
- showLightTopicPicker && /* @__PURE__ */ jsxs5("div", { className: "relative shrink-0 max-w-[min(100%,280px)]", children: [
3327
+ initialElements == null && activePreset.examples && activePreset.examples.length > 0 && /* @__PURE__ */ jsxs5("div", { className: "relative shrink-0 max-w-[min(280px,46vw)]", children: [
2688
3328
  /* @__PURE__ */ jsx5(
2689
3329
  "select",
2690
3330
  {
2691
- value: lightTopicIndex,
2692
- onChange: (e) => {
2693
- const idx = Number(e.target.value);
2694
- setLightTopicIndex(idx);
2695
- state.setSelectedId(null);
2696
- state.setShowProperties(false);
2697
- },
2698
- className: "w-full appearance-none text-xs md:text-sm pl-3 pr-8 py-2 rounded-full border border-slate-200 bg-sky-50 text-slate-800 shadow-sm hover:bg-sky-100 focus:outline-none focus:ring-2 focus:ring-sky-200 truncate",
2699
- title: "Load JEE/NEET topic scene",
2700
- "aria-label": "Light syllabus topic",
2701
- children: LIGHT_TOPICS.map((t, i) => /* @__PURE__ */ jsx5("option", { value: i, children: t.name }, t.name))
3331
+ value: exampleIndex,
3332
+ onChange: (e) => setExampleIndex(Number(e.target.value)),
3333
+ className: "appearance-none w-full text-xs md:text-sm pl-3 pr-8 py-2 rounded-full border border-violet-200 bg-violet-50/90 text-violet-900 shadow-sm hover:bg-violet-100 focus:outline-none focus:ring-2 focus:ring-violet-200 truncate",
3334
+ title: "Load example scene",
3335
+ children: activePreset.examples.map((ex, i) => /* @__PURE__ */ jsx5("option", { value: i, children: ex.name }, ex.name))
2702
3336
  }
2703
3337
  ),
2704
- /* @__PURE__ */ jsx5("span", { className: "pointer-events-none absolute inset-y-0 right-2 flex items-center text-slate-500 text-xs", children: "\u25BC" })
3338
+ /* @__PURE__ */ jsx5("span", { className: "pointer-events-none absolute inset-y-0 right-2 flex items-center text-violet-600 text-xs", children: "\u25BC" })
2705
3339
  ] }),
2706
3340
  !hideDocumentTitle && /* @__PURE__ */ jsx5("h1", { className: "text-lg md:text-2xl font-semibold tracking-tight text-slate-800 truncate min-w-0", children: displayTitle })
2707
3341
  ] }),
@@ -2789,7 +3423,8 @@ var Editor = forwardRef(function Editor2({
2789
3423
  onBackgroundMouseMove: state.isViewMode ? void 0 : state.handleMouseMove,
2790
3424
  onBackgroundMouseUp: state.isViewMode ? void 0 : state.handleMouseUp,
2791
3425
  onBackgroundMouseLeave: state.isViewMode ? void 0 : state.handleMouseUp,
2792
- onElementMouseDown: state.onElementMouseDown
3426
+ onElementMouseDown: state.onElementMouseDown,
3427
+ onHandlePointerDown: state.isViewMode ? void 0 : state.onHandlePointerDown
2793
3428
  }
2794
3429
  ),
2795
3430
  state.showProperties && state.selectedId && !state.isViewMode && /* @__PURE__ */ jsx5(