@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/index.cjs CHANGED
@@ -55,6 +55,13 @@ var getBounds = (x1, y1, x2, y2) => {
55
55
  height: Math.max(1, maxY - minY)
56
56
  };
57
57
  };
58
+ var OPTICS_CHORD_EPS = 1;
59
+ function opticsEffectiveChord(x1, y1, x2, y2) {
60
+ const d = distance(x1, y1, x2, y2);
61
+ if (d >= OPTICS_CHORD_EPS) return d;
62
+ const b = getBounds(x1, y1, x2, y2);
63
+ return Math.max(12, b.width, b.height);
64
+ }
58
65
  var getMouseCoords = (svg, e) => {
59
66
  const ctm = svg.getScreenCTM();
60
67
  if (!ctm) return { x: 0, y: 0 };
@@ -63,6 +70,29 @@ var getMouseCoords = (svg, e) => {
63
70
  y: (e.clientY - ctm.f) / ctm.d
64
71
  };
65
72
  };
73
+ function quadraticChordWorldBounds(x1, y1, x2, y2, curveHeight, pad = 18) {
74
+ const d = distance(x1, y1, x2, y2);
75
+ const angleRad = Math.atan2(y2 - y1, x2 - x1);
76
+ const lx = d / 2;
77
+ const ly = -curveHeight * 2;
78
+ const apexX = x1 + lx * Math.cos(angleRad) - ly * Math.sin(angleRad);
79
+ const apexY = y1 + lx * Math.sin(angleRad) + ly * Math.cos(angleRad);
80
+ const xs = [x1, x2, apexX];
81
+ const ys = [y1, y2, apexY];
82
+ const minX = Math.min(...xs);
83
+ const minY = Math.min(...ys);
84
+ const maxX = Math.max(...xs);
85
+ const maxY = Math.max(...ys);
86
+ return paddedBounds(minX, minY, maxX, maxY, pad);
87
+ }
88
+ function paddedBounds(minX, minY, maxX, maxY, pad) {
89
+ return {
90
+ x: minX - pad,
91
+ y: minY - pad,
92
+ width: maxX - minX + 2 * pad,
93
+ height: maxY - minY + 2 * pad
94
+ };
95
+ }
66
96
 
67
97
  // src/core/renderers/registry.ts
68
98
  var rendererMap = /* @__PURE__ */ new Map();
@@ -72,20 +102,227 @@ var registerRenderer = (type, renderer) => {
72
102
  var getRenderer = (type) => rendererMap.get(type);
73
103
  var hasRenderer = (type) => rendererMap.has(type);
74
104
 
75
- // src/core/renderers/primitives.tsx
105
+ // src/core/renderers/selectionChrome.tsx
76
106
  var import_jsx_runtime = require("react/jsx-runtime");
107
+ var SELECTION_STROKE = "#2563eb";
108
+ var HANDLE_END = "#ef4444";
109
+ var BEZIER_ANCHOR = "#10b981";
110
+ var BEZIER_CP = "#3b82f6";
111
+ function selectionDashRect(x, y, width2, height) {
112
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
113
+ "rect",
114
+ {
115
+ x,
116
+ y,
117
+ width: width2,
118
+ height,
119
+ fill: "none",
120
+ stroke: SELECTION_STROKE,
121
+ strokeWidth: 1,
122
+ strokeDasharray: "4 4",
123
+ pointerEvents: "none"
124
+ }
125
+ );
126
+ }
127
+ function chordBoundsDashRect(el, padX = 15, padY = 20) {
128
+ const b = getBounds(el.x1, el.y1, el.x2, el.y2);
129
+ return selectionDashRect(b.minX - padX, b.minY - padY, b.width + 2 * padX, b.height + 2 * padY);
130
+ }
131
+ function worldChordHitLine(el, strokeWidth = 24) {
132
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
133
+ "line",
134
+ {
135
+ x1: el.x1,
136
+ y1: el.y1,
137
+ x2: el.x2,
138
+ y2: el.y2,
139
+ stroke: "transparent",
140
+ strokeWidth,
141
+ strokeLinecap: "round"
142
+ }
143
+ );
144
+ }
145
+ function endpointHandles(el, ctx) {
146
+ if (!ctx.isSelected || ctx.viewMode || ctx.isGhost || !ctx.onHandlePointerDown) return null;
147
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
148
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
149
+ "circle",
150
+ {
151
+ cx: el.x1,
152
+ cy: el.y1,
153
+ r: 5,
154
+ fill: HANDLE_END,
155
+ cursor: "grab",
156
+ onMouseDown: (e) => ctx.onHandlePointerDown(e, "start")
157
+ }
158
+ ),
159
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
160
+ "circle",
161
+ {
162
+ cx: el.x2,
163
+ cy: el.y2,
164
+ r: 5,
165
+ fill: HANDLE_END,
166
+ cursor: "grab",
167
+ onMouseDown: (e) => ctx.onHandlePointerDown(e, "end")
168
+ }
169
+ )
170
+ ] });
171
+ }
172
+ function ChordInteractionOverlay({
173
+ el,
174
+ ctx,
175
+ padX = 15,
176
+ padY = 20,
177
+ hitWidth = 24
178
+ }) {
179
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
180
+ worldChordHitLine(el, hitWidth),
181
+ ctx.isSelected && !ctx.viewMode && !ctx.isGhost ? chordBoundsDashRect(el, padX, padY) : null,
182
+ endpointHandles(el, ctx)
183
+ ] });
184
+ }
185
+ function LocalFatPathHit({
186
+ dPath,
187
+ strokeWidth = 24,
188
+ strokeLinecap = "round",
189
+ strokeLinejoin
190
+ }) {
191
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
192
+ "path",
193
+ {
194
+ d: dPath,
195
+ fill: "none",
196
+ stroke: "transparent",
197
+ strokeWidth,
198
+ strokeLinecap,
199
+ strokeLinejoin
200
+ }
201
+ );
202
+ }
203
+ function OriginHitPlate({
204
+ ctx,
205
+ hitHalfSize,
206
+ selectionHalfSize,
207
+ children
208
+ }) {
209
+ const selHalf = selectionHalfSize ?? hitHalfSize + 10;
210
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
211
+ ctx.isSelected && !ctx.viewMode && !ctx.isGhost ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
212
+ "rect",
213
+ {
214
+ x: -selHalf,
215
+ y: -selHalf,
216
+ width: 2 * selHalf,
217
+ height: 2 * selHalf,
218
+ fill: "none",
219
+ stroke: SELECTION_STROKE,
220
+ strokeWidth: 1,
221
+ strokeDasharray: "4 4",
222
+ pointerEvents: "none"
223
+ }
224
+ ) : null,
225
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: -hitHalfSize, y: -hitHalfSize, width: 2 * hitHalfSize, height: 2 * hitHalfSize, fill: "transparent" }),
226
+ children
227
+ ] });
228
+ }
229
+ function bboxInsetSelectionChrome(minX, minY, width2, height, ctx, inset = 10) {
230
+ if (!ctx.isSelected || ctx.viewMode || ctx.isGhost) return null;
231
+ return selectionDashRect(minX - inset, minY - inset, width2 + 2 * inset, height + 2 * inset);
232
+ }
233
+ function BBoxInteractionOverlay({
234
+ minX,
235
+ minY,
236
+ width: width2,
237
+ height,
238
+ ctx,
239
+ inset = 10,
240
+ includeHitFill = true
241
+ }) {
242
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
243
+ includeHitFill ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: minX, y: minY, width: width2, height, fill: "transparent" }) : null,
244
+ bboxInsetSelectionChrome(minX, minY, width2, height, ctx, inset)
245
+ ] });
246
+ }
247
+ function BezierEditChrome({
248
+ x1,
249
+ y1,
250
+ x2,
251
+ y2,
252
+ cp0,
253
+ ctx,
254
+ pts,
255
+ pad = 10
256
+ }) {
257
+ const show = ctx.isSelected && !ctx.viewMode && !ctx.isGhost && ctx.onHandlePointerDown;
258
+ if (!show) return null;
259
+ const bMinX = Math.min(...pts.map((p) => p.x));
260
+ const bMaxX = Math.max(...pts.map((p) => p.x));
261
+ const bMinY = Math.min(...pts.map((p) => p.y));
262
+ const bMaxY = Math.max(...pts.map((p) => p.y));
263
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
264
+ selectionDashRect(bMinX - pad, bMinY - pad, bMaxX - bMinX + 2 * pad, bMaxY - bMinY + 2 * pad),
265
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
266
+ "polyline",
267
+ {
268
+ points: pts.map((p) => `${p.x},${p.y}`).join(" "),
269
+ fill: "none",
270
+ stroke: "#94a3b8",
271
+ strokeDasharray: "4 4",
272
+ strokeWidth: 1,
273
+ pointerEvents: "none"
274
+ }
275
+ ),
276
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
277
+ "circle",
278
+ {
279
+ cx: x1,
280
+ cy: y1,
281
+ r: 6,
282
+ fill: BEZIER_ANCHOR,
283
+ cursor: "grab",
284
+ onMouseDown: (e) => ctx.onHandlePointerDown(e, "start")
285
+ }
286
+ ),
287
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
288
+ "circle",
289
+ {
290
+ cx: x2,
291
+ cy: y2,
292
+ r: 6,
293
+ fill: BEZIER_ANCHOR,
294
+ cursor: "grab",
295
+ onMouseDown: (e) => ctx.onHandlePointerDown(e, "end")
296
+ }
297
+ ),
298
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
299
+ "circle",
300
+ {
301
+ cx: cp0.x,
302
+ cy: cp0.y,
303
+ r: 6,
304
+ fill: BEZIER_CP,
305
+ cursor: "grab",
306
+ onMouseDown: (e) => ctx.onHandlePointerDown(e, "cp", 0)
307
+ }
308
+ )
309
+ ] });
310
+ }
311
+
312
+ // src/core/renderers/primitives.tsx
313
+ var import_jsx_runtime2 = require("react/jsx-runtime");
77
314
  var colorOf = (el) => el.color || "#0f172a";
78
315
  var strokeOf = (el) => el.strokeWidth || 2;
79
316
  var fontOf = (el) => el.fontSize || 16;
80
- registerRenderer("point", (el) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
81
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "0", cy: "0", r: 3, fill: colorOf(el) }),
82
- el.label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: "8", y: "-8", fontSize: fontOf(el), fill: colorOf(el), children: String(el.label) })
83
- ] }));
84
- registerRenderer("text", (el) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
317
+ registerRenderer("point", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(OriginHitPlate, { ctx, hitHalfSize: 28, selectionHalfSize: 32, children: [
318
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "0", cy: "0", r: 3, fill: colorOf(el) }),
319
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: "8", y: "-8", fontSize: fontOf(el), fill: colorOf(el), children: String(el.label) }) : null
320
+ ] }) }));
321
+ registerRenderer("text", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(OriginHitPlate, { ctx, hitHalfSize: Math.max(40, (el.label || el.value || "").length * 4), selectionHalfSize: void 0, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
85
322
  "text",
86
323
  {
87
- x: el.x1,
88
- y: el.y1,
324
+ x: 0,
325
+ y: 0,
89
326
  dominantBaseline: "central",
90
327
  textAnchor: "middle",
91
328
  fill: colorOf(el),
@@ -94,24 +331,27 @@ registerRenderer("text", (el) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
94
331
  fontWeight: el.fontWeight || "normal",
95
332
  children: String(el.label || el.value || "")
96
333
  }
97
- ));
98
- var lineLike = (el, arrow = false) => {
334
+ ) }) }));
335
+ var lineLike = (el, ctx, arrow = false) => {
99
336
  const d = distance(el.x1, el.y1, el.x2, el.y2);
100
337
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
101
338
  const dashed = el.lineStyle === "dashed" || el.type === "dashed" || el.type === "dashed_line" ? "6 6" : el.lineStyle === "dotted" ? "2 4" : void 0;
102
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
103
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: colorOf(el), strokeWidth: strokeOf(el), strokeDasharray: dashed, strokeLinecap: "round" }),
104
- arrow && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polygon", { points: `${d},0 ${d - 12},-6 ${d - 12},6`, fill: colorOf(el) }),
105
- el.label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: d / 2, y: -10, fontSize: fontOf(el), fill: colorOf(el), textAnchor: "middle", children: String(el.label) })
339
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { children: [
340
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
341
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: colorOf(el), strokeWidth: strokeOf(el), strokeDasharray: dashed, strokeLinecap: "round" }),
342
+ arrow ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polygon", { points: `${d},0 ${d - 12},-6 ${d - 12},6`, fill: colorOf(el) }) : null,
343
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: d / 2, y: -10, fontSize: fontOf(el), fill: colorOf(el), textAnchor: "middle", children: String(el.label) }) : null
344
+ ] }),
345
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChordInteractionOverlay, { el, ctx })
106
346
  ] });
107
347
  };
108
- registerRenderer("line", (el) => lineLike(el));
109
- registerRenderer("dashed", (el) => lineLike({ ...el, lineStyle: "dashed" }));
110
- registerRenderer("dashed_line", (el) => lineLike({ ...el, lineStyle: "dashed" }));
111
- registerRenderer("vector", (el) => lineLike(el, true));
112
- registerRenderer("ray", (el) => lineLike(el, true));
113
- registerRenderer("wire", (el) => lineLike(el));
114
- registerRenderer("table_edge", (el) => {
348
+ registerRenderer("line", (el, ctx) => lineLike(el, ctx));
349
+ registerRenderer("dashed", (el, ctx) => lineLike({ ...el, lineStyle: "dashed" }, ctx));
350
+ registerRenderer("dashed_line", (el, ctx) => lineLike({ ...el, lineStyle: "dashed" }, ctx));
351
+ registerRenderer("vector", (el, ctx) => lineLike(el, ctx, true));
352
+ registerRenderer("ray", (el, ctx) => lineLike(el, ctx, true));
353
+ registerRenderer("wire", (el, ctx) => lineLike(el, ctx));
354
+ registerRenderer("table_edge", (el, ctx) => {
115
355
  const minX = Math.min(el.x1, el.x2);
116
356
  const maxX = Math.max(el.x1, el.x2);
117
357
  const minY = Math.min(el.y1, el.y2);
@@ -123,42 +363,46 @@ registerRenderer("table_edge", (el) => {
123
363
  const numV = Math.max(2, Math.floor(h / 12));
124
364
  for (let j = 0; j <= numH; j++) {
125
365
  const x = minX + j / numH * w;
126
- hashes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: x, y1: minY, x2: x - 8, y2: minY + 10, stroke: colorOf(el), strokeWidth: 1 }, `h-${j}`));
366
+ hashes.push(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: x, y1: minY, x2: x - 8, y2: minY + 10, stroke: colorOf(el), strokeWidth: 1 }, `h-${j}`));
127
367
  }
128
368
  for (let j = 0; j <= numV; j++) {
129
369
  const y = minY + j / numV * h;
130
- hashes.push(/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: maxX, y1: y, x2: maxX - 10, y2: y + 8, stroke: colorOf(el), strokeWidth: 1 }, `v-${j}`));
370
+ hashes.push(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: maxX, y1: y, x2: maxX - 10, y2: y + 8, stroke: colorOf(el), strokeWidth: 1 }, `v-${j}`));
131
371
  }
132
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
133
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("polyline", { points: `${minX},${minY} ${maxX},${minY} ${maxX},${maxY}`, fill: "none", stroke: colorOf(el), strokeWidth: strokeOf(el) }),
372
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { children: [
373
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polyline", { points: `${minX},${minY} ${maxX},${minY} ${maxX},${maxY}`, fill: "none", stroke: colorOf(el), strokeWidth: strokeOf(el) }),
134
374
  hashes,
135
- el.label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { x: minX + w / 2, y: minY - 10, fill: colorOf(el), textAnchor: "middle", fontSize: fontOf(el), children: String(el.label) })
375
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: minX + w / 2, y: minY - 10, fill: colorOf(el), textAnchor: "middle", fontSize: fontOf(el), children: String(el.label) }) : null,
376
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BBoxInteractionOverlay, { minX, minY, width: w, height: h, ctx, inset: 12 }),
377
+ endpointHandles(el, ctx)
136
378
  ] });
137
379
  });
138
380
 
139
381
  // src/core/renderers/mechanics.tsx
140
- var import_jsx_runtime2 = require("react/jsx-runtime");
382
+ var import_jsx_runtime3 = require("react/jsx-runtime");
141
383
  var c = (el) => el.color || "#0f172a";
142
384
  var sw = (el) => el.strokeWidth || 2;
143
385
  var fo = (el) => el.fillOpacity ?? 0.2;
386
+ var font = (el) => el.fontSize || 18;
387
+ var rotOf = (el) => el.rotation || 0;
144
388
  registerRenderer("slab", (el) => {
145
389
  const b = getBounds(el.x1, el.y1, el.x2, el.y2);
146
390
  const depth = el.curveHeight ?? 20;
147
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { children: [
148
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: `M ${b.minX} ${b.minY} L ${b.maxX} ${b.minY} L ${b.maxX} ${b.maxY} L ${b.minX} ${b.maxY} Z`, fill: c(el), fillOpacity: fo(el), stroke: c(el), strokeWidth: sw(el) }),
149
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: `M ${b.minX} ${b.minY} L ${b.minX + depth} ${b.minY - depth} L ${b.maxX + depth} ${b.minY - depth} L ${b.maxX} ${b.minY} Z`, fill: c(el), fillOpacity: Math.max(0, fo(el) - 0.1), stroke: c(el), strokeWidth: sw(el) }),
150
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: `M ${b.maxX} ${b.minY} L ${b.maxX + depth} ${b.minY - depth} L ${b.maxX + depth} ${b.maxY - depth} L ${b.maxX} ${b.maxY} Z`, fill: c(el), fillOpacity: Math.min(1, fo(el) + 0.1), stroke: c(el), strokeWidth: sw(el) })
391
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
392
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: `M ${b.minX} ${b.minY} L ${b.maxX} ${b.minY} L ${b.maxX} ${b.maxY} L ${b.minX} ${b.maxY} Z`, fill: c(el), fillOpacity: fo(el), stroke: c(el), strokeWidth: sw(el) }),
393
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: `M ${b.minX} ${b.minY} L ${b.minX + depth} ${b.minY - depth} L ${b.maxX + depth} ${b.minY - depth} L ${b.maxX} ${b.minY} Z`, fill: c(el), fillOpacity: Math.max(0, fo(el) - 0.1), stroke: c(el), strokeWidth: sw(el) }),
394
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: `M ${b.maxX} ${b.minY} L ${b.maxX + depth} ${b.minY - depth} L ${b.maxX + depth} ${b.maxY - depth} L ${b.maxX} ${b.maxY} Z`, fill: c(el), fillOpacity: Math.min(1, fo(el) + 0.1), stroke: c(el), strokeWidth: sw(el) })
151
395
  ] });
152
396
  });
153
397
  registerRenderer("sphere", (el) => {
154
398
  const r = Math.max(4, distance(el.x1, el.y1, el.x2, el.y2));
155
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: el.x1, cy: el.y1, r, fill: c(el), fillOpacity: fo(el), stroke: c(el), strokeWidth: sw(el) });
399
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: el.x1, cy: el.y1, r, fill: c(el), fillOpacity: fo(el), stroke: c(el), strokeWidth: sw(el) });
156
400
  });
157
401
  registerRenderer("shell", (el) => {
158
402
  const r = Math.max(10, distance(el.x1, el.y1, el.x2, el.y2));
159
403
  const t = el.curveHeight ?? 10;
160
404
  const innerR = Math.max(1, r - t);
161
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
405
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
162
406
  "path",
163
407
  {
164
408
  d: `M ${el.x1 - r} ${el.y1} A ${r} ${r} 0 1 0 ${el.x1 + r} ${el.y1} A ${r} ${r} 0 1 0 ${el.x1 - r} ${el.y1} Z M ${el.x1 - innerR} ${el.y1} A ${innerR} ${innerR} 0 1 1 ${el.x1 + innerR} ${el.y1} A ${innerR} ${innerR} 0 1 1 ${el.x1 - innerR} ${el.y1} Z`,
@@ -174,11 +418,11 @@ registerRenderer("cylinder", (el) => {
174
418
  const d = distance(el.x1, el.y1, el.x2, el.y2);
175
419
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
176
420
  const r = el.curveHeight ?? 20;
177
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
178
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: `M 0 ${-r} L ${d} ${-r} A 10 ${r} 0 0 1 ${d} ${r} L 0 ${r} Z`, fill: c(el), fillOpacity: fo(el) }),
179
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: `M 0 ${-r} L ${d} ${-r} M 0 ${r} L ${d} ${r}`, fill: "none", stroke: c(el), strokeWidth: sw(el) }),
180
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: d, cy: 0, rx: 10, ry: r, fill: c(el), fillOpacity: fo(el), stroke: c(el), strokeWidth: sw(el) }),
181
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: 0, cy: 0, rx: 10, ry: r, fill: "none", stroke: c(el), strokeWidth: sw(el), strokeDasharray: "2 4" })
421
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
422
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: `M 0 ${-r} L ${d} ${-r} A 10 ${r} 0 0 1 ${d} ${r} L 0 ${r} Z`, fill: c(el), fillOpacity: fo(el) }),
423
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: `M 0 ${-r} L ${d} ${-r} M 0 ${r} L ${d} ${r}`, fill: "none", stroke: c(el), strokeWidth: sw(el) }),
424
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: d, cy: 0, rx: 10, ry: r, fill: c(el), fillOpacity: fo(el), stroke: c(el), strokeWidth: sw(el) }),
425
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: 0, cy: 0, rx: 10, ry: r, fill: "none", stroke: c(el), strokeWidth: sw(el), strokeDasharray: "2 4" })
182
426
  ] });
183
427
  });
184
428
  registerRenderer("ladder", (el) => {
@@ -187,11 +431,11 @@ registerRenderer("ladder", (el) => {
187
431
  const hw = el.curveHeight ?? 10;
188
432
  const rungs = [];
189
433
  for (let i = 15; i < d - 10; i += 15) {
190
- rungs.push(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: i, y1: -hw, x2: i, y2: hw, stroke: c(el), strokeWidth: Math.max(1, sw(el) - 1) }, i));
434
+ rungs.push(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: i, y1: -hw, x2: i, y2: hw, stroke: c(el), strokeWidth: Math.max(1, sw(el) - 1) }, i));
191
435
  }
192
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
193
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: 0, y1: -hw, x2: d, y2: -hw, stroke: c(el), strokeWidth: sw(el) }),
194
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: 0, y1: hw, x2: d, y2: hw, stroke: c(el), strokeWidth: sw(el) }),
436
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
437
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: -hw, x2: d, y2: -hw, stroke: c(el), strokeWidth: sw(el) }),
438
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: hw, x2: d, y2: hw, stroke: c(el), strokeWidth: sw(el) }),
195
439
  rungs
196
440
  ] });
197
441
  });
@@ -204,59 +448,93 @@ registerRenderer("spring", (el) => {
204
448
  path += ` L ${i * 12 + 3} 8 L ${i * 12 + 9} -8`;
205
449
  }
206
450
  path += ` L ${d} 0`;
207
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: path, fill: "none", stroke: c(el), strokeWidth: sw(el), strokeLinejoin: "round" }) });
451
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: path, fill: "none", stroke: c(el), strokeWidth: sw(el), strokeLinejoin: "round" }) });
208
452
  });
209
453
  registerRenderer("rod", (el) => {
210
454
  const d = distance(el.x1, el.y1, el.x2, el.y2);
211
455
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
212
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c(el), strokeWidth: Math.max(4, sw(el)), strokeLinecap: "round" }) });
456
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c(el), strokeWidth: Math.max(4, sw(el)), strokeLinecap: "round" }) });
213
457
  });
214
458
  registerRenderer("arc_arrow", (el) => {
215
459
  const r = Math.max(8, distance(el.x1, el.y1, el.x2, el.y2));
216
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
217
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: `M ${r} 0 A ${r} ${r} 0 0 0 ${-r} 0`, fill: "none", stroke: c(el), strokeWidth: sw(el) }),
218
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polygon", { points: `${-r},2 ${-r - 6},-10 ${-r + 6},-10`, fill: c(el) })
460
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
461
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: `M ${r} 0 A ${r} ${r} 0 0 0 ${-r} 0`, fill: "none", stroke: c(el), strokeWidth: sw(el) }),
462
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: `${-r},2 ${-r - 6},-10 ${-r + 6},-10`, fill: c(el) })
219
463
  ] });
220
464
  });
221
- registerRenderer("lens_convex", (el) => {
222
- const b = getBounds(el.x1, el.y1, el.x2, el.y2);
223
- const cx = b.minX + b.width / 2;
224
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx, cy: b.minY + b.height / 2, rx: Math.max(6, b.width / 2), ry: b.height / 2, fill: c(el), fillOpacity: Math.max(0.15, fo(el)), stroke: c(el), strokeWidth: sw(el) });
465
+ registerRenderer("lens_convex", (el, ctx) => {
466
+ const d = opticsEffectiveChord(el.x1, el.y1, el.x2, el.y2);
467
+ const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
468
+ const arcH = el.curveHeight !== void 0 ? Number(el.curveHeight) : d * 0.15;
469
+ const fs = el.fontSize || 16;
470
+ const pathD = `M 0 0 Q ${d / 2} ${-arcH * 2} ${d} 0 Q ${d / 2} ${arcH * 2} 0 0 Z`;
471
+ const bulgePad = Math.ceil(2 * arcH) + Math.ceil(fs + 14);
472
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
473
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
474
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: pathD, fill: c(el), fillOpacity: Math.max(0.15, fo(el)), stroke: c(el), strokeWidth: sw(el) }),
475
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: d / 2, y: -arcH - 10, fontSize: fs, fill: c(el), textAnchor: "middle", children: String(el.label) }) : null,
476
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LocalFatPathHit, { dPath: pathD, strokeWidth: 28, strokeLinejoin: "round" })
477
+ ] }),
478
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChordInteractionOverlay, { el, ctx, padX: 15 + bulgePad, padY: 20 + bulgePad })
479
+ ] });
225
480
  });
226
- registerRenderer("lens_concave", (el) => {
227
- const b = getBounds(el.x1, el.y1, el.x2, el.y2);
228
- const cx = b.minX + b.width / 2;
229
- const rx = Math.max(6, b.width / 2);
230
- const ry = b.height / 2;
231
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
232
- "path",
233
- {
234
- d: `M ${cx - rx} ${b.minY} Q ${cx - rx * 0.2} ${b.minY + ry} ${cx - rx} ${b.maxY} M ${cx + rx} ${b.minY} Q ${cx + rx * 0.2} ${b.minY + ry} ${cx + rx} ${b.maxY}`,
235
- fill: "none",
236
- stroke: c(el),
237
- strokeWidth: sw(el)
238
- }
239
- );
481
+ registerRenderer("lens_concave", (el, ctx) => {
482
+ const d = opticsEffectiveChord(el.x1, el.y1, el.x2, el.y2);
483
+ const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
484
+ const bow = el.curveHeight !== void 0 ? Number(el.curveHeight) : Math.max(10, d * 0.04);
485
+ const hw = Math.max(15, d * 0.05);
486
+ const fs = el.fontSize || 16;
487
+ const pathD = `M 0 ${-hw} L 0 ${hw} Q ${d / 2} ${hw - bow * 2} ${d} ${hw} L ${d} ${-hw} Q ${d / 2} ${-hw + bow * 2} 0 ${-hw} Z`;
488
+ const bulgePad = Math.ceil(hw + 2 * bow) + Math.ceil(fs + 14);
489
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
490
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
491
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: pathD, fill: c(el), fillOpacity: Math.max(0.15, fo(el)), stroke: c(el), strokeWidth: sw(el) }),
492
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: d / 2, y: -hw - 10, fontSize: fs, fill: c(el), textAnchor: "middle", children: String(el.label) }) : null,
493
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LocalFatPathHit, { dPath: pathD, strokeWidth: 28, strokeLinejoin: "round" })
494
+ ] }),
495
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChordInteractionOverlay, { el, ctx, padX: 15 + bulgePad, padY: 20 + bulgePad })
496
+ ] });
240
497
  });
241
- registerRenderer("mirror_concave", (el) => {
242
- const b = getBounds(el.x1, el.y1, el.x2, el.y2);
243
- const ch = el.curveHeight ?? 40;
244
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: `M ${b.minX} ${b.minY} Q ${b.maxX + ch} ${b.minY + b.height / 2} ${b.minX} ${b.maxY}`, fill: "none", stroke: c(el), strokeWidth: sw(el) });
498
+ registerRenderer("mirror_concave", (el, ctx) => {
499
+ const d = opticsEffectiveChord(el.x1, el.y1, el.x2, el.y2);
500
+ const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
501
+ const arcH = el.curveHeight !== void 0 ? Number(el.curveHeight) : d * 0.2;
502
+ const fs = el.fontSize || 16;
503
+ const pathD = `M 0 0 Q ${d / 2} ${arcH * 2} ${d} 0`;
504
+ const bulgePad = Math.ceil(2 * arcH) + 24;
505
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
506
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
507
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: pathD, fill: "none", stroke: c(el), strokeWidth: sw(el) }),
508
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: d / 2, y: -15, fontSize: fs, fill: c(el), textAnchor: "middle", children: String(el.label) }) : null,
509
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LocalFatPathHit, { dPath: pathD, strokeWidth: 28, strokeLinecap: "round" })
510
+ ] }),
511
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChordInteractionOverlay, { el, ctx, padX: 15 + bulgePad, padY: 20 + bulgePad })
512
+ ] });
245
513
  });
246
- registerRenderer("mirror_convex", (el) => {
247
- const b = getBounds(el.x1, el.y1, el.x2, el.y2);
248
- const ch = el.curveHeight ?? 40;
249
- const midY = b.minY + b.height / 2;
250
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: `M ${b.minX} ${b.minY} Q ${b.minX - ch} ${midY} ${b.minX} ${b.maxY}`, fill: "none", stroke: c(el), strokeWidth: sw(el) });
514
+ registerRenderer("mirror_convex", (el, ctx) => {
515
+ const d = opticsEffectiveChord(el.x1, el.y1, el.x2, el.y2);
516
+ const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
517
+ const arcH = el.curveHeight !== void 0 ? Number(el.curveHeight) : d * 0.2;
518
+ const fs = el.fontSize || 16;
519
+ const pathD = `M 0 0 Q ${d / 2} ${-arcH * 2} ${d} 0`;
520
+ const bulgePad = Math.ceil(2 * arcH) + 24;
521
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
522
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
523
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: pathD, fill: "none", stroke: c(el), strokeWidth: sw(el) }),
524
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: d / 2, y: -15, fontSize: fs, fill: c(el), textAnchor: "middle", children: String(el.label) }) : null,
525
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LocalFatPathHit, { dPath: pathD, strokeWidth: 28, strokeLinecap: "round" })
526
+ ] }),
527
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChordInteractionOverlay, { el, ctx, padX: 15 + bulgePad, padY: 20 + bulgePad })
528
+ ] });
251
529
  });
252
530
  registerRenderer("prism", (el) => {
253
531
  const d = distance(el.x1, el.y1, el.x2, el.y2);
254
532
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
255
533
  const py = -d * 0.866;
256
534
  const fs = el.fontSize || 16;
257
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
258
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polygon", { points: `0,0 ${d},0 ${d / 2},${py}`, fill: c(el), fillOpacity: Math.max(0.15, fo(el)), stroke: c(el), strokeWidth: sw(el), strokeLinejoin: "round" }),
259
- el.label && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: d / 2, y: py - 10, fontSize: fs, fill: c(el), textAnchor: "middle", children: String(el.label) })
535
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
536
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: `0,0 ${d},0 ${d / 2},${py}`, fill: c(el), fillOpacity: Math.max(0.15, fo(el)), stroke: c(el), strokeWidth: sw(el), strokeLinejoin: "round" }),
537
+ el.label && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: d / 2, y: py - 10, fontSize: fs, fill: c(el), textAnchor: "middle", children: String(el.label) })
260
538
  ] });
261
539
  });
262
540
  registerRenderer("slit_single", (el) => {
@@ -265,18 +543,18 @@ registerRenderer("slit_single", (el) => {
265
543
  const gap = 16;
266
544
  const thick = Math.max(4, sw(el));
267
545
  const fs = el.fontSize || 16;
268
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
269
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: 0, y1: 0, x2: d / 2 - gap / 2, y2: 0, stroke: c(el), strokeWidth: thick, strokeLinecap: "square" }),
270
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: d / 2 + gap / 2, y1: 0, x2: d, y2: 0, stroke: c(el), strokeWidth: thick, strokeLinecap: "square" }),
271
- el.label && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: d / 2, y: -14, fontSize: fs, fill: c(el), textAnchor: "middle", children: String(el.label) })
546
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
547
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: 0, x2: d / 2 - gap / 2, y2: 0, stroke: c(el), strokeWidth: thick, strokeLinecap: "square" }),
548
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: d / 2 + gap / 2, y1: 0, x2: d, y2: 0, stroke: c(el), strokeWidth: thick, strokeLinecap: "square" }),
549
+ el.label && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: d / 2, y: -14, fontSize: fs, fill: c(el), textAnchor: "middle", children: String(el.label) })
272
550
  ] });
