@skillpet/circuit 0.6.2 → 0.6.4

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.
@@ -15146,8 +15146,8 @@ var _SvgFigure = class _SvgFigure {
15146
15146
  let [x0, y0] = this.xform(x, y);
15147
15147
  const fontsize = opts.fontsize ?? 14;
15148
15148
  let font = opts.fontfamily ?? "sans-serif";
15149
- if (font.toLowerCase() === "sans-serif" || font.toLowerCase() === "arial") {
15150
- font = "sans";
15149
+ if (font.toLowerCase() === "sans" || font.toLowerCase() === "arial") {
15150
+ font = "sans-serif";
15151
15151
  }
15152
15152
  const valign = opts.valign ?? "center";
15153
15153
  if (valign === "base") {
@@ -15159,14 +15159,13 @@ var _SvgFigure = class _SvgFigure {
15159
15159
  const rot = opts.rotation ?? 0;
15160
15160
  const transform = rot !== 0 ? ` transform="rotate(${fmt(rot)} ${fmt(x0)} ${fmt(y0)})"` : "";
15161
15161
  const color = opts.color ?? "black";
15162
- const textY = y0 - fontsize;
15163
15162
  let textContent;
15164
15163
  if (opts.textDecoration) {
15165
15164
  textContent = `<tspan text-decoration="${opts.textDecoration}">${escapeXml(s)}</tspan>`;
15166
15165
  } else {
15167
15166
  textContent = escapeXml(s);
15168
15167
  }
15169
- let inner2 = `<text x="${fmt(x0)}" y="${fmt(textY)}" dominant-baseline="${baseline}" fill="${escapeXml(color)}" font-size="${fmt(fontsize)}" font-family="${escapeXml(font)}" text-anchor="${anchor}"${transform}><tspan x="${fmt(x0)}" dy="${fmt(fontsize)}" dominant-baseline="${baseline}">${textContent}</tspan></text>`;
15168
+ let inner2 = `<text x="${fmt(x0)}" y="${fmt(y0)}" dominant-baseline="${baseline}" fill="${escapeXml(color)}" font-size="${fmt(fontsize)}" font-family="${escapeXml(font)}" text-anchor="${anchor}"${transform}>${textContent}</text>`;
15170
15169
  if (opts.href) {
15171
15170
  inner2 = `<a href="${escapeXml(opts.href)}">${inner2}</a>`;
15172
15171
  }
@@ -17345,51 +17344,55 @@ function resolveElementColor(el) {
17345
17344
  const p = mergeParamsFirstWins(el.userParams, el.elmParams, el.defaults, el.dwgParams);
17346
17345
  return p.color ?? "black";
17347
17346
  }
17348
- function getOutputAnchors(el) {
17349
- const pts = [];
17350
- const aa = el.absanchors;
17351
- if (aa.end) pts.push(aa.end);
17352
- if (aa.out && (!aa.end || !pointsClose(aa.out, aa.end, 0.01))) pts.push(aa.out);
17353
- return pts;
17347
+ var EXCLUDED_ANCHORS = /* @__PURE__ */ new Set([
17348
+ "xy",
17349
+ "center",
17350
+ "istart",
17351
+ "iend",
17352
+ "mid",
17353
+ "label",
17354
+ "vd",
17355
+ "vs",
17356
+ "n1",
17357
+ "n2",
17358
+ "n1a",
17359
+ "n2a"
17360
+ ]);
17361
+ function isConnectionAnchor(name) {
17362
+ return !EXCLUDED_ANCHORS.has(name);
17354
17363
  }
17355
- function getInputAnchors(el) {
17364
+ function getConnectionAnchors(el) {
17356
17365
  const pts = [];
17357
- const aa = el.absanchors;
17358
- if (aa.start) pts.push(aa.start);
17359
- for (const [name, pt] of Object.entries(aa)) {
17360
- if (name.startsWith("in") && /^in\d+$/.test(name)) {
17361
- if (!aa.start || !pointsClose(pt, aa.start, 0.01)) pts.push(pt);
17362
- }
17366
+ for (const [name, pt] of Object.entries(el.absanchors)) {
17367
+ if (isConnectionAnchor(name)) pts.push(pt);
17363
17368
  }
17364
17369
  return pts;
17365
17370
  }
17366
- function pointsClose(a, b, tol) {
17367
- return Math.hypot(a.x - b.x, a.y - b.y) <= tol;
17368
- }
17369
17371
  function buildConnectionGraph(elements) {
17370
17372
  const edges = [];
17371
17373
  const seen = /* @__PURE__ */ new Set();
17372
17374
  const tol = 0.5;
17373
17375
  for (let i = 0; i < elements.length; i++) {
17374
- const fromEl = elements[i];
17375
- const fromPts = getOutputAnchors(fromEl);
17376
- if (fromPts.length === 0) continue;
17377
- const fromColor = resolveElementColor(fromEl);
17378
- for (let j = 0; j < elements.length; j++) {
17379
- if (i === j) continue;
17380
- const toEl = elements[j];
17381
- const toPts = getInputAnchors(toEl);
17382
- if (toPts.length === 0) continue;
17383
- const toColor = resolveElementColor(toEl);
17384
- if (colorsEqual(fromColor, toColor)) continue;
17385
- for (const fp of fromPts) {
17386
- for (const tp of toPts) {
17387
- if (!pointsClose(fp, tp, tol)) continue;
17376
+ const elA = elements[i];
17377
+ const colorA = resolveElementColor(elA);
17378
+ const anchorsA = getConnectionAnchors(elA);
17379
+ if (anchorsA.length === 0) continue;
17380
+ for (let j = i + 1; j < elements.length; j++) {
17381
+ const elB = elements[j];
17382
+ const colorB = resolveElementColor(elB);
17383
+ if (colorsEqual(colorA, colorB)) continue;
17384
+ const anchorsB = getConnectionAnchors(elB);
17385
+ if (anchorsB.length === 0) continue;
17386
+ for (const pa of anchorsA) {
17387
+ for (const pb of anchorsB) {
17388
+ if (Math.hypot(pa.x - pb.x, pa.y - pb.y) > tol) continue;
17388
17389
  const key = `${i}|${j}`;
17389
17390
  if (seen.has(key)) continue;
17390
17391
  seen.add(key);
17391
- edges.push({ fromEl, toEl, fromColor, toColor, junctionPt: fp });
17392
+ edges.push({ elA, elB, colorA, colorB, junctionPt: pa });
17393
+ break;
17392
17394
  }
17395
+ if (seen.has(`${i}|${j}`)) break;
17393
17396
  }
17394
17397
  }
17395
17398
  }
@@ -17405,26 +17408,28 @@ function assignLeadTransitionIds(elements, edges, scale) {
17405
17408
  const cleanups = [];
17406
17409
  let n = 0;
17407
17410
  for (const edge of edges) {
17408
- const lead2Seg = findLeadSegmentFor(edge.fromEl, "out", edge.junctionPt);
17409
- const lead1Seg = findLeadSegmentFor(edge.toEl, "in", edge.junctionPt);
17410
- if (!lead2Seg && !lead1Seg) continue;
17411
- if (lead2Seg?.gradientStrokeId && lead1Seg?.gradientStrokeId) continue;
17411
+ const segA = findSegAtJunction(edge.elA, edge.junctionPt);
17412
+ const segB = findSegAtJunction(edge.elB, edge.junctionPt);
17413
+ if (!segA && !segB) continue;
17414
+ if (segA?.gradientStrokeId && segB?.gradientStrokeId) continue;
17412
17415
  let gx1, gy1, gx2, gy2;
17413
- if (lead2Seg && lead1Seg) {
17414
- const from = segEndpointsSvg(edge.fromEl, lead2Seg, scale);
17415
- const to = segEndpointsSvg(edge.toEl, lead1Seg, scale);
17416
- gx1 = from.x1;
17417
- gy1 = from.y1;
17418
- gx2 = to.x2;
17419
- gy2 = to.y2;
17420
- } else if (lead2Seg) {
17421
- const c = segEndpointsSvg(edge.fromEl, lead2Seg, scale);
17416
+ if (segA && segB) {
17417
+ const a = segEndpointsSvg(edge.elA, segA, scale);
17418
+ const b = segEndpointsSvg(edge.elB, segB, scale);
17419
+ const aNear = nearFarSvg(edge.elA, segA, edge.junctionPt, scale);
17420
+ const bNear = nearFarSvg(edge.elB, segB, edge.junctionPt, scale);
17421
+ gx1 = aNear.farX;
17422
+ gy1 = aNear.farY;
17423
+ gx2 = bNear.farX;
17424
+ gy2 = bNear.farY;
17425
+ } else if (segA) {
17426
+ const c = segEndpointsSvg(edge.elA, segA, scale);
17422
17427
  gx1 = c.x1;
17423
17428
  gy1 = c.y1;
17424
17429
  gx2 = c.x2;
17425
17430
  gy2 = c.y2;
17426
17431
  } else {
17427
- const c = segEndpointsSvg(edge.toEl, lead1Seg, scale);
17432
+ const c = segEndpointsSvg(edge.elB, segB, scale);
17428
17433
  gx1 = c.x1;
17429
17434
  gy1 = c.y1;
17430
17435
  gx2 = c.x2;
@@ -17432,17 +17437,17 @@ function assignLeadTransitionIds(elements, edges, scale) {
17432
17437
  }
17433
17438
  if (gradientVectorLength({ x1: gx1, y1: gy1, x2: gx2, y2: gy2 }) < 1e-6) continue;
17434
17439
  const id = `sd-trans-${n++}`;
17435
- parts.push(gradientXml(id, gx1, gy1, gx2, gy2, edge.fromColor, edge.toColor));
17436
- if (lead2Seg && !lead2Seg.gradientStrokeId) {
17437
- lead2Seg.gradientStrokeId = id;
17440
+ parts.push(gradientXml(id, gx1, gy1, gx2, gy2, edge.colorA, edge.colorB));
17441
+ if (segA && !segA.gradientStrokeId) {
17442
+ segA.gradientStrokeId = id;
17438
17443
  cleanups.push(() => {
17439
- lead2Seg.gradientStrokeId = void 0;
17444
+ segA.gradientStrokeId = void 0;
17440
17445
  });
17441
17446
  }
17442
- if (lead1Seg && !lead1Seg.gradientStrokeId) {
17443
- lead1Seg.gradientStrokeId = id;
17447
+ if (segB && !segB.gradientStrokeId) {
17448
+ segB.gradientStrokeId = id;
17444
17449
  cleanups.push(() => {
17445
- lead1Seg.gradientStrokeId = void 0;
17450
+ segB.gradientStrokeId = void 0;
17446
17451
  });
17447
17452
  }
17448
17453
  }
@@ -17453,26 +17458,21 @@ function assignLeadTransitionIds(elements, edges, scale) {
17453
17458
  }
17454
17459
  };
17455
17460
  }
17456
- function findLeadSegmentFor(el, side, junctionPt) {
17457
- const role = side === "in" ? "lead1" : "lead2";
17461
+ function findSegAtJunction(el, junctionPt) {
17462
+ const tol = 0.6;
17458
17463
  for (const s of el.segments) {
17459
- if (s instanceof Segment && s.role === role) return s;
17464
+ if (!(s instanceof Segment)) continue;
17465
+ if (s.role !== "lead1" && s.role !== "lead2") continue;
17466
+ if (segTouchesPoint(el, s, junctionPt, tol)) return s;
17460
17467
  }
17461
- return findSegmentNearPoint(el, junctionPt);
17462
- }
17463
- function findSegmentNearPoint(el, absPt) {
17464
17468
  let best;
17465
17469
  let bestDist = Infinity;
17466
- const tol = 0.6;
17467
17470
  for (const s of el.segments) {
17468
17471
  if (!(s instanceof Segment)) continue;
17469
17472
  if (s.path.length < 2) continue;
17470
17473
  if (s.role === "body") continue;
17471
- const p0 = el.transform.transform(s.path[0]);
17472
- const pN = el.transform.transform(s.path[s.path.length - 1]);
17473
- const d0 = Math.hypot(p0.x - absPt.x, p0.y - absPt.y);
17474
- const dN = Math.hypot(pN.x - absPt.x, pN.y - absPt.y);
17475
- const d = Math.min(d0, dN);
17474
+ if (s.path.length > 6) continue;
17475
+ const d = segDistToPoint(el, s, junctionPt);
17476
17476
  if (d < bestDist && d < tol) {
17477
17477
  bestDist = d;
17478
17478
  best = s;
@@ -17480,6 +17480,25 @@ function findSegmentNearPoint(el, absPt) {
17480
17480
  }
17481
17481
  return best;
17482
17482
  }
17483
+ function segTouchesPoint(el, seg, absPt, tol) {
17484
+ return segDistToPoint(el, seg, absPt) < tol;
17485
+ }
17486
+ function segDistToPoint(el, seg, absPt) {
17487
+ const p0 = el.transform.transform(seg.path[0]);
17488
+ const pN = el.transform.transform(seg.path[seg.path.length - 1]);
17489
+ return Math.min(
17490
+ Math.hypot(p0.x - absPt.x, p0.y - absPt.y),
17491
+ Math.hypot(pN.x - absPt.x, pN.y - absPt.y)
17492
+ );
17493
+ }
17494
+ function nearFarSvg(el, seg, junctionPt, scale) {
17495
+ const p0 = el.transform.transform(seg.path[0]);
17496
+ const pN = el.transform.transform(seg.path[seg.path.length - 1]);
17497
+ const d0 = Math.hypot(p0.x - junctionPt.x, p0.y - junctionPt.y);
17498
+ const dN = Math.hypot(pN.x - junctionPt.x, pN.y - junctionPt.y);
17499
+ const far = d0 > dN ? p0 : pN;
17500
+ return { farX: far.x * scale, farY: -far.y * scale };
17501
+ }
17483
17502
  function segEndpointsSvg(el, seg, scale) {
17484
17503
  const p0 = el.transform.transform(seg.path[0]);
17485
17504
  const p1 = el.transform.transform(seg.path[seg.path.length - 1]);
@@ -8,19 +8,19 @@
8
8
  import type { Element } from "./element.js";
9
9
  import { Point } from "./geometry/point.js";
10
10
  interface ConnectionEdge {
11
- fromEl: Element;
12
- toEl: Element;
13
- fromColor: string;
14
- toColor: string;
15
- /** The absolute point where the connection occurs. */
11
+ elA: Element;
12
+ elB: Element;
13
+ colorA: string;
14
+ colorB: string;
15
+ /** The absolute point where the two elements meet. */
16
16
  junctionPt: Point;
17
17
  }
18
18
  /**
19
- * Find directed pairs (fromEl toEl) where an output anchor of fromEl meets
20
- * an input anchor of toEl (within tolerance) and resolved stroke colors differ.
19
+ * Find pairs of elements that share a connection point (any pin anchor within
20
+ * tolerance) and have different resolved colors.
21
21
  *
22
- * Checks end/out start/in1/in2/… to support both two-terminal chains
23
- * and multi-terminal elements (logic gates, opamps, etc.).
22
+ * Checks pin-level anchors (start, end, out, in1, in2, base, collector, etc.)
23
+ * so connections via named pins on multi-terminal elements are detected.
24
24
  */
25
25
  export declare function buildConnectionGraph(elements: readonly Element[]): ConnectionEdge[];
26
26
  /**
package/dist/index.cjs CHANGED
@@ -15533,8 +15533,8 @@ var _SvgFigure = class _SvgFigure {
15533
15533
  let [x0, y0] = this.xform(x, y);
15534
15534
  const fontsize = opts.fontsize ?? 14;
15535
15535
  let font = opts.fontfamily ?? "sans-serif";
15536
- if (font.toLowerCase() === "sans-serif" || font.toLowerCase() === "arial") {
15537
- font = "sans";
15536
+ if (font.toLowerCase() === "sans" || font.toLowerCase() === "arial") {
15537
+ font = "sans-serif";
15538
15538
  }
15539
15539
  const valign = opts.valign ?? "center";
15540
15540
  if (valign === "base") {
@@ -15546,14 +15546,13 @@ var _SvgFigure = class _SvgFigure {
15546
15546
  const rot = opts.rotation ?? 0;
15547
15547
  const transform = rot !== 0 ? ` transform="rotate(${fmt(rot)} ${fmt(x0)} ${fmt(y0)})"` : "";
15548
15548
  const color = opts.color ?? "black";
15549
- const textY = y0 - fontsize;
15550
15549
  let textContent;
15551
15550
  if (opts.textDecoration) {
15552
15551
  textContent = `<tspan text-decoration="${opts.textDecoration}">${escapeXml(s)}</tspan>`;
15553
15552
  } else {
15554
15553
  textContent = escapeXml(s);
15555
15554
  }
15556
- let inner2 = `<text x="${fmt(x0)}" y="${fmt(textY)}" dominant-baseline="${baseline}" fill="${escapeXml(color)}" font-size="${fmt(fontsize)}" font-family="${escapeXml(font)}" text-anchor="${anchor}"${transform}><tspan x="${fmt(x0)}" dy="${fmt(fontsize)}" dominant-baseline="${baseline}">${textContent}</tspan></text>`;
15555
+ let inner2 = `<text x="${fmt(x0)}" y="${fmt(y0)}" dominant-baseline="${baseline}" fill="${escapeXml(color)}" font-size="${fmt(fontsize)}" font-family="${escapeXml(font)}" text-anchor="${anchor}"${transform}>${textContent}</text>`;
15557
15556
  if (opts.href) {
15558
15557
  inner2 = `<a href="${escapeXml(opts.href)}">${inner2}</a>`;
15559
15558
  }
@@ -17732,51 +17731,55 @@ function resolveElementColor(el) {
17732
17731
  const p = mergeParamsFirstWins(el.userParams, el.elmParams, el.defaults, el.dwgParams);
17733
17732
  return p.color ?? "black";
17734
17733
  }
17735
- function getOutputAnchors(el) {
17736
- const pts = [];
17737
- const aa = el.absanchors;
17738
- if (aa.end) pts.push(aa.end);
17739
- if (aa.out && (!aa.end || !pointsClose(aa.out, aa.end, 0.01))) pts.push(aa.out);
17740
- return pts;
17734
+ var EXCLUDED_ANCHORS = /* @__PURE__ */ new Set([
17735
+ "xy",
17736
+ "center",
17737
+ "istart",
17738
+ "iend",
17739
+ "mid",
17740
+ "label",
17741
+ "vd",
17742
+ "vs",
17743
+ "n1",
17744
+ "n2",
17745
+ "n1a",
17746
+ "n2a"
17747
+ ]);
17748
+ function isConnectionAnchor(name) {
17749
+ return !EXCLUDED_ANCHORS.has(name);
17741
17750
  }
17742
- function getInputAnchors(el) {
17751
+ function getConnectionAnchors(el) {
17743
17752
  const pts = [];
17744
- const aa = el.absanchors;
17745
- if (aa.start) pts.push(aa.start);
17746
- for (const [name, pt] of Object.entries(aa)) {
17747
- if (name.startsWith("in") && /^in\d+$/.test(name)) {
17748
- if (!aa.start || !pointsClose(pt, aa.start, 0.01)) pts.push(pt);
17749
- }
17753
+ for (const [name, pt] of Object.entries(el.absanchors)) {
17754
+ if (isConnectionAnchor(name)) pts.push(pt);
17750
17755
  }
17751
17756
  return pts;
17752
17757
  }
17753
- function pointsClose(a, b, tol) {
17754
- return Math.hypot(a.x - b.x, a.y - b.y) <= tol;
17755
- }
17756
17758
  function buildConnectionGraph(elements) {
17757
17759
  const edges = [];
17758
17760
  const seen = /* @__PURE__ */ new Set();
17759
17761
  const tol = 0.5;
17760
17762
  for (let i = 0; i < elements.length; i++) {
17761
- const fromEl = elements[i];
17762
- const fromPts = getOutputAnchors(fromEl);
17763
- if (fromPts.length === 0) continue;
17764
- const fromColor = resolveElementColor(fromEl);
17765
- for (let j = 0; j < elements.length; j++) {
17766
- if (i === j) continue;
17767
- const toEl = elements[j];
17768
- const toPts = getInputAnchors(toEl);
17769
- if (toPts.length === 0) continue;
17770
- const toColor = resolveElementColor(toEl);
17771
- if (colorsEqual(fromColor, toColor)) continue;
17772
- for (const fp of fromPts) {
17773
- for (const tp of toPts) {
17774
- if (!pointsClose(fp, tp, tol)) continue;
17763
+ const elA = elements[i];
17764
+ const colorA = resolveElementColor(elA);
17765
+ const anchorsA = getConnectionAnchors(elA);
17766
+ if (anchorsA.length === 0) continue;
17767
+ for (let j = i + 1; j < elements.length; j++) {
17768
+ const elB = elements[j];
17769
+ const colorB = resolveElementColor(elB);
17770
+ if (colorsEqual(colorA, colorB)) continue;
17771
+ const anchorsB = getConnectionAnchors(elB);
17772
+ if (anchorsB.length === 0) continue;
17773
+ for (const pa of anchorsA) {
17774
+ for (const pb of anchorsB) {
17775
+ if (Math.hypot(pa.x - pb.x, pa.y - pb.y) > tol) continue;
17775
17776
  const key = `${i}|${j}`;
17776
17777
  if (seen.has(key)) continue;
17777
17778
  seen.add(key);
17778
- edges.push({ fromEl, toEl, fromColor, toColor, junctionPt: fp });
17779
+ edges.push({ elA, elB, colorA, colorB, junctionPt: pa });
17780
+ break;
17779
17781
  }
17782
+ if (seen.has(`${i}|${j}`)) break;
17780
17783
  }
17781
17784
  }
17782
17785
  }
@@ -17792,26 +17795,28 @@ function assignLeadTransitionIds(elements, edges, scale) {
17792
17795
  const cleanups = [];
17793
17796
  let n = 0;
17794
17797
  for (const edge of edges) {
17795
- const lead2Seg = findLeadSegmentFor(edge.fromEl, "out", edge.junctionPt);
17796
- const lead1Seg = findLeadSegmentFor(edge.toEl, "in", edge.junctionPt);
17797
- if (!lead2Seg && !lead1Seg) continue;
17798
- if (lead2Seg?.gradientStrokeId && lead1Seg?.gradientStrokeId) continue;
17798
+ const segA = findSegAtJunction(edge.elA, edge.junctionPt);
17799
+ const segB = findSegAtJunction(edge.elB, edge.junctionPt);
17800
+ if (!segA && !segB) continue;
17801
+ if (segA?.gradientStrokeId && segB?.gradientStrokeId) continue;
17799
17802
  let gx1, gy1, gx2, gy2;
17800
- if (lead2Seg && lead1Seg) {
17801
- const from = segEndpointsSvg(edge.fromEl, lead2Seg, scale);
17802
- const to = segEndpointsSvg(edge.toEl, lead1Seg, scale);
17803
- gx1 = from.x1;
17804
- gy1 = from.y1;
17805
- gx2 = to.x2;
17806
- gy2 = to.y2;
17807
- } else if (lead2Seg) {
17808
- const c = segEndpointsSvg(edge.fromEl, lead2Seg, scale);
17803
+ if (segA && segB) {
17804
+ const a = segEndpointsSvg(edge.elA, segA, scale);
17805
+ const b = segEndpointsSvg(edge.elB, segB, scale);
17806
+ const aNear = nearFarSvg(edge.elA, segA, edge.junctionPt, scale);
17807
+ const bNear = nearFarSvg(edge.elB, segB, edge.junctionPt, scale);
17808
+ gx1 = aNear.farX;
17809
+ gy1 = aNear.farY;
17810
+ gx2 = bNear.farX;
17811
+ gy2 = bNear.farY;
17812
+ } else if (segA) {
17813
+ const c = segEndpointsSvg(edge.elA, segA, scale);
17809
17814
  gx1 = c.x1;
17810
17815
  gy1 = c.y1;
17811
17816
  gx2 = c.x2;
17812
17817
  gy2 = c.y2;
17813
17818
  } else {
17814
- const c = segEndpointsSvg(edge.toEl, lead1Seg, scale);
17819
+ const c = segEndpointsSvg(edge.elB, segB, scale);
17815
17820
  gx1 = c.x1;
17816
17821
  gy1 = c.y1;
17817
17822
  gx2 = c.x2;
@@ -17819,17 +17824,17 @@ function assignLeadTransitionIds(elements, edges, scale) {
17819
17824
  }
17820
17825
  if (gradientVectorLength({ x1: gx1, y1: gy1, x2: gx2, y2: gy2 }) < 1e-6) continue;
17821
17826
  const id = `sd-trans-${n++}`;
17822
- parts.push(gradientXml(id, gx1, gy1, gx2, gy2, edge.fromColor, edge.toColor));
17823
- if (lead2Seg && !lead2Seg.gradientStrokeId) {
17824
- lead2Seg.gradientStrokeId = id;
17827
+ parts.push(gradientXml(id, gx1, gy1, gx2, gy2, edge.colorA, edge.colorB));
17828
+ if (segA && !segA.gradientStrokeId) {
17829
+ segA.gradientStrokeId = id;
17825
17830
  cleanups.push(() => {
17826
- lead2Seg.gradientStrokeId = void 0;
17831
+ segA.gradientStrokeId = void 0;
17827
17832
  });
17828
17833
  }
17829
- if (lead1Seg && !lead1Seg.gradientStrokeId) {
17830
- lead1Seg.gradientStrokeId = id;
17834
+ if (segB && !segB.gradientStrokeId) {
17835
+ segB.gradientStrokeId = id;
17831
17836
  cleanups.push(() => {
17832
- lead1Seg.gradientStrokeId = void 0;
17837
+ segB.gradientStrokeId = void 0;
17833
17838
  });
17834
17839
  }
17835
17840
  }
@@ -17840,26 +17845,21 @@ function assignLeadTransitionIds(elements, edges, scale) {
17840
17845
  }
17841
17846
  };
17842
17847
  }
17843
- function findLeadSegmentFor(el, side, junctionPt) {
17844
- const role = side === "in" ? "lead1" : "lead2";
17848
+ function findSegAtJunction(el, junctionPt) {
17849
+ const tol = 0.6;
17845
17850
  for (const s of el.segments) {
17846
- if (s instanceof Segment && s.role === role) return s;
17851
+ if (!(s instanceof Segment)) continue;
17852
+ if (s.role !== "lead1" && s.role !== "lead2") continue;
17853
+ if (segTouchesPoint(el, s, junctionPt, tol)) return s;
17847
17854
  }
17848
- return findSegmentNearPoint(el, junctionPt);
17849
- }
17850
- function findSegmentNearPoint(el, absPt) {
17851
17855
  let best;
17852
17856
  let bestDist = Infinity;
17853
- const tol = 0.6;
17854
17857
  for (const s of el.segments) {
17855
17858
  if (!(s instanceof Segment)) continue;
17856
17859
  if (s.path.length < 2) continue;
17857
17860
  if (s.role === "body") continue;
17858
- const p0 = el.transform.transform(s.path[0]);
17859
- const pN = el.transform.transform(s.path[s.path.length - 1]);
17860
- const d0 = Math.hypot(p0.x - absPt.x, p0.y - absPt.y);
17861
- const dN = Math.hypot(pN.x - absPt.x, pN.y - absPt.y);
17862
- const d = Math.min(d0, dN);
17861
+ if (s.path.length > 6) continue;
17862
+ const d = segDistToPoint(el, s, junctionPt);
17863
17863
  if (d < bestDist && d < tol) {
17864
17864
  bestDist = d;
17865
17865
  best = s;
@@ -17867,6 +17867,25 @@ function findSegmentNearPoint(el, absPt) {
17867
17867
  }
17868
17868
  return best;
17869
17869
  }
17870
+ function segTouchesPoint(el, seg, absPt, tol) {
17871
+ return segDistToPoint(el, seg, absPt) < tol;
17872
+ }
17873
+ function segDistToPoint(el, seg, absPt) {
17874
+ const p0 = el.transform.transform(seg.path[0]);
17875
+ const pN = el.transform.transform(seg.path[seg.path.length - 1]);
17876
+ return Math.min(
17877
+ Math.hypot(p0.x - absPt.x, p0.y - absPt.y),
17878
+ Math.hypot(pN.x - absPt.x, pN.y - absPt.y)
17879
+ );
17880
+ }
17881
+ function nearFarSvg(el, seg, junctionPt, scale) {
17882
+ const p0 = el.transform.transform(seg.path[0]);
17883
+ const pN = el.transform.transform(seg.path[seg.path.length - 1]);
17884
+ const d0 = Math.hypot(p0.x - junctionPt.x, p0.y - junctionPt.y);
17885
+ const dN = Math.hypot(pN.x - junctionPt.x, pN.y - junctionPt.y);
17886
+ const far = d0 > dN ? p0 : pN;
17887
+ return { farX: far.x * scale, farY: -far.y * scale };
17888
+ }
17870
17889
  function segEndpointsSvg(el, seg, scale) {
17871
17890
  const p0 = el.transform.transform(seg.path[0]);
17872
17891
  const p1 = el.transform.transform(seg.path[seg.path.length - 1]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillpet/circuit",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
4
4
  "description": "Circuit diagram library — render electrical schematics from JSON, with interactive SVG, themes, and Vue/React components",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",