273
551
  });
274
552
  registerRenderer("slit_double", (el) => {
275
553
  const b = getBounds(el.x1, el.y1, el.x2, el.y2);
276
554
  const cx = b.minX + b.width / 2;
277
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { children: [
278
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: cx - 6, y1: b.minY, x2: cx - 6, y2: b.maxY, stroke: c(el), strokeWidth: sw(el) }),
279
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: cx + 6, y1: b.minY, x2: cx + 6, y2: b.maxY, stroke: c(el), strokeWidth: sw(el) })
555
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
556
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: cx - 6, y1: b.minY, x2: cx - 6, y2: b.maxY, stroke: c(el), strokeWidth: sw(el) }),
557
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: cx + 6, y1: b.minY, x2: cx + 6, y2: b.maxY, stroke: c(el), strokeWidth: sw(el) })
280
558
  ] });
281
559
  });
282
560
  registerRenderer("photon", (el) => {
@@ -292,19 +570,19 @@ registerRenderer("photon", (el) => {
292
570
  path += `L ${x} ${y} `;
293
571
  }
294
572
  path += `L ${d} 0`;
295
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
296
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: path, fill: "none", stroke: c(el), strokeWidth: sw(el) }),
297
- el.label && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: d / 2, y: -12, textAnchor: "middle", fill: c(el), children: String(el.label) })
573
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
574
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: path, fill: "none", stroke: c(el), strokeWidth: sw(el) }),
575
+ el.label && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: d / 2, y: -12, textAnchor: "middle", fill: c(el), children: String(el.label) })
298
576
  ] });
299
577
  });
300
578
  registerRenderer("glass_slab", (el) => {
301
579
  const b = getBounds(el.x1, el.y1, el.x2, el.y2);
302
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: b.minX, y: b.minY, width: b.width, height: b.height, fill: c(el), fillOpacity: Math.max(0.1, fo(el)), stroke: c(el), strokeWidth: sw(el) });
580
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: b.minX, y: b.minY, width: b.width, height: b.height, fill: c(el), fillOpacity: Math.max(0.1, fo(el)), stroke: c(el), strokeWidth: sw(el) });
303
581
  });
304
582
  registerRenderer("pipe", (el) => {
305
583
  const b = getBounds(el.x1, el.y1, el.x2, el.y2);
306
584
  const neck = el.curveHeight ?? b.height * 0.35;
307
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
585
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
308
586
  "path",
309
587
  {
310
588
  d: `M ${b.minX} ${b.minY} C ${b.minX + b.width * 0.3} ${b.minY}, ${b.minX + b.width * 0.3} ${b.minY + neck}, ${b.minX + b.width * 0.5} ${b.minY + neck} C ${b.minX + b.width * 0.7} ${b.minY + neck}, ${b.minX + b.width * 0.7} ${b.minY}, ${b.maxX} ${b.minY} L ${b.maxX} ${b.maxY} C ${b.minX + b.width * 0.7} ${b.maxY}, ${b.minX + b.width * 0.7} ${b.maxY - neck}, ${b.minX + b.width * 0.5} ${b.maxY - neck} C ${b.minX + b.width * 0.3} ${b.maxY - neck}, ${b.minX + b.width * 0.3} ${b.maxY}, ${b.minX} ${b.maxY} Z`,
@@ -317,69 +595,230 @@ registerRenderer("pipe", (el) => {
317
595
  });
318
596
  registerRenderer("liquid", (el) => {
319
597
  const b = getBounds(el.x1, el.y1, el.x2, el.y2);
320
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { children: [
321
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: `M ${b.minX} ${b.minY} Q ${b.minX + b.width / 2} ${b.minY + 10} ${b.maxX} ${b.minY} L ${b.maxX} ${b.maxY} L ${b.minX} ${b.maxY} Z`, fill: c(el), fillOpacity: Math.max(0.2, fo(el)) }),
322
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: `M ${b.minX} ${b.minY} Q ${b.minX + b.width / 2} ${b.minY + 10} ${b.maxX} ${b.minY}`, fill: "none", stroke: c(el), strokeWidth: 2 })
598
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
599
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: `M ${b.minX} ${b.minY} Q ${b.minX + b.width / 2} ${b.minY + 10} ${b.maxX} ${b.minY} L ${b.maxX} ${b.maxY} L ${b.minX} ${b.maxY} Z`, fill: c(el), fillOpacity: Math.max(0.2, fo(el)) }),
600
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: `M ${b.minX} ${b.minY} Q ${b.minX + b.width / 2} ${b.minY + 10} ${b.maxX} ${b.minY}`, fill: "none", stroke: c(el), strokeWidth: 2 })
323
601
  ] });
324
602
  });
325
603
  registerRenderer("orbit", (el) => {
326
604
  const b = getBounds(el.x1, el.y1, el.x2, el.y2);
327
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: b.minX + b.width / 2, cy: b.minY + b.height / 2, rx: b.width / 2, ry: b.height / 2, fill: "none", stroke: c(el), strokeWidth: sw(el), strokeDasharray: "3 3" });
605
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: b.minX + b.width / 2, cy: b.minY + b.height / 2, rx: b.width / 2, ry: b.height / 2, fill: "none", stroke: c(el), strokeWidth: sw(el), strokeDasharray: "3 3" });
328
606
  });
329
- registerRenderer("pole_piece", (el) => {
607
+ registerRenderer("pole_piece", (el, ctx) => {
330
608
  const b = getBounds(el.x1, el.y1, el.x2, el.y2);
331
609
  const poleLabel = String(el.label || "N");
332
610
  const isNorth = poleLabel.toUpperCase().includes("N");
333
611
  const blockFill = isNorth ? "#ef4444" : "#3b82f6";
334
612
  const labelFs = Math.min(b.width, b.height) * 0.5 + 10;
335
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { children: [
336
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: b.minX, y: b.minY, width: b.width, height: b.height, fill: blockFill, fillOpacity: 0.2, stroke: c(el), strokeWidth: sw(el), rx: "4" }),
337
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: b.minX + b.width / 2, y: b.minY + b.height / 2, textAnchor: "middle", dominantBaseline: "central", fill: c(el), fontWeight: "bold", fontSize: labelFs, children: poleLabel })
613
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
614
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: b.minX, y: b.minY, width: b.width, height: b.height, fill: blockFill, fillOpacity: 0.2, stroke: c(el), strokeWidth: sw(el), rx: "4" }),
615
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: b.minX + b.width / 2, y: b.minY + b.height / 2, textAnchor: "middle", dominantBaseline: "central", fill: c(el), fontWeight: "bold", fontSize: labelFs, children: poleLabel }),
616
+ bboxInsetSelectionChrome(b.minX, b.minY, b.width, b.height, ctx)
338
617
  ] });
339
618
  });
340
- registerRenderer("b_field_line", (el) => {
619
+ registerRenderer("b_field_line", (el, ctx) => {
341
620
  const d = distance(el.x1, el.y1, el.x2, el.y2);
342
621
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
343
622
  const fs = el.fontSize || 18;
344
623
  const mid = d / 2;
345
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
346
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c(el), strokeWidth: sw(el), strokeLinecap: "round" }),
347
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polygon", { points: `${mid + 6},0 ${mid - 6},-5 ${mid - 6},5`, fill: c(el) }),
348
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: mid, y: -10, textAnchor: "middle", fill: c(el), fontSize: fs, children: String(el.label) }) : null
624
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
625
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
626
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c(el), strokeWidth: sw(el), strokeLinecap: "round" }),
627
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: `${mid + 6},0 ${mid - 6},-5 ${mid - 6},5`, fill: c(el) }),
628
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: mid, y: -10, textAnchor: "middle", fill: c(el), fontSize: fs, children: String(el.label) }) : null
629
+ ] }),
630
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChordInteractionOverlay, { el, ctx })
349
631
  ] });
350
632
  });
351
- registerRenderer("b_field_curve", (el) => {
633
+ registerRenderer("b_field_curve", (el, ctx) => {
352
634
  const d = distance(el.x1, el.y1, el.x2, el.y2);
353
635
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
354
636
  const curveHeight = el.curveHeight !== void 0 ? el.curveHeight : d * 0.4;
355
637
  const fs = el.fontSize || 18;
356
638
  const apexY = -curveHeight;
357
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
358
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
359
- "path",
360
- {
361
- d: `M 0 0 Q ${d / 2} ${-curveHeight * 2} ${d} 0`,
362
- fill: "none",
363
- stroke: c(el),
364
- strokeWidth: sw(el),
365
- strokeLinecap: "round"
366
- }
367
- ),
368
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polygon", { points: `${d / 2 + 6},${apexY} ${d / 2 - 6},${apexY - 5} ${d / 2 - 6},${apexY + 5}`, fill: c(el) }),
369
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: d / 2, y: apexY - 15, textAnchor: "middle", fill: c(el), fontSize: fs, children: String(el.label) }) : null
639
+ const bb = quadraticChordWorldBounds(el.x1, el.y1, el.x2, el.y2, curveHeight);
640
+ const curvePath = `M 0 0 Q ${d / 2} ${-curveHeight * 2} ${d} 0`;
641
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
642
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
643
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: curvePath, fill: "none", stroke: c(el), strokeWidth: sw(el), strokeLinecap: "round" }),
644
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: `${d / 2 + 6},${apexY} ${d / 2 - 6},${apexY - 5} ${d / 2 - 6},${apexY + 5}`, fill: c(el) }),
645
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: d / 2, y: apexY - 15, textAnchor: "middle", fill: c(el), fontSize: fs, children: String(el.label) }) : null,
646
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LocalFatPathHit, { dPath: curvePath, strokeWidth: 24, strokeLinecap: "round" })
647
+ ] }),
648
+ ctx.isSelected && !ctx.viewMode && !ctx.isGhost ? selectionDashRect(bb.x, bb.y, bb.width, bb.height) : null,
649
+ endpointHandles(el, ctx)
370
650
  ] });
371
651
  });
372
- registerRenderer("axes_3d", (el) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { children: [
373
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: el.x1, y1: el.y1, x2: el.x1 + 100, y2: el.y1, stroke: c(el), strokeWidth: sw(el) }),
374
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: el.x1, y1: el.y1, x2: el.x1, y2: el.y1 - 100, stroke: c(el), strokeWidth: sw(el) }),
375
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: el.x1, y1: el.y1, x2: el.x1 + 70, y2: el.y1 + 50, stroke: c(el), strokeWidth: sw(el) })
652
+ registerRenderer("axes_3d", (el) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
653
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: el.x1, y1: el.y1, x2: el.x1 + 100, y2: el.y1, stroke: c(el), strokeWidth: sw(el) }),
654
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: el.x1, y1: el.y1, x2: el.x1, y2: el.y1 - 100, stroke: c(el), strokeWidth: sw(el) }),
655
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: el.x1, y1: el.y1, x2: el.x1 + 70, y2: el.y1 + 50, stroke: c(el), strokeWidth: sw(el) })
376
656
  ] }));
657
+ registerRenderer("string", (el, ctx) => {
658
+ const d = distance(el.x1, el.y1, el.x2, el.y2);
659
+ const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
660
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
661
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c(el), strokeWidth: sw(el), strokeLinecap: "round" }) }),
662
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChordInteractionOverlay, { el, ctx })
663
+ ] });
664
+ });
665
+ registerRenderer("surface", (el, ctx) => {
666
+ const d = distance(el.x1, el.y1, el.x2, el.y2);
667
+ const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
668
+ const hatchCount = Math.max(0, Math.floor(d / 12));
669
+ const hatches = [];
670
+ for (let i = 0; i <= hatchCount; i++) {
671
+ hatches.push(/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: i * 12, y1: 0, x2: i * 12 - 8, y2: 12, stroke: c(el), strokeWidth: 1.5 }, i));
672
+ }
673
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
674
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
675
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c(el), strokeWidth: sw(el) + 0.5, strokeLinecap: "round" }),
676
+ hatches
677
+ ] }),
678
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChordInteractionOverlay, { el, ctx })
679
+ ] });
680
+ });
681
+ registerRenderer("block", (el, ctx) => {
682
+ const w = Math.abs(el.x2 - el.x1);
683
+ const h = Math.abs(el.y2 - el.y1);
684
+ const cx = (el.x1 + el.x2) / 2;
685
+ const cy = (el.y1 + el.y2) / 2;
686
+ const rot = rotOf(el);
687
+ const b = getBounds(el.x1, el.y1, el.x2, el.y2);
688
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
689
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${cx}, ${cy}) rotate(${rot})`, children: [
690
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: -w / 2, y: -h / 2, width: w, height: h, fill: "#f1f5f9", stroke: c(el), strokeWidth: sw(el) }),
691
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
692
+ "text",
693
+ {
694
+ x: 0,
695
+ y: el.value ? -6 : 0,
696
+ dominantBaseline: "central",
697
+ textAnchor: "middle",
698
+ fill: c(el),
699
+ fontSize: 18,
700
+ fontFamily: "serif",
701
+ fontWeight: "bold",
702
+ children: String(el.label)
703
+ }
704
+ ) : null,
705
+ el.value ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
706
+ "text",
707
+ {
708
+ x: 0,
709
+ y: el.label ? 10 : 0,
710
+ dominantBaseline: "central",
711
+ textAnchor: "middle",
712
+ fill: "#4b5563",
713
+ fontSize: 14,
714
+ fontFamily: "sans-serif",
715
+ children: String(el.value)
716
+ }
717
+ ) : null
718
+ ] }),
719
+ bboxInsetSelectionChrome(b.minX, b.minY, b.width, b.height, ctx),
720
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChordInteractionOverlay, { el, ctx })
721
+ ] });
722
+ });
723
+ registerRenderer("cart", (el, ctx) => {
724
+ const w = Math.abs(el.x2 - el.x1);
725
+ const h = Math.abs(el.y2 - el.y1);
726
+ const cx = (el.x1 + el.x2) / 2;
727
+ const cy = (el.y1 + el.y2) / 2;
728
+ const rot = rotOf(el);
729
+ const wheelR = Math.min(w * 0.15, h * 0.25, 12);
730
+ const b = getBounds(el.x1, el.y1, el.x2, el.y2);
731
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
732
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${cx}, ${cy}) rotate(${rot})`, children: [
733
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: -w / 2, y: -h / 2, width: w, height: h - wheelR, fill: "#f1f5f9", stroke: c(el), strokeWidth: sw(el) }),
734
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: -w / 2 + wheelR * 1.5, cy: h / 2 - wheelR, r: wheelR, fill: "#cbd5e1", stroke: c(el), strokeWidth: sw(el) }),
735
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: w / 2 - wheelR * 1.5, cy: h / 2 - wheelR, r: wheelR, fill: "#cbd5e1", stroke: c(el), strokeWidth: sw(el) }),
736
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
737
+ "text",
738
+ {
739
+ x: 0,
740
+ y: el.value ? -8 : -2,
741
+ dominantBaseline: "central",
742
+ textAnchor: "middle",
743
+ fill: c(el),
744
+ fontSize: 18,
745
+ fontFamily: "serif",
746
+ fontWeight: "bold",
747
+ children: String(el.label)
748
+ }
749
+ ) : null,
750
+ el.value ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
751
+ "text",
752
+ {
753
+ x: 0,
754
+ y: el.label ? 8 : -2,
755
+ dominantBaseline: "central",
756
+ textAnchor: "middle",
757
+ fill: "#4b5563",
758
+ fontSize: 14,
759
+ fontFamily: "sans-serif",
760
+ children: String(el.value)
761
+ }
762
+ ) : null
763
+ ] }),
764
+ bboxInsetSelectionChrome(b.minX, b.minY, b.width, b.height, ctx),
765
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChordInteractionOverlay, { el, ctx })
766
+ ] });
767
+ });
768
+ registerRenderer("particle", (el, ctx) => {
769
+ const d = distance(el.x1, el.y1, el.x2, el.y2);
770
+ const r = Math.max(5, d);
771
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
772
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
773
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: 0, cy: 0, r, fill: c(el) }),
774
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: 0, y: -r - 12, dominantBaseline: "central", textAnchor: "middle", fill: c(el), fontSize: font(el), fontFamily: "serif", fontWeight: "bold", children: String(el.label) }) : null,
775
+ el.value ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: 0, y: r + 12, dominantBaseline: "central", textAnchor: "middle", fill: "#4b5563", fontSize: 14, fontFamily: "sans-serif", children: String(el.value) }) : null
776
+ ] }),
777
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChordInteractionOverlay, { el, ctx })
778
+ ] });
779
+ });
780
+ registerRenderer("disk", (el, ctx) => {
781
+ const d = distance(el.x1, el.y1, el.x2, el.y2);
782
+ const r = Math.max(10, d);
783
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
784
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
785
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: 0, cy: 0, r, fill: "#e2e8f0", stroke: c(el), strokeWidth: sw(el) }),
786
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: 0, x2: r, y2: 0, stroke: c(el), strokeWidth: sw(el) }),
787
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: 0, cy: 0, r: 3, fill: c(el) }),
788
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: 0, y: -r - 12, dominantBaseline: "central", textAnchor: "middle", fill: c(el), fontSize: font(el), fontFamily: "serif", fontWeight: "bold", children: String(el.label) }) : null,
789
+ el.value ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: 0, y: r + 12, dominantBaseline: "central", textAnchor: "middle", fill: "#4b5563", fontSize: 14, fontFamily: "sans-serif", children: String(el.value) }) : null
790
+ ] }),
791
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChordInteractionOverlay, { el, ctx })
792
+ ] });
793
+ });
794
+ registerRenderer("com", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(OriginHitPlate, { ctx, hitHalfSize: 22, selectionHalfSize: 28, children: [
795
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: 0, cy: 0, r: 10, fill: "none", stroke: c(el), strokeWidth: sw(el) }),
796
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M 0 -10 A 10 10 0 0 1 10 0 L 0 0 Z", fill: c(el) }),
797
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M 0 10 A 10 10 0 0 1 -10 0 L 0 0 Z", fill: c(el) })
798
+ ] }) }));
799
+ registerRenderer("pivot", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(OriginHitPlate, { ctx, hitHalfSize: 24, selectionHalfSize: 32, children: [
800
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: "0,0 -12,20 12,20", fill: "#f8fafc", stroke: c(el), strokeWidth: sw(el), strokeLinejoin: "round" }),
801
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: -20, y1: 20, x2: 20, y2: 20, stroke: c(el), strokeWidth: sw(el), strokeLinecap: "round" }),
802
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: 0, cy: 0, r: 3, fill: c(el) })
803
+ ] }) }));
804
+ registerRenderer("pulley", (el, ctx) => {
805
+ const d = distance(el.x1, el.y1, el.x2, el.y2);
806
+ const r = Math.max(10, d);
807
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
808
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
809
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: 0, cy: 0, r, fill: "#f8fafc", stroke: c(el), strokeWidth: sw(el) }),
810
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: 0, cy: 0, r: 4, fill: c(el) }),
811
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: 0, y: -r - 12, dominantBaseline: "central", textAnchor: "middle", fill: c(el), fontSize: font(el), fontFamily: "serif", fontWeight: "bold", children: String(el.label) }) : null
812
+ ] }),
813
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChordInteractionOverlay, { el, ctx })
814
+ ] });
815
+ });
377
816
  registerRenderer("wedge", (el) => {
378
817
  const dx = el.x2 - el.x1;
379
818
  const dy = el.y2 - el.y1;
380
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
381
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polygon", { points: `0,0 ${dx},0 ${dx},${dy}`, fill: "#f1f5f9", stroke: c(el), strokeWidth: sw(el), strokeLinejoin: "round" }),
382
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
819
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
820
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: `0,0 ${dx},0 ${dx},${dy}`, fill: "#f1f5f9", stroke: c(el), strokeWidth: sw(el), strokeLinejoin: "round" }),
821
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
383
822
  "text",
384
823
  {
385
824
  x: dx * 0.6,
@@ -399,11 +838,11 @@ registerRenderer("dimension", (el) => {
399
838
  const d = distance(el.x1, el.y1, el.x2, el.y2);
400
839
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
401
840
  const w = Math.max(1, sw(el) - 0.5);
402
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
403
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c(el), strokeWidth: w, strokeDasharray: "4 4" }),
404
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polygon", { points: `0,0 10,-5 10,5`, fill: c(el) }),
405
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("polygon", { points: `${d},0 ${d - 10},-5 ${d - 10},5`, fill: c(el) }),
406
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: d / 2, y: -10, textAnchor: "middle", fill: c(el), fontSize: 18, fontFamily: "serif", fontStyle: "italic", fontWeight: "bold", children: String(el.label) }) : null
841
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
842
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c(el), strokeWidth: w, strokeDasharray: "4 4" }),
843
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: `0,0 10,-5 10,5`, fill: c(el) }),
844
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: `${d},0 ${d - 10},-5 ${d - 10},5`, fill: c(el) }),
845
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: d / 2, y: -10, textAnchor: "middle", fill: c(el), fontSize: 18, fontFamily: "serif", fontStyle: "italic", fontWeight: "bold", children: String(el.label) }) : null
407
846
  ] });
408
847
  });
409
848
  registerRenderer("arc", (el) => {
@@ -422,10 +861,10 @@ registerRenderer("arc", (el) => {
422
861
  const tx = textDist * Math.cos(midAngle * Math.PI / 180);
423
862
  const ty = textDist * Math.sin(midAngle * Math.PI / 180);
424
863
  const refLen = Math.max(d, 50);
425
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
426
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: 0, y1: 0, x2: refLen, y2: 0, stroke: c(el), strokeWidth: 1.5, strokeDasharray: "4 4" }),
427
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: `M ${startX} ${startY} A ${R} ${R} 0 0 ${sweepFlag} ${endX} ${endY}`, fill: "none", stroke: c(el), strokeWidth: sw(el) }),
428
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
864
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
865
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: 0, x2: refLen, y2: 0, stroke: c(el), strokeWidth: 1.5, strokeDasharray: "4 4" }),
866
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: `M ${startX} ${startY} A ${R} ${R} 0 0 ${sweepFlag} ${endX} ${endY}`, fill: "none", stroke: c(el), strokeWidth: sw(el) }),
867
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
429
868
  "text",
430
869
  {
431
870
  x: tx,
@@ -446,35 +885,35 @@ registerRenderer("nucleus", (el) => {
446
885
  const fs = el.fontSize || 18;
447
886
  const stroke2 = c(el);
448
887
  const w = sw(el);
449
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
450
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: 0, cy: 0, rx: 24, ry: 8, fill: "none", stroke: stroke2, strokeWidth: w, transform: "rotate(30)" }),
451
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: 0, cy: 0, rx: 24, ry: 8, fill: "none", stroke: stroke2, strokeWidth: w, transform: "rotate(-30)" }),
452
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ellipse", { cx: 0, cy: 0, rx: 24, ry: 8, fill: "none", stroke: stroke2, strokeWidth: w, transform: "rotate(90)" }),
453
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: 0, cy: 0, r: 6, fill: "#ef4444" }),
454
- el.label && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { x: 0, y: 36, fontSize: fs, fill: stroke2, textAnchor: "middle", children: String(el.label) })
888
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
889
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: 0, cy: 0, rx: 24, ry: 8, fill: "none", stroke: stroke2, strokeWidth: w, transform: "rotate(30)" }),
890
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: 0, cy: 0, rx: 24, ry: 8, fill: "none", stroke: stroke2, strokeWidth: w, transform: "rotate(-30)" }),
891
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { cx: 0, cy: 0, rx: 24, ry: 8, fill: "none", stroke: stroke2, strokeWidth: w, transform: "rotate(90)" }),
892
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: 0, cy: 0, r: 6, fill: "#ef4444" }),
893
+ el.label && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: 0, y: 36, fontSize: fs, fill: stroke2, textAnchor: "middle", children: String(el.label) })
455
894
  ] });
456
895
  });
457
896
  ["cone", "curved_wedge", "incline", "container", "hinge"].forEach(
458
897
  (t) => registerRenderer(t, (el) => {
459
898
  const d = distance(el.x1, el.y1, el.x2, el.y2);
460
899
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
461
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c(el), strokeWidth: sw(el) }) });
900
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c(el), strokeWidth: sw(el) }) });
462
901
  })
463
902
  );
464
903
  ["semicircle", "quarter_circle", "axes"].forEach(
465
904
  (t) => registerRenderer(t, (el) => {
466
905
  const b = getBounds(el.x1, el.y1, el.x2, el.y2);
467
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: b.minX, y: b.minY, width: b.width, height: b.height, fill: "none", stroke: c(el), strokeWidth: sw(el) });
906
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: b.minX, y: b.minY, width: b.width, height: b.height, fill: "none", stroke: c(el), strokeWidth: sw(el) });
468
907
  })
469
908
  );
470
909
 
471
910
  // src/core/renderers/magnetism.tsx
472
- var import_jsx_runtime3 = require("react/jsx-runtime");
911
+ var import_jsx_runtime4 = require("react/jsx-runtime");
473
912
  var c2 = (el) => el.color || "#0f172a";
474
913
  var sw2 = (el) => el.strokeWidth || 2;
475
914
  var fontSizeOf = (el) => el.fontSize || 18;
476
915
  var dashOf = (el) => el.lineStyle === "dashed" || el.type === "dashed_line" ? "6 6" : el.lineStyle === "dotted" ? "2 4" : void 0;
477
- registerRenderer("b_region_in", (el) => {
916
+ registerRenderer("b_region_in", (el, ctx) => {
478
917
  const b = getBounds(el.x1, el.y1, el.x2, el.y2);
479
918
  const minX = b.minX;
480
919
  const maxX = b.maxX;
@@ -487,21 +926,22 @@ registerRenderer("b_region_in", (el) => {
487
926
  for (let px = minX + spacing / 2; px <= maxX; px += spacing) {
488
927
  for (let py = minY + spacing / 2; py <= maxY; py += spacing) {
489
928
  marks.push(
490
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${px}, ${py})`, children: [
491
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: 0, cy: 0, r: markR, fill: "none", stroke: c2(el), strokeWidth: sw2(el) * 0.75 }),
492
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: -markR * 0.7, y1: -markR * 0.7, x2: markR * 0.7, y2: markR * 0.7, stroke: c2(el), strokeWidth: sw2(el) * 0.75 }),
493
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: markR * 0.7, y1: -markR * 0.7, x2: -markR * 0.7, y2: markR * 0.7, stroke: c2(el), strokeWidth: sw2(el) * 0.75 })
929
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { transform: `translate(${px}, ${py})`, children: [
930
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: 0, cy: 0, r: markR, fill: "none", stroke: c2(el), strokeWidth: sw2(el) * 0.75 }),
931
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: -markR * 0.7, y1: -markR * 0.7, x2: markR * 0.7, y2: markR * 0.7, stroke: c2(el), strokeWidth: sw2(el) * 0.75 }),
932
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: markR * 0.7, y1: -markR * 0.7, x2: -markR * 0.7, y2: markR * 0.7, stroke: c2(el), strokeWidth: sw2(el) * 0.75 })
494
933
  ] }, `${px}-${py}`)
495
934
  );
496
935
  }
497
936
  }
498
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
499
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: minX, y: minY, width: b.width, height: b.height, fill: c2(el), stroke: c2(el), strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.1 }),
937
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { children: [
938
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: minX, y: minY, width: b.width, height: b.height, fill: c2(el), stroke: c2(el), strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.1 }),
500
939
  marks,
501
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: midX, y: minY - 10, fontSize: fontSizeOf(el), fill: c2(el), textAnchor: "middle", fontWeight: "bold", children: String(el.label) }) : null
940
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("text", { x: midX, y: minY - 10, fontSize: fontSizeOf(el), fill: c2(el), textAnchor: "middle", fontWeight: "bold", children: String(el.label) }) : null,
941
+ bboxInsetSelectionChrome(minX, minY, b.width, b.height, ctx)
502
942
  ] });
503
943
  });
504
- registerRenderer("b_region_out", (el) => {
944
+ registerRenderer("b_region_out", (el, ctx) => {
505
945
  const b = getBounds(el.x1, el.y1, el.x2, el.y2);
506
946
  const minX = b.minX;
507
947
  const maxX = b.maxX;
@@ -514,93 +954,100 @@ registerRenderer("b_region_out", (el) => {
514
954
  for (let px = minX + spacing / 2; px <= maxX; px += spacing) {
515
955
  for (let py = minY + spacing / 2; py <= maxY; py += spacing) {
516
956
  marks.push(
517
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${px}, ${py})`, children: [
518
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: 0, cy: 0, r: markR, fill: "none", stroke: c2(el), strokeWidth: sw2(el) * 0.75 }),
519
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: 0, cy: 0, r: 2, fill: c2(el) })
957
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { transform: `translate(${px}, ${py})`, children: [
958
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: 0, cy: 0, r: markR, fill: "none", stroke: c2(el), strokeWidth: sw2(el) * 0.75 }),
959
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: 0, cy: 0, r: 2, fill: c2(el) })
520
960
  ] }, `${px}-${py}`)
521
961
  );
522
962
  }
523
963
  }
524
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
525
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: minX, y: minY, width: b.width, height: b.height, fill: c2(el), stroke: c2(el), strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.1 }),
964
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { children: [
965
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: minX, y: minY, width: b.width, height: b.height, fill: c2(el), stroke: c2(el), strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.1 }),
526
966
  marks,
527
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: midX, y: minY - 10, fontSize: fontSizeOf(el), fill: c2(el), textAnchor: "middle", fontWeight: "bold", children: String(el.label) }) : null
967
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("text", { x: midX, y: minY - 10, fontSize: fontSizeOf(el), fill: c2(el), textAnchor: "middle", fontWeight: "bold", children: String(el.label) }) : null,
968
+ bboxInsetSelectionChrome(minX, minY, b.width, b.height, ctx)
528
969
  ] });
529
970
  });
530
- registerRenderer("current_wire", (el) => {
971
+ registerRenderer("current_wire", (el, ctx) => {
531
972
  const d = distance(el.x1, el.y1, el.x2, el.y2);
532
973
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
533
974
  const mid = d / 2;
534
975
  const flip = angle > 90 || angle <= -90;
535
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
536
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c2(el), strokeWidth: sw2(el), strokeDasharray: dashOf(el), strokeLinecap: "round" }),
537
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: `${mid + 8},0 ${mid - 6},-7 ${mid - 6},7`, fill: c2(el) }),
538
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
539
- "text",
540
- {
541
- x: mid,
542
- y: -15,
543
- fontSize: fontSizeOf(el),
544
- fill: c2(el),
545
- textAnchor: "middle",
546
- fontStyle: "italic",
547
- transform: flip ? `rotate(180, ${mid}, -15)` : void 0,
548
- children: String(el.label)
549
- }
550
- ) : null
976
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { children: [
977
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
978
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c2(el), strokeWidth: sw2(el), strokeDasharray: dashOf(el), strokeLinecap: "round" }),
979
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polygon", { points: `${mid + 8},0 ${mid - 6},-7 ${mid - 6},7`, fill: c2(el) }),
980
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
981
+ "text",
982
+ {
983
+ x: mid,
984
+ y: -15,
985
+ fontSize: fontSizeOf(el),
986
+ fill: c2(el),
987
+ textAnchor: "middle",
988
+ fontStyle: "italic",
989
+ transform: flip ? `rotate(180, ${mid}, -15)` : void 0,
990
+ children: String(el.label)
991
+ }
992
+ ) : null
993
+ ] }),
994
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChordInteractionOverlay, { el, ctx })
551
995
  ] });
552
996
  });
553
- registerRenderer("bar_magnet", (el) => {
997
+ registerRenderer("bar_magnet", (el, ctx) => {
554
998
  const d = distance(el.x1, el.y1, el.x2, el.y2);
555
999
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
556
1000
  const magW = Math.max(20, sw2(el) * 10);
557
1001
  const flipLabel = angle > 90 || angle <= -90;
558
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
559
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: 0, y: -magW / 2, width: d / 2, height: magW, fill: "#ef4444", stroke: c2(el), strokeWidth: 1 }),
560
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { x: d / 2, y: -magW / 2, width: d / 2, height: magW, fill: "#3b82f6", stroke: c2(el), strokeWidth: 1 }),
561
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
562
- "text",
563
- {
564
- x: d / 4,
565
- y: 0,
566
- fill: "white",
567
- fontSize: magW * 0.6,
568
- fontWeight: "bold",
569
- textAnchor: "middle",
570
- dominantBaseline: "central",
571
- transform: flipLabel ? `rotate(180, ${d / 4}, 0)` : void 0,
572
- children: "N"
573
- }
574
- ),
575
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
576
- "text",
577
- {
578
- x: d * 0.75,
579
- y: 0,
580
- fill: "white",
581
- fontSize: magW * 0.6,
582
- fontWeight: "bold",
583
- textAnchor: "middle",
584
- dominantBaseline: "central",
585
- transform: flipLabel ? `rotate(180, ${d * 0.75}, 0)` : void 0,
586
- children: "S"
587
- }
588
- ),
589
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
590
- "text",
591
- {
592
- x: d / 2,
593
- y: -magW / 2 - 10,
594
- fill: c2(el),
595
- fontSize: fontSizeOf(el),
596
- textAnchor: "middle",
597
- transform: flipLabel ? `rotate(180, ${d / 2}, ${-magW / 2 - 10})` : void 0,
598
- children: String(el.label)
599
- }
600
- ) : null
1002
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { children: [
1003
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1004
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: 0, y: -magW / 2, width: d / 2, height: magW, fill: "#ef4444", stroke: c2(el), strokeWidth: 1 }),
1005
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: d / 2, y: -magW / 2, width: d / 2, height: magW, fill: "#3b82f6", stroke: c2(el), strokeWidth: 1 }),
1006
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1007
+ "text",
1008
+ {
1009
+ x: d / 4,
1010
+ y: 0,
1011
+ fill: "white",
1012
+ fontSize: magW * 0.6,
1013
+ fontWeight: "bold",
1014
+ textAnchor: "middle",
1015
+ dominantBaseline: "central",
1016
+ transform: flipLabel ? `rotate(180, ${d / 4}, 0)` : void 0,
1017
+ children: "N"
1018
+ }
1019
+ ),
1020
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1021
+ "text",
1022
+ {
1023
+ x: d * 0.75,
1024
+ y: 0,
1025
+ fill: "white",
1026
+ fontSize: magW * 0.6,
1027
+ fontWeight: "bold",
1028
+ textAnchor: "middle",
1029
+ dominantBaseline: "central",
1030
+ transform: flipLabel ? `rotate(180, ${d * 0.75}, 0)` : void 0,
1031
+ children: "S"
1032
+ }
1033
+ ),
1034
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1035
+ "text",
1036
+ {
1037
+ x: d / 2,
1038
+ y: -magW / 2 - 10,
1039
+ fill: c2(el),
1040
+ fontSize: fontSizeOf(el),
1041
+ textAnchor: "middle",
1042
+ transform: flipLabel ? `rotate(180, ${d / 2}, ${-magW / 2 - 10})` : void 0,
1043
+ children: String(el.label)
1044
+ }
1045
+ ) : null
1046
+ ] }),
1047
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChordInteractionOverlay, { el, ctx })
601
1048
  ] });
602
1049
  });
603
- registerRenderer("coil", (el) => {
1050
+ registerRenderer("coil", (el, ctx) => {
604
1051
  const d = distance(el.x1, el.y1, el.x2, el.y2);
605
1052
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
606
1053
  const coils = Math.max(Math.floor(d / 15), 1);
@@ -609,35 +1056,39 @@ registerRenderer("coil", (el) => {
609
1056
  dPath += `C ${i * (d / coils) - d / (coils * 2)} -15, ${i * (d / coils)} 20, ${i * (d / coils)} 0 `;
610
1057
  }
611
1058
  const flip = angle > 90 || angle <= -90;
612
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
613
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: dPath, fill: "none", stroke: c2(el), strokeWidth: sw2(el), strokeLinejoin: "round" }),
614
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
615
- "text",
616
- {
617
- x: d / 2,
618
- y: -20,
619
- fontSize: fontSizeOf(el),
620
- fill: c2(el),
621
- textAnchor: "middle",
622
- transform: flip ? `rotate(180, ${d / 2}, -20)` : void 0,
623
- children: String(el.label)
624
- }
625
- ) : null
1059
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { children: [
1060
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1061
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: dPath, fill: "none", stroke: c2(el), strokeWidth: sw2(el), strokeLinejoin: "round" }),
1062
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1063
+ "text",
1064
+ {
1065
+ x: d / 2,
1066
+ y: -20,
1067
+ fontSize: fontSizeOf(el),
1068
+ fill: c2(el),
1069
+ textAnchor: "middle",
1070
+ transform: flip ? `rotate(180, ${d / 2}, -20)` : void 0,
1071
+ children: String(el.label)
1072
+ }
1073
+ ) : null,
1074
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(LocalFatPathHit, { dPath, strokeWidth: 24, strokeLinejoin: "round" })
1075
+ ] }),
1076
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChordInteractionOverlay, { el, ctx })
626
1077
  ] });
627
1078
  });
628
- registerRenderer("meter", (el) => {
1079
+ registerRenderer("meter", (el, ctx) => {
629
1080
  const r = Math.max(16, sw2(el) * 5);
630
1081
  const label = String(el.label || "G");
631
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
632
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: 0, cy: 0, r, fill: "white", stroke: c2(el), strokeWidth: sw2(el) }),
633
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: 0, y: 0, fontSize: fontSizeOf(el), fill: c2(el), fontWeight: "bold", textAnchor: "middle", dominantBaseline: "central", children: label })
634
- ] });
1082
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(OriginHitPlate, { ctx, hitHalfSize: 20, selectionHalfSize: 30, children: [
1083
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: 0, cy: 0, r, fill: "white", stroke: c2(el), strokeWidth: sw2(el) }),
1084
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("text", { x: 0, y: 0, fontSize: fontSizeOf(el), fill: c2(el), fontWeight: "bold", textAnchor: "middle", dominantBaseline: "central", children: label })
1085
+ ] }) });
635
1086
  });
636
- registerRenderer("ac_source", (el) => {
1087
+ registerRenderer("ac_source", (el, ctx) => {
637
1088
  const r = Math.max(16, sw2(el) * 5);
638
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
639
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: 0, cy: 0, r, fill: "white", stroke: c2(el), strokeWidth: sw2(el) }),
640
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1089
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(OriginHitPlate, { ctx, hitHalfSize: 22, selectionHalfSize: 30, children: [
1090
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: 0, cy: 0, r, fill: "white", stroke: c2(el), strokeWidth: sw2(el) }),
1091
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
641
1092
  "path",
642
1093
  {
643
1094
  d: `M ${-r * 0.6} 0 Q ${-r * 0.3} ${-r * 0.6} 0 0 T ${r * 0.6} 0`,
@@ -646,28 +1097,30 @@ registerRenderer("ac_source", (el) => {
646
1097
  strokeWidth: sw2(el) * 0.8
647
1098
  }
648
1099
  ),
649
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: 0, y: r + 18, fontSize: fontSizeOf(el), fill: c2(el), textAnchor: "middle", children: String(el.label) }) : null
650
- ] });
1100
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("text", { x: 0, y: r + 18, fontSize: fontSizeOf(el), fill: c2(el), textAnchor: "middle", children: String(el.label) }) : null
1101
+ ] }) });
651
1102
  });
652
- registerRenderer("bezier", (el) => {
1103
+ registerRenderer("bezier", (el, ctx) => {
653
1104
  const { x1, y1, x2, y2 } = el;
654
1105
  const cps = el.cps?.length ? el.cps : [{ x: (x1 + x2) / 2, y: Math.min(y1, y2) - 50 }];
655
1106
  const cp0 = cps[0];
656
1107
  const dPath = `M ${x1} ${y1} Q ${cp0.x} ${cp0.y} ${x2} ${y2}`;
1108
+ const pts = [{ x: x1, y: y1 }, cp0, { x: x2, y: y2 }];
657
1109
  const labelX = 0.25 * x1 + 0.5 * cp0.x + 0.25 * x2;
658
1110
  const labelY = 0.25 * y1 + 0.5 * cp0.y + 0.25 * y2;
659
1111
  const angleRad = Math.atan2(y2 - y1, x2 - x1);
660
1112
  const showArrow = Boolean(el.showArrow);
661
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
662
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: dPath, fill: "none", stroke: "transparent", strokeWidth: 24, strokeLinecap: "round", strokeLinejoin: "round" }),
663
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: dPath, fill: "none", stroke: c2(el), strokeWidth: sw2(el), strokeDasharray: dashOf(el), strokeLinecap: "round", strokeLinejoin: "round" }),
664
- showArrow ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("g", { transform: `translate(${labelX}, ${labelY}) rotate(${angleRad * 180 / Math.PI})`, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { points: "6,0 -6,-5 -6,5", fill: c2(el) }) }) : null,
665
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { x: labelX, y: labelY - 14, fontSize: fontSizeOf(el), fill: c2(el), textAnchor: "middle", children: String(el.label) }) : null
1113
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { children: [
1114
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: dPath, fill: "none", stroke: "transparent", strokeWidth: 24, strokeLinecap: "round", strokeLinejoin: "round" }),
1115
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: dPath, fill: "none", stroke: c2(el), strokeWidth: sw2(el), strokeDasharray: dashOf(el), strokeLinecap: "round", strokeLinejoin: "round" }),
1116
+ showArrow ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("g", { transform: `translate(${labelX}, ${labelY}) rotate(${angleRad * 180 / Math.PI})`, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polygon", { points: "6,0 -6,-5 -6,5", fill: c2(el) }) }) : null,
1117
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("text", { x: labelX, y: labelY - 14, fontSize: fontSizeOf(el), fill: c2(el), textAnchor: "middle", children: String(el.label) }) : null,
1118
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(BezierEditChrome, { x1, y1, x2, y2, cp0, ctx, pts })
666
1119
  ] });
667
1120
  });
668
1121
 
669
1122
  // src/core/renderers/graph.tsx
670
- var import_jsx_runtime4 = require("react/jsx-runtime");
1123
+ var import_jsx_runtime5 = require("react/jsx-runtime");
671
1124
  var colorOf2 = (el) => el.color || "#0f172a";
672
1125
  var strokeOf2 = (el) => el.strokeWidth || 2;
673
1126
  var fontOf2 = (el) => el.fontSize || 16;
@@ -686,68 +1139,70 @@ registerRenderer("axes", (el, ctx) => {
686
1139
  const midY = (minY + maxY) / 2;
687
1140
  const w = Math.max(1, maxX - minX);
688
1141
  const h = Math.max(1, maxY - minY);
689
- const c7 = colorOf2(el);
690
- const sw7 = strokeOf2(el);
1142
+ const c6 = colorOf2(el);
1143
+ const sw6 = strokeOf2(el);
691
1144
  const dash = dashOf2(el);
692
1145
  if (maxX - minX <= 1 && maxY - minY <= 1) {
693
1146
  const cx = el.x1;
694
1147
  const cy = el.y1;
695
1148
  const arm = 40;
696
1149
  const fs = fontOf2(el);
697
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { opacity: op(ctx), children: [
698
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: cx, y1: cy, x2: cx + arm, y2: cy, stroke: c7, strokeWidth: sw7 }),
699
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polygon", { points: `${cx + arm},${cy} ${cx + arm - 8},${cy - 5} ${cx + arm - 8},${cy + 5}`, fill: c7 }),
700
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1150
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("g", { opacity: op(ctx), transform: `translate(${cx}, ${cy})`, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(OriginHitPlate, { ctx, hitHalfSize: 52, selectionHalfSize: 58, children: [
1151
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: arm, y2: 0, stroke: c6, strokeWidth: sw6 }),
1152
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: `${arm},0 ${arm - 8},-5 ${arm - 8},5`, fill: c6 }),
1153
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
701
1154
  "text",
702
1155
  {
703
- x: cx + arm + 10,
704
- y: cy,
1156
+ x: arm + 10,
1157
+ y: 0,
705
1158
  dominantBaseline: "central",
706
1159
  textAnchor: "middle",
707
- fill: c7,
1160
+ fill: c6,
708
1161
  fontSize: fs,
709
1162
  fontFamily: "serif",
710
1163
  fontStyle: "italic",
711
1164
  children: "x"
712
1165
  }
713
1166
  ),
714
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: cx, y1: cy, x2: cx, y2: cy - arm, stroke: c7, strokeWidth: sw7 }),
715
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polygon", { points: `${cx},${cy - arm} ${cx - 5},${cy - arm + 8} ${cx + 5},${cy - arm + 8}`, fill: c7 }),
716
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1167
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: -arm, stroke: c6, strokeWidth: sw6 }),
1168
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: `0,${-arm} -5,${-arm + 8} 5,${-arm + 8}`, fill: c6 }),
1169
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
717
1170
  "text",
718
1171
  {
719
- x: cx,
720
- y: cy - arm - 12,
1172
+ x: 0,
1173
+ y: -arm - 12,
721
1174
  dominantBaseline: "central",
722
1175
  textAnchor: "middle",
723
- fill: c7,
1176
+ fill: c6,
724
1177
  fontSize: fs,
725
1178
  fontFamily: "serif",
726
1179
  fontStyle: "italic",
727
1180
  children: "y"
728
1181
  }
729
1182
  ),
730
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx, cy, r: 2, fill: c7 })
731
- ] });
1183
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: 0, cy: 0, r: 2, fill: c6 })
1184
+ ] }) });
732
1185
  }
733
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { opacity: op(ctx), children: [
734
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
735
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { pointerEvents: "none", children: [
736
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: midX, y1: minY, x2: midX, y2: maxY, stroke: c7, strokeWidth: sw7, strokeDasharray: dash, opacity: 0.8 }),
737
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: minX, y1: midY, x2: maxX, y2: midY, stroke: c7, strokeWidth: sw7, strokeDasharray: dash, opacity: 0.8 }),
738
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polygon", { points: `${maxX},${midY} ${maxX - 10},${midY - 5} ${maxX - 10},${midY + 5}`, fill: c7, opacity: 0.8 }),
739
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polygon", { points: `${midX},${minY} ${midX - 5},${minY + 10} ${midX + 5},${minY + 10}`, fill: c7, opacity: 0.8 })
740
- ] })
1186
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { opacity: op(ctx), children: [
1187
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
1188
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
1189
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { pointerEvents: "none", children: [
1190
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: midX, y1: minY, x2: midX, y2: maxY, stroke: c6, strokeWidth: sw6, strokeDasharray: dash, opacity: 0.8 }),
1191
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: minX, y1: midY, x2: maxX, y2: midY, stroke: c6, strokeWidth: sw6, strokeDasharray: dash, opacity: 0.8 }),
1192
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: `${maxX},${midY} ${maxX - 10},${midY - 5} ${maxX - 10},${midY + 5}`, fill: c6, opacity: 0.8 }),
1193
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: `${midX},${minY} ${midX - 5},${minY + 10} ${midX + 5},${minY + 10}`, fill: c6, opacity: 0.8 })
1194
+ ] }),
1195
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChordInteractionOverlay, { el, ctx })
741
1196
  ] });
742
1197
  });
743
1198
  registerRenderer("circle", (el, ctx) => {
744
1199
  const r = distance(el.x1, el.y1, el.x2, el.y2);
745
- const c7 = colorOf2(el);
746
- const sw7 = strokeOf2(el);
1200
+ const c6 = colorOf2(el);
1201
+ const sw6 = strokeOf2(el);
747
1202
  const dash = dashOf2(el);
748
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { opacity: op(ctx), children: [
749
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: el.x1, cy: el.y1, r, fill: "none", stroke: c7, strokeWidth: sw7, strokeDasharray: dash, pointerEvents: "none" }),
750
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: el.x1, cy: el.y1, r, fill: "transparent", stroke: "transparent", strokeWidth: 20 })
1203
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { opacity: op(ctx), children: [
1204
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: el.x1, cy: el.y1, r, fill: "none", stroke: c6, strokeWidth: sw6, strokeDasharray: dash, pointerEvents: "none" }),
1205
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChordInteractionOverlay, { el, ctx })
751
1206
  ] });
752
1207
  });
753
1208
  registerRenderer("ellipse", (el, ctx) => {
@@ -759,22 +1214,25 @@ registerRenderer("ellipse", (el, ctx) => {
759
1214
  const midY = (minY + maxY) / 2;
760
1215
  const w = Math.max(1, maxX - minX);
761
1216
  const h = Math.max(1, maxY - minY);
762
- const c7 = colorOf2(el);
763
- const sw7 = strokeOf2(el);
1217
+ const c6 = colorOf2(el);
1218
+ const sw6 = strokeOf2(el);
764
1219
  const dash = dashOf2(el);
765
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { opacity: op(ctx), children: [
766
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
767
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ellipse", { cx: midX, cy: midY, rx: w / 2, ry: h / 2, fill: "none", stroke: c7, strokeWidth: sw7, strokeDasharray: dash, pointerEvents: "none" })
1220
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { opacity: op(ctx), children: [
1221
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
1222
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
1223
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ellipse", { cx: midX, cy: midY, rx: w / 2, ry: h / 2, fill: "none", stroke: c6, strokeWidth: sw6, strokeDasharray: dash, pointerEvents: "none" }),
1224
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChordInteractionOverlay, { el, ctx })
768
1225
  ] });
769
1226
  });
770
1227
  registerRenderer("rectangle", (el, ctx) => {
771
1228
  const b = getBounds(el.x1, el.y1, el.x2, el.y2);
772
- const c7 = colorOf2(el);
773
- const sw7 = strokeOf2(el);
1229
+ const c6 = colorOf2(el);
1230
+ const sw6 = strokeOf2(el);
774
1231
  const dash = dashOf2(el);
775
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { opacity: op(ctx), children: [
776
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: b.minX, y: b.minY, width: b.width, height: b.height, fill: "transparent" }),
777
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1232
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { opacity: op(ctx), children: [
1233
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: b.minX, y: b.minY, width: b.width, height: b.height, fill: "transparent" }),
1234
+ bboxInsetSelectionChrome(b.minX, b.minY, b.width, b.height, ctx),
1235
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
778
1236
  "rect",
779
1237
  {
780
1238
  x: b.minX,
@@ -782,13 +1240,14 @@ registerRenderer("rectangle", (el, ctx) => {
782
1240
  width: b.width,
783
1241
  height: b.height,
784
1242
  fill: "none",
785
- stroke: c7,
786
- strokeWidth: sw7,
1243
+ stroke: c6,
1244
+ strokeWidth: sw6,
787
1245
  strokeDasharray: dash,
788
1246
  strokeLinejoin: "round",
789
1247
  pointerEvents: "none"
790
1248
  }
791
- )
1249
+ ),
1250
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChordInteractionOverlay, { el, ctx })
792
1251
  ] });
793
1252
  });
794
1253
  registerRenderer("angle", (el, ctx) => {
@@ -802,8 +1261,8 @@ registerRenderer("angle", (el, ctx) => {
802
1261
  const maxY = Math.max(y1, y2);
803
1262
  const w = Math.max(1, maxX - minX);
804
1263
  const h = Math.max(1, maxY - minY);
805
- const c7 = colorOf2(el);
806
- const sw7 = strokeOf2(el);
1264
+ const c6 = colorOf2(el);
1265
+ const sw6 = strokeOf2(el);
807
1266
  const dash = dashOf2(el);
808
1267
  const fs = fontOf2(el);
809
1268
  const angleRad = Math.atan2(dy, dx);
@@ -820,28 +1279,29 @@ registerRenderer("angle", (el, ctx) => {
820
1279
  const arcEndX = x1 + arcR * Math.cos(angleRad);
821
1280
  const arcEndY = y1 + arcR * Math.sin(angleRad);
822
1281
  const deg = angleRad * 180 / Math.PI;
823
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { opacity: op(ctx), children: [
824
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
825
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { pointerEvents: "none", children: [
826
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1, y1, x2: x1 + dx, y2: y1, stroke: c7, strokeWidth: sw7, strokeDasharray: "4 4", opacity: 0.6 }),
827
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1, y1, x2, y2, stroke: c7, strokeWidth: sw7, strokeLinecap: "round", strokeDasharray: dash }),
828
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("g", { transform: `translate(${x2}, ${y2}) rotate(${deg})`, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polygon", { points: "0,0 -12,-6 -12,6", fill: c7 }) }),
829
- dist > 20 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1282
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { opacity: op(ctx), children: [
1283
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
1284
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
1285
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { pointerEvents: "none", children: [
1286
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1, y1, x2: x1 + dx, y2: y1, stroke: c6, strokeWidth: sw6, strokeDasharray: "4 4", opacity: 0.6 }),
1287
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1, y1, x2, y2, stroke: c6, strokeWidth: sw6, strokeLinecap: "round", strokeDasharray: dash }),
1288
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("g", { transform: `translate(${x2}, ${y2}) rotate(${deg})`, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: "0,0 -12,-6 -12,6", fill: c6 }) }),
1289
+ dist > 20 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
830
1290
  "path",
831
1291
  {
832
1292
  d: `M ${arcStartX} ${arcStartY} A ${arcR} ${arcR} 0 0 ${sweepFlag} ${arcEndX} ${arcEndY}`,
833
1293
  fill: "none",
834
- stroke: c7,
835
- strokeWidth: sw7
1294
+ stroke: c6,
1295
+ strokeWidth: sw6
836
1296
  }
837
1297
  ),
838
- el.label && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1298
+ el.label && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
839
1299
  "text",
840
1300
  {
841
1301
  x: x1 + (arcR + 16) * Math.cos(midA),
842
1302
  y: y1 + (arcR + 16) * Math.sin(midA),
843
1303
  fontSize: fs,
844
- fill: c7,
1304
+ fill: c6,
845
1305
  fontFamily: "serif",
846
1306
  fontStyle: "italic",
847
1307
  dominantBaseline: "central",
@@ -849,7 +1309,8 @@ registerRenderer("angle", (el, ctx) => {
849
1309
  children: String(el.label)
850
1310
  }
851
1311
  )
852
- ] })
1312
+ ] }),
1313
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChordInteractionOverlay, { el, ctx })
853
1314
  ] });
854
1315
  });
855
1316
  registerRenderer("parabola_v", (el, ctx) => {
@@ -861,22 +1322,24 @@ registerRenderer("parabola_v", (el, ctx) => {
861
1322
  const w = Math.max(1, maxX - minX);
862
1323
  const h = Math.max(1, maxY - minY);
863
1324
  const cpY = 2 * el.y1 - el.y2;
864
- const c7 = colorOf2(el);
865
- const sw7 = strokeOf2(el);
1325
+ const c6 = colorOf2(el);
1326
+ const sw6 = strokeOf2(el);
866
1327
  const dash = dashOf2(el);
867
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { opacity: op(ctx), children: [
868
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
869
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1328
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { opacity: op(ctx), children: [
1329
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
1330
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
1331
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
870
1332
  "path",
871
1333
  {
872
1334
  d: `M ${minX} ${el.y2} Q ${midX} ${cpY} ${maxX} ${el.y2}`,
873
1335
  fill: "none",
874
- stroke: c7,
875
- strokeWidth: sw7,
1336
+ stroke: c6,
1337
+ strokeWidth: sw6,
876
1338
  strokeDasharray: dash,
877
1339
  pointerEvents: "none"
878
1340
  }
879
- )
1341
+ ),
1342
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChordInteractionOverlay, { el, ctx })
880
1343
  ] });
881
1344
  });
882
1345
  registerRenderer("parabola_h", (el, ctx) => {
@@ -888,22 +1351,24 @@ registerRenderer("parabola_h", (el, ctx) => {
888
1351
  const w = Math.max(1, maxX - minX);
889
1352
  const h = Math.max(1, maxY - minY);
890
1353
  const cpX = 2 * el.x1 - el.x2;
891
- const c7 = colorOf2(el);
892
- const sw7 = strokeOf2(el);
1354
+ const c6 = colorOf2(el);
1355
+ const sw6 = strokeOf2(el);
893
1356
  const dash = dashOf2(el);
894
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { opacity: op(ctx), children: [
895
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
896
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1357
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { opacity: op(ctx), children: [
1358
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
1359
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
1360
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
897
1361
  "path",
898
1362
  {
899
1363
  d: `M ${el.x2} ${minY} Q ${cpX} ${midY} ${el.x2} ${maxY}`,
900
1364
  fill: "none",
901
- stroke: c7,
902
- strokeWidth: sw7,
1365
+ stroke: c6,
1366
+ strokeWidth: sw6,
903
1367
  strokeDasharray: dash,
904
1368
  pointerEvents: "none"
905
1369
  }
906
- )
1370
+ ),
1371
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChordInteractionOverlay, { el, ctx })
907
1372
  ] });
908
1373
  });
909
1374
  registerRenderer("hyperbola", (el, ctx) => {
@@ -915,15 +1380,17 @@ registerRenderer("hyperbola", (el, ctx) => {
915
1380
  const midY = (minY + maxY) / 2;
916
1381
  const w = Math.max(1, maxX - minX);
917
1382
  const h = Math.max(1, maxY - minY);
918
- const c7 = colorOf2(el);
919
- const sw7 = strokeOf2(el);
1383
+ const c6 = colorOf2(el);
1384
+ const sw6 = strokeOf2(el);
920
1385
  const dash = dashOf2(el);
921
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { opacity: op(ctx), children: [
922
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
923
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { pointerEvents: "none", children: [
924
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: `M ${minX} ${minY} Q ${midX} ${midY} ${minX} ${maxY}`, fill: "none", stroke: c7, strokeWidth: sw7, strokeDasharray: dash }),
925
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: `M ${maxX} ${minY} Q ${midX} ${midY} ${maxX} ${maxY}`, fill: "none", stroke: c7, strokeWidth: sw7, strokeDasharray: dash })
926
- ] })
1386
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { opacity: op(ctx), children: [
1387
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
1388
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
1389
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { pointerEvents: "none", children: [
1390
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: `M ${minX} ${minY} Q ${midX} ${midY} ${minX} ${maxY}`, fill: "none", stroke: c6, strokeWidth: sw6, strokeDasharray: dash }),
1391
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: `M ${maxX} ${minY} Q ${midX} ${midY} ${maxX} ${maxY}`, fill: "none", stroke: c6, strokeWidth: sw6, strokeDasharray: dash })
1392
+ ] }),
1393
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChordInteractionOverlay, { el, ctx })
927
1394
  ] });
928
1395
  });
929
1396
  registerRenderer("modulus", (el, ctx) => {
@@ -934,24 +1401,26 @@ registerRenderer("modulus", (el, ctx) => {
934
1401
  const midX = (minX + maxX) / 2;
935
1402
  const w = Math.max(1, maxX - minX);
936
1403
  const h = Math.max(1, maxY - minY);
937
- const c7 = colorOf2(el);
938
- const sw7 = strokeOf2(el);
1404
+ const c6 = colorOf2(el);
1405
+ const sw6 = strokeOf2(el);
939
1406
  const dash = dashOf2(el);
940
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { opacity: op(ctx), children: [
941
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
942
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1407
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { opacity: op(ctx), children: [
1408
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
1409
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
1410
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
943
1411
  "polyline",
944
1412
  {
945
1413
  points: `${minX},${minY} ${midX},${maxY} ${maxX},${minY}`,
946
1414
  fill: "none",
947
- stroke: c7,
948
- strokeWidth: sw7,
1415
+ stroke: c6,
1416
+ strokeWidth: sw6,
949
1417
  strokeLinecap: "round",
950
1418
  strokeLinejoin: "round",
951
1419
  strokeDasharray: dash,
952
1420
  pointerEvents: "none"
953
1421
  }
954
- )
1422
+ ),
1423
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChordInteractionOverlay, { el, ctx })
955
1424
  ] });
956
1425
  });
957
1426
  registerRenderer("exponential", (el, ctx) => {
@@ -962,22 +1431,24 @@ registerRenderer("exponential", (el, ctx) => {
962
1431
  const midX = (minX + maxX) / 2;
963
1432
  const w = Math.max(1, maxX - minX);
964
1433
  const h = Math.max(1, maxY - minY);
965
- const c7 = colorOf2(el);
966
- const sw7 = strokeOf2(el);
1434
+ const c6 = colorOf2(el);
1435
+ const sw6 = strokeOf2(el);
967
1436
  const dash = dashOf2(el);
968
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { opacity: op(ctx), children: [
969
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
970
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1437
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { opacity: op(ctx), children: [
1438
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
1439
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
1440
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
971
1441
  "path",
972
1442
  {
973
1443
  d: `M ${minX} ${maxY} Q ${midX + w * 0.2} ${maxY} ${maxX} ${minY}`,
974
1444
  fill: "none",
975
- stroke: c7,
976
- strokeWidth: sw7,
1445
+ stroke: c6,
1446
+ strokeWidth: sw6,
977
1447
  strokeDasharray: dash,
978
1448
  pointerEvents: "none"
979
1449
  }
980
- )
1450
+ ),
1451
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChordInteractionOverlay, { el, ctx })
981
1452
  ] });
982
1453
  });
983
1454
  registerRenderer("logarithmic", (el, ctx) => {
@@ -988,22 +1459,24 @@ registerRenderer("logarithmic", (el, ctx) => {
988
1459
  const midY = (minY + maxY) / 2;
989
1460
  const w = Math.max(1, maxX - minX);
990
1461
  const h = Math.max(1, maxY - minY);
991
- const c7 = colorOf2(el);
992
- const sw7 = strokeOf2(el);
1462
+ const c6 = colorOf2(el);
1463
+ const sw6 = strokeOf2(el);
993
1464
  const dash = dashOf2(el);
994
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { opacity: op(ctx), children: [
995
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
996
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1465
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { opacity: op(ctx), children: [
1466
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
1467
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
1468
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
997
1469
  "path",
998
1470
  {
999
1471
  d: `M ${minX} ${maxY} Q ${minX} ${midY - h * 0.2} ${maxX} ${minY}`,
1000
1472
  fill: "none",
1001
- stroke: c7,
1002
- strokeWidth: sw7,
1473
+ stroke: c6,
1474
+ strokeWidth: sw6,
1003
1475
  strokeDasharray: dash,
1004
1476
  pointerEvents: "none"
1005
1477
  }
1006
- )
1478
+ ),
1479
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChordInteractionOverlay, { el, ctx })
1007
1480
  ] });
1008
1481
  });
1009
1482
  registerRenderer("sine_wave", (el, ctx) => {
@@ -1011,29 +1484,30 @@ registerRenderer("sine_wave", (el, ctx) => {
1011
1484
  const maxX = Math.max(el.x1, el.x2);
1012
1485
  const minY = Math.min(el.y1, el.y2);
1013
1486
  const maxY = Math.max(el.y1, el.y2);
1014
- const midX = (minX + maxX) / 2;
1015
1487
  const midY = (minY + maxY) / 2;
1016
1488
  const w = Math.max(1, maxX - minX);
1017
1489
  const h = Math.max(1, maxY - minY);
1018
1490
  const s = w / 4;
1019
- const c7 = colorOf2(el);
1020
- const sw7 = strokeOf2(el);
1491
+ const c6 = colorOf2(el);
1492
+ const sw6 = strokeOf2(el);
1021
1493
  const dash = dashOf2(el);
1022
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { opacity: op(ctx), children: [
1023
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
1024
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1494
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { opacity: op(ctx), children: [
1495
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
1496
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
1497
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1025
1498
  "path",
1026
1499
  {
1027
1500
  d: `M ${minX} ${midY} Q ${minX + s / 2} ${minY}, ${minX + s} ${midY} T ${minX + 2 * s} ${midY} T ${minX + 3 * s} ${midY} T ${maxX} ${midY}`,
1028
1501
  fill: "none",
1029
- stroke: c7,
1030
- strokeWidth: sw7,
1502
+ stroke: c6,
1503
+ strokeWidth: sw6,
1031
1504
  strokeLinecap: "round",
1032
1505
  strokeLinejoin: "round",
1033
1506
  strokeDasharray: dash,
1034
1507
  pointerEvents: "none"
1035
1508
  }
1036
- )
1509
+ ),
1510
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChordInteractionOverlay, { el, ctx })
1037
1511
  ] });
1038
1512
  });
1039
1513
  registerRenderer("step_function", (el, ctx) => {
@@ -1043,8 +1517,8 @@ registerRenderer("step_function", (el, ctx) => {
1043
1517
  const maxY = Math.max(el.y1, el.y2);
1044
1518
  const w = Math.max(1, maxX - minX);
1045
1519
  const h = Math.max(1, maxY - minY);
1046
- const c7 = colorOf2(el);
1047
- const sw7 = strokeOf2(el);
1520
+ const c6 = colorOf2(el);
1521
+ const sw6 = strokeOf2(el);
1048
1522
  const dash = dashOf2(el);
1049
1523
  const stepsCount = 4;
1050
1524
  const stepW = w / stepsCount;
@@ -1055,16 +1529,18 @@ registerRenderer("step_function", (el, ctx) => {
1055
1529
  const curY = maxY - i * stepH;
1056
1530
  const nextX = curX + stepW;
1057
1531
  steps.push(
1058
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { children: [
1059
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: curX, y1: curY, x2: nextX, y2: curY, stroke: c7, strokeWidth: sw7, strokeDasharray: dash }),
1060
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: curX, cy: curY, r: sw7 * 1.5, fill: c7 }),
1061
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: nextX, cy: curY, r: sw7 * 1.5, fill: "white", stroke: c7, strokeWidth: sw7 * 0.75 })
1532
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { children: [
1533
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: curX, y1: curY, x2: nextX, y2: curY, stroke: c6, strokeWidth: sw6, strokeDasharray: dash }),
1534
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: curX, cy: curY, r: sw6 * 1.5, fill: c6 }),
1535
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: nextX, cy: curY, r: sw6 * 1.5, fill: "white", stroke: c6, strokeWidth: sw6 * 0.75 })
1062
1536
  ] }, i)
1063
1537
  );
1064
1538
  }
1065
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { opacity: op(ctx), children: [
1066
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
1067
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("g", { pointerEvents: "none", children: steps })
1539
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { opacity: op(ctx), children: [
1540
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
1541
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
1542
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("g", { pointerEvents: "none", children: steps }),
1543
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChordInteractionOverlay, { el, ctx })
1068
1544
  ] });
1069
1545
  });
1070
1546
  registerRenderer("shaded_area", (el, ctx) => {
@@ -1075,37 +1551,40 @@ registerRenderer("shaded_area", (el, ctx) => {
1075
1551
  const midX = (minX + maxX) / 2;
1076
1552
  const w = Math.max(1, maxX - minX);
1077
1553
  const h = Math.max(1, maxY - minY);
1078
- const c7 = colorOf2(el);
1079
- const sw7 = strokeOf2(el);
1554
+ const c6 = colorOf2(el);
1555
+ const sw6 = strokeOf2(el);
1080
1556
  const dash = dashOf2(el);
1081
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { opacity: op(ctx), children: [
1082
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
1083
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { pointerEvents: "none", children: [
1084
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1557
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { opacity: op(ctx), children: [
1558
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: minX, y: minY, width: w, height: h, fill: "transparent" }),
1559
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
1560
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { pointerEvents: "none", children: [
1561
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1085
1562
  "path",
1086
1563
  {
1087
1564
  d: `M ${minX} ${maxY} L ${minX} ${minY + h * 0.3} Q ${midX} ${minY} ${maxX} ${minY + h * 0.3} L ${maxX} ${maxY} Z`,
1088
- fill: c7,
1565
+ fill: c6,
1089
1566
  opacity: 0.2
1090
1567
  }
1091
1568
  ),
1092
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: `M ${minX} ${minY + h * 0.3} Q ${midX} ${minY} ${maxX} ${minY + h * 0.3}`, fill: "none", stroke: c7, strokeWidth: sw7, strokeDasharray: dash })
1093
- ] })
1569
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: `M ${minX} ${minY + h * 0.3} Q ${midX} ${minY} ${maxX} ${minY + h * 0.3}`, fill: "none", stroke: c6, strokeWidth: sw6, strokeDasharray: dash })
1570
+ ] }),
1571
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChordInteractionOverlay, { el, ctx })
1094
1572
  ] });
1095
1573
  });
1096
1574
 
1097
1575
  // src/core/renderers/waveOscillation.tsx
1098
- var import_jsx_runtime5 = require("react/jsx-runtime");
1576
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1099
1577
  var c3 = (el) => el.color || "#0f172a";
1100
1578
  var sw3 = (el) => el.strokeWidth || 2;
1101
- var font = (el) => el.fontSize || 18;
1102
- registerRenderer("mass_box", (el) => {
1579
+ var font2 = (el) => el.fontSize || 18;
1580
+ registerRenderer("mass_box", (el, ctx) => {
1103
1581
  const size = el.size || 30;
1104
1582
  const color = c3(el);
1105
1583
  const strokeWidth = sw3(el);
1106
- const fontSize = font(el);
1107
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1108
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1584
+ const fontSize = font2(el);
1585
+ const plate = Math.max(36, size / 2 + 24);
1586
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(OriginHitPlate, { ctx, hitHalfSize: plate, selectionHalfSize: plate + 8, children: [
1587
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1109
1588
  "rect",
1110
1589
  {
1111
1590
  x: -size / 2,
@@ -1116,54 +1595,56 @@ registerRenderer("mass_box", (el) => {
1116
1595
  fillOpacity: 0.2,
1117
1596
  stroke: color,
1118
1597
  strokeWidth,
1119
- rx: 4
1598
+ rx: 4,
1599
+ pointerEvents: "none"
1120
1600
  }
1121
1601
  ),
1122
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: 0, y: 0, dominantBaseline: "central", textAnchor: "middle", fontSize, fill: color, fontWeight: "bold", children: String(el.label) }) : null
1123
- ] });
1602
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: 0, y: 0, dominantBaseline: "central", textAnchor: "middle", fontSize, fill: color, fontWeight: "bold", pointerEvents: "none", children: String(el.label) }) : null
1603
+ ] }) });
1124
1604
  });
1125
- registerRenderer("tuning_fork", (el) => {
1605
+ registerRenderer("tuning_fork", (el, ctx) => {
1126
1606
  const color = c3(el);
1127
1607
  const strokeWidth = sw3(el);
1128
- const fontSize = font(el);
1608
+ const fontSize = font2(el);
1129
1609
  const w = Math.max(3, strokeWidth);
1130
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1131
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1610
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(OriginHitPlate, { ctx, hitHalfSize: 38, selectionHalfSize: 46, children: [
1611
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1132
1612
  "path",
1133
1613
  {
1134
1614
  d: "M -10 -20 L -10 0 Q -10 10 0 10 Q 10 10 10 0 L 10 -20",
1135
1615
  fill: "none",
1136
1616
  stroke: color,
1137
1617
  strokeWidth: w,
1138
- strokeLinecap: "round"
1618
+ strokeLinecap: "round",
1619
+ pointerEvents: "none"
1139
1620
  }
1140
1621
  ),
1141
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "0", y1: "10", x2: "0", y2: "30", stroke: color, strokeWidth: Math.max(4, strokeWidth), strokeLinecap: "round" }),
1142
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: 0, y: 45, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
1143
- ] });
1622
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "0", y1: "10", x2: "0", y2: "30", stroke: color, strokeWidth: Math.max(4, strokeWidth), strokeLinecap: "round", pointerEvents: "none" }),
1623
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: 0, y: 45, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
1624
+ ] }) });
1144
1625
  });
1145
- registerRenderer("speaker", (el) => {
1626
+ registerRenderer("speaker", (el, ctx) => {
1146
1627
  const color = c3(el);
1147
- const fontSize = font(el);
1148
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1149
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: "-10,-15 5,-25 5,25 -10,15", fill: color }),
1150
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("rect", { x: "-20", y: "-15", width: "10", height: "30", fill: color }),
1151
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M 12 -10 Q 18 0 12 10 M 18 -20 Q 28 0 18 20", fill: "none", stroke: color, strokeWidth: 2, strokeLinecap: "round" }),
1152
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: 0, y: 40, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
1153
- ] });
1154
- });
1155
- registerRenderer("wavefronts", (el) => {
1628
+ const fontSize = font2(el);
1629
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(OriginHitPlate, { ctx, hitHalfSize: 36, selectionHalfSize: 44, children: [
1630
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polygon", { points: "-10,-15 5,-25 5,25 -10,15", fill: color, pointerEvents: "none" }),
1631
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { x: "-20", y: "-15", width: "10", height: "30", fill: color, pointerEvents: "none" }),
1632
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M 12 -10 Q 18 0 12 10 M 18 -20 Q 28 0 18 20", fill: "none", stroke: color, strokeWidth: 2, strokeLinecap: "round", pointerEvents: "none" }),
1633
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: 0, y: 40, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
1634
+ ] }) });
1635
+ });
1636
+ registerRenderer("wavefronts", (el, ctx) => {
1156
1637
  const rings = el.rings || 5;
1157
1638
  const vRatio = el.velocityRatio ?? 0;
1158
1639
  const color = c3(el);
1159
1640
  const strokeWidth = sw3(el);
1160
- const fontSize = font(el);
1641
+ const fontSize = font2(el);
1161
1642
  const circles = [];
1162
1643
  for (let i = 1; i <= rings; i++) {
1163
1644
  const r = i * 20;
1164
1645
  const cx = vRatio * r;
1165
1646
  circles.push(
1166
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1647
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1167
1648
  "circle",
1168
1649
  {
1169
1650
  cx,
@@ -1172,23 +1653,25 @@ registerRenderer("wavefronts", (el) => {
1172
1653
  fill: "none",
1173
1654
  stroke: color,
1174
1655
  strokeWidth,
1175
- opacity: 1 - i / rings * 0.5
1656
+ opacity: 1 - i / rings * 0.5,
1657
+ pointerEvents: "none"
1176
1658
  },
1177
1659
  i
1178
1660
  )
1179
1661
  );
1180
1662
  }
1181
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1182
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: 0, cy: 0, r: 4, fill: color }),
1663
+ const extent = rings * 20 + Math.abs(vRatio) * rings * 20 + 30;
1664
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(OriginHitPlate, { ctx, hitHalfSize: extent, selectionHalfSize: extent + 10, children: [
1665
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: 0, cy: 0, r: 4, fill: color, pointerEvents: "none" }),
1183
1666
  circles,
1184
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: 0, y: rings * 20 + 20, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
1185
- ] });
1667
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: 0, y: rings * 20 + 20, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
1668
+ ] }) });
1186
1669
  });
1187
- registerRenderer("pendulum", (el) => {
1670
+ registerRenderer("pendulum", (el, ctx) => {
1188
1671
  const size = el.size || 15;
1189
1672
  const color = c3(el);
1190
1673
  const strokeWidth = sw3(el);
1191
- const fontSize = font(el);
1674
+ const fontSize = font2(el);
1192
1675
  const theta = Math.atan2(el.x2 - el.x1, el.y2 - el.y1);
1193
1676
  const thetaDeg = theta * (180 / Math.PI);
1194
1677
  const l = Math.hypot(el.x2 - el.x1, el.y2 - el.y1);
@@ -1197,84 +1680,90 @@ registerRenderer("pendulum", (el) => {
1197
1680
  const arcY = Math.cos(theta) * arcR;
1198
1681
  const arcFlag = theta > 0 ? 0 : 1;
1199
1682
  const showF = el.showForces;
1200
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1201
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: -20, y1: 0, x2: 20, y2: 0, stroke: color, strokeWidth: 2 }),
1202
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: -15, y1: 0, x2: -10, y2: -5, stroke: color }),
1203
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: 5, y2: -5, stroke: color }),
1204
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 15, y1: 0, x2: 20, y2: -5, stroke: color }),
1205
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: l + 20, stroke: color, strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.5 }),
1206
- Math.abs(theta) > 0.05 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1207
- "path",
1208
- {
1209
- d: `M 0 ${arcR} A ${arcR} ${arcR} 0 0 ${arcFlag} ${arcX} ${arcY}`,
1210
- fill: "none",
1211
- stroke: color,
1212
- strokeWidth: 1.5
1213
- }
1214
- ) : null,
1215
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `rotate(${-thetaDeg})`, children: [
1216
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: l, stroke: color, strokeWidth }),
1217
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: 0, cy: l, r: size, fill: color, stroke: "#fff", strokeWidth: 2 }),
1218
- showF ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(0, ${l})`, children: [
1219
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `rotate(${thetaDeg})`, children: [
1220
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: 40, stroke: "#ef4444", strokeWidth: 2 }),
1221
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: "0,45 -4,37 4,37", fill: "#ef4444" }),
1222
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: 10, y: 40, fontSize: 12, fill: "#ef4444", children: "F_g" })
1223
- ] }),
1224
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: -40, stroke: "#3b82f6", strokeWidth: 2 }),
1225
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: "0,-45 -4,-37 4,-37", fill: "#3b82f6" }),
1226
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: 10, y: -35, fontSize: 12, fill: "#3b82f6", children: "T" }),
1227
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: -35 * Math.sin(theta), y2: 0, stroke: "#10b981", strokeWidth: 2 }),
1228
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1229
- "polygon",
1230
- {
1231
- points: `${-40 * Math.sin(theta)},0 ${-32 * Math.sin(theta)},-4 ${-32 * Math.sin(theta)},4`,
1232
- fill: "#10b981"
1233
- }
1234
- ),
1235
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: -45 * Math.sin(theta), y: -10, fontSize: 12, fill: "#10b981", children: "F_g sin(\u03B8)" })
1236
- ] }) : null,
1237
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: 0, y: l + size + 15, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
1238
- ] })
1683
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
1684
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1685
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: -20, y1: 0, x2: 20, y2: 0, stroke: color, strokeWidth: 2 }),
1686
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: -15, y1: 0, x2: -10, y2: -5, stroke: color }),
1687
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: 5, y2: -5, stroke: color }),
1688
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 15, y1: 0, x2: 20, y2: -5, stroke: color }),
1689
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: l + 20, stroke: color, strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.5 }),
1690
+ Math.abs(theta) > 0.05 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1691
+ "path",
1692
+ {
1693
+ d: `M 0 ${arcR} A ${arcR} ${arcR} 0 0 ${arcFlag} ${arcX} ${arcY}`,
1694
+ fill: "none",
1695
+ stroke: color,
1696
+ strokeWidth: 1.5
1697
+ }
1698
+ ) : null,
1699
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `rotate(${-thetaDeg})`, children: [
1700
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: l, stroke: color, strokeWidth }),
1701
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: 0, cy: l, r: size, fill: color, stroke: "#fff", strokeWidth: 2 }),
1702
+ showF ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(0, ${l})`, children: [
1703
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `rotate(${thetaDeg})`, children: [
1704
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: 40, stroke: "#ef4444", strokeWidth: 2 }),
1705
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polygon", { points: "0,45 -4,37 4,37", fill: "#ef4444" }),
1706
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: 10, y: 40, fontSize: 12, fill: "#ef4444", children: "F_g" })
1707
+ ] }),
1708
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: -40, stroke: "#3b82f6", strokeWidth: 2 }),
1709
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polygon", { points: "0,-45 -4,-37 4,-37", fill: "#3b82f6" }),
1710
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: 10, y: -35, fontSize: 12, fill: "#3b82f6", children: "T" }),
1711
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: -35 * Math.sin(theta), y2: 0, stroke: "#10b981", strokeWidth: 2 }),
1712
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1713
+ "polygon",
1714
+ {
1715
+ points: `${-40 * Math.sin(theta)},0 ${-32 * Math.sin(theta)},-4 ${-32 * Math.sin(theta)},4`,
1716
+ fill: "#10b981"
1717
+ }
1718
+ ),
1719
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: -45 * Math.sin(theta), y: -10, fontSize: 12, fill: "#10b981", children: "F_g sin(\u03B8)" })
1720
+ ] }) : null,
1721
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: 0, y: l + size + 15, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
1722
+ ] })
1723
+ ] }),
1724
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChordInteractionOverlay, { el, ctx, padX: Math.max(80, l + size + 30), padY: Math.max(80, l + size + 30) })
1239
1725
  ] });
1240
1726
  });
1241
- registerRenderer("phasor", (el) => {
1727
+ registerRenderer("phasor", (el, ctx) => {
1242
1728
  const dist = distance(el.x1, el.y1, el.x2, el.y2);
1243
1729
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1244
1730
  const dx = el.x2 - el.x1;
1245
1731
  const dy = el.y2 - el.y1;
1246
1732
  const color = c3(el);
1247
1733
  const strokeWidth = sw3(el);
1248
- const fontSize = font(el);
1734
+ const fontSize = font2(el);
1249
1735
  const showProj = el.showProjection;
1250
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1251
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: 0, cy: 0, r: dist, fill: "none", stroke: color, strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.5 }),
1252
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: -dist - 10, y1: 0, x2: dist + 10, y2: 0, stroke: color, strokeWidth: 1, opacity: 0.3 }),
1253
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: -dist - 10, x2: 0, y2: dist + 10, stroke: color, strokeWidth: 1, opacity: 0.3 }),
1254
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth }),
1255
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: `${dist},0 ${dist - 12},-6 ${dist - 12},6`, fill: color }),
1256
- showProj ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `rotate(${-angle})`, children: [
1257
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: dx, y1: dy, x2: dx, y2: 0, stroke: "#ef4444", strokeWidth: 1, strokeDasharray: "4 4" }),
1258
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: dx, y1: dy, x2: 0, y2: dy, stroke: "#3b82f6", strokeWidth: 1, strokeDasharray: "4 4" }),
1259
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: dx, y2: 0, stroke: "#ef4444", strokeWidth: 2 }),
1260
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: `${dx},0 ${dx > 0 ? dx - 8 : dx + 8},-4 ${dx > 0 ? dx - 8 : dx + 8},4`, fill: "#ef4444" }),
1261
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: dy, stroke: "#3b82f6", strokeWidth: 2 }),
1262
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polygon", { points: `0,${dy} -4,${dy > 0 ? dy - 8 : dy + 8} 4,${dy > 0 ? dy - 8 : dy + 8}`, fill: "#3b82f6" })
1263
- ] }) : null,
1264
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1265
- "path",
1266
- {
1267
- d: `M ${dist * 0.3} 0 A ${dist * 0.3} ${dist * 0.3} 0 0 0 ${dist * 0.3 * Math.cos(-angle * Math.PI / 180)} ${dist * 0.3 * Math.sin(-angle * Math.PI / 180)}`,
1268
- fill: "none",
1269
- stroke: color,
1270
- strokeWidth: 1.5,
1271
- transform: `rotate(${-angle})`
1272
- }
1273
- ),
1274
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: dist / 2, y: -15, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
1736
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
1737
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1738
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: 0, cy: 0, r: dist, fill: "none", stroke: color, strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.5, pointerEvents: "none" }),
1739
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: -dist - 10, y1: 0, x2: dist + 10, y2: 0, stroke: color, strokeWidth: 1, opacity: 0.3, pointerEvents: "none" }),
1740
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: -dist - 10, x2: 0, y2: dist + 10, stroke: color, strokeWidth: 1, opacity: 0.3, pointerEvents: "none" }),
1741
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth, pointerEvents: "none" }),
1742
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polygon", { points: `${dist},0 ${dist - 12},-6 ${dist - 12},6`, fill: color, pointerEvents: "none" }),
1743
+ showProj ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `rotate(${-angle})`, children: [
1744
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: dx, y1: dy, x2: dx, y2: 0, stroke: "#ef4444", strokeWidth: 1, strokeDasharray: "4 4" }),
1745
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: dx, y1: dy, x2: 0, y2: dy, stroke: "#3b82f6", strokeWidth: 1, strokeDasharray: "4 4" }),
1746
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: dx, y2: 0, stroke: "#ef4444", strokeWidth: 2 }),
1747
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polygon", { points: `${dx},0 ${dx > 0 ? dx - 8 : dx + 8},-4 ${dx > 0 ? dx - 8 : dx + 8},4`, fill: "#ef4444" }),
1748
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: dy, stroke: "#3b82f6", strokeWidth: 2 }),
1749
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polygon", { points: `0,${dy} -4,${dy > 0 ? dy - 8 : dy + 8} 4,${dy > 0 ? dy - 8 : dy + 8}`, fill: "#3b82f6" })
1750
+ ] }) : null,
1751
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1752
+ "path",
1753
+ {
1754
+ d: `M ${dist * 0.3} 0 A ${dist * 0.3} ${dist * 0.3} 0 0 0 ${dist * 0.3 * Math.cos(-angle * Math.PI / 180)} ${dist * 0.3 * Math.sin(-angle * Math.PI / 180)}`,
1755
+ fill: "none",
1756
+ stroke: color,
1757
+ strokeWidth: 1.5,
1758
+ transform: `rotate(${-angle})`
1759
+ }
1760
+ ),
1761
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: dist / 2, y: -15, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
1762
+ ] }),
1763
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChordInteractionOverlay, { el, ctx, padX: dist + 28, padY: dist + 28 })
1275
1764
  ] });
1276
1765
  });
1277
- registerRenderer("sine_wave", (el) => {
1766
+ registerRenderer("sine_wave", (el, ctx) => {
1278
1767
  const dist = distance(el.x1, el.y1, el.x2, el.y2);
1279
1768
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1280
1769
  const amp = el.amplitude || 40;
@@ -1282,19 +1771,23 @@ registerRenderer("sine_wave", (el) => {
1282
1771
  const wl = dist / (loops / 2);
1283
1772
  const color = c3(el);
1284
1773
  const strokeWidth = sw3(el);
1285
- const fontSize = font(el);
1774
+ const fontSize = font2(el);
1286
1775
  let d = "M 0 0";
1287
1776
  const step = Math.max(1, dist / 100);
1288
1777
  for (let i = 0; i <= dist; i += step) {
1289
1778
  d += ` L ${i} ${-amp * Math.sin(i / wl * 2 * Math.PI)}`;
1290
1779
  }
1291
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1292
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.5 }),
1293
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d, fill: "none", stroke: color, strokeWidth, strokeLinejoin: "round" }),
1294
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: dist / 2, y: -amp - 15, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
1780
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
1781
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1782
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.5, pointerEvents: "none" }),
1783
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LocalFatPathHit, { dPath: d, strokeWidth: 22, strokeLinejoin: "round" }),
1784
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d, fill: "none", stroke: color, strokeWidth, strokeLinejoin: "round", pointerEvents: "none" }),
1785
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: dist / 2, y: -amp - 15, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
1786
+ ] }),
1787
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChordInteractionOverlay, { el, ctx, padY: amp + 28 })
1295
1788
  ] });
1296
1789
  });
1297
- registerRenderer("standing_wave", (el) => {
1790
+ registerRenderer("standing_wave", (el, ctx) => {
1298
1791
  const dist = distance(el.x1, el.y1, el.x2, el.y2);
1299
1792
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1300
1793
  const amp = el.amplitude || 40;
@@ -1302,7 +1795,7 @@ registerRenderer("standing_wave", (el) => {
1302
1795
  const wl = dist / (loops / 2);
1303
1796
  const color = c3(el);
1304
1797
  const strokeWidth = sw3(el);
1305
- const fontSize = font(el);
1798
+ const fontSize = font2(el);
1306
1799
  let d1 = "M 0 0";
1307
1800
  let d2 = "M 0 0";
1308
1801
  const step = Math.max(1, dist / 100);
@@ -1311,14 +1804,19 @@ registerRenderer("standing_wave", (el) => {
1311
1804
  d1 += ` L ${i} ${-y}`;
1312
1805
  d2 += ` L ${i} ${y}`;
1313
1806
  }
1314
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1315
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth: 1, opacity: 0.5 }),
1316
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: d1, fill: color, fillOpacity: 0.1, stroke: color, strokeWidth, strokeLinejoin: "round" }),
1317
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: d2, fill: "none", stroke: color, strokeWidth, strokeDasharray: "4 4", strokeLinejoin: "round" }),
1318
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: dist / 2, y: -amp - 15, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
1807
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
1808
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1809
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth: 1, opacity: 0.5, pointerEvents: "none" }),
1810
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LocalFatPathHit, { dPath: d1, strokeWidth: 22, strokeLinejoin: "round" }),
1811
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LocalFatPathHit, { dPath: d2, strokeWidth: 22, strokeLinejoin: "round" }),
1812
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: d1, fill: color, fillOpacity: 0.1, stroke: color, strokeWidth, strokeLinejoin: "round", pointerEvents: "none" }),
1813
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: d2, fill: "none", stroke: color, strokeWidth, strokeDasharray: "4 4", strokeLinejoin: "round", pointerEvents: "none" }),
1814
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: dist / 2, y: -amp - 15, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
1815
+ ] }),
1816
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChordInteractionOverlay, { el, ctx, padY: amp + 28 })
1319
1817
  ] });
1320
1818
  });
1321
- registerRenderer("shm_graph", (el) => {
1819
+ registerRenderer("shm_graph", (el, ctx) => {
1322
1820
  const dist = distance(el.x1, el.y1, el.x2, el.y2);
1323
1821
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1324
1822
  const amp = el.amplitude || 40;
@@ -1327,7 +1825,7 @@ registerRenderer("shm_graph", (el) => {
1327
1825
  const gType = el.graphType || "x";
1328
1826
  const color = c3(el);
1329
1827
  const strokeWidth = sw3(el);
1330
- const fontSize = font(el);
1828
+ const fontSize = font2(el);
1331
1829
  let dX = `M 0 ${-amp}`;
1332
1830
  let dV = "M 0 0";
1333
1831
  let dA = `M 0 ${amp}`;
@@ -1338,16 +1836,28 @@ registerRenderer("shm_graph", (el) => {
1338
1836
  dV += ` L ${i} ${-amp * 0.8 * -Math.sin(phase)}`;
1339
1837
  dA += ` L ${i} ${-amp * 0.6 * -Math.cos(phase)}`;
1340
1838
  }
1341
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1342
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth: 1, opacity: 0.5 }),
1343
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: -amp * 1.2, x2: 0, y2: amp * 1.2, stroke: color, strokeWidth: 1, opacity: 0.5 }),
1344
- (gType === "x" || gType === "all") && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: dX, fill: "none", stroke: "#3b82f6", strokeWidth, strokeLinejoin: "round" }),
1345
- (gType === "v" || gType === "all") && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: dV, fill: "none", stroke: "#ef4444", strokeWidth, strokeLinejoin: "round" }),
1346
- (gType === "a" || gType === "all") && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: dA, fill: "none", stroke: "#10b981", strokeWidth, strokeLinejoin: "round" }),
1347
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: dist / 2, y: -amp - 15, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
1839
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
1840
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1841
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth: 1, opacity: 0.5, pointerEvents: "none" }),
1842
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: -amp * 1.2, x2: 0, y2: amp * 1.2, stroke: color, strokeWidth: 1, opacity: 0.5, pointerEvents: "none" }),
1843
+ gType === "x" || gType === "all" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1844
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LocalFatPathHit, { dPath: dX, strokeWidth: 20, strokeLinejoin: "round" }),
1845
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: dX, fill: "none", stroke: "#3b82f6", strokeWidth, strokeLinejoin: "round", pointerEvents: "none" })
1846
+ ] }) : null,
1847
+ gType === "v" || gType === "all" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1848
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LocalFatPathHit, { dPath: dV, strokeWidth: 20, strokeLinejoin: "round" }),
1849
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: dV, fill: "none", stroke: "#ef4444", strokeWidth, strokeLinejoin: "round", pointerEvents: "none" })
1850
+ ] }) : null,
1851
+ gType === "a" || gType === "all" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1852
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LocalFatPathHit, { dPath: dA, strokeWidth: 20, strokeLinejoin: "round" }),
1853
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: dA, fill: "none", stroke: "#10b981", strokeWidth, strokeLinejoin: "round", pointerEvents: "none" })
1854
+ ] }) : null,
1855
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: dist / 2, y: -amp - 15, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
1856
+ ] }),
1857
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChordInteractionOverlay, { el, ctx, padY: amp * 1.2 + 28 })
1348
1858
  ] });
1349
1859
  });
1350
- registerRenderer("energy_graph", (el) => {
1860
+ registerRenderer("energy_graph", (el, ctx) => {
1351
1861
  const dist = distance(el.x1, el.y1, el.x2, el.y2);
1352
1862
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1353
1863
  const amp = el.amplitude || 50;
@@ -1356,7 +1866,7 @@ registerRenderer("energy_graph", (el) => {
1356
1866
  const dom = el.domain || "time";
1357
1867
  const color = c3(el);
1358
1868
  const strokeWidth = sw3(el);
1359
- const fontSize = font(el);
1869
+ const fontSize = font2(el);
1360
1870
  let dU = "M 0 0";
1361
1871
  let dK = `M 0 ${-amp}`;
1362
1872
  const step = Math.max(1, dist / 100);
@@ -1376,22 +1886,27 @@ registerRenderer("energy_graph", (el) => {
1376
1886
  dK += ` L ${i} ${-(amp - uVal)}`;
1377
1887
  }
1378
1888
  }
1379
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1380
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth: 1, opacity: 0.5 }),
1381
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: -amp, x2: dist, y2: -amp, stroke: color, strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.5 }),
1382
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: dU, fill: "none", stroke: "#10b981", strokeWidth, strokeLinejoin: "round" }),
1383
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: dK, fill: "none", stroke: "#ef4444", strokeWidth, strokeLinejoin: "round" }),
1384
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: dist / 2, y: -amp - 15, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
1889
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
1890
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1891
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth: 1, opacity: 0.5, pointerEvents: "none" }),
1892
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: -amp, x2: dist, y2: -amp, stroke: color, strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.5, pointerEvents: "none" }),
1893
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LocalFatPathHit, { dPath: dU, strokeWidth: 20, strokeLinejoin: "round" }),
1894
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LocalFatPathHit, { dPath: dK, strokeWidth: 20, strokeLinejoin: "round" }),
1895
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: dU, fill: "none", stroke: "#10b981", strokeWidth, strokeLinejoin: "round", pointerEvents: "none" }),
1896
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: dK, fill: "none", stroke: "#ef4444", strokeWidth, strokeLinejoin: "round", pointerEvents: "none" }),
1897
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: dist / 2, y: -amp - 15, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
1898
+ ] }),
1899
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChordInteractionOverlay, { el, ctx, padY: amp + 28 })
1385
1900
  ] });
1386
1901
  });
1387
- registerRenderer("strobe_shm", (el) => {
1902
+ registerRenderer("strobe_shm", (el, ctx) => {
1388
1903
  const dist = distance(el.x1, el.y1, el.x2, el.y2);
1389
1904
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1390
1905
  const amp = el.amplitude || 40;
1391
1906
  const loops = el.loops || 1;
1392
1907
  const color = c3(el);
1393
1908
  const strokeWidth = sw3(el);
1394
- const fontSize = font(el);
1909
+ const fontSize = font2(el);
1395
1910
  const rows = 10;
1396
1911
  const pts = [];
1397
1912
  const circles = [];
@@ -1401,77 +1916,182 @@ registerRenderer("strobe_shm", (el) => {
1401
1916
  const cx = amp * Math.cos(t * 2 * Math.PI * loops);
1402
1917
  const cy = t * dist;
1403
1918
  pts.push(`${cx},${cy}`);
1404
- circles.push(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx, cy, r: strokeWidth * 2, fill: color }, `c${i}`));
1919
+ circles.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx, cy, r: strokeWidth * 2, fill: color, pointerEvents: "none" }, `c${i}`));
1405
1920
  lines.push(
1406
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: -amp - 20, y1: cy, x2: amp + 20, y2: cy, stroke: color, strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.3 }, `l${i}`)
1921
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: -amp - 20, y1: cy, x2: amp + 20, y2: cy, stroke: color, strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.3 }, `l${i}`)
1407
1922
  );
1408
1923
  }
1409
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1410
- lines,
1411
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: pts.join(" "), fill: "none", stroke: color, strokeWidth: 1, opacity: 0.4 }),
1412
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: dist, stroke: color, strokeWidth: 1, opacity: 0.5 }),
1413
- circles,
1414
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: 0, y: -15, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
1924
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
1925
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1926
+ lines,
1927
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polyline", { points: pts.join(" "), fill: "none", stroke: color, strokeWidth: 1, opacity: 0.4, pointerEvents: "none" }),
1928
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: dist, stroke: color, strokeWidth: 1, opacity: 0.5, pointerEvents: "none" }),
1929
+ circles,
1930
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: 0, y: -15, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
1931
+ ] }),
1932
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChordInteractionOverlay, { el, ctx, padX: amp + 35, padY: dist + 35 })
1415
1933
  ] });
1416
1934
  });
1417
- registerRenderer("torsion_pendulum", (el) => {
1935
+ registerRenderer("torsion_pendulum", (el, ctx) => {
1418
1936
  const dist = distance(el.x1, el.y1, el.x2, el.y2);
1419
1937
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1420
1938
  const color = c3(el);
1421
1939
  const strokeWidth = sw3(el);
1422
- const fontSize = font(el);
1423
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1424
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth }),
1425
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("ellipse", { cx: dist, cy: 0, rx: 40, ry: 15, fill: color, fillOpacity: 0.2, stroke: color, strokeWidth }),
1426
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: dist, y1: 0, x2: dist + 40, y2: 0, stroke: color, strokeWidth, strokeDasharray: "2 2" }),
1427
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: dist, y1: 0, x2: dist + 30, y2: 20, stroke: "#ef4444", strokeWidth }),
1428
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: `M ${dist + 20} 0 A 20 7.5 0 0 1 ${dist + 15} 10`, fill: "none", stroke: "#ef4444", strokeWidth: 1.5 }),
1429
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: dist / 2, y: -15, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
1940
+ const fontSize = font2(el);
1941
+ const R = Math.max(18, Math.min(58, el.size || 26 + dist * 0.2));
1942
+ const halfT = Math.max(5, Math.min(11, R * 0.14));
1943
+ const twistDeg = Math.max(-45, Math.min(45, el.rotation ?? 16));
1944
+ const tw = twistDeg * Math.PI / 180;
1945
+ const rArc = Math.min(14, R * 0.35);
1946
+ const arcX2 = rArc * Math.sin(tw);
1947
+ const arcY2 = -rArc * Math.cos(tw);
1948
+ const fiberStart = 4;
1949
+ const fiberEnd = Math.max(fiberStart + 8, dist - halfT);
1950
+ const helixD = [];
1951
+ const helAmp = Math.min(2.8, halfT * 0.35);
1952
+ const helSteps = 24;
1953
+ for (let i = 0; i <= helSteps; i++) {
1954
+ const u = i / helSteps;
1955
+ const x = fiberStart + u * (fiberEnd - fiberStart);
1956
+ const y = helAmp * Math.sin(u * Math.PI * 5);
1957
+ helixD.push(i === 0 ? `M ${x} ${y}` : ` L ${x} ${y}`);
1958
+ }
1959
+ const pad = Math.max(R + halfT + 28, dist * 0.35 + 40);
1960
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
1961
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1962
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { x: -28, y: -14, width: 56, height: 10, rx: 2, fill: "#e2e8f0", stroke: color, strokeWidth: 1.25, pointerEvents: "none" }),
1963
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { x: -8, y: -4, width: 16, height: 8, rx: 1, fill: "#94a3b8", stroke: color, strokeWidth: 1, pointerEvents: "none" }),
1964
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: -22, y1: -9, x2: 22, y2: -9, stroke: "#64748b", strokeWidth: 1, pointerEvents: "none" }),
1965
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1966
+ "path",
1967
+ {
1968
+ d: helixD.join(""),
1969
+ fill: "none",
1970
+ stroke: color,
1971
+ strokeWidth: 1,
1972
+ strokeLinecap: "round",
1973
+ opacity: 0.35,
1974
+ pointerEvents: "none"
1975
+ }
1976
+ ),
1977
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1978
+ "line",
1979
+ {
1980
+ x1: fiberStart,
1981
+ y1: 0,
1982
+ x2: fiberEnd,
1983
+ y2: 0,
1984
+ stroke: "#64748b",
1985
+ strokeWidth: Math.max(1, strokeWidth * 0.45),
1986
+ strokeLinecap: "round",
1987
+ pointerEvents: "none"
1988
+ }
1989
+ ),
1990
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1991
+ "rect",
1992
+ {
1993
+ x: dist - halfT,
1994
+ y: -R,
1995
+ width: 2 * halfT,
1996
+ height: 2 * R,
1997
+ rx: halfT - 0.5,
1998
+ fill: "#f8fafc",
1999
+ stroke: color,
2000
+ strokeWidth,
2001
+ pointerEvents: "none"
2002
+ }
2003
+ ),
2004
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2005
+ "line",
2006
+ {
2007
+ x1: dist - halfT + 1,
2008
+ y1: 0,
2009
+ x2: dist + halfT - 1,
2010
+ y2: 0,
2011
+ stroke: "#cbd5e1",
2012
+ strokeWidth: 1,
2013
+ pointerEvents: "none"
2014
+ }
2015
+ ),
2016
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: dist, cy: 0, r: Math.min(5, halfT - 1), fill: "#94a3b8", stroke: color, strokeWidth: 1, pointerEvents: "none" }),
2017
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2018
+ "line",
2019
+ {
2020
+ x1: dist,
2021
+ y1: 0,
2022
+ x2: dist,
2023
+ y2: -(R - 6),
2024
+ stroke: "#94a3b8",
2025
+ strokeWidth: 1.25,
2026
+ strokeDasharray: "3 3",
2027
+ opacity: 0.65,
2028
+ pointerEvents: "none"
2029
+ }
2030
+ ),
2031
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("g", { transform: `translate(${dist},0) rotate(${twistDeg})`, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: -(R - 5), stroke: "#dc2626", strokeWidth: Math.max(1.5, strokeWidth * 0.75), strokeLinecap: "round", pointerEvents: "none" }) }),
2032
+ Math.abs(twistDeg) >= 2 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("g", { transform: `translate(${dist},0)`, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2033
+ "path",
2034
+ {
2035
+ d: `M 0 ${-rArc} A ${rArc} ${rArc} 0 0 ${twistDeg >= 0 ? 1 : 0} ${arcX2} ${arcY2}`,
2036
+ fill: "none",
2037
+ stroke: "#dc2626",
2038
+ strokeWidth: 1.25,
2039
+ pointerEvents: "none"
2040
+ }
2041
+ ) }) : null,
2042
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: dist, y: R + 22, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2043
+ ] }),
2044
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChordInteractionOverlay, { el, ctx, padX: pad, padY: pad })
1430
2045
  ] });
1431
2046
  });
1432
- registerRenderer("vane_liquid", (el) => {
2047
+ registerRenderer("vane_liquid", (el, ctx) => {
1433
2048
  const dist = distance(el.x1, el.y1, el.x2, el.y2);
1434
2049
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1435
2050
  const color = c3(el);
1436
2051
  const strokeWidth = sw3(el);
1437
- const fontSize = font(el);
2052
+ const fontSize = font2(el);
1438
2053
  const beakerW = Math.max(40, dist * 0.4);
1439
2054
  const beakerH = Math.max(40, dist * 0.5);
1440
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1441
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth }),
1442
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: dist, y1: -beakerW * 0.3, x2: dist, y2: beakerW * 0.3, stroke: color, strokeWidth: strokeWidth * 1.5, strokeLinecap: "round" }),
1443
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1444
- "path",
1445
- {
1446
- d: `M ${dist - beakerH} ${-beakerW / 2} L ${dist + 10} ${-beakerW / 2} L ${dist + 10} ${beakerW / 2} L ${dist - beakerH} ${beakerW / 2}`,
1447
- fill: "none",
1448
- stroke: color,
1449
- strokeWidth: 2,
1450
- strokeLinecap: "round",
1451
- strokeLinejoin: "round"
1452
- }
1453
- ),
1454
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1455
- "rect",
1456
- {
1457
- x: dist - beakerH + 2,
1458
- y: -beakerW / 2 + 2,
1459
- width: beakerH + 8,
1460
- height: beakerW - 4,
1461
- fill: "#38bdf8",
1462
- fillOpacity: 0.3
1463
- }
1464
- ),
1465
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: dist / 2, y: -beakerW / 2 - 15, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
2055
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
2056
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2057
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth, pointerEvents: "none" }),
2058
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: dist, y1: -beakerW * 0.3, x2: dist, y2: beakerW * 0.3, stroke: color, strokeWidth: strokeWidth * 1.5, strokeLinecap: "round", pointerEvents: "none" }),
2059
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2060
+ "path",
2061
+ {
2062
+ d: `M ${dist - beakerH} ${-beakerW / 2} L ${dist + 10} ${-beakerW / 2} L ${dist + 10} ${beakerW / 2} L ${dist - beakerH} ${beakerW / 2}`,
2063
+ fill: "none",
2064
+ stroke: color,
2065
+ strokeWidth: 2,
2066
+ strokeLinecap: "round",
2067
+ strokeLinejoin: "round",
2068
+ pointerEvents: "none"
2069
+ }
2070
+ ),
2071
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2072
+ "rect",
2073
+ {
2074
+ x: dist - beakerH + 2,
2075
+ y: -beakerW / 2 + 2,
2076
+ width: beakerH + 8,
2077
+ height: beakerW - 4,
2078
+ fill: "#38bdf8",
2079
+ fillOpacity: 0.3,
2080
+ pointerEvents: "none"
2081
+ }
2082
+ ),
2083
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: dist / 2, y: -beakerW / 2 - 15, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2084
+ ] }),
2085
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChordInteractionOverlay, { el, ctx, padX: beakerH + 40, padY: beakerW + 40 })
1466
2086
  ] });
1467
2087
  });
1468
- registerRenderer("wave_pulse", (el) => {
2088
+ registerRenderer("wave_pulse", (el, ctx) => {
1469
2089
  const dist = distance(el.x1, el.y1, el.x2, el.y2);
1470
2090
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1471
2091
  const amp = el.amplitude || 50;
1472
2092
  const color = c3(el);
1473
2093
  const strokeWidth = sw3(el);
1474
- const fontSize = font(el);
2094
+ const fontSize = font2(el);
1475
2095
  const mid = dist / 2;
1476
2096
  const spread = dist / 6;
1477
2097
  let d = "M 0 0";
@@ -1480,13 +2100,17 @@ registerRenderer("wave_pulse", (el) => {
1480
2100
  const y = amp * Math.exp(-Math.pow(i - mid, 2) / (2 * spread * spread));
1481
2101
  d += ` L ${i} ${-y}`;
1482
2102
  }
1483
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1484
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth, strokeLinecap: "round" }),
1485
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d, fill: color, fillOpacity: 0.1, stroke: color, strokeWidth, strokeLinejoin: "round" }),
1486
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: mid, y: -amp - 15, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
2103
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
2104
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2105
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth, strokeLinecap: "round", pointerEvents: "none" }),
2106
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LocalFatPathHit, { dPath: d, strokeWidth: 22, strokeLinejoin: "round" }),
2107
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d, fill: color, fillOpacity: 0.1, stroke: color, strokeWidth, strokeLinejoin: "round", pointerEvents: "none" }),
2108
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: mid, y: -amp - 15, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2109
+ ] }),
2110
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChordInteractionOverlay, { el, ctx, padY: amp + 28 })
1487
2111
  ] });
1488
2112
  });
1489
- registerRenderer("damped_wave", (el) => {
2113
+ registerRenderer("damped_wave", (el, ctx) => {
1490
2114
  const dist = distance(el.x1, el.y1, el.x2, el.y2);
1491
2115
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1492
2116
  const amp = el.amplitude || 50;
@@ -1494,7 +2118,7 @@ registerRenderer("damped_wave", (el) => {
1494
2118
  const freq = el.frequency || 10;
1495
2119
  const color = c3(el);
1496
2120
  const strokeWidth = sw3(el);
1497
- const fontSize = font(el);
2121
+ const fontSize = font2(el);
1498
2122
  let d = `M 0 ${-amp}`;
1499
2123
  let env1 = `M 0 ${-amp}`;
1500
2124
  let env2 = `M 0 ${amp}`;
@@ -1506,15 +2130,19 @@ registerRenderer("damped_wave", (el) => {
1506
2130
  env1 += ` L ${i} ${-envY}`;
1507
2131
  env2 += ` L ${i} ${envY}`;
1508
2132
  }
1509
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1510
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth: 1, opacity: 0.3 }),
1511
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: env1, fill: "none", stroke: color, strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.5 }),
1512
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: env2, fill: "none", stroke: color, strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.5 }),
1513
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d, fill: "none", stroke: color, strokeWidth, strokeLinejoin: "round" }),
1514
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: dist / 2, y: -amp - 15, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
2133
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
2134
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2135
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth: 1, opacity: 0.3, pointerEvents: "none" }),
2136
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: env1, fill: "none", stroke: color, strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.5, pointerEvents: "none" }),
2137
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: env2, fill: "none", stroke: color, strokeWidth: 1, strokeDasharray: "4 4", opacity: 0.5, pointerEvents: "none" }),
2138
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LocalFatPathHit, { dPath: d, strokeWidth: 22, strokeLinejoin: "round" }),
2139
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d, fill: "none", stroke: color, strokeWidth, strokeLinejoin: "round", pointerEvents: "none" }),
2140
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: dist / 2, y: -amp - 15, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2141
+ ] }),
2142
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChordInteractionOverlay, { el, ctx, padY: amp + 28 })
1515
2143
  ] });
1516
2144
  });
1517
- registerRenderer("beats_graph", (el) => {
2145
+ registerRenderer("beats_graph", (el, ctx) => {
1518
2146
  const dist = distance(el.x1, el.y1, el.x2, el.y2);
1519
2147
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1520
2148
  const amp = el.amplitude || 30;
@@ -1522,7 +2150,7 @@ registerRenderer("beats_graph", (el) => {
1522
2150
  const beatF = el.beatFreq || 2;
1523
2151
  const color = c3(el);
1524
2152
  const strokeWidth = sw3(el);
1525
- const fontSize = font(el);
2153
+ const fontSize = font2(el);
1526
2154
  let d = "M 0 0";
1527
2155
  let env1 = "M 0 0";
1528
2156
  let env2 = "M 0 0";
@@ -1534,197 +2162,66 @@ registerRenderer("beats_graph", (el) => {
1534
2162
  env1 += ` L ${i} ${-Math.abs(env)}`;
1535
2163
  env2 += ` L ${i} ${Math.abs(env)}`;
1536
2164
  }
1537
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1538
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth: 1, opacity: 0.3 }),
1539
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: env1, fill: "none", stroke: color, strokeWidth: 1, strokeDasharray: "2 4", opacity: 0.5 }),
1540
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: env2, fill: "none", stroke: color, strokeWidth: 1, strokeDasharray: "2 4", opacity: 0.5 }),
1541
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d, fill: "none", stroke: color, strokeWidth, strokeLinejoin: "round" }),
1542
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: dist / 2, y: -amp - 20, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
1543
- ] });
1544
- });
1545
- registerRenderer("mach_cone", (el) => {
1546
- const dist = distance(el.x1, el.y1, el.x2, el.y2);
1547
- const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1548
- const M = el.machNumber || 2;
1549
- const coneAngle = Math.asin(1 / Math.max(1.01, M));
1550
- const h = dist;
1551
- const r = h * Math.tan(coneAngle);
1552
- const color = c3(el);
1553
- const strokeWidth = sw3(el);
1554
- const fontSize = font(el);
1555
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1556
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: h, y2: 0, stroke: color, strokeWidth: 1, strokeDasharray: "4 4" }),
1557
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: h, y1: 0, x2: 0, y2: -r, stroke: color, strokeWidth }),
1558
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: h, y1: 0, x2: 0, y2: r, stroke: color, strokeWidth }),
1559
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: h, cy: 0, r: 4, fill: color }),
1560
- [0.2, 0.5, 0.8].map((t) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: h * t, cy: 0, r: (1 - t) * r, fill: "none", stroke: color, strokeWidth: 1, opacity: 0.6 }, t)),
1561
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: h / 2, y: r + 20, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
1562
- ] });
1563
- });
1564
- registerRenderer("wall", (el) => {
1565
- const dist = distance(el.x1, el.y1, el.x2, el.y2);
1566
- const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1567
- const color = c3(el);
1568
- const strokeWidth = sw3(el);
1569
- const fontSize = font(el);
1570
- const hashes = [];
1571
- const numHashes = Math.max(2, Math.floor(dist / 10));
1572
- for (let j = 0; j <= numHashes; j++) {
1573
- const i = j / numHashes * dist;
1574
- hashes.push(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: i, y1: 0, x2: i - 8, y2: -12, stroke: color, strokeWidth: 1 }, j));
1575
- }
1576
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1577
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth }),
1578
- hashes,
1579
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("text", { x: dist / 2, y: 20, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
1580
- ] });
1581
- });
1582
-
1583
- // src/core/renderers/mechanical.tsx
1584
- var import_jsx_runtime6 = require("react/jsx-runtime");
1585
- var c4 = (el) => el.color || "#0f172a";
1586
- var sw4 = (el) => el.strokeWidth || 2;
1587
- var font2 = (el) => el.fontSize || 18;
1588
- registerRenderer("string", (el) => {
1589
- const d = distance(el.x1, el.y1, el.x2, el.y2);
1590
- const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1591
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c4(el), strokeWidth: sw4(el), strokeLinecap: "round" }) });
1592
- });
1593
- registerRenderer("surface", (el) => {
1594
- const d = distance(el.x1, el.y1, el.x2, el.y2);
1595
- const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1596
- const hatchCount = Math.max(0, Math.floor(d / 12));
1597
- const hatches = [];
1598
- for (let i = 0; i <= hatchCount; i++) {
1599
- hatches.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: i * 12, y1: 0, x2: i * 12 - 8, y2: 12, stroke: c4(el), strokeWidth: 1.5 }, i));
1600
- }
1601
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1602
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c4(el), strokeWidth: sw4(el) + 0.5, strokeLinecap: "round" }),
1603
- hatches
1604
- ] });
1605
- });
1606
- var rotOf = (el) => el.rotation || 0;
1607
- registerRenderer("block", (el) => {
1608
- const w = Math.abs(el.x2 - el.x1);
1609
- const h = Math.abs(el.y2 - el.y1);
1610
- const cx = (el.x1 + el.x2) / 2;
1611
- const cy = (el.y1 + el.y2) / 2;
1612
- const rot = rotOf(el);
1613
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${cx}, ${cy}) rotate(${rot})`, children: [
1614
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { x: -w / 2, y: -h / 2, width: w, height: h, fill: "#f1f5f9", stroke: c4(el), strokeWidth: sw4(el) }),
1615
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1616
- "text",
1617
- {
1618
- x: 0,
1619
- y: el.value ? -6 : 0,
1620
- dominantBaseline: "central",
1621
- textAnchor: "middle",
1622
- fill: c4(el),
1623
- fontSize: 18,
1624
- fontFamily: "serif",
1625
- fontWeight: "bold",
1626
- children: String(el.label)
1627
- }
1628
- ) : null,
1629
- el.value ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1630
- "text",
1631
- {
1632
- x: 0,
1633
- y: el.label ? 10 : 0,
1634
- dominantBaseline: "central",
1635
- textAnchor: "middle",
1636
- fill: "#4b5563",
1637
- fontSize: 14,
1638
- fontFamily: "sans-serif",
1639
- children: String(el.value)
1640
- }
1641
- ) : null
1642
- ] });
1643
- });
1644
- registerRenderer("cart", (el) => {
1645
- const w = Math.abs(el.x2 - el.x1);
1646
- const h = Math.abs(el.y2 - el.y1);
1647
- const cx = (el.x1 + el.x2) / 2;
1648
- const cy = (el.y1 + el.y2) / 2;
1649
- const rot = rotOf(el);
1650
- const wheelR = Math.min(w * 0.15, h * 0.25, 12);
1651
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${cx}, ${cy}) rotate(${rot})`, children: [
1652
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("rect", { x: -w / 2, y: -h / 2, width: w, height: h - wheelR, fill: "#f1f5f9", stroke: c4(el), strokeWidth: sw4(el) }),
1653
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: -w / 2 + wheelR * 1.5, cy: h / 2 - wheelR, r: wheelR, fill: "#cbd5e1", stroke: c4(el), strokeWidth: sw4(el) }),
1654
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: w / 2 - wheelR * 1.5, cy: h / 2 - wheelR, r: wheelR, fill: "#cbd5e1", stroke: c4(el), strokeWidth: sw4(el) }),
1655
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1656
- "text",
1657
- {
1658
- x: 0,
1659
- y: el.value ? -8 : -2,
1660
- dominantBaseline: "central",
1661
- textAnchor: "middle",
1662
- fill: c4(el),
1663
- fontSize: 18,
1664
- fontFamily: "serif",
1665
- fontWeight: "bold",
1666
- children: String(el.label)
1667
- }
1668
- ) : null,
1669
- el.value ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1670
- "text",
1671
- {
1672
- x: 0,
1673
- y: el.label ? 8 : -2,
1674
- dominantBaseline: "central",
1675
- textAnchor: "middle",
1676
- fill: "#4b5563",
1677
- fontSize: 14,
1678
- fontFamily: "sans-serif",
1679
- children: String(el.value)
1680
- }
1681
- ) : null
2165
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
2166
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2167
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth: 1, opacity: 0.3, pointerEvents: "none" }),
2168
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: env1, fill: "none", stroke: color, strokeWidth: 1, strokeDasharray: "2 4", opacity: 0.5, pointerEvents: "none" }),
2169
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: env2, fill: "none", stroke: color, strokeWidth: 1, strokeDasharray: "2 4", opacity: 0.5, pointerEvents: "none" }),
2170
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LocalFatPathHit, { dPath: d, strokeWidth: 22, strokeLinejoin: "round" }),
2171
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d, fill: "none", stroke: color, strokeWidth, strokeLinejoin: "round", pointerEvents: "none" }),
2172
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: dist / 2, y: -amp - 20, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2173
+ ] }),
2174
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChordInteractionOverlay, { el, ctx, padY: amp + 28 })
1682
2175
  ] });
1683
2176
  });
1684
- registerRenderer("particle", (el) => {
1685
- const d = distance(el.x1, el.y1, el.x2, el.y2);
1686
- const r = Math.max(5, d);
1687
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1688
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: 0, cy: 0, r, fill: c4(el) }),
1689
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: 0, y: -r - 12, dominantBaseline: "central", textAnchor: "middle", fill: c4(el), fontSize: font2(el), fontFamily: "serif", fontWeight: "bold", children: String(el.label) }) : null,
1690
- el.value ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: 0, y: r + 12, dominantBaseline: "central", textAnchor: "middle", fill: "#4b5563", fontSize: 14, fontFamily: "sans-serif", children: String(el.value) }) : null
2177
+ registerRenderer("mach_cone", (el, ctx) => {
2178
+ const dist = distance(el.x1, el.y1, el.x2, el.y2);
2179
+ const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
2180
+ const M = el.machNumber || 2;
2181
+ const coneAngle = Math.asin(1 / Math.max(1.01, M));
2182
+ const h = dist;
2183
+ const r = h * Math.tan(coneAngle);
2184
+ const color = c3(el);
2185
+ const strokeWidth = sw3(el);
2186
+ const fontSize = font2(el);
2187
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
2188
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2189
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: h, y2: 0, stroke: color, strokeWidth: 1, strokeDasharray: "4 4", pointerEvents: "none" }),
2190
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: h, y1: 0, x2: 0, y2: -r, stroke: color, strokeWidth, pointerEvents: "none" }),
2191
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: h, y1: 0, x2: 0, y2: r, stroke: color, strokeWidth, pointerEvents: "none" }),
2192
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: h, cy: 0, r: 4, fill: color, pointerEvents: "none" }),
2193
+ [0.2, 0.5, 0.8].map((t) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: h * t, cy: 0, r: (1 - t) * r, fill: "none", stroke: color, strokeWidth: 1, opacity: 0.6, pointerEvents: "none" }, t)),
2194
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: h / 2, y: r + 20, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2195
+ ] }),
2196
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChordInteractionOverlay, { el, ctx, padY: r + 35 })
1691
2197
  ] });
1692
2198
  });
1693
- registerRenderer("disk", (el) => {
1694
- const d = distance(el.x1, el.y1, el.x2, el.y2);
1695
- const r = Math.max(10, d);
1696
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1697
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: 0, cy: 0, r, fill: "#e2e8f0", stroke: c4(el), strokeWidth: sw4(el) }),
1698
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: r, y2: 0, stroke: c4(el), strokeWidth: sw4(el) }),
1699
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: 0, cy: 0, r: 3, fill: c4(el) }),
1700
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: 0, y: -r - 12, dominantBaseline: "central", textAnchor: "middle", fill: c4(el), fontSize: font2(el), fontFamily: "serif", fontWeight: "bold", children: String(el.label) }) : null,
1701
- el.value ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: 0, y: r + 12, dominantBaseline: "central", textAnchor: "middle", fill: "#4b5563", fontSize: 14, fontFamily: "sans-serif", children: String(el.value) }) : null
1702
- ] });
1703
- });
1704
- registerRenderer("com", (el) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1705
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: 0, cy: 0, r: 10, fill: "none", stroke: c4(el), strokeWidth: sw4(el) }),
1706
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M 0 -10 A 10 10 0 0 1 10 0 L 0 0 Z", fill: c4(el) }),
1707
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "M 0 10 A 10 10 0 0 1 -10 0 L 0 0 Z", fill: c4(el) })
1708
- ] }));
1709
- registerRenderer("pivot", (el) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1710
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polygon", { points: "0,0 -12,20 12,20", fill: "#f8fafc", stroke: c4(el), strokeWidth: sw4(el), strokeLinejoin: "round" }),
1711
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: -20, y1: 20, x2: 20, y2: 20, stroke: c4(el), strokeWidth: sw4(el), strokeLinecap: "round" }),
1712
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: 0, cy: 0, r: 3, fill: c4(el) })
1713
- ] }));
1714
- registerRenderer("pulley", (el) => {
1715
- const d = distance(el.x1, el.y1, el.x2, el.y2);
1716
- const r = Math.max(10, d);
1717
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1718
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: 0, cy: 0, r, fill: "#f8fafc", stroke: c4(el), strokeWidth: sw4(el) }),
1719
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { cx: 0, cy: 0, r: 4, fill: c4(el) }),
1720
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: 0, y: -r - 12, dominantBaseline: "central", textAnchor: "middle", fill: c4(el), fontSize: font2(el), fontFamily: "serif", fontWeight: "bold", children: String(el.label) }) : null
2199
+ registerRenderer("wall", (el, ctx) => {
2200
+ const dist = distance(el.x1, el.y1, el.x2, el.y2);
2201
+ const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
2202
+ const color = c3(el);
2203
+ const strokeWidth = sw3(el);
2204
+ const fontSize = font2(el);
2205
+ const hashes = [];
2206
+ const numHashes = Math.max(2, Math.floor(dist / 10));
2207
+ for (let j = 0; j <= numHashes; j++) {
2208
+ const i = j / numHashes * dist;
2209
+ hashes.push(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: i, y1: 0, x2: i - 8, y2: -12, stroke: color, strokeWidth: 1, pointerEvents: "none" }, j));
2210
+ }
2211
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { children: [
2212
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2213
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: 0, y1: 0, x2: dist, y2: 0, stroke: color, strokeWidth, pointerEvents: "none" }),
2214
+ hashes,
2215
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: dist / 2, y: 20, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2216
+ ] }),
2217
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChordInteractionOverlay, { el, ctx, padY: 35 })
1721
2218
  ] });
1722
2219
  });
1723
2220
 
1724
2221
  // src/core/renderers/rotation.tsx
1725
2222
  var import_jsx_runtime7 = require("react/jsx-runtime");
1726
- var c5 = (el) => el.color || "#0f172a";
1727
- var sw5 = (el) => el.strokeWidth || 2;
2223
+ var c4 = (el) => el.color || "#0f172a";
2224
+ var sw4 = (el) => el.strokeWidth || 2;
1728
2225
  var font3 = (el) => el.fontSize || 18;
1729
2226
  function generateSpringPath(dist, coils, width2 = 20) {
1730
2227
  const ccount = Math.max(3, coils ?? Math.floor(dist / 20));
@@ -1740,35 +2237,39 @@ function generateSpringPath(dist, coils, width2 = 20) {
1740
2237
  }
1741
2238
  return d;
1742
2239
  }
1743
- registerRenderer("point_mass", (el) => {
2240
+ registerRenderer("point_mass", (el, ctx) => {
1744
2241
  const size = el.size ?? 15;
1745
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1746
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: size, fill: c5(el), fillOpacity: 0.8, stroke: c5(el), strokeWidth: 2 }),
2242
+ const plate = Math.max(36, size + 22);
2243
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(OriginHitPlate, { ctx, hitHalfSize: plate / 2, selectionHalfSize: plate / 2 + 8, children: [
2244
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: size, fill: c4(el), fillOpacity: 0.8, stroke: c4(el), strokeWidth: 2 }),
1747
2245
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: size * 0.3, fill: "#fff", fillOpacity: 0.5 }),
1748
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 0, y: size + 15, fontSize: font3(el), fill: c5(el), textAnchor: "middle", fontWeight: "bold", children: String(el.label) }) : null
1749
- ] });
2246
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 0, y: size + 15, fontSize: font3(el), fill: c4(el), textAnchor: "middle", fontWeight: "bold", pointerEvents: "none", children: String(el.label) }) : null
2247
+ ] }) });
1750
2248
  });
1751
- registerRenderer("com_indicator", (el) => {
2249
+ registerRenderer("com_indicator", (el, ctx) => {
1752
2250
  const size = el.size ?? 15;
1753
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1754
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: size, fill: "none", stroke: c5(el), strokeWidth: sw5(el) }),
1755
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: -size * 1.3, y1: 0, x2: size * 1.3, y2: 0, stroke: c5(el), strokeWidth: sw5(el) }),
1756
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: 0, y1: -size * 1.3, x2: 0, y2: size * 1.3, stroke: c5(el), strokeWidth: sw5(el) }),
1757
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: `M 0 0 L ${size} 0 A ${size} ${size} 0 0 0 0 ${-size} Z`, fill: c5(el), fillOpacity: 0.5 }),
1758
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: `M 0 0 L ${-size} 0 A ${size} ${size} 0 0 0 0 ${size} Z`, fill: c5(el), fillOpacity: 0.5 }),
1759
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 0, y: size + 15, fontSize: font3(el), fill: c5(el), textAnchor: "middle", fontWeight: "bold", children: String(el.label) }) : null
1760
- ] });
1761
- });
1762
- registerRenderer("pivot", (el) => {
2251
+ const plate = Math.max(40, size * 2 + 12);
2252
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(OriginHitPlate, { ctx, hitHalfSize: plate / 2, selectionHalfSize: plate / 2 + 10, children: [
2253
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: size, fill: "none", stroke: c4(el), strokeWidth: sw4(el) }),
2254
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: -size * 1.3, y1: 0, x2: size * 1.3, y2: 0, stroke: c4(el), strokeWidth: sw4(el), pointerEvents: "none" }),
2255
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: 0, y1: -size * 1.3, x2: 0, y2: size * 1.3, stroke: c4(el), strokeWidth: sw4(el), pointerEvents: "none" }),
2256
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: `M 0 0 L ${size} 0 A ${size} ${size} 0 0 0 0 ${-size} Z`, fill: c4(el), fillOpacity: 0.5, pointerEvents: "none" }),
2257
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: `M 0 0 L ${-size} 0 A ${size} ${size} 0 0 0 0 ${size} Z`, fill: c4(el), fillOpacity: 0.5, pointerEvents: "none" }),
2258
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 0, y: size + 15, fontSize: font3(el), fill: c4(el), textAnchor: "middle", fontWeight: "bold", pointerEvents: "none", children: String(el.label) }) : null
2259
+ ] }) });
2260
+ });
2261
+ registerRenderer("pivot", (el, ctx) => {
1763
2262
  const size = el.size ?? 20;
1764
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
2263
+ const plate = Math.max(44, size * 2 + 24);
2264
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(OriginHitPlate, { ctx, hitHalfSize: plate / 2, selectionHalfSize: plate / 2 + 10, children: [
1765
2265
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1766
2266
  "polygon",
1767
2267
  {
1768
2268
  points: `0,0 ${-size},${size * 1.5} ${size},${size * 1.5}`,
1769
2269
  fill: "none",
1770
- stroke: c5(el),
1771
- strokeWidth: sw5(el)
2270
+ stroke: c4(el),
2271
+ strokeWidth: sw4(el),
2272
+ pointerEvents: "none"
1772
2273
  }
1773
2274
  ),
1774
2275
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
@@ -1778,12 +2279,13 @@ registerRenderer("pivot", (el) => {
1778
2279
  y1: size * 1.5,
1779
2280
  x2: size * 1.5,
1780
2281
  y2: size * 1.5,
1781
- stroke: c5(el),
1782
- strokeWidth: sw5(el) + 1,
1783
- strokeLinecap: "round"
2282
+ stroke: c4(el),
2283
+ strokeWidth: sw4(el) + 1,
2284
+ strokeLinecap: "round",
2285
+ pointerEvents: "none"
1784
2286
  }
1785
2287
  ),
1786
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: sw5(el) * 1.5, fill: c5(el) }),
2288
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: sw4(el) * 1.5, fill: c4(el), pointerEvents: "none" }),
1787
2289
  Array.from({ length: 5 }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1788
2290
  "line",
1789
2291
  {
@@ -1791,198 +2293,250 @@ registerRenderer("pivot", (el) => {
1791
2293
  y1: size * 1.5,
1792
2294
  x2: -size + i * size * 0.5 - 5,
1793
2295
  y2: size * 1.5 + 8,
1794
- stroke: c5(el),
1795
- strokeWidth: 1
2296
+ stroke: c4(el),
2297
+ strokeWidth: 1,
2298
+ pointerEvents: "none"
1796
2299
  },
1797
2300
  i
1798
2301
  )),
1799
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 0, y: size * 1.5 + 20, fontSize: font3(el), fill: c5(el), textAnchor: "middle", children: String(el.label) }) : null
1800
- ] });
2302
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 0, y: size * 1.5 + 20, fontSize: font3(el), fill: c4(el), textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2303
+ ] }) });
1801
2304
  });
1802
- registerRenderer("block_mass", (el) => {
2305
+ registerRenderer("block_mass", (el, ctx) => {
1803
2306
  const minX = Math.min(el.x1, el.x2);
1804
2307
  const minY = Math.min(el.y1, el.y2);
1805
2308
  const w = Math.max(1, Math.abs(el.x2 - el.x1));
1806
2309
  const h = Math.max(1, Math.abs(el.y2 - el.y1));
1807
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${minX}, ${minY})`, children: [
1808
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("rect", { x: 0, y: 0, width: w, height: h, fill: c5(el), fillOpacity: 0.2, stroke: c5(el), strokeWidth: sw5(el), rx: 2 }),
1809
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: w / 2, y: h / 2, dominantBaseline: "central", textAnchor: "middle", fontSize: font3(el), fill: c5(el), fontWeight: "bold", children: String(el.label) }) : null
2310
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { children: [
2311
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${minX}, ${minY})`, children: [
2312
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("rect", { x: 0, y: 0, width: w, height: h, fill: c4(el), fillOpacity: 0.2, stroke: c4(el), strokeWidth: sw4(el), rx: 2 }),
2313
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: w / 2, y: h / 2, dominantBaseline: "central", textAnchor: "middle", fontSize: font3(el), fill: c4(el), fontWeight: "bold", pointerEvents: "none", children: String(el.label) }) : null
2314
+ ] }),
2315
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
2316
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChordInteractionOverlay, { el, ctx })
1810
2317
  ] });
1811
2318
  });
1812
- registerRenderer("system_boundary", (el) => {
2319
+ registerRenderer("system_boundary", (el, ctx) => {
1813
2320
  const minX = Math.min(el.x1, el.x2);
1814
2321
  const minY = Math.min(el.y1, el.y2);
1815
2322
  const w = Math.max(1, Math.abs(el.x2 - el.x1));
1816
2323
  const h = Math.max(1, Math.abs(el.y2 - el.y1));
1817
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${minX}, ${minY})`, children: [
1818
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("rect", { x: 0, y: 0, width: w, height: h, fill: "none", stroke: c5(el), strokeWidth: sw5(el), strokeDasharray: "8 6", rx: 15 }),
1819
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: w / 2, y: -10, fontSize: font3(el), fill: c5(el), textAnchor: "middle", fontStyle: "italic", children: String(el.label) }) : null
2324
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { children: [
2325
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${minX}, ${minY})`, children: [
2326
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("rect", { x: 0, y: 0, width: w, height: h, fill: "none", stroke: c4(el), strokeWidth: sw4(el), strokeDasharray: "8 6", rx: 15 }),
2327
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: w / 2, y: -10, fontSize: font3(el), fill: c4(el), textAnchor: "middle", fontStyle: "italic", pointerEvents: "none", children: String(el.label) }) : null
2328
+ ] }),
2329
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
2330
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChordInteractionOverlay, { el, ctx })
1820
2331
  ] });
1821
2332
  });
1822
- registerRenderer("rocket", (el) => {
2333
+ registerRenderer("rocket", (el, ctx) => {
1823
2334
  const dx = el.x2 - el.x1;
1824
2335
  const dy = el.y2 - el.y1;
1825
2336
  const dist = Math.hypot(dx, dy);
1826
2337
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1827
2338
  const minX = Math.min(el.x1, el.x2);
1828
2339
  const maxX = Math.max(el.x1, el.x2);
1829
- const minY = Math.min(el.y1, el.y2);
1830
- const maxY = Math.max(el.y1, el.y2);
1831
2340
  const w = Math.max(1, maxX - minX);
1832
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1833
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1834
- "path",
1835
- {
1836
- d: `M 0 ${-w / 4} L ${dist * 0.7} ${-w / 4} L ${dist} 0 L ${dist * 0.7} ${w / 4} L 0 ${w / 4} Z`,
1837
- fill: "none",
1838
- stroke: c5(el),
1839
- strokeWidth: sw5(el)
1840
- }
1841
- ),
1842
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polygon", { points: `0,${-w / 4} ${dist * 0.2},${-w / 4} 0,${-w / 2}`, fill: c5(el), fillOpacity: 0.8 }),
1843
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polygon", { points: `0,${w / 4} ${dist * 0.2},${w / 4} 0,${w / 2}`, fill: c5(el), fillOpacity: 0.8 }),
1844
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: `M 0 ${-w / 6} Q ${-dist * 0.3} 0 0 ${w / 6}`, fill: "#f97316", fillOpacity: 0.8 }),
1845
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: `M 0 ${-w / 8} Q ${-dist * 0.5} 0 0 ${w / 8}`, fill: "#ef4444", fillOpacity: 0.6 }),
1846
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: dist / 2, y: -w / 2 - 10, fontSize: font3(el), fill: c5(el), textAnchor: "middle", children: String(el.label) }) : null
2341
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { children: [
2342
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2343
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2344
+ "path",
2345
+ {
2346
+ d: `M 0 ${-w / 4} L ${dist * 0.7} ${-w / 4} L ${dist} 0 L ${dist * 0.7} ${w / 4} L 0 ${w / 4} Z`,
2347
+ fill: "none",
2348
+ stroke: c4(el),
2349
+ strokeWidth: sw4(el),
2350
+ pointerEvents: "none"
2351
+ }
2352
+ ),
2353
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polygon", { points: `0,${-w / 4} ${dist * 0.2},${-w / 4} 0,${-w / 2}`, fill: c4(el), fillOpacity: 0.8, pointerEvents: "none" }),
2354
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polygon", { points: `0,${w / 4} ${dist * 0.2},${w / 4} 0,${w / 2}`, fill: c4(el), fillOpacity: 0.8, pointerEvents: "none" }),
2355
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: `M 0 ${-w / 6} Q ${-dist * 0.3} 0 0 ${w / 6}`, fill: "#f97316", fillOpacity: 0.8, pointerEvents: "none" }),
2356
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: `M 0 ${-w / 8} Q ${-dist * 0.5} 0 0 ${w / 8}`, fill: "#ef4444", fillOpacity: 0.6, pointerEvents: "none" }),
2357
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: dist / 2, y: -w / 2 - 10, fontSize: font3(el), fill: c4(el), textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2358
+ ] }),
2359
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChordInteractionOverlay, { el, ctx })
1847
2360
  ] });
1848
2361
  });
1849
- registerRenderer("dashed_path", (el) => {
2362
+ registerRenderer("dashed_path", (el, ctx) => {
1850
2363
  const d = distance(el.x1, el.y1, el.x2, el.y2);
1851
2364
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1852
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1853
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c5(el), strokeWidth: sw5(el), strokeLinecap: "round", strokeDasharray: "8 6" }),
1854
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: d / 2, y: -10, fontSize: font3(el), fill: c5(el), textAnchor: "middle", children: String(el.label) }) : null
2365
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { children: [
2366
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2367
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c4(el), strokeWidth: sw4(el), strokeLinecap: "round", strokeDasharray: "8 6", pointerEvents: "none" }),
2368
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: d / 2, y: -10, fontSize: font3(el), fill: c4(el), textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2369
+ ] }),
2370
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChordInteractionOverlay, { el, ctx })
1855
2371
  ] });
1856
2372
  });
1857
- registerRenderer("uniform_rod", (el) => {
2373
+ registerRenderer("uniform_rod", (el, ctx) => {
1858
2374
  const dist = distance(el.x1, el.y1, el.x2, el.y2);
1859
2375
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1860
- const strokeWidth = sw5(el);
1861
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1862
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("rect", { x: 0, y: -strokeWidth / 2, width: dist, height: strokeWidth, fill: c5(el), rx: strokeWidth / 4 }),
1863
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: dist / 2, cy: 0, r: Math.max(2, strokeWidth / 4), fill: "#fff" }),
1864
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: dist / 2, y: -strokeWidth / 2 - 10, fontSize: font3(el), fill: c5(el), textAnchor: "middle", children: String(el.label) }) : null
2376
+ const strokeWidth = sw4(el);
2377
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { children: [
2378
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2379
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("rect", { x: 0, y: -strokeWidth / 2, width: dist, height: strokeWidth, fill: c4(el), rx: strokeWidth / 4, pointerEvents: "none" }),
2380
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: dist / 2, cy: 0, r: Math.max(2, strokeWidth / 4), fill: "#fff", pointerEvents: "none" }),
2381
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: dist / 2, y: -strokeWidth / 2 - 10, fontSize: font3(el), fill: c4(el), textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2382
+ ] }),
2383
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChordInteractionOverlay, { el, ctx })
1865
2384
  ] });
1866
2385
  });
1867
- registerRenderer("solid_disk", (el) => {
2386
+ registerRenderer("solid_disk", (el, ctx) => {
1868
2387
  const dx = el.x2 - el.x1;
1869
2388
  const dy = el.y2 - el.y1;
1870
2389
  const radius = Math.hypot(dx, dy);
1871
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1872
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: radius, fill: c5(el), fillOpacity: 0.4, stroke: c5(el), strokeWidth: sw5(el) }),
1873
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 0, y: radius + 20, fontSize: font3(el), fill: c5(el), textAnchor: "middle", children: String(el.label) }) : null
2390
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { children: [
2391
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
2392
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: radius, fill: c4(el), fillOpacity: 0.4, stroke: c4(el), strokeWidth: sw4(el), pointerEvents: "none" }),
2393
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 0, y: radius + 20, fontSize: font3(el), fill: c4(el), textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2394
+ ] }),
2395
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChordInteractionOverlay, { el, ctx })
1874
2396
  ] });
1875
2397
  });
1876
- registerRenderer("hoop_ring", (el) => {
2398
+ registerRenderer("hoop_ring", (el, ctx) => {
1877
2399
  const dx = el.x2 - el.x1;
1878
2400
  const dy = el.y2 - el.y1;
1879
2401
  const radius = Math.hypot(dx, dy);
1880
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1881
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: radius, fill: "none", stroke: c5(el), strokeWidth: Math.max(4, sw5(el) * 2) }),
1882
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 0, y: radius + 20, fontSize: font3(el), fill: c5(el), textAnchor: "middle", children: String(el.label) }) : null
2402
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { children: [
2403
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
2404
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: radius, fill: "none", stroke: c4(el), strokeWidth: Math.max(4, sw4(el) * 2), pointerEvents: "none" }),
2405
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 0, y: radius + 20, fontSize: font3(el), fill: c4(el), textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2406
+ ] }),
2407
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChordInteractionOverlay, { el, ctx })
1883
2408
  ] });
1884
2409
  });
1885
- registerRenderer("rolling_body", (el) => {
2410
+ registerRenderer("rolling_body", (el, ctx) => {
1886
2411
  const dx = el.x2 - el.x1;
1887
2412
  const dy = el.y2 - el.y1;
1888
2413
  const radius = Math.hypot(dx, dy);
1889
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1890
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: radius, fill: c5(el), fillOpacity: 0.1, stroke: c5(el), strokeWidth: sw5(el) }),
1891
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: -radius, y1: 0, x2: radius, y2: 0, stroke: c5(el), strokeWidth: 1, strokeDasharray: "4 4" }),
1892
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: 0, y1: -radius, x2: 0, y2: radius, stroke: c5(el), strokeWidth: 1, strokeDasharray: "4 4" }),
1893
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: 3, fill: c5(el) }),
1894
- el.showVelocity ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { children: [
1895
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: 0, y1: 0, x2: radius * 1.5, y2: 0, stroke: "#ef4444", strokeWidth: 2 }),
1896
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polygon", { points: `${radius * 1.5},0 ${radius * 1.5 - 8},-4 ${radius * 1.5 - 8},4`, fill: "#ef4444" }),
1897
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: radius * 1.5 + 10, y: 5, fill: "#ef4444", fontSize: 14, fontStyle: "italic", children: "v" })
1898
- ] }) : null,
1899
- el.showOmega ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(0, ${-radius * 1.2})`, children: [
1900
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: `M ${-radius * 0.4} 0 A ${radius * 0.4} ${radius * 0.4} 0 0 1 ${radius * 0.4} 0`, fill: "none", stroke: "#10b981", strokeWidth: 2 }),
1901
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polygon", { points: `${radius * 0.4},0 ${radius * 0.4 - 4},-6 ${radius * 0.4 + 4},-6`, fill: "#10b981" }),
1902
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 0, y: -10, fill: "#10b981", fontSize: 14, fontStyle: "italic", textAnchor: "middle", children: "\u03C9" })
1903
- ] }) : null,
1904
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 0, y: radius + 20, fontSize: font3(el), fill: c5(el), textAnchor: "middle", children: String(el.label) }) : null
2414
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { children: [
2415
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
2416
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: radius, fill: c4(el), fillOpacity: 0.1, stroke: c4(el), strokeWidth: sw4(el), pointerEvents: "none" }),
2417
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: -radius, y1: 0, x2: radius, y2: 0, stroke: c4(el), strokeWidth: 1, strokeDasharray: "4 4", pointerEvents: "none" }),
2418
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: 0, y1: -radius, x2: 0, y2: radius, stroke: c4(el), strokeWidth: 1, strokeDasharray: "4 4", pointerEvents: "none" }),
2419
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: 3, fill: c4(el), pointerEvents: "none" }),
2420
+ el.showVelocity ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { pointerEvents: "none", children: [
2421
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: 0, y1: 0, x2: radius * 1.5, y2: 0, stroke: "#ef4444", strokeWidth: 2 }),
2422
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polygon", { points: `${radius * 1.5},0 ${radius * 1.5 - 8},-4 ${radius * 1.5 - 8},4`, fill: "#ef4444" }),
2423
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: radius * 1.5 + 10, y: 5, fill: "#ef4444", fontSize: 14, fontStyle: "italic", children: "v" })
2424
+ ] }) : null,
2425
+ el.showOmega ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(0, ${-radius * 1.2})`, pointerEvents: "none", children: [
2426
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: `M ${-radius * 0.4} 0 A ${radius * 0.4} ${radius * 0.4} 0 0 1 ${radius * 0.4} 0`, fill: "none", stroke: "#10b981", strokeWidth: 2 }),
2427
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polygon", { points: `${radius * 0.4},0 ${radius * 0.4 - 4},-6 ${radius * 0.4 + 4},-6`, fill: "#10b981" }),
2428
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 0, y: -10, fill: "#10b981", fontSize: 14, fontStyle: "italic", textAnchor: "middle", children: "\u03C9" })
2429
+ ] }) : null,
2430
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 0, y: radius + 20, fontSize: font3(el), fill: c4(el), textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2431
+ ] }),
2432
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChordInteractionOverlay, { el, ctx })
1905
2433
  ] });
1906
2434
  });
1907
- registerRenderer("inclined_wedge", (el) => {
2435
+ registerRenderer("inclined_wedge", (el, ctx) => {
1908
2436
  const { x1, y1, x2, y2 } = el;
1909
2437
  const dx = x2 - x1;
1910
2438
  const dy = y2 - y1;
1911
2439
  const angle = Math.atan2(dy, dx) * (180 / Math.PI);
2440
+ const b = getBounds(x1, y1, x2, y2);
2441
+ const triMinX = Math.min(x1, x2);
2442
+ const triMinY = Math.min(y1, y2);
2443
+ const triW = b.width;
2444
+ const triH = b.height;
1912
2445
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { children: [
1913
2446
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1914
2447
  "polygon",
1915
2448
  {
1916
2449
  points: `${x1},${y1} ${x1},${y2} ${x2},${y2}`,
1917
- fill: c5(el),
2450
+ fill: c4(el),
1918
2451
  fillOpacity: 0.1,
1919
- stroke: c5(el),
1920
- strokeWidth: sw5(el),
1921
- strokeLinejoin: "round"
2452
+ stroke: c4(el),
2453
+ strokeWidth: sw4(el),
2454
+ strokeLinejoin: "round",
2455
+ pointerEvents: "none"
1922
2456
  }
1923
2457
  ),
1924
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: (x1 + x2) / 2, y: y2 - 10, fontSize: font3(el), fill: c5(el), textAnchor: "middle", children: String(el.label) }) : null,
2458
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: (x1 + x2) / 2, y: y2 - 10, fontSize: font3(el), fill: c4(el), textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null,
1925
2459
  Math.abs(x2 - x1) > 20 && Math.abs(y2 - y1) > 20 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1926
2460
  "path",
1927
2461
  {
1928
2462
  d: `M ${x2 > x1 ? x2 - 20 : x2 + 20} ${y2} A 20 20 0 0 ${x2 > x1 ? 0 : 1} ${x2 > x1 ? x2 - 20 * Math.cos(angle * Math.PI / 180) : x2 + 20 * Math.cos(angle * Math.PI / 180)} ${y2 - 20 * Math.sin(Math.abs(angle) * Math.PI / 180)}`,
1929
2463
  fill: "none",
1930
- stroke: c5(el),
1931
- strokeWidth: 1
2464
+ stroke: c4(el),
2465
+ strokeWidth: 1,
2466
+ pointerEvents: "none"
1932
2467
  }
1933
- ) : null
2468
+ ) : null,
2469
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(BBoxInteractionOverlay, { minX: triMinX, minY: triMinY, width: triW, height: triH, ctx }),
2470
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChordInteractionOverlay, { el, ctx })
1934
2471
  ] });
1935
2472
  });
1936
- registerRenderer("pulley", (el) => {
2473
+ registerRenderer("pulley", (el, ctx) => {
1937
2474
  const dx = el.x2 - el.x1;
1938
2475
  const dy = el.y2 - el.y1;
1939
2476
  const radius = Math.hypot(dx, dy);
1940
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
1941
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: `M 0 0 L 0 ${-radius * 1.5}`, fill: "none", stroke: c5(el), strokeWidth: sw5(el) }),
1942
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: -radius * 0.8, y1: -radius * 1.5, x2: radius * 0.8, y2: -radius * 1.5, stroke: c5(el), strokeWidth: sw5(el) }),
1943
- Array.from({ length: 5 }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1944
- "line",
1945
- {
1946
- x1: -radius * 0.8 + i * radius * 0.4,
1947
- y1: -radius * 1.5,
1948
- x2: -radius * 0.8 + i * radius * 0.4 + 5,
1949
- y2: -radius * 1.5 - 5,
1950
- stroke: c5(el),
1951
- strokeWidth: 1
1952
- },
1953
- i
1954
- )),
1955
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: radius, fill: "#fff", stroke: c5(el), strokeWidth: sw5(el) }),
1956
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: radius * 0.8, fill: "none", stroke: c5(el), strokeWidth: 1, opacity: 0.3 }),
1957
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: 3, fill: c5(el) }),
1958
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 0, y: -radius * 1.5 - 15, fontSize: font3(el), fill: c5(el), textAnchor: "middle", children: String(el.label) }) : null
2477
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { children: [
2478
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
2479
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: `M 0 0 L 0 ${-radius * 1.5}`, fill: "none", stroke: c4(el), strokeWidth: sw4(el), pointerEvents: "none" }),
2480
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: -radius * 0.8, y1: -radius * 1.5, x2: radius * 0.8, y2: -radius * 1.5, stroke: c4(el), strokeWidth: sw4(el), pointerEvents: "none" }),
2481
+ Array.from({ length: 5 }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2482
+ "line",
2483
+ {
2484
+ x1: -radius * 0.8 + i * radius * 0.4,
2485
+ y1: -radius * 1.5,
2486
+ x2: -radius * 0.8 + i * radius * 0.4 + 5,
2487
+ y2: -radius * 1.5 - 5,
2488
+ stroke: c4(el),
2489
+ strokeWidth: 1,
2490
+ pointerEvents: "none"
2491
+ },
2492
+ i
2493
+ )),
2494
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: radius, fill: "#fff", stroke: c4(el), strokeWidth: sw4(el), pointerEvents: "none" }),
2495
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: radius * 0.8, fill: "none", stroke: c4(el), strokeWidth: 1, opacity: 0.3, pointerEvents: "none" }),
2496
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("circle", { cx: 0, cy: 0, r: 3, fill: c4(el), pointerEvents: "none" }),
2497
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: 0, y: -radius * 1.5 - 15, fontSize: font3(el), fill: c4(el), textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2498
+ ] }),
2499
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChordInteractionOverlay, { el, ctx })
1959
2500
  ] });
1960
2501
  });
1961
- registerRenderer("curve_arrow", (el) => {
2502
+ registerRenderer("curve_arrow", (el, ctx) => {
1962
2503
  const dist = distance(el.x1, el.y1, el.x2, el.y2);
1963
2504
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1964
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1965
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: `M 0 0 Q ${dist / 2} ${-dist * 0.5} ${dist} 0`, fill: "none", stroke: c5(el), strokeWidth: sw5(el), strokeLinecap: "round" }),
1966
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polygon", { points: `${dist},0 ${dist - 8},-6 ${dist - 4},-1`, fill: c5(el), transform: `rotate(20 ${dist} 0)` }),
1967
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: dist / 2, y: -dist * 0.3, fontSize: font3(el), fill: c5(el), textAnchor: "middle", children: String(el.label) }) : null
2505
+ const curvePath = `M 0 0 Q ${dist / 2} ${-dist * 0.5} ${dist} 0`;
2506
+ const bb = quadraticChordWorldBounds(el.x1, el.y1, el.x2, el.y2, dist / 4);
2507
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { children: [
2508
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2509
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: curvePath, fill: "none", stroke: c4(el), strokeWidth: sw4(el), strokeLinecap: "round", pointerEvents: "none" }),
2510
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(LocalFatPathHit, { dPath: curvePath, strokeWidth: 24, strokeLinecap: "round" }),
2511
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polygon", { points: `${dist},0 ${dist - 8},-6 ${dist - 4},-1`, fill: c4(el), transform: `rotate(20 ${dist} 0)`, pointerEvents: "none" }),
2512
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: dist / 2, y: -dist * 0.3, fontSize: font3(el), fill: c4(el), textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2513
+ ] }),
2514
+ ctx.isSelected && !ctx.viewMode && !ctx.isGhost ? selectionDashRect(bb.x, bb.y, bb.width, bb.height) : null,
2515
+ endpointHandles(el, ctx)
1968
2516
  ] });
1969
2517
  });
1970
- registerRenderer("spring", (el) => {
2518
+ registerRenderer("spring", (el, ctx) => {
1971
2519
  const dist = distance(el.x1, el.y1, el.x2, el.y2);
1972
2520
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
1973
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
1974
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1975
- "path",
1976
- {
1977
- d: generateSpringPath(dist, el.coils),
1978
- fill: "none",
1979
- stroke: c5(el),
1980
- strokeWidth: sw5(el),
1981
- strokeLinecap: "round",
1982
- strokeLinejoin: "round"
1983
- }
1984
- ),
1985
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: dist / 2, y: -20, fontSize: font3(el), fill: c5(el), textAnchor: "middle", children: String(el.label) }) : null
2521
+ const pathD = generateSpringPath(dist, el.coils);
2522
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { children: [
2523
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2524
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(LocalFatPathHit, { dPath: pathD, strokeWidth: 26, strokeLinecap: "round", strokeLinejoin: "round" }),
2525
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2526
+ "path",
2527
+ {
2528
+ d: pathD,
2529
+ fill: "none",
2530
+ stroke: c4(el),
2531
+ strokeWidth: sw4(el),
2532
+ strokeLinecap: "round",
2533
+ strokeLinejoin: "round",
2534
+ pointerEvents: "none"
2535
+ }
2536
+ ),
2537
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("text", { x: dist / 2, y: -20, fontSize: font3(el), fill: c4(el), textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2538
+ ] }),
2539
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChordInteractionOverlay, { el, ctx })
1986
2540
  ] });
1987
2541
  });
1988
2542
 
@@ -1991,18 +2545,6 @@ var import_jsx_runtime8 = require("react/jsx-runtime");
1991
2545
  var drawColor = (el, ctx) => ctx?.isSelected ? "#2563eb" : el.color || "#111827";
1992
2546
  var drawStroke = (el, ctx) => ctx?.isSelected ? 2.5 : el.strokeWidth || 2;
1993
2547
  registerRenderer("wire", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("g", { opacity: ctx?.isGhost ? 0.5 : 1, children: [
1994
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1995
- "line",
1996
- {
1997
- x1: el.x1,
1998
- y1: el.y1,
1999
- x2: el.x2,
2000
- y2: el.y2,
2001
- stroke: "transparent",
2002
- strokeWidth: 20,
2003
- strokeLinecap: "round"
2004
- }
2005
- ),
2006
2548
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2007
2549
  "line",
2008
2550
  {
@@ -2015,7 +2557,8 @@ registerRenderer("wire", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime8.js
2015
2557
  strokeLinecap: "round",
2016
2558
  pointerEvents: "none"
2017
2559
  }
2018
- )
2560
+ ),
2561
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ChordInteractionOverlay, { el, ctx, hitWidth: 20 })
2019
2562
  ] }));
2020
2563
  var rotated = (el, inner, ctx) => {
2021
2564
  const d = distance(el.x1, el.y1, el.x2, el.y2);
@@ -2032,10 +2575,37 @@ var rotated = (el, inner, ctx) => {
2032
2575
  }
2033
2576
  const labelDist = 20;
2034
2577
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("g", { opacity: ctx?.isGhost ? 0.5 : 1, children: [
2035
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: el.x1, y1: el.y1, x2: el.x2, y2: el.y2, stroke: "transparent", strokeWidth: 20 }),
2036
2578
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${a})`, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("g", { children: inner }) }),
2037
- el.label && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("text", { x: mx + nx * labelDist, y: my + ny * labelDist, dominantBaseline: "central", textAnchor: "middle", fill: drawColor(el, ctx), fontSize: "16", fontFamily: "serif", fontWeight: "bold", children: String(el.label) }),
2038
- el.value && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("text", { x: mx - nx * labelDist, y: my - ny * labelDist, dominantBaseline: "central", textAnchor: "middle", fill: "#4b5563", fontSize: "14", fontFamily: "sans-serif", children: String(el.value) })
2579
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2580
+ "text",
2581
+ {
2582
+ x: mx + nx * labelDist,
2583
+ y: my + ny * labelDist,
2584
+ dominantBaseline: "central",
2585
+ textAnchor: "middle",
2586
+ fill: drawColor(el, ctx),
2587
+ fontSize: "16",
2588
+ fontFamily: "serif",
2589
+ fontWeight: "bold",
2590
+ pointerEvents: "none",
2591
+ children: String(el.label)
2592
+ }
2593
+ ) : null,
2594
+ el.value ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2595
+ "text",
2596
+ {
2597
+ x: mx - nx * labelDist,
2598
+ y: my - ny * labelDist,
2599
+ dominantBaseline: "central",
2600
+ textAnchor: "middle",
2601
+ fill: "#4b5563",
2602
+ fontSize: "14",
2603
+ fontFamily: "sans-serif",
2604
+ pointerEvents: "none",
2605
+ children: String(el.value)
2606
+ }
2607
+ ) : null,
2608
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ChordInteractionOverlay, { el, ctx, hitWidth: 20 })
2039
2609
  ] });
2040
2610
  };
2041
2611
  registerRenderer("resistor", (el, ctx) => {
@@ -2079,7 +2649,7 @@ registerRenderer("battery", (el, ctx) => {
2079
2649
  ] }), ctx);
2080
2650
  })
2081
2651
  );
2082
- registerRenderer("ground", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, opacity: ctx?.isGhost ? 0.5 : 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M 0 0 L 0 15 M -12 15 L 12 15 M -8 20 L 8 20 M -4 25 L 4 25", fill: "none", stroke: drawColor(el, ctx), strokeWidth: drawStroke(el, ctx) }) }));
2652
+ registerRenderer("ground", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, opacity: ctx?.isGhost ? 0.5 : 1, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(OriginHitPlate, { ctx, hitHalfSize: 28, selectionHalfSize: 34, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M 0 0 L 0 15 M -12 15 L 12 15 M -8 20 L 8 20 M -4 25 L 4 25", fill: "none", stroke: drawColor(el, ctx), strokeWidth: drawStroke(el, ctx) }) }) }));
2083
2653
 
2084
2654
  // src/core/renderers/circuitAutoNodes.ts
2085
2655
  var getCircuitAutoNodes = (elements) => {
@@ -2096,8 +2666,8 @@ var getCircuitAutoNodes = (elements) => {
2096
2666
 
2097
2667
  // src/core/renderers/thermo.tsx
2098
2668
  var import_jsx_runtime9 = require("react/jsx-runtime");
2099
- var c6 = (el) => el.color || "#0f172a";
2100
- var sw6 = (el) => el.strokeWidth || 2;
2669
+ var c5 = (el) => el.color || "#0f172a";
2670
+ var sw5 = (el) => el.strokeWidth || 2;
2101
2671
  var font4 = (el) => el.fontSize || 18;
2102
2672
  var seededRandom = (seed) => {
2103
2673
  const x = Math.sin(seed) * 1e4;
@@ -2108,36 +2678,40 @@ var strokeDashFromLineStyle = (el) => {
2108
2678
  if (el.lineStyle === "dotted") return "2 4";
2109
2679
  return void 0;
2110
2680
  };
2111
- registerRenderer("pv_axes", (el) => {
2681
+ registerRenderer("pv_axes", (el, ctx) => {
2112
2682
  const minX = Math.min(el.x1, el.x2);
2113
2683
  const minY = Math.min(el.y1, el.y2);
2114
2684
  const w = Math.max(1, Math.abs(el.x2 - el.x1));
2115
2685
  const h = Math.max(1, Math.abs(el.y2 - el.y1));
2116
2686
  const yLabel = String(el.yLabel ?? "P");
2117
2687
  const xLabel = String(el.xLabel ?? "V");
2118
- const color = c6(el);
2119
- const strokeWidth = sw6(el);
2688
+ const color = c5(el);
2689
+ const strokeWidth = sw5(el);
2120
2690
  const fontSize = font4(el);
2121
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${minX}, ${minY})`, children: [
2122
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: h, stroke: color, strokeWidth }),
2123
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polygon", { points: "0,0 -5,10 5,10", fill: color }),
2124
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: -15, y: 10, fontSize, fill: color, fontWeight: "bold", children: yLabel }),
2125
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: 0, y1: h, x2: w, y2: h, stroke: color, strokeWidth }),
2126
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polygon", { points: `${w},${h} ${w - 10},${h - 5} ${w - 10},${h + 5}`, fill: color }),
2127
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w - 10, y: h + 20, fontSize, fill: color, fontWeight: "bold", children: xLabel }),
2128
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: -10, y: h + 15, fontSize: 12, fill: color, children: "0" }),
2129
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: -10, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
2130
- ] });
2131
- });
2132
- registerRenderer("piston_cylinder", (el) => {
2691
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { children: [
2692
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${minX}, ${minY})`, children: [
2693
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: h, stroke: color, strokeWidth }),
2694
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polygon", { points: "0,0 -5,10 5,10", fill: color }),
2695
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: -15, y: 10, fontSize, fill: color, fontWeight: "bold", children: yLabel }),
2696
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: 0, y1: h, x2: w, y2: h, stroke: color, strokeWidth }),
2697
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polygon", { points: `${w},${h} ${w - 10},${h - 5} ${w - 10},${h + 5}`, fill: color }),
2698
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w - 10, y: h + 20, fontSize, fill: color, fontWeight: "bold", children: xLabel }),
2699
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: -10, y: h + 15, fontSize: 12, fill: color, children: "0" }),
2700
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: -10, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2701
+ ] }),
2702
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(BBoxInteractionOverlay, { minX, minY, width: w, height: h, ctx }),
2703
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChordInteractionOverlay, { el, ctx })
2704
+ ] });
2705
+ });
2706
+ registerRenderer("piston_cylinder", (el, ctx) => {
2133
2707
  const minX = Math.min(el.x1, el.x2);
2134
2708
  const minY = Math.min(el.y1, el.y2);
2135
2709
  const w = Math.max(1, Math.abs(el.x2 - el.x1));
2136
2710
  const h = Math.max(1, Math.abs(el.y2 - el.y1));
2137
2711
  const compression = el.compression ?? 0.5;
2138
2712
  const pistonY = h * compression;
2139
- const color = c6(el);
2140
- const strokeWidth = sw6(el);
2713
+ const color = c5(el);
2714
+ const strokeWidth = sw5(el);
2141
2715
  const particles = [];
2142
2716
  const numParticles = 40;
2143
2717
  for (let i = 0; i < numParticles; i++) {
@@ -2145,136 +2719,144 @@ registerRenderer("piston_cylinder", (el) => {
2145
2719
  const py = pistonY + 10 + seededRandom(Number(el.id) + i * 100) * (h - pistonY - 15);
2146
2720
  particles.push(/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: px, cy: py, r: "2", fill: "#94a3b8" }, i));
2147
2721
  }
2148
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${minX}, ${minY})`, children: [
2149
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2150
- "polyline",
2151
- {
2152
- points: `0,0 0,${h} ${w},${h} ${w},0`,
2153
- fill: "#f8fafc",
2154
- stroke: color,
2155
- strokeWidth,
2156
- strokeLinecap: "square",
2157
- strokeLinejoin: "miter"
2158
- }
2159
- ),
2160
- particles,
2161
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(0, ${pistonY})`, children: [
2162
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("rect", { x: "2", y: "0", width: w - 4, height: "12", fill: "#64748b", rx: "2" }),
2163
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: w / 2, y1: "0", x2: w / 2, y2: "-40", stroke: "#64748b", strokeWidth: "4" }),
2164
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: w / 2 - 15, y1: "-40", x2: w / 2 + 15, y2: "-40", stroke: "#64748b", strokeWidth: "6", strokeLinecap: "round" }),
2165
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: w / 2, y1: "-50", x2: w / 2, y2: "-42", stroke: "#ef4444", strokeWidth: "2" }),
2166
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polygon", { points: `${w / 2},-42 ${w / 2 - 4},-46 ${w / 2 + 4},-46`, fill: "#ef4444" })
2722
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { children: [
2723
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${minX}, ${minY})`, children: [
2724
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2725
+ "polyline",
2726
+ {
2727
+ points: `0,0 0,${h} ${w},${h} ${w},0`,
2728
+ fill: "#f8fafc",
2729
+ stroke: color,
2730
+ strokeWidth,
2731
+ strokeLinecap: "square",
2732
+ strokeLinejoin: "miter",
2733
+ pointerEvents: "none"
2734
+ }
2735
+ ),
2736
+ particles,
2737
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(0, ${pistonY})`, children: [
2738
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("rect", { x: "2", y: "0", width: w - 4, height: "12", fill: "#64748b", rx: "2" }),
2739
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: w / 2, y1: "0", x2: w / 2, y2: "-40", stroke: "#64748b", strokeWidth: "4" }),
2740
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: w / 2 - 15, y1: "-40", x2: w / 2 + 15, y2: "-40", stroke: "#64748b", strokeWidth: "6", strokeLinecap: "round" }),
2741
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: w / 2, y1: "-50", x2: w / 2, y2: "-42", stroke: "#ef4444", strokeWidth: "2" }),
2742
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polygon", { points: `${w / 2},-42 ${w / 2 - 4},-46 ${w / 2 + 4},-46`, fill: "#ef4444" })
2743
+ ] }),
2744
+ el.showHeat ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${w / 2}, ${h + 15})`, children: [
2745
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M-10,0 Q-15,-10 -5,-20 Q0,-10 10,-15 Q5,-5 10,0 Z", fill: "#ef4444", opacity: "0.8" }),
2746
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M-5,0 Q-8,-6 0,-12 Q2,-5 5,0 Z", fill: "#f59e0b" }),
2747
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: "25", y: "-5", fontSize: "14", fill: "#ef4444", fontWeight: "bold", children: "Q" })
2748
+ ] }) : null,
2749
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: h + (el.showHeat ? 35 : 20), textAnchor: "middle", fill: color, fontSize: font4(el), pointerEvents: "none", children: String(el.label) }) : null
2167
2750
  ] }),
2168
- el.showHeat ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${w / 2}, ${h + 15})`, children: [
2169
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M-10,0 Q-15,-10 -5,-20 Q0,-10 10,-15 Q5,-5 10,0 Z", fill: "#ef4444", opacity: "0.8" }),
2170
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M-5,0 Q-8,-6 0,-12 Q2,-5 5,0 Z", fill: "#f59e0b" }),
2171
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: "25", y: "-5", fontSize: "14", fill: "#ef4444", fontWeight: "bold", children: "Q" })
2172
- ] }) : null,
2173
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: h + (el.showHeat ? 35 : 20), textAnchor: "middle", fill: color, fontSize: font4(el), children: String(el.label) }) : null
2751
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
2752
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChordInteractionOverlay, { el, ctx })
2174
2753
  ] });
2175
2754
  });
2176
- registerRenderer("heat_engine", (el) => {
2755
+ registerRenderer("heat_engine", (el, ctx) => {
2177
2756
  const minX = Math.min(el.x1, el.x2);
2178
2757
  const minY = Math.min(el.y1, el.y2);
2179
2758
  const w = Math.max(1, Math.abs(el.x2 - el.x1));
2180
2759
  const h = Math.max(1, Math.abs(el.y2 - el.y1));
2181
2760
  const isEngine = el.engineType !== "refrigerator";
2182
2761
  const r = Math.min(w * 0.3, h * 0.2);
2183
- const color = c6(el);
2184
- const strokeWidth = sw6(el);
2762
+ const color = c5(el);
2763
+ const strokeWidth = sw5(el);
2185
2764
  const fontSize = font4(el);
2186
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${minX}, ${minY})`, children: [
2187
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("rect", { x: 0, y: 0, width: w, height: h * 0.2, fill: "#ef4444", rx: "4" }),
2188
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: h * 0.1, fontSize, fill: "#fff", textAnchor: "middle", dominantBaseline: "central", children: "T_H" }),
2189
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("rect", { x: 0, y: h * 0.8, width: w, height: h * 0.2, fill: "#3b82f6", rx: "4" }),
2190
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: h * 0.9, fontSize, fill: "#fff", textAnchor: "middle", dominantBaseline: "central", children: "T_C" }),
2191
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: w / 2, cy: h / 2, r, fill: "#fff", stroke: color, strokeWidth }),
2192
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2193
- "text",
2194
- {
2195
- x: w / 2,
2196
- y: h / 2,
2197
- fontSize: fontSize * 1.2,
2198
- fill: color,
2199
- textAnchor: "middle",
2200
- dominantBaseline: "central",
2201
- fontWeight: "bold",
2202
- children: isEngine ? "E" : "R"
2203
- }
2204
- ),
2205
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { strokeWidth: 2, stroke: color, children: [
2206
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: w / 2, y1: h * 0.2, x2: w / 2, y2: h * 0.5 - r - 5 }),
2207
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2208
- "polygon",
2209
- {
2210
- points: isEngine ? `${w / 2},${h * 0.5 - r - 2} ${w / 2 - 5},${h * 0.5 - r - 10} ${w / 2 + 5},${h * 0.5 - r - 10}` : `${w / 2},${h * 0.2 + 2} ${w / 2 - 5},${h * 0.2 + 10} ${w / 2 + 5},${h * 0.2 + 10}`,
2211
- fill: color
2212
- }
2213
- ),
2214
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2 + 10, y: h * 0.35, fontSize: 14, fill: color, dominantBaseline: "central", children: "Q_H" }),
2215
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: w / 2, y1: h * 0.5 + r + 5, x2: w / 2, y2: h * 0.8 }),
2216
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2217
- "polygon",
2218
- {
2219
- points: isEngine ? `${w / 2},${h * 0.8 - 2} ${w / 2 - 5},${h * 0.8 - 10} ${w / 2 + 5},${h * 0.8 - 10}` : `${w / 2},${h * 0.5 + r + 2} ${w / 2 - 5},${h * 0.5 + r + 10} ${w / 2 + 5},${h * 0.5 + r + 10}`,
2220
- fill: color
2221
- }
2222
- ),
2223
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2 + 10, y: h * 0.65, fontSize: 14, fill: color, dominantBaseline: "central", children: "Q_C" }),
2224
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2225
- "line",
2226
- {
2227
- x1: isEngine ? w / 2 + r + 5 : w,
2228
- y1: h / 2,
2229
- x2: isEngine ? w : w / 2 + r + 5,
2230
- y2: h / 2
2231
- }
2232
- ),
2765
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { children: [
2766
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${minX}, ${minY})`, children: [
2767
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("rect", { x: 0, y: 0, width: w, height: h * 0.2, fill: "#ef4444", rx: "4", pointerEvents: "none" }),
2768
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: h * 0.1, fontSize, fill: "#fff", textAnchor: "middle", dominantBaseline: "central", children: "T_H" }),
2769
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("rect", { x: 0, y: h * 0.8, width: w, height: h * 0.2, fill: "#3b82f6", rx: "4" }),
2770
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: h * 0.9, fontSize, fill: "#fff", textAnchor: "middle", dominantBaseline: "central", children: "T_C" }),
2771
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: w / 2, cy: h / 2, r, fill: "#fff", stroke: color, strokeWidth }),
2233
2772
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2234
- "polygon",
2773
+ "text",
2235
2774
  {
2236
- points: isEngine ? `${w},${h / 2} ${w - 8},${h / 2 - 5} ${w - 8},${h / 2 + 5}` : `${w / 2 + r + 5},${h / 2} ${w / 2 + r + 13},${h / 2 - 5} ${w / 2 + r + 13},${h / 2 + 5}`,
2237
- fill: color
2775
+ x: w / 2,
2776
+ y: h / 2,
2777
+ fontSize: fontSize * 1.2,
2778
+ fill: color,
2779
+ textAnchor: "middle",
2780
+ dominantBaseline: "central",
2781
+ fontWeight: "bold",
2782
+ children: isEngine ? "E" : "R"
2238
2783
  }
2239
2784
  ),
2240
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w * 0.8, y: h / 2 - 15, fontSize: 14, fill: color, textAnchor: "middle", children: "W" })
2785
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { strokeWidth: 2, stroke: color, children: [
2786
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: w / 2, y1: h * 0.2, x2: w / 2, y2: h * 0.5 - r - 5 }),
2787
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2788
+ "polygon",
2789
+ {
2790
+ points: isEngine ? `${w / 2},${h * 0.5 - r - 2} ${w / 2 - 5},${h * 0.5 - r - 10} ${w / 2 + 5},${h * 0.5 - r - 10}` : `${w / 2},${h * 0.2 + 2} ${w / 2 - 5},${h * 0.2 + 10} ${w / 2 + 5},${h * 0.2 + 10}`,
2791
+ fill: color
2792
+ }
2793
+ ),
2794
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2 + 10, y: h * 0.35, fontSize: 14, fill: color, dominantBaseline: "central", children: "Q_H" }),
2795
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: w / 2, y1: h * 0.5 + r + 5, x2: w / 2, y2: h * 0.8 }),
2796
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2797
+ "polygon",
2798
+ {
2799
+ points: isEngine ? `${w / 2},${h * 0.8 - 2} ${w / 2 - 5},${h * 0.8 - 10} ${w / 2 + 5},${h * 0.8 - 10}` : `${w / 2},${h * 0.5 + r + 2} ${w / 2 - 5},${h * 0.5 + r + 10} ${w / 2 + 5},${h * 0.5 + r + 10}`,
2800
+ fill: color
2801
+ }
2802
+ ),
2803
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2 + 10, y: h * 0.65, fontSize: 14, fill: color, dominantBaseline: "central", children: "Q_C" }),
2804
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2805
+ "line",
2806
+ {
2807
+ x1: isEngine ? w / 2 + r + 5 : w,
2808
+ y1: h / 2,
2809
+ x2: isEngine ? w : w / 2 + r + 5,
2810
+ y2: h / 2
2811
+ }
2812
+ ),
2813
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2814
+ "polygon",
2815
+ {
2816
+ points: isEngine ? `${w},${h / 2} ${w - 8},${h / 2 - 5} ${w - 8},${h / 2 + 5}` : `${w / 2 + r + 5},${h / 2} ${w / 2 + r + 13},${h / 2 - 5} ${w / 2 + r + 13},${h / 2 + 5}`,
2817
+ fill: color
2818
+ }
2819
+ ),
2820
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w * 0.8, y: h / 2 - 15, fontSize: 14, fill: color, textAnchor: "middle", children: "W" })
2821
+ ] }),
2822
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: h + 20, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2241
2823
  ] }),
2242
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: h + 20, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
2824
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
2825
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChordInteractionOverlay, { el, ctx })
2243
2826
  ] });
2244
2827
  });
2245
- registerRenderer("conduction_rod", (el) => {
2828
+ registerRenderer("conduction_rod", (el, ctx) => {
2246
2829
  const dist = distance(el.x1, el.y1, el.x2, el.y2);
2247
2830
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
2248
2831
  const gradId = `grad-rod-${el.id}`;
2249
- const color = c6(el);
2250
- const strokeWidth = sw6(el);
2832
+ const color = c5(el);
2833
+ const strokeWidth = sw5(el);
2251
2834
  const fontSize = font4(el);
2252
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2253
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("linearGradient", { id: gradId, x1: "0%", y1: "0%", x2: "100%", y2: "0%", children: [
2254
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("stop", { offset: "0%", stopColor: "#ef4444" }),
2255
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("stop", { offset: "100%", stopColor: "#3b82f6" })
2256
- ] }) }),
2257
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("rect", { x: 0, y: -20, width: dist, height: 40, fill: `url(#${gradId})`, stroke: color, strokeWidth, rx: "4" }),
2258
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { stroke: "#fff", strokeWidth: "2", opacity: 0.6, children: [
2259
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: dist * 0.2, y1: 0, x2: dist * 0.4, y2: 0 }),
2260
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polygon", { points: `${dist * 0.4},0 ${dist * 0.4 - 6},-4 ${dist * 0.4 - 6},4`, fill: "#fff" }),
2261
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: dist * 0.6, y1: 0, x2: dist * 0.8, y2: 0 }),
2262
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polygon", { points: `${dist * 0.8},0 ${dist * 0.8 - 6},-4 ${dist * 0.8 - 6},4`, fill: "#fff" })
2835
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { children: [
2836
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2837
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("linearGradient", { id: gradId, x1: "0%", y1: "0%", x2: "100%", y2: "0%", children: [
2838
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("stop", { offset: "0%", stopColor: "#ef4444" }),
2839
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("stop", { offset: "100%", stopColor: "#3b82f6" })
2840
+ ] }) }),
2841
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("rect", { x: 0, y: -20, width: dist, height: 40, fill: `url(#${gradId})`, stroke: color, strokeWidth, rx: "4", pointerEvents: "none" }),
2842
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { stroke: "#fff", strokeWidth: "2", opacity: 0.6, children: [
2843
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: dist * 0.2, y1: 0, x2: dist * 0.4, y2: 0 }),
2844
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polygon", { points: `${dist * 0.4},0 ${dist * 0.4 - 6},-4 ${dist * 0.4 - 6},4`, fill: "#fff" }),
2845
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: dist * 0.6, y1: 0, x2: dist * 0.8, y2: 0 }),
2846
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polygon", { points: `${dist * 0.8},0 ${dist * 0.8 - 6},-4 ${dist * 0.8 - 6},4`, fill: "#fff" })
2847
+ ] }),
2848
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: 0, y: -30, fontSize, fill: "#ef4444", textAnchor: "middle", fontWeight: "bold", children: "T_H" }),
2849
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: dist, y: -30, fontSize, fill: "#3b82f6", textAnchor: "middle", fontWeight: "bold", children: "T_C" }),
2850
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: dist / 2, y: 35, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2263
2851
  ] }),
2264
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: 0, y: -30, fontSize, fill: "#ef4444", textAnchor: "middle", fontWeight: "bold", children: "T_H" }),
2265
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: dist, y: -30, fontSize, fill: "#3b82f6", textAnchor: "middle", fontWeight: "bold", children: "T_C" }),
2266
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: dist / 2, y: 35, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
2852
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChordInteractionOverlay, { el, ctx, padY: 48 })
2267
2853
  ] });
2268
2854
  });
2269
- registerRenderer("thermo_process", (el) => {
2855
+ registerRenderer("thermo_process", (el, ctx) => {
2270
2856
  const pType = el.processType || "isotherm";
2271
2857
  const dx = el.x2 - el.x1;
2272
2858
  const dy = el.y2 - el.y1;
2273
- let pathD = "";
2274
- if (pType === "isobaric") pathD = `M 0 0 L ${dx} 0`;
2275
- else if (pType === "isochoric") pathD = `M 0 0 L 0 ${dy}`;
2276
- else if (pType === "adiabatic") pathD = `M 0 0 Q ${dx * 0.1} ${dy * 0.9} ${dx} ${dy}`;
2277
- else pathD = `M 0 0 Q ${dx * 0.2} ${dy * 0.8} ${dx} ${dy}`;
2859
+ const pathD = pType === "isobaric" ? `M 0 0 L ${dx} 0` : pType === "isochoric" ? `M 0 0 L 0 ${dy}` : pType === "adiabatic" ? `M 0 0 Q ${dx * 0.1} ${dy * 0.9} ${dx} ${dy}` : `M 0 0 Q ${dx * 0.2} ${dy * 0.8} ${dx} ${dy}`;
2278
2860
  let midX = dx / 2;
2279
2861
  let midY = dy / 2;
2280
2862
  if (pType === "isotherm" || pType === "adiabatic") {
@@ -2285,16 +2867,20 @@ registerRenderer("thermo_process", (el) => {
2285
2867
  }
2286
2868
  const arrowAngle = Math.atan2(dy, dx) * 180 / Math.PI;
2287
2869
  const dash = strokeDashFromLineStyle(el);
2288
- const color = c6(el);
2289
- const strokeWidth = sw6(el);
2870
+ const color = c5(el);
2871
+ const strokeWidth = sw5(el);
2290
2872
  const fontSize = font4(el);
2291
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
2292
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: pathD, fill: "none", stroke: color, strokeWidth, strokeDasharray: dash }),
2293
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("g", { transform: `translate(${midX}, ${midY}) rotate(${arrowAngle})`, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polygon", { points: "0,0 -8,-5 -8,5", fill: color }) }),
2294
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: midX + 15, y: midY - 10, fontSize, fill: color, children: String(el.label) }) : null
2873
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { children: [
2874
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
2875
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LocalFatPathHit, { dPath: pathD, strokeWidth: 22, strokeLinejoin: "round", strokeLinecap: "round" }),
2876
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: pathD, fill: "none", stroke: color, strokeWidth, strokeDasharray: dash, pointerEvents: "none" }),
2877
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("g", { transform: `translate(${midX}, ${midY}) rotate(${arrowAngle})`, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polygon", { points: "0,0 -8,-5 -8,5", fill: color, pointerEvents: "none" }) }),
2878
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: midX + 15, y: midY - 10, fontSize, fill: color, pointerEvents: "none", children: String(el.label) }) : null
2879
+ ] }),
2880
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChordInteractionOverlay, { el, ctx })
2295
2881
  ] });
2296
2882
  });
2297
- registerRenderer("carnot_cycle", (el) => {
2883
+ registerRenderer("carnot_cycle", (el, ctx) => {
2298
2884
  const minX = Math.min(el.x1, el.x2);
2299
2885
  const minY = Math.min(el.y1, el.y2);
2300
2886
  const w = Math.max(1, Math.abs(el.x2 - el.x1));
@@ -2307,35 +2893,39 @@ registerRenderer("carnot_cycle", (el) => {
2307
2893
  const ad1 = `Q ${w * 0.7} ${h * 0.6} ${p3.x} ${p3.y}`;
2308
2894
  const iso2 = `Q ${w * 0.6} ${h * 0.85} ${p4.x} ${p4.y}`;
2309
2895
  const ad2 = `Q ${w * 0.2} ${h * 0.4} ${p1.x} ${p1.y}`;
2310
- const color = c6(el);
2311
- const strokeWidth = sw6(el);
2896
+ const color = c5(el);
2897
+ const strokeWidth = sw5(el);
2312
2898
  const fontSize = font4(el);
2313
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${minX}, ${minY})`, children: [
2314
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: `${iso1} ${ad1} ${iso2} ${ad2} Z`, fill: color, fillOpacity: 0.1 }),
2315
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: iso1, fill: "none", stroke: "#ef4444", strokeWidth }),
2316
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: `M ${p2.x} ${p2.y} ${ad1}`, fill: "none", stroke: "#64748b", strokeWidth, strokeDasharray: "4 4" }),
2317
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: `M ${p3.x} ${p3.y} ${iso2}`, fill: "none", stroke: "#3b82f6", strokeWidth }),
2318
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: `M ${p4.x} ${p4.y} ${ad2}`, fill: "none", stroke: "#64748b", strokeWidth, strokeDasharray: "4 4" }),
2319
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: p1.x, cy: p1.y, r: "3", fill: "#1e293b" }),
2320
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: p2.x, cy: p2.y, r: "3", fill: "#1e293b" }),
2321
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: p3.x, cy: p3.y, r: "3", fill: "#1e293b" }),
2322
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: p4.x, cy: p4.y, r: "3", fill: "#1e293b" }),
2323
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w * 0.4, y: h * 0.15, fontSize: "14", fill: "#ef4444", fontWeight: "bold", children: "T_H" }),
2324
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w * 0.6, y: h * 0.95, fontSize: "14", fill: "#3b82f6", fontWeight: "bold", children: "T_C" }),
2325
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: h / 2 + 5, fontSize: 16, fill: color, textAnchor: "middle", fontWeight: "bold", children: "W" }),
2326
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: -15, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
2327
- ] });
2328
- });
2329
- registerRenderer("maxwell_boltzmann", (el) => {
2899
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { children: [
2900
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${minX}, ${minY})`, children: [
2901
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: `${iso1} ${ad1} ${iso2} ${ad2} Z`, fill: color, fillOpacity: 0.1, pointerEvents: "none" }),
2902
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: iso1, fill: "none", stroke: "#ef4444", strokeWidth, pointerEvents: "none" }),
2903
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: `M ${p2.x} ${p2.y} ${ad1}`, fill: "none", stroke: "#64748b", strokeWidth, strokeDasharray: "4 4", pointerEvents: "none" }),
2904
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: `M ${p3.x} ${p3.y} ${iso2}`, fill: "none", stroke: "#3b82f6", strokeWidth, pointerEvents: "none" }),
2905
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: `M ${p4.x} ${p4.y} ${ad2}`, fill: "none", stroke: "#64748b", strokeWidth, strokeDasharray: "4 4", pointerEvents: "none" }),
2906
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: p1.x, cy: p1.y, r: "3", fill: "#1e293b", pointerEvents: "none" }),
2907
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: p2.x, cy: p2.y, r: "3", fill: "#1e293b", pointerEvents: "none" }),
2908
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: p3.x, cy: p3.y, r: "3", fill: "#1e293b", pointerEvents: "none" }),
2909
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: p4.x, cy: p4.y, r: "3", fill: "#1e293b", pointerEvents: "none" }),
2910
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w * 0.4, y: h * 0.15, fontSize: "14", fill: "#ef4444", fontWeight: "bold", children: "T_H" }),
2911
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w * 0.6, y: h * 0.95, fontSize: "14", fill: "#3b82f6", fontWeight: "bold", children: "T_C" }),
2912
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: h / 2 + 5, fontSize: 16, fill: color, textAnchor: "middle", fontWeight: "bold", children: "W" }),
2913
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: -15, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2914
+ ] }),
2915
+ bboxInsetSelectionChrome(minX, minY, w, h, ctx),
2916
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChordInteractionOverlay, { el, ctx })
2917
+ ] });
2918
+ });
2919
+ registerRenderer("maxwell_boltzmann", (el, ctx) => {
2330
2920
  const minX = Math.min(el.x1, el.x2);
2331
2921
  const minY = Math.min(el.y1, el.y2);
2332
2922
  const w = Math.max(1, Math.abs(el.x2 - el.x1));
2333
2923
  const h = Math.max(1, Math.abs(el.y2 - el.y1));
2334
2924
  const t1 = el.t1 || 300;
2335
2925
  const t2 = el.t2 || 600;
2336
- const strokeWidth = sw6(el);
2926
+ const strokeWidth = sw5(el);
2337
2927
  const fontSize = font4(el);
2338
- const color = c6(el);
2928
+ const color = c5(el);
2339
2929
  const buildCurve = (temp, peakHeight, strokeCol, dash) => {
2340
2930
  let d = `M 0 ${h}`;
2341
2931
  const k = 1 / temp;
@@ -2348,66 +2938,75 @@ registerRenderer("maxwell_boltzmann", (el) => {
2348
2938
  const y = h - rawVal / maxRawVal * peakHeight;
2349
2939
  d += ` L ${x} ${y}`;
2350
2940
  }
2351
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d, fill: "none", stroke: strokeCol, strokeWidth, strokeDasharray: dash, strokeLinejoin: "round" });
2941
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d, fill: "none", stroke: strokeCol, strokeWidth, strokeDasharray: dash, strokeLinejoin: "round", pointerEvents: "none" });
2352
2942
  };
2353
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${minX}, ${minY})`, children: [
2354
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: h, stroke: "#1e293b", strokeWidth: "2" }),
2355
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: 0, y1: h, x2: w, y2: h, stroke: "#1e293b", strokeWidth: "2" }),
2356
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: -25, y: h / 2, fontSize: 14, fill: "#1e293b", transform: `rotate(-90, -25, ${h / 2})`, textAnchor: "middle", children: "N(v)" }),
2357
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: h + 20, fontSize: 14, fill: "#1e293b", textAnchor: "middle", children: "Speed (v)" }),
2358
- buildCurve(t1, h * 0.8, "#ef4444"),
2359
- buildCurve(t2, h * 0.5, "#3b82f6", "4 4"),
2360
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("rect", { x: w - 70, y: 10, width: 10, height: 10, fill: "#ef4444" }),
2361
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("text", { x: w - 55, y: 20, fontSize: 12, fill: "#1e293b", children: [
2362
- "T\u2081 = ",
2363
- t1,
2364
- "K"
2943
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { children: [
2944
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${minX}, ${minY})`, children: [
2945
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: 0, y1: 0, x2: 0, y2: h, stroke: "#1e293b", strokeWidth: "2" }),
2946
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: 0, y1: h, x2: w, y2: h, stroke: "#1e293b", strokeWidth: "2" }),
2947
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: -25, y: h / 2, fontSize: 14, fill: "#1e293b", transform: `rotate(-90, -25, ${h / 2})`, textAnchor: "middle", children: "N(v)" }),
2948
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: h + 20, fontSize: 14, fill: "#1e293b", textAnchor: "middle", children: "Speed (v)" }),
2949
+ buildCurve(t1, h * 0.8, "#ef4444"),
2950
+ buildCurve(t2, h * 0.5, "#3b82f6", "4 4"),
2951
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("rect", { x: w - 70, y: 10, width: 10, height: 10, fill: "#ef4444" }),
2952
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("text", { x: w - 55, y: 20, fontSize: 12, fill: "#1e293b", children: [
2953
+ "T\u2081 = ",
2954
+ t1,
2955
+ "K"
2956
+ ] }),
2957
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("rect", { x: w - 70, y: 30, width: 10, height: 10, fill: "#3b82f6" }),
2958
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("text", { x: w - 55, y: 40, fontSize: 12, fill: "#1e293b", children: [
2959
+ "T\u2082 = ",
2960
+ t2,
2961
+ "K"
2962
+ ] }),
2963
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: -15, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2365
2964
  ] }),
2366
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("rect", { x: w - 70, y: 30, width: 10, height: 10, fill: "#3b82f6" }),
2367
- /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("text", { x: w - 55, y: 40, fontSize: 12, fill: "#1e293b", children: [
2368
- "T\u2082 = ",
2369
- t2,
2370
- "K"
2371
- ] }),
2372
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: w / 2, y: -15, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
2965
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(BBoxInteractionOverlay, { minX, minY, width: w, height: h, ctx }),
2966
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChordInteractionOverlay, { el, ctx })
2373
2967
  ] });
2374
2968
  });
2375
- registerRenderer("diatomic_gas", (el) => {
2376
- const color = c6(el);
2377
- const strokeWidth = sw6(el);
2969
+ registerRenderer("diatomic_gas", (el, ctx) => {
2970
+ const color = c5(el);
2378
2971
  const fontSize = font4(el);
2379
2972
  const showRot = el.showRotations !== false;
2380
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
2381
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: -15, cy: 0, r: 8, fill: color, stroke: "#fff", strokeWidth: 2 }),
2382
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: 15, cy: 0, r: 8, fill: color, stroke: "#fff", strokeWidth: 2 }),
2383
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: -10, y1: 0, x2: 10, y2: 0, stroke: color, strokeWidth: 4 }),
2384
- showRot ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { stroke: "#94a3b8", strokeWidth: 1.5, fill: "none", children: [
2973
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(OriginHitPlate, { ctx, hitHalfSize: 38, selectionHalfSize: 46, children: [
2974
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: -15, cy: 0, r: 8, fill: color, stroke: "#fff", strokeWidth: 2, pointerEvents: "none" }),
2975
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: 15, cy: 0, r: 8, fill: color, stroke: "#fff", strokeWidth: 2, pointerEvents: "none" }),
2976
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: -10, y1: 0, x2: 10, y2: 0, stroke: color, strokeWidth: 4, pointerEvents: "none" }),
2977
+ showRot ? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { stroke: "#94a3b8", strokeWidth: 1.5, fill: "none", pointerEvents: "none", children: [
2385
2978
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M -10 -15 A 15 15 0 0 1 10 -15", strokeDasharray: "2 2" }),
2386
2979
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polygon", { points: "10,-15 6,-18 6,-12", fill: "#94a3b8" }),
2387
2980
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M -25 0 A 25 10 0 0 1 0 10 A 25 10 0 0 1 25 0", strokeDasharray: "2 2" })
2388
2981
  ] }) : null,
2389
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: 0, y: 25, fontSize, fill: color, textAnchor: "middle", children: String(el.label) }) : null
2390
- ] });
2982
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: 0, y: 25, fontSize, fill: color, textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
2983
+ ] }) });
2391
2984
  });
2392
- registerRenderer("random_walk", (el) => {
2985
+ registerRenderer("random_walk", (el, ctx) => {
2393
2986
  const steps = el.steps || 5;
2394
2987
  const dist = Math.hypot(el.x2 - el.x1, el.y2 - el.y1);
2395
2988
  let d = "M 0 0";
2396
2989
  let curX = 0;
2397
2990
  let curY = 0;
2398
- const points = [/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: "0", cy: "0", r: "4", fill: "#22c55e" }, "start")];
2991
+ const points = [/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: "0", cy: "0", r: "4", fill: "#22c55e", pointerEvents: "none" }, "start")];
2399
2992
  for (let i = 1; i <= steps; i++) {
2400
2993
  const angle = seededRandom(Number(el.id) + i * 7) * Math.PI * 2;
2401
2994
  const stepDist = dist / steps * (0.5 + seededRandom(Number(el.id) + i * 13));
2402
2995
  curX += Math.cos(angle) * stepDist;
2403
2996
  curY += Math.sin(angle) * stepDist;
2404
2997
  d += ` L ${curX} ${curY}`;
2405
- points.push(/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: curX, cy: curY, r: i === steps ? 4 : 2, fill: i === steps ? "#ef4444" : c6(el) }, i));
2998
+ points.push(
2999
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("circle", { cx: curX, cy: curY, r: i === steps ? 4 : 2, fill: i === steps ? "#ef4444" : c5(el), pointerEvents: "none" }, i)
3000
+ );
2406
3001
  }
2407
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
2408
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d, fill: "none", stroke: c6(el), strokeWidth: sw6(el), strokeLinejoin: "round" }),
2409
- points,
2410
- el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: dist / 2, y: dist / 2, fontSize: font4(el), fill: c6(el), textAnchor: "middle", children: String(el.label) }) : null
3002
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { children: [
3003
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
3004
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(LocalFatPathHit, { dPath: d, strokeWidth: 20, strokeLinejoin: "round" }),
3005
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d, fill: "none", stroke: c5(el), strokeWidth: sw5(el), strokeLinejoin: "round", pointerEvents: "none" }),
3006
+ points,
3007
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("text", { x: dist / 2, y: dist / 2, fontSize: font4(el), fill: c5(el), textAnchor: "middle", pointerEvents: "none", children: String(el.label) }) : null
3008
+ ] }),
3009
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ChordInteractionOverlay, { el, ctx, padX: Math.max(40, dist), padY: Math.max(40, dist) })
2411
3010
  ] });
2412
3011
  });
2413
3012
 
@@ -2426,7 +3025,7 @@ var ringPolygonPoints = (sides, radius, startDeg) => Array.from({ length: sides
2426
3025
  const angle = (i * 360 / sides + startDeg) * (Math.PI / 180);
2427
3026
  return `${radius * Math.cos(angle)},${radius * Math.sin(angle)}`;
2428
3027
  }).join(" ");
2429
- registerRenderer("atom", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
3028
+ registerRenderer("atom", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(OriginHitPlate, { ctx, hitHalfSize: 22, selectionHalfSize: 30, children: [
2430
3029
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("rect", { x: "-20", y: "-15", width: "40", height: "30", fill: "white", rx: "6" }),
2431
3030
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2432
3031
  "text",
@@ -2442,17 +3041,18 @@ registerRenderer("atom", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.j
2442
3041
  children: String(el.label || "C")
2443
3042
  }
2444
3043
  )
2445
- ] }));
3044
+ ] }) }));
2446
3045
  registerRenderer("charge_plus", (el, ctx) => {
2447
3046
  const stroke2 = colorOf3(el, ctx.isSelected);
2448
- const sw7 = strokeOf3(el, ctx.isSelected);
3047
+ const sw6 = strokeOf3(el, ctx.isSelected);
2449
3048
  const baseSw = el.strokeWidth || 2;
2450
3049
  const r = Math.max(12, baseSw * 4);
2451
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
2452
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("circle", { cx: "0", cy: "0", r, fill: "white", stroke: stroke2, strokeWidth: sw7 }),
2453
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: -r / 2, y1: "0", x2: r / 2, y2: "0", stroke: stroke2, strokeWidth: sw7 }),
2454
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: -r / 2, x2: "0", y2: r / 2, stroke: stroke2, strokeWidth: sw7 }),
2455
- el.label && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3050
+ const plate = Math.max(28, r + (el.label ? 26 : 8));
3051
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(OriginHitPlate, { ctx, hitHalfSize: plate, selectionHalfSize: plate + 10, children: [
3052
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("circle", { cx: "0", cy: "0", r, fill: "white", stroke: stroke2, strokeWidth: sw6 }),
3053
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: -r / 2, y1: "0", x2: r / 2, y2: "0", stroke: stroke2, strokeWidth: sw6 }),
3054
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: -r / 2, x2: "0", y2: r / 2, stroke: stroke2, strokeWidth: sw6 }),
3055
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2456
3056
  "text",
2457
3057
  {
2458
3058
  x: "0",
@@ -2464,18 +3064,19 @@ registerRenderer("charge_plus", (el, ctx) => {
2464
3064
  textAnchor: "middle",
2465
3065
  children: String(el.label)
2466
3066
  }
2467
- )
2468
- ] });
3067
+ ) : null
3068
+ ] }) });
2469
3069
  });
2470
3070
  registerRenderer("charge_minus", (el, ctx) => {
2471
3071
  const stroke2 = colorOf3(el, ctx.isSelected);
2472
- const sw7 = strokeOf3(el, ctx.isSelected);
3072
+ const sw6 = strokeOf3(el, ctx.isSelected);
2473
3073
  const baseSw = el.strokeWidth || 2;
2474
3074
  const r = Math.max(12, baseSw * 4);
2475
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
2476
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("circle", { cx: "0", cy: "0", r, fill: "white", stroke: stroke2, strokeWidth: sw7 }),
2477
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: -r / 2, y1: "0", x2: r / 2, y2: "0", stroke: stroke2, strokeWidth: sw7 }),
2478
- el.label && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3075
+ const plate = Math.max(28, r + (el.label ? 26 : 8));
3076
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(OriginHitPlate, { ctx, hitHalfSize: plate, selectionHalfSize: plate + 10, children: [
3077
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("circle", { cx: "0", cy: "0", r, fill: "white", stroke: stroke2, strokeWidth: sw6 }),
3078
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: -r / 2, y1: "0", x2: r / 2, y2: "0", stroke: stroke2, strokeWidth: sw6 }),
3079
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2479
3080
  "text",
2480
3081
  {
2481
3082
  x: "0",
@@ -2487,24 +3088,24 @@ registerRenderer("charge_minus", (el, ctx) => {
2487
3088
  textAnchor: "middle",
2488
3089
  children: String(el.label)
2489
3090
  }
2490
- )
2491
- ] });
3091
+ ) : null
3092
+ ] }) });
2492
3093
  });
2493
- registerRenderer("radical", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("circle", { cx: el.x1, cy: el.y1, r: "3.5", fill: colorOf3(el, ctx.isSelected) }));
2494
- registerRenderer("lone_pair", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
3094
+ registerRenderer("radical", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(OriginHitPlate, { ctx, hitHalfSize: 14, selectionHalfSize: 22, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("circle", { cx: "0", cy: "0", r: "3.5", fill: colorOf3(el, ctx.isSelected) }) }) }));
3095
+ registerRenderer("lone_pair", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(OriginHitPlate, { ctx, hitHalfSize: 16, selectionHalfSize: 24, children: [
2495
3096
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("circle", { cx: "-4", cy: "0", r: "2.5", fill: colorOf3(el, ctx.isSelected) }),
2496
3097
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("circle", { cx: "4", cy: "0", r: "2.5", fill: colorOf3(el, ctx.isSelected) })
2497
- ] }));
3098
+ ] }) }));
2498
3099
  registerRenderer("benzene", (el, ctx) => {
2499
3100
  const r = 30;
2500
3101
  const stroke2 = colorOf3(el, ctx.isSelected);
2501
- const sw7 = strokeOf3(el, ctx.isSelected);
2502
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: [
2503
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("polygon", { points: ringPolygonPoints(6, r, -30), fill: "white", stroke: stroke2, strokeWidth: sw7, strokeLinejoin: "round" }),
2504
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("circle", { cx: "0", cy: "0", r: r - 10, fill: "none", stroke: stroke2, strokeWidth: sw7 })
2505
- ] });
3102
+ const sw6 = strokeOf3(el, ctx.isSelected);
3103
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(OriginHitPlate, { ctx, hitHalfSize: 36, selectionHalfSize: 44, children: [
3104
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("polygon", { points: ringPolygonPoints(6, r, -30), fill: "white", stroke: stroke2, strokeWidth: sw6, strokeLinejoin: "round" }),
3105
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("circle", { cx: "0", cy: "0", r: r - 10, fill: "none", stroke: stroke2, strokeWidth: sw6 })
3106
+ ] }) });
2506
3107
  });
2507
- registerRenderer("cyclohexane", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3108
+ registerRenderer("cyclohexane", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(OriginHitPlate, { ctx, hitHalfSize: 36, selectionHalfSize: 44, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2508
3109
  "polygon",
2509
3110
  {
2510
3111
  points: ringPolygonPoints(6, 30, -30),
@@ -2513,8 +3114,8 @@ registerRenderer("cyclohexane", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runt
2513
3114
  strokeWidth: strokeOf3(el, ctx.isSelected),
2514
3115
  strokeLinejoin: "round"
2515
3116
  }
2516
- ) }));
2517
- registerRenderer("cyclopentane", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3117
+ ) }) }));
3118
+ registerRenderer("cyclopentane", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(OriginHitPlate, { ctx, hitHalfSize: 36, selectionHalfSize: 44, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2518
3119
  "polygon",
2519
3120
  {
2520
3121
  points: ringPolygonPoints(5, 30, -90),
@@ -2523,8 +3124,8 @@ registerRenderer("cyclopentane", (el, ctx) => /* @__PURE__ */ (0, import_jsx_run
2523
3124
  strokeWidth: strokeOf3(el, ctx.isSelected),
2524
3125
  strokeLinejoin: "round"
2525
3126
  }
2526
- ) }));
2527
- registerRenderer("cyclobutane", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3127
+ ) }) }));
3128
+ registerRenderer("cyclobutane", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(OriginHitPlate, { ctx, hitHalfSize: 36, selectionHalfSize: 44, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2528
3129
  "polygon",
2529
3130
  {
2530
3131
  points: ringPolygonPoints(4, 30, -45),
@@ -2533,8 +3134,8 @@ registerRenderer("cyclobutane", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runt
2533
3134
  strokeWidth: strokeOf3(el, ctx.isSelected),
2534
3135
  strokeLinejoin: "round"
2535
3136
  }
2536
- ) }));
2537
- registerRenderer("cyclopropane", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
3137
+ ) }) }));
3138
+ registerRenderer("cyclopropane", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(OriginHitPlate, { ctx, hitHalfSize: 36, selectionHalfSize: 44, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2538
3139
  "polygon",
2539
3140
  {
2540
3141
  points: ringPolygonPoints(3, 30, -90),
@@ -2543,74 +3144,94 @@ registerRenderer("cyclopropane", (el, ctx) => /* @__PURE__ */ (0, import_jsx_run
2543
3144
  strokeWidth: strokeOf3(el, ctx.isSelected),
2544
3145
  strokeLinejoin: "round"
2545
3146
  }
2546
- ) }));
3147
+ ) }) }));
2547
3148
  var drawBond = (el, style, isSelected) => {
2548
3149
  const d = distance(el.x1, el.y1, el.x2, el.y2);
2549
3150
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
2550
3151
  const stroke2 = colorOf3(el, isSelected);
2551
- const sw7 = strokeOf3(el, isSelected);
3152
+ const sw6 = strokeOf3(el, isSelected);
2552
3153
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2553
- style === "single" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "0", x2: d, y2: "0", stroke: stroke2, strokeWidth: sw7, strokeLinecap: "round" }),
3154
+ style === "single" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "0", x2: d, y2: "0", stroke: stroke2, strokeWidth: sw6, strokeLinecap: "round" }),
2554
3155
  style === "double" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2555
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "-3.5", x2: d, y2: "-3.5", stroke: stroke2, strokeWidth: sw7, strokeLinecap: "round" }),
2556
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "3.5", x2: d, y2: "3.5", stroke: stroke2, strokeWidth: sw7, strokeLinecap: "round" })
3156
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "-3.5", x2: d, y2: "-3.5", stroke: stroke2, strokeWidth: sw6, strokeLinecap: "round" }),
3157
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "3.5", x2: d, y2: "3.5", stroke: stroke2, strokeWidth: sw6, strokeLinecap: "round" })
2557
3158
  ] }),
2558
3159
  style === "triple" && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
2559
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "-6", x2: d, y2: "-6", stroke: stroke2, strokeWidth: sw7, strokeLinecap: "round" }),
2560
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "0", x2: d, y2: "0", stroke: stroke2, strokeWidth: sw7, strokeLinecap: "round" }),
2561
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "6", x2: d, y2: "6", stroke: stroke2, strokeWidth: sw7, strokeLinecap: "round" })
3160
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "-6", x2: d, y2: "-6", stroke: stroke2, strokeWidth: sw6, strokeLinecap: "round" }),
3161
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "0", x2: d, y2: "0", stroke: stroke2, strokeWidth: sw6, strokeLinecap: "round" }),
3162
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "6", x2: d, y2: "6", stroke: stroke2, strokeWidth: sw6, strokeLinecap: "round" })
2562
3163
  ] }),
2563
3164
  style === "wedge" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("polygon", { points: `0,0 ${d},-5 ${d},5`, fill: stroke2 }),
2564
- style === "dash" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "0", x2: d, y2: "0", stroke: stroke2, strokeWidth: sw7 + 1, strokeDasharray: "5 5", strokeLinecap: "square" })
3165
+ style === "dash" && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "0", x2: d, y2: "0", stroke: stroke2, strokeWidth: sw6 + 1, strokeDasharray: "5 5", strokeLinecap: "square" })
2565
3166
  ] });
2566
3167
  };
2567
- registerRenderer("single_bond", (el, ctx) => drawBond(el, "single", ctx.isSelected));
2568
- registerRenderer("double_bond", (el, ctx) => drawBond(el, "double", ctx.isSelected));
2569
- registerRenderer("triple_bond", (el, ctx) => drawBond(el, "triple", ctx.isSelected));
2570
- registerRenderer("wedge_bond", (el, ctx) => drawBond(el, "wedge", ctx.isSelected));
2571
- registerRenderer("dash_bond", (el, ctx) => drawBond(el, "dash", ctx.isSelected));
3168
+ registerRenderer("single_bond", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { children: [
3169
+ drawBond(el, "single", ctx.isSelected),
3170
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChordInteractionOverlay, { el, ctx })
3171
+ ] }));
3172
+ registerRenderer("double_bond", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { children: [
3173
+ drawBond(el, "double", ctx.isSelected),
3174
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChordInteractionOverlay, { el, ctx })
3175
+ ] }));
3176
+ registerRenderer("triple_bond", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { children: [
3177
+ drawBond(el, "triple", ctx.isSelected),
3178
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChordInteractionOverlay, { el, ctx })
3179
+ ] }));
3180
+ registerRenderer("wedge_bond", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { children: [
3181
+ drawBond(el, "wedge", ctx.isSelected),
3182
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChordInteractionOverlay, { el, ctx })
3183
+ ] }));
3184
+ registerRenderer("dash_bond", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { children: [
3185
+ drawBond(el, "dash", ctx.isSelected),
3186
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChordInteractionOverlay, { el, ctx })
3187
+ ] }));
2572
3188
  var lineMid = (el) => ({ x: (el.x1 + el.x2) / 2, y: (el.y1 + el.y2) / 2 });
2573
3189
  registerRenderer("reaction_arrow", (el, ctx) => {
2574
3190
  const d = distance(el.x1, el.y1, el.x2, el.y2);
2575
3191
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
2576
3192
  const stroke2 = colorOf3(el, ctx.isSelected);
2577
- const sw7 = strokeOf3(el, ctx.isSelected);
3193
+ const sw6 = strokeOf3(el, ctx.isSelected);
2578
3194
  const mid = lineMid(el);
2579
3195
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { children: [
2580
3196
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2581
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "0", x2: d, y2: "0", stroke: stroke2, strokeWidth: sw7 }),
3197
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "0", x2: d, y2: "0", stroke: stroke2, strokeWidth: sw6 }),
2582
3198
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("polygon", { points: `${d},0 ${d - 12},-6 ${d - 12},6`, fill: stroke2 })
2583
3199
  ] }),
2584
- el.label && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("text", { x: mid.x, y: mid.y - 18, textAnchor: "middle", fill: "#0f766e", fontSize: "14", fontFamily: "sans-serif", fontWeight: "bold", children: String(el.label) }),
2585
- el.value && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("text", { x: mid.x, y: mid.y + 18, textAnchor: "middle", fill: "#4b5563", fontSize: "13", fontFamily: "sans-serif", children: String(el.value) })
3200
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChordInteractionOverlay, { el, ctx }),
3201
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("text", { x: mid.x, y: mid.y - 18, textAnchor: "middle", fill: "#0f766e", fontSize: "14", fontFamily: "sans-serif", fontWeight: "bold", pointerEvents: "none", children: String(el.label) }) : null,
3202
+ el.value ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("text", { x: mid.x, y: mid.y + 18, textAnchor: "middle", fill: "#4b5563", fontSize: "13", fontFamily: "sans-serif", pointerEvents: "none", children: String(el.value) }) : null
2586
3203
  ] });
2587
3204
  });
2588
3205
  registerRenderer("equilibrium_arrow", (el, ctx) => {
2589
3206
  const d = distance(el.x1, el.y1, el.x2, el.y2);
2590
3207
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
2591
3208
  const stroke2 = colorOf3(el, ctx.isSelected);
2592
- const sw7 = strokeOf3(el, ctx.isSelected);
3209
+ const sw6 = strokeOf3(el, ctx.isSelected);
2593
3210
  const mid = lineMid(el);
2594
3211
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { children: [
2595
3212
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2596
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "-4", x2: d, y2: "-4", stroke: stroke2, strokeWidth: sw7 }),
3213
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "-4", x2: d, y2: "-4", stroke: stroke2, strokeWidth: sw6 }),
2597
3214
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("polygon", { points: `${d},-4 ${d - 10},-10 ${d - 10},-4`, fill: stroke2 }),
2598
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "4", x2: d, y2: "4", stroke: stroke2, strokeWidth: sw7 }),
3215
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "4", x2: d, y2: "4", stroke: stroke2, strokeWidth: sw6 }),
2599
3216
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("polygon", { points: `0,4 10,4 10,10`, fill: stroke2 })
2600
3217
  ] }),
2601
- el.label && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("text", { x: mid.x, y: mid.y - 18, textAnchor: "middle", fill: "#0f766e", fontSize: "14", fontFamily: "sans-serif", fontWeight: "bold", children: String(el.label) }),
2602
- el.value && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("text", { x: mid.x, y: mid.y + 18, textAnchor: "middle", fill: "#4b5563", fontSize: "13", fontFamily: "sans-serif", children: String(el.value) })
3218
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChordInteractionOverlay, { el, ctx }),
3219
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("text", { x: mid.x, y: mid.y - 18, textAnchor: "middle", fill: "#0f766e", fontSize: "14", fontFamily: "sans-serif", fontWeight: "bold", pointerEvents: "none", children: String(el.label) }) : null,
3220
+ el.value ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("text", { x: mid.x, y: mid.y + 18, textAnchor: "middle", fill: "#4b5563", fontSize: "13", fontFamily: "sans-serif", pointerEvents: "none", children: String(el.value) }) : null
2603
3221
  ] });
2604
3222
  });
2605
3223
  registerRenderer("resonance_arrow", (el, ctx) => {
2606
3224
  const d = distance(el.x1, el.y1, el.x2, el.y2);
2607
3225
  const angle = angleDeg(el.x1, el.y1, el.x2, el.y2);
2608
3226
  const stroke2 = colorOf3(el, ctx.isSelected);
2609
- const sw7 = strokeOf3(el, ctx.isSelected);
2610
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2611
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "0", x2: d, y2: "0", stroke: stroke2, strokeWidth: sw7 }),
2612
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("polygon", { points: `0,0 12,-6 12,6`, fill: stroke2 }),
2613
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("polygon", { points: `${d},0 ${d - 12},-6 ${d - 12},6`, fill: stroke2 })
3227
+ const sw6 = strokeOf3(el, ctx.isSelected);
3228
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { children: [
3229
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
3230
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("line", { x1: "0", y1: "0", x2: d, y2: "0", stroke: stroke2, strokeWidth: sw6 }),
3231
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("polygon", { points: `0,0 12,-6 12,6`, fill: stroke2 }),
3232
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("polygon", { points: `${d},0 ${d - 12},-6 ${d - 12},6`, fill: stroke2 })
3233
+ ] }),
3234
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChordInteractionOverlay, { el, ctx })
2614
3235
  ] });
2615
3236
  });
2616
3237
  registerRenderer("curved_arrow", (el, ctx) => {
@@ -2619,10 +3240,17 @@ registerRenderer("curved_arrow", (el, ctx) => {
2619
3240
  const h = d / 3;
2620
3241
  const arrowAngle = Math.atan2(h, d / 2) * (180 / Math.PI);
2621
3242
  const stroke2 = colorOf3(el, ctx.isSelected);
2622
- const sw7 = strokeOf3(el, ctx.isSelected);
2623
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
2624
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: `M 0 0 Q ${d / 2} ${-h} ${d} 0`, fill: "none", stroke: stroke2, strokeWidth: sw7 }),
2625
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("g", { transform: `translate(${d}, 0) rotate(${arrowAngle})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("polygon", { points: "0,0 -12,-6 -12,6", fill: stroke2 }) })
3243
+ const sw6 = strokeOf3(el, ctx.isSelected);
3244
+ const curvePath = `M 0 0 Q ${d / 2} ${-h} ${d} 0`;
3245
+ const bb = quadraticChordWorldBounds(el.x1, el.y1, el.x2, el.y2, h / 2);
3246
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { children: [
3247
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${angle})`, children: [
3248
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: curvePath, fill: "none", stroke: stroke2, strokeWidth: sw6 }),
3249
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(LocalFatPathHit, { dPath: curvePath, strokeWidth: 24, strokeLinecap: "round" }),
3250
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("g", { transform: `translate(${d}, 0) rotate(${arrowAngle})`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("polygon", { points: "0,0 -12,-6 -12,6", fill: stroke2 }) })
3251
+ ] }),
3252
+ ctx.isSelected && !ctx.viewMode && !ctx.isGhost ? selectionDashRect(bb.x, bb.y, bb.width, bb.height) : null,
3253
+ endpointHandles(el, ctx)
2626
3254
  ] });
2627
3255
  });
2628
3256
 
@@ -2644,28 +3272,28 @@ var rotated2 = (el, content, selected) => {
2644
3272
  ny = -ny;
2645
3273
  }
2646
3274
  const labelDist = 18;
2647
- const c7 = stroke(el, selected);
3275
+ const c6 = stroke(el, selected);
2648
3276
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("g", { children: [
2649
3277
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${a})`, children: content }),
2650
- el.label && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("text", { x: mx + nx * labelDist, y: my + ny * labelDist, dominantBaseline: "central", textAnchor: "middle", fill: c7, fontSize: 14, fontFamily: "sans-serif", children: String(el.label) })
3278
+ el.label && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("text", { x: mx + nx * labelDist, y: my + ny * labelDist, dominantBaseline: "central", textAnchor: "middle", fill: c6, fontSize: 14, fontFamily: "sans-serif", children: String(el.label) })
2651
3279
  ] });
2652
3280
  };
2653
3281
  registerRenderer("charge_pos", (el, ctx) => {
2654
- const c7 = ctx.isSelected ? "#2563eb" : "#ef4444";
2655
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, opacity: ctx?.isGhost ? 0.5 : 1, children: [
2656
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: "0", cy: "0", r: "10", fill: "#fecaca", stroke: c7, strokeWidth: width(el, ctx.isSelected) }),
2657
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: "0", y1: "-4", x2: "0", y2: "4", stroke: c7, strokeWidth: 2 }),
2658
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: "-4", y1: "0", x2: "4", y2: "0", stroke: c7, strokeWidth: 2 }),
2659
- el.label && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("text", { x: 0, y: -16, textAnchor: "middle", fill: c7, fontSize: el.fontSize || 14, children: String(el.label) })
2660
- ] });
3282
+ const c6 = ctx.isSelected ? "#2563eb" : "#ef4444";
3283
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, opacity: ctx?.isGhost ? 0.5 : 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(OriginHitPlate, { ctx, hitHalfSize: 22, selectionHalfSize: 28, children: [
3284
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: "0", cy: "0", r: "10", fill: "#fecaca", stroke: c6, strokeWidth: width(el, ctx.isSelected) }),
3285
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: "0", y1: "-4", x2: "0", y2: "4", stroke: c6, strokeWidth: 2 }),
3286
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: "-4", y1: "0", x2: "4", y2: "0", stroke: c6, strokeWidth: 2 }),
3287
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("text", { x: 0, y: -16, textAnchor: "middle", fill: c6, fontSize: el.fontSize || 14, children: String(el.label) }) : null
3288
+ ] }) });
2661
3289
  });
2662
3290
  registerRenderer("charge_neg", (el, ctx) => {
2663
- const c7 = ctx.isSelected ? "#2563eb" : "#3b82f6";
2664
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1})`, opacity: ctx?.isGhost ? 0.5 : 1, children: [
2665
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: "0", cy: "0", r: "10", fill: "#bfdbfe", stroke: c7, strokeWidth: width(el, ctx.isSelected) }),
2666
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: "-4", y1: "0", x2: "4", y2: "0", stroke: c7, strokeWidth: 2 }),
2667
- el.label && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("text", { x: 0, y: -16, textAnchor: "middle", fill: c7, fontSize: el.fontSize || 14, children: String(el.label) })
2668
- ] });
3291
+ const c6 = ctx.isSelected ? "#2563eb" : "#3b82f6";
3292
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("g", { transform: `translate(${el.x1}, ${el.y1})`, opacity: ctx?.isGhost ? 0.5 : 1, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(OriginHitPlate, { ctx, hitHalfSize: 22, selectionHalfSize: 28, children: [
3293
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: "0", cy: "0", r: "10", fill: "#bfdbfe", stroke: c6, strokeWidth: width(el, ctx.isSelected) }),
3294
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: "-4", y1: "0", x2: "4", y2: "0", stroke: c6, strokeWidth: 2 }),
3295
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("text", { x: 0, y: -16, textAnchor: "middle", fill: c6, fontSize: el.fontSize || 14, children: String(el.label) }) : null
3296
+ ] }) });
2669
3297
  });
2670
3298
  registerRenderer("dipole", (el, ctx) => {
2671
3299
  const w = width(el, ctx.isSelected);
@@ -2673,7 +3301,8 @@ registerRenderer("dipole", (el, ctx) => {
2673
3301
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: el.x1, y1: el.y1, x2: el.x2, y2: el.y2, stroke: "#64748b", strokeWidth: w, strokeDasharray: "4 4" }),
2674
3302
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: el.x1, cy: el.y1, r: "8", fill: "#fecaca", stroke: ctx.isSelected ? "#2563eb" : "#ef4444", strokeWidth: w }),
2675
3303
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: el.x2, cy: el.y2, r: "8", fill: "#bfdbfe", stroke: ctx.isSelected ? "#2563eb" : "#3b82f6", strokeWidth: w }),
2676
- el.label && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("text", { x: (el.x1 + el.x2) / 2, y: el.y1 - 16, textAnchor: "middle", fill: "#475569", fontSize: 14, children: String(el.label) })
3304
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("text", { x: (el.x1 + el.x2) / 2, y: el.y1 - 16, textAnchor: "middle", fill: "#475569", fontSize: 14, children: String(el.label) }) : null,
3305
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChordInteractionOverlay, { el, ctx })
2677
3306
  ] });
2678
3307
  });
2679
3308
  var plateGlyphs = (d, isPos) => {
@@ -2688,31 +3317,38 @@ var plateGlyphs = (d, isPos) => {
2688
3317
  return glyphs;
2689
3318
  };
2690
3319
  registerRenderer("charged_plate_pos", (el, ctx) => {
2691
- const c7 = ctx.isSelected ? "#2563eb" : "#ef4444";
3320
+ const c6 = ctx.isSelected ? "#2563eb" : "#ef4444";
2692
3321
  const d = distance(el.x1, el.y1, el.x2, el.y2);
2693
3322
  const a = angleDeg(el.x1, el.y1, el.x2, el.y2);
2694
- const sw7 = Math.max(12, el.strokeWidth * 4 || 8);
2695
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${a})`, opacity: ctx?.isGhost ? 0.5 : 1, children: [
2696
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c7, strokeWidth: sw7, strokeLinecap: "round" }),
2697
- plateGlyphs(d, true)
3323
+ const sw6 = Math.max(12, el.strokeWidth * 4 || 8);
3324
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("g", { opacity: ctx?.isGhost ? 0.5 : 1, children: [
3325
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${a})`, children: [
3326
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c6, strokeWidth: sw6, strokeLinecap: "round" }),
3327
+ plateGlyphs(d, true)
3328
+ ] }),
3329
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChordInteractionOverlay, { el, ctx })
2698
3330
  ] });
2699
3331
  });
2700
3332
  registerRenderer("charged_plate_neg", (el, ctx) => {
2701
- const c7 = ctx.isSelected ? "#2563eb" : "#3b82f6";
3333
+ const c6 = ctx.isSelected ? "#2563eb" : "#3b82f6";
2702
3334
  const d = distance(el.x1, el.y1, el.x2, el.y2);
2703
3335
  const a = angleDeg(el.x1, el.y1, el.x2, el.y2);
2704
- const sw7 = Math.max(12, el.strokeWidth * 4 || 8);
2705
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${a})`, opacity: ctx?.isGhost ? 0.5 : 1, children: [
2706
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c7, strokeWidth: sw7, strokeLinecap: "round" }),
2707
- plateGlyphs(d, false)
3336
+ const sw6 = Math.max(12, el.strokeWidth * 4 || 8);
3337
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("g", { opacity: ctx?.isGhost ? 0.5 : 1, children: [
3338
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${a})`, children: [
3339
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: 0, y1: 0, x2: d, y2: 0, stroke: c6, strokeWidth: sw6, strokeLinecap: "round" }),
3340
+ plateGlyphs(d, false)
3341
+ ] }),
3342
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChordInteractionOverlay, { el, ctx })
2708
3343
  ] });
2709
3344
  });
2710
3345
  registerRenderer("gaussian_sphere", (el, ctx) => {
2711
3346
  const r = Math.max(10, distance(el.x1, el.y1, el.x2, el.y2));
2712
- const c7 = ctx.isSelected ? "#2563eb" : "#10b981";
3347
+ const c6 = ctx.isSelected ? "#2563eb" : "#10b981";
2713
3348
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("g", { opacity: ctx?.isGhost ? 0.5 : 1, children: [
2714
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: el.x1, cy: el.y1, r, fill: "#d1fae5", fillOpacity: 0.35, stroke: c7, strokeWidth: width(el, ctx.isSelected), strokeDasharray: "5 5" }),
2715
- el.label && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("text", { x: el.x1, y: el.y1 - r - 12, textAnchor: "middle", fill: c7, fontSize: 13, children: String(el.label) })
3349
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("circle", { cx: el.x1, cy: el.y1, r, fill: "#d1fae5", fillOpacity: 0.35, stroke: c6, strokeWidth: width(el, ctx.isSelected), strokeDasharray: "5 5" }),
3350
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("text", { x: el.x1, y: el.y1 - r - 12, textAnchor: "middle", fill: c6, fontSize: 13, children: String(el.label) }) : null,
3351
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChordInteractionOverlay, { el, ctx })
2716
3352
  ] });
2717
3353
  });
2718
3354
  registerRenderer("gaussian_cylinder", (el, ctx) => {
@@ -2720,23 +3356,22 @@ registerRenderer("gaussian_cylinder", (el, ctx) => {
2720
3356
  const a = angleDeg(el.x1, el.y1, el.x2, el.y2);
2721
3357
  const r = el.curveHeight ?? 40;
2722
3358
  const capRx = 15;
2723
- const c7 = ctx.isSelected ? "#2563eb" : "#10b981";
2724
- const sw7 = width(el, ctx.isSelected);
3359
+ const c6 = ctx.isSelected ? "#2563eb" : "#10b981";
3360
+ const sw6 = width(el, ctx.isSelected);
2725
3361
  const dash = "6 6";
2726
3362
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("g", { opacity: ctx?.isGhost ? 0.5 : 1, children: [
2727
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: el.x1, y1: el.y1, x2: el.x2, y2: el.y2, stroke: "transparent", strokeWidth: Math.max(24, r * 2), strokeLinecap: "round" }),
2728
3363
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("g", { transform: `translate(${el.x1}, ${el.y1}) rotate(${a})`, children: [
2729
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: 0, y1: -r, x2: d, y2: -r, stroke: c7, strokeWidth: sw7, strokeDasharray: dash }),
2730
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: 0, y1: r, x2: d, y2: r, stroke: c7, strokeWidth: sw7, strokeDasharray: dash }),
2731
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("ellipse", { cx: 0, cy: 0, rx: capRx, ry: r, fill: c7, fillOpacity: 0.05, stroke: c7, strokeWidth: sw7, strokeDasharray: dash }),
2732
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("ellipse", { cx: d, cy: 0, rx: capRx, ry: r, fill: c7, fillOpacity: 0.05, stroke: c7, strokeWidth: sw7, strokeDasharray: dash }),
2733
- el.label && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("text", { x: d / 2, y: -r - 10, textAnchor: "middle", fill: c7, fontSize: el.fontSize || 14, children: String(el.label) })
2734
- ] })
3364
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: 0, y1: -r, x2: d, y2: -r, stroke: c6, strokeWidth: sw6, strokeDasharray: dash }),
3365
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: 0, y1: r, x2: d, y2: r, stroke: c6, strokeWidth: sw6, strokeDasharray: dash }),
3366
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("ellipse", { cx: 0, cy: 0, rx: capRx, ry: r, fill: c6, fillOpacity: 0.05, stroke: c6, strokeWidth: sw6, strokeDasharray: dash }),
3367
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("ellipse", { cx: d, cy: 0, rx: capRx, ry: r, fill: c6, fillOpacity: 0.05, stroke: c6, strokeWidth: sw6, strokeDasharray: dash }),
3368
+ el.label ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("text", { x: d / 2, y: -r - 10, textAnchor: "middle", fill: c6, fontSize: el.fontSize || 14, children: String(el.label) }) : null
3369
+ ] }),
3370
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChordInteractionOverlay, { el, ctx })
2735
3371
  ] });
2736
3372
  });
2737
- registerRenderer(
2738
- "mirror_plane",
2739
- (el, ctx) => rotated2(
3373
+ registerRenderer("mirror_plane", (el, ctx) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("g", { children: [
3374
+ rotated2(
2740
3375
  el,
2741
3376
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2742
3377
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: 0, y1: -24, x2: 0, y2: 24, stroke: stroke(el, ctx.isSelected), strokeWidth: width(el, ctx.isSelected) }),
@@ -2746,21 +3381,25 @@ registerRenderer(
2746
3381
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: 0, y1: 18, x2: -8, y2: 24, stroke: stroke(el, ctx.isSelected), strokeWidth: 1.5 })
2747
3382
  ] }),
2748
3383
  ctx.isSelected
2749
- )
2750
- );
3384
+ ),
3385
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChordInteractionOverlay, { el, ctx })
3386
+ ] }));
2751
3387
  registerRenderer("energy_level", (el, ctx) => {
2752
3388
  const d = Math.max(24, distance(el.x1, el.y1, el.x2, el.y2));
2753
- const c7 = stroke(el, ctx.isSelected);
3389
+ const c6 = stroke(el, ctx.isSelected);
2754
3390
  const energyText = el.energy != null ? String(el.energy) : "";
2755
- return rotated2(
2756
- el,
2757
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
2758
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: 0, y1: -8, x2: d, y2: -8, stroke: c7, strokeWidth: width(el, ctx.isSelected) }),
2759
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: 0, y1: 8, x2: d, y2: 8, stroke: c7, strokeWidth: width(el, ctx.isSelected) }),
2760
- energyText && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("text", { x: d / 2, y: 22, textAnchor: "middle", fontSize: 10, fill: "#4b5563", children: energyText })
2761
- ] }),
2762
- ctx.isSelected
2763
- );
3391
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("g", { children: [
3392
+ rotated2(
3393
+ el,
3394
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
3395
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: 0, y1: -8, x2: d, y2: -8, stroke: c6, strokeWidth: width(el, ctx.isSelected) }),
3396
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("line", { x1: 0, y1: 8, x2: d, y2: 8, stroke: c6, strokeWidth: width(el, ctx.isSelected) }),
3397
+ energyText ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("text", { x: d / 2, y: 22, textAnchor: "middle", fontSize: 10, fill: "#4b5563", children: energyText }) : null
3398
+ ] }),
3399
+ ctx.isSelected
3400
+ ),
3401
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ChordInteractionOverlay, { el, ctx })
3402
+ ] });
2764
3403
  });
2765
3404
 
2766
3405
  // src/core/Renderer.tsx
@@ -2782,7 +3421,8 @@ var Renderer = ({
2782
3421
  onBackgroundMouseMove,
2783
3422
  onBackgroundMouseUp,
2784
3423
  onBackgroundMouseLeave,
2785
- onElementMouseDown
3424
+ onElementMouseDown,
3425
+ onHandlePointerDown
2786
3426
  }) => {
2787
3427
  const autoNodes = (0, import_react.useMemo)(() => getCircuitAutoNodes(elements), [elements]);
2788
3428
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
@@ -2801,14 +3441,19 @@ var Renderer = ({
2801
3441
  ] }),
2802
3442
  elements.map((el) => {
2803
3443
  const renderer = getRenderer(String(el.type));
2804
- const content = renderer ? renderer(el, { isSelected: selectedId === el.id, viewMode }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(UnknownElement, { x: el.x1, y: el.y1, type: String(el.type) });
3444
+ const ctx = {
3445
+ isSelected: selectedId === el.id,
3446
+ viewMode,
3447
+ onHandlePointerDown: !viewMode && onHandlePointerDown ? (e, kind, cpIndex) => onHandlePointerDown(e, el, kind, cpIndex) : void 0
3448
+ };
3449
+ const content = renderer ? renderer(el, ctx) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(UnknownElement, { x: el.x1, y: el.y1, type: String(el.type) });
2805
3450
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("g", { onMouseDown: (e) => onElementMouseDown?.(e, el), cursor: viewMode ? "default" : "move", children: content }, String(el.id));
2806
3451
  }),
2807
3452
  autoNodes.map(([nx, ny], idx) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("circle", { cx: nx, cy: ny, r: 4, fill: "#111827", pointerEvents: "none" }, `junction-${idx}`)),
2808
3453
  drawing && (() => {
2809
3454
  const renderer = getRenderer(String(drawing.type));
2810
3455
  if (!renderer) return null;
2811
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("g", { opacity: 0.5, children: renderer(drawing, { isGhost: true, viewMode }) });
3456
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("g", { opacity: 0.5, children: renderer(drawing, { isGhost: true, viewMode, onHandlePointerDown: void 0 }) });
2812
3457
  })()
2813
3458
  ]
2814
3459
  }