@skillpet/circuit 0.6.1 → 0.6.3

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.
@@ -17345,29 +17345,56 @@ function resolveElementColor(el) {
17345
17345
  const p = mergeParamsFirstWins(el.userParams, el.elmParams, el.defaults, el.dwgParams);
17346
17346
  return p.color ?? "black";
17347
17347
  }
17348
+ var EXCLUDED_ANCHORS = /* @__PURE__ */ new Set([
17349
+ "xy",
17350
+ "center",
17351
+ "istart",
17352
+ "iend",
17353
+ "mid",
17354
+ "label",
17355
+ "vd",
17356
+ "vs",
17357
+ "n1",
17358
+ "n2",
17359
+ "n1a",
17360
+ "n2a"
17361
+ ]);
17362
+ function isConnectionAnchor(name) {
17363
+ return !EXCLUDED_ANCHORS.has(name);
17364
+ }
17365
+ function getConnectionAnchors(el) {
17366
+ const pts = [];
17367
+ for (const [name, pt] of Object.entries(el.absanchors)) {
17368
+ if (isConnectionAnchor(name)) pts.push(pt);
17369
+ }
17370
+ return pts;
17371
+ }
17348
17372
  function buildConnectionGraph(elements) {
17349
17373
  const edges = [];
17350
17374
  const seen = /* @__PURE__ */ new Set();
17351
17375
  const tol = 0.5;
17352
17376
  for (let i = 0; i < elements.length; i++) {
17353
- const fromEl = elements[i];
17354
- const fromEnd = fromEl.absanchors.end;
17355
- if (!fromEnd) continue;
17356
- for (let j = 0; j < elements.length; j++) {
17357
- if (i === j) continue;
17358
- const toEl = elements[j];
17359
- const toStart = toEl.absanchors.start;
17360
- if (!toStart) continue;
17361
- const dx = fromEnd.x - toStart.x;
17362
- const dy = fromEnd.y - toStart.y;
17363
- if (Math.hypot(dx, dy) > tol) continue;
17364
- const fromColor = resolveElementColor(fromEl);
17365
- const toColor = resolveElementColor(toEl);
17366
- if (colorsEqual(fromColor, toColor)) continue;
17367
- const key = `${i}|${j}`;
17368
- if (seen.has(key)) continue;
17369
- seen.add(key);
17370
- edges.push({ fromEl, toEl, fromColor, toColor });
17377
+ const elA = elements[i];
17378
+ const colorA = resolveElementColor(elA);
17379
+ const anchorsA = getConnectionAnchors(elA);
17380
+ if (anchorsA.length === 0) continue;
17381
+ for (let j = i + 1; j < elements.length; j++) {
17382
+ const elB = elements[j];
17383
+ const colorB = resolveElementColor(elB);
17384
+ if (colorsEqual(colorA, colorB)) continue;
17385
+ const anchorsB = getConnectionAnchors(elB);
17386
+ if (anchorsB.length === 0) continue;
17387
+ for (const pa of anchorsA) {
17388
+ for (const pb of anchorsB) {
17389
+ if (Math.hypot(pa.x - pb.x, pa.y - pb.y) > tol) continue;
17390
+ const key = `${i}|${j}`;
17391
+ if (seen.has(key)) continue;
17392
+ seen.add(key);
17393
+ edges.push({ elA, elB, colorA, colorB, junctionPt: pa });
17394
+ break;
17395
+ }
17396
+ if (seen.has(`${i}|${j}`)) break;
17397
+ }
17371
17398
  }
17372
17399
  }
17373
17400
  return edges;
@@ -17382,26 +17409,28 @@ function assignLeadTransitionIds(elements, edges, scale) {
17382
17409
  const cleanups = [];
17383
17410
  let n = 0;
17384
17411
  for (const edge of edges) {
17385
- const lead2Seg = findLeadSegment(edge.fromEl, "lead2");
17386
- const lead1Seg = findLeadSegment(edge.toEl, "lead1");
17387
- if (!lead2Seg && !lead1Seg) continue;
17388
- if (lead2Seg?.gradientStrokeId && lead1Seg?.gradientStrokeId) continue;
17412
+ const segA = findSegAtJunction(edge.elA, edge.junctionPt);
17413
+ const segB = findSegAtJunction(edge.elB, edge.junctionPt);
17414
+ if (!segA && !segB) continue;
17415
+ if (segA?.gradientStrokeId && segB?.gradientStrokeId) continue;
17389
17416
  let gx1, gy1, gx2, gy2;
17390
- if (lead2Seg && lead1Seg) {
17391
- const from = segEndpointsSvg(edge.fromEl, lead2Seg, scale);
17392
- const to = segEndpointsSvg(edge.toEl, lead1Seg, scale);
17393
- gx1 = from.x1;
17394
- gy1 = from.y1;
17395
- gx2 = to.x2;
17396
- gy2 = to.y2;
17397
- } else if (lead2Seg) {
17398
- const c = segEndpointsSvg(edge.fromEl, lead2Seg, scale);
17417
+ if (segA && segB) {
17418
+ const a = segEndpointsSvg(edge.elA, segA, scale);
17419
+ const b = segEndpointsSvg(edge.elB, segB, scale);
17420
+ const aNear = nearFarSvg(edge.elA, segA, edge.junctionPt, scale);
17421
+ const bNear = nearFarSvg(edge.elB, segB, edge.junctionPt, scale);
17422
+ gx1 = aNear.farX;
17423
+ gy1 = aNear.farY;
17424
+ gx2 = bNear.farX;
17425
+ gy2 = bNear.farY;
17426
+ } else if (segA) {
17427
+ const c = segEndpointsSvg(edge.elA, segA, scale);
17399
17428
  gx1 = c.x1;
17400
17429
  gy1 = c.y1;
17401
17430
  gx2 = c.x2;
17402
17431
  gy2 = c.y2;
17403
17432
  } else {
17404
- const c = segEndpointsSvg(edge.toEl, lead1Seg, scale);
17433
+ const c = segEndpointsSvg(edge.elB, segB, scale);
17405
17434
  gx1 = c.x1;
17406
17435
  gy1 = c.y1;
17407
17436
  gx2 = c.x2;
@@ -17409,17 +17438,17 @@ function assignLeadTransitionIds(elements, edges, scale) {
17409
17438
  }
17410
17439
  if (gradientVectorLength({ x1: gx1, y1: gy1, x2: gx2, y2: gy2 }) < 1e-6) continue;
17411
17440
  const id = `sd-trans-${n++}`;
17412
- parts.push(gradientXml(id, gx1, gy1, gx2, gy2, edge.fromColor, edge.toColor));
17413
- if (lead2Seg && !lead2Seg.gradientStrokeId) {
17414
- lead2Seg.gradientStrokeId = id;
17441
+ parts.push(gradientXml(id, gx1, gy1, gx2, gy2, edge.colorA, edge.colorB));
17442
+ if (segA && !segA.gradientStrokeId) {
17443
+ segA.gradientStrokeId = id;
17415
17444
  cleanups.push(() => {
17416
- lead2Seg.gradientStrokeId = void 0;
17445
+ segA.gradientStrokeId = void 0;
17417
17446
  });
17418
17447
  }
17419
- if (lead1Seg && !lead1Seg.gradientStrokeId) {
17420
- lead1Seg.gradientStrokeId = id;
17448
+ if (segB && !segB.gradientStrokeId) {
17449
+ segB.gradientStrokeId = id;
17421
17450
  cleanups.push(() => {
17422
- lead1Seg.gradientStrokeId = void 0;
17451
+ segB.gradientStrokeId = void 0;
17423
17452
  });
17424
17453
  }
17425
17454
  }
@@ -17430,11 +17459,46 @@ function assignLeadTransitionIds(elements, edges, scale) {
17430
17459
  }
17431
17460
  };
17432
17461
  }
17433
- function findLeadSegment(el, role) {
17462
+ function findSegAtJunction(el, junctionPt) {
17463
+ const tol = 0.6;
17434
17464
  for (const s of el.segments) {
17435
- if (s instanceof Segment && s.role === role) return s;
17465
+ if (!(s instanceof Segment)) continue;
17466
+ if (s.role !== "lead1" && s.role !== "lead2") continue;
17467
+ if (segTouchesPoint(el, s, junctionPt, tol)) return s;
17436
17468
  }
17437
- return void 0;
17469
+ let best;
17470
+ let bestDist = Infinity;
17471
+ for (const s of el.segments) {
17472
+ if (!(s instanceof Segment)) continue;
17473
+ if (s.path.length < 2) continue;
17474
+ if (s.role === "body") continue;
17475
+ if (s.path.length > 6) continue;
17476
+ const d = segDistToPoint(el, s, junctionPt);
17477
+ if (d < bestDist && d < tol) {
17478
+ bestDist = d;
17479
+ best = s;
17480
+ }
17481
+ }
17482
+ return best;
17483
+ }
17484
+ function segTouchesPoint(el, seg, absPt, tol) {
17485
+ return segDistToPoint(el, seg, absPt) < tol;
17486
+ }
17487
+ function segDistToPoint(el, seg, absPt) {
17488
+ const p0 = el.transform.transform(seg.path[0]);
17489
+ const pN = el.transform.transform(seg.path[seg.path.length - 1]);
17490
+ return Math.min(
17491
+ Math.hypot(p0.x - absPt.x, p0.y - absPt.y),
17492
+ Math.hypot(pN.x - absPt.x, pN.y - absPt.y)
17493
+ );
17494
+ }
17495
+ function nearFarSvg(el, seg, junctionPt, scale) {
17496
+ const p0 = el.transform.transform(seg.path[0]);
17497
+ const pN = el.transform.transform(seg.path[seg.path.length - 1]);
17498
+ const d0 = Math.hypot(p0.x - junctionPt.x, p0.y - junctionPt.y);
17499
+ const dN = Math.hypot(pN.x - junctionPt.x, pN.y - junctionPt.y);
17500
+ const far = d0 > dN ? p0 : pN;
17501
+ return { farX: far.x * scale, farY: -far.y * scale };
17438
17502
  }
17439
17503
  function segEndpointsSvg(el, seg, scale) {
17440
17504
  const p0 = el.transform.transform(seg.path[0]);
@@ -6,18 +6,21 @@
6
6
  * a smooth color fade at the junction.
7
7
  */
8
8
  import type { Element } from "./element.js";
9
+ import { Point } from "./geometry/point.js";
9
10
  interface ConnectionEdge {
10
- fromEl: Element;
11
- toEl: Element;
12
- fromColor: string;
13
- toColor: string;
11
+ elA: Element;
12
+ elB: Element;
13
+ colorA: string;
14
+ colorB: string;
15
+ /** The absolute point where the two elements meet. */
16
+ junctionPt: Point;
14
17
  }
15
18
  /**
16
- * Find directed pairs (fromEl toEl) where `fromEl.absanchors.end` meets
17
- * `toEl.absanchors.start` (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.
18
21
  *
19
- * Uses all element pairs, not only consecutive indices, so chain order in the
20
- * array can differ from electrical adjacency (e.g. explicit `at` / `to`).
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.
21
24
  */
22
25
  export declare function buildConnectionGraph(elements: readonly Element[]): ConnectionEdge[];
23
26
  /**
package/dist/index.cjs CHANGED
@@ -17732,29 +17732,56 @@ function resolveElementColor(el) {
17732
17732
  const p = mergeParamsFirstWins(el.userParams, el.elmParams, el.defaults, el.dwgParams);
17733
17733
  return p.color ?? "black";
17734
17734
  }
17735
+ var EXCLUDED_ANCHORS = /* @__PURE__ */ new Set([
17736
+ "xy",
17737
+ "center",
17738
+ "istart",
17739
+ "iend",
17740
+ "mid",
17741
+ "label",
17742
+ "vd",
17743
+ "vs",
17744
+ "n1",
17745
+ "n2",
17746
+ "n1a",
17747
+ "n2a"
17748
+ ]);
17749
+ function isConnectionAnchor(name) {
17750
+ return !EXCLUDED_ANCHORS.has(name);
17751
+ }
17752
+ function getConnectionAnchors(el) {
17753
+ const pts = [];
17754
+ for (const [name, pt] of Object.entries(el.absanchors)) {
17755
+ if (isConnectionAnchor(name)) pts.push(pt);
17756
+ }
17757
+ return pts;
17758
+ }
17735
17759
  function buildConnectionGraph(elements) {
17736
17760
  const edges = [];
17737
17761
  const seen = /* @__PURE__ */ new Set();
17738
17762
  const tol = 0.5;
17739
17763
  for (let i = 0; i < elements.length; i++) {
17740
- const fromEl = elements[i];
17741
- const fromEnd = fromEl.absanchors.end;
17742
- if (!fromEnd) continue;
17743
- for (let j = 0; j < elements.length; j++) {
17744
- if (i === j) continue;
17745
- const toEl = elements[j];
17746
- const toStart = toEl.absanchors.start;
17747
- if (!toStart) continue;
17748
- const dx = fromEnd.x - toStart.x;
17749
- const dy = fromEnd.y - toStart.y;
17750
- if (Math.hypot(dx, dy) > tol) continue;
17751
- const fromColor = resolveElementColor(fromEl);
17752
- const toColor = resolveElementColor(toEl);
17753
- if (colorsEqual(fromColor, toColor)) continue;
17754
- const key = `${i}|${j}`;
17755
- if (seen.has(key)) continue;
17756
- seen.add(key);
17757
- edges.push({ fromEl, toEl, fromColor, toColor });
17764
+ const elA = elements[i];
17765
+ const colorA = resolveElementColor(elA);
17766
+ const anchorsA = getConnectionAnchors(elA);
17767
+ if (anchorsA.length === 0) continue;
17768
+ for (let j = i + 1; j < elements.length; j++) {
17769
+ const elB = elements[j];
17770
+ const colorB = resolveElementColor(elB);
17771
+ if (colorsEqual(colorA, colorB)) continue;
17772
+ const anchorsB = getConnectionAnchors(elB);
17773
+ if (anchorsB.length === 0) continue;
17774
+ for (const pa of anchorsA) {
17775
+ for (const pb of anchorsB) {
17776
+ if (Math.hypot(pa.x - pb.x, pa.y - pb.y) > tol) continue;
17777
+ const key = `${i}|${j}`;
17778
+ if (seen.has(key)) continue;
17779
+ seen.add(key);
17780
+ edges.push({ elA, elB, colorA, colorB, junctionPt: pa });
17781
+ break;
17782
+ }
17783
+ if (seen.has(`${i}|${j}`)) break;
17784
+ }
17758
17785
  }
17759
17786
  }
17760
17787
  return edges;
@@ -17769,26 +17796,28 @@ function assignLeadTransitionIds(elements, edges, scale) {
17769
17796
  const cleanups = [];
17770
17797
  let n = 0;
17771
17798
  for (const edge of edges) {
17772
- const lead2Seg = findLeadSegment(edge.fromEl, "lead2");
17773
- const lead1Seg = findLeadSegment(edge.toEl, "lead1");
17774
- if (!lead2Seg && !lead1Seg) continue;
17775
- if (lead2Seg?.gradientStrokeId && lead1Seg?.gradientStrokeId) continue;
17799
+ const segA = findSegAtJunction(edge.elA, edge.junctionPt);
17800
+ const segB = findSegAtJunction(edge.elB, edge.junctionPt);
17801
+ if (!segA && !segB) continue;
17802
+ if (segA?.gradientStrokeId && segB?.gradientStrokeId) continue;
17776
17803
  let gx1, gy1, gx2, gy2;
17777
- if (lead2Seg && lead1Seg) {
17778
- const from = segEndpointsSvg(edge.fromEl, lead2Seg, scale);
17779
- const to = segEndpointsSvg(edge.toEl, lead1Seg, scale);
17780
- gx1 = from.x1;
17781
- gy1 = from.y1;
17782
- gx2 = to.x2;
17783
- gy2 = to.y2;
17784
- } else if (lead2Seg) {
17785
- const c = segEndpointsSvg(edge.fromEl, lead2Seg, scale);
17804
+ if (segA && segB) {
17805
+ const a = segEndpointsSvg(edge.elA, segA, scale);
17806
+ const b = segEndpointsSvg(edge.elB, segB, scale);
17807
+ const aNear = nearFarSvg(edge.elA, segA, edge.junctionPt, scale);
17808
+ const bNear = nearFarSvg(edge.elB, segB, edge.junctionPt, scale);
17809
+ gx1 = aNear.farX;
17810
+ gy1 = aNear.farY;
17811
+ gx2 = bNear.farX;
17812
+ gy2 = bNear.farY;
17813
+ } else if (segA) {
17814
+ const c = segEndpointsSvg(edge.elA, segA, scale);
17786
17815
  gx1 = c.x1;
17787
17816
  gy1 = c.y1;
17788
17817
  gx2 = c.x2;
17789
17818
  gy2 = c.y2;
17790
17819
  } else {
17791
- const c = segEndpointsSvg(edge.toEl, lead1Seg, scale);
17820
+ const c = segEndpointsSvg(edge.elB, segB, scale);
17792
17821
  gx1 = c.x1;
17793
17822
  gy1 = c.y1;
17794
17823
  gx2 = c.x2;
@@ -17796,17 +17825,17 @@ function assignLeadTransitionIds(elements, edges, scale) {
17796
17825
  }
17797
17826
  if (gradientVectorLength({ x1: gx1, y1: gy1, x2: gx2, y2: gy2 }) < 1e-6) continue;
17798
17827
  const id = `sd-trans-${n++}`;
17799
- parts.push(gradientXml(id, gx1, gy1, gx2, gy2, edge.fromColor, edge.toColor));
17800
- if (lead2Seg && !lead2Seg.gradientStrokeId) {
17801
- lead2Seg.gradientStrokeId = id;
17828
+ parts.push(gradientXml(id, gx1, gy1, gx2, gy2, edge.colorA, edge.colorB));
17829
+ if (segA && !segA.gradientStrokeId) {
17830
+ segA.gradientStrokeId = id;
17802
17831
  cleanups.push(() => {
17803
- lead2Seg.gradientStrokeId = void 0;
17832
+ segA.gradientStrokeId = void 0;
17804
17833
  });
17805
17834
  }
17806
- if (lead1Seg && !lead1Seg.gradientStrokeId) {
17807
- lead1Seg.gradientStrokeId = id;
17835
+ if (segB && !segB.gradientStrokeId) {
17836
+ segB.gradientStrokeId = id;
17808
17837
  cleanups.push(() => {
17809
- lead1Seg.gradientStrokeId = void 0;
17838
+ segB.gradientStrokeId = void 0;
17810
17839
  });
17811
17840
  }
17812
17841
  }
@@ -17817,11 +17846,46 @@ function assignLeadTransitionIds(elements, edges, scale) {
17817
17846
  }
17818
17847
  };
17819
17848
  }
17820
- function findLeadSegment(el, role) {
17849
+ function findSegAtJunction(el, junctionPt) {
17850
+ const tol = 0.6;
17821
17851
  for (const s of el.segments) {
17822
- if (s instanceof Segment && s.role === role) return s;
17852
+ if (!(s instanceof Segment)) continue;
17853
+ if (s.role !== "lead1" && s.role !== "lead2") continue;
17854
+ if (segTouchesPoint(el, s, junctionPt, tol)) return s;
17823
17855
  }
17824
- return void 0;
17856
+ let best;
17857
+ let bestDist = Infinity;
17858
+ for (const s of el.segments) {
17859
+ if (!(s instanceof Segment)) continue;
17860
+ if (s.path.length < 2) continue;
17861
+ if (s.role === "body") continue;
17862
+ if (s.path.length > 6) continue;
17863
+ const d = segDistToPoint(el, s, junctionPt);
17864
+ if (d < bestDist && d < tol) {
17865
+ bestDist = d;
17866
+ best = s;
17867
+ }
17868
+ }
17869
+ return best;
17870
+ }
17871
+ function segTouchesPoint(el, seg, absPt, tol) {
17872
+ return segDistToPoint(el, seg, absPt) < tol;
17873
+ }
17874
+ function segDistToPoint(el, seg, absPt) {
17875
+ const p0 = el.transform.transform(seg.path[0]);
17876
+ const pN = el.transform.transform(seg.path[seg.path.length - 1]);
17877
+ return Math.min(
17878
+ Math.hypot(p0.x - absPt.x, p0.y - absPt.y),
17879
+ Math.hypot(pN.x - absPt.x, pN.y - absPt.y)
17880
+ );
17881
+ }
17882
+ function nearFarSvg(el, seg, junctionPt, scale) {
17883
+ const p0 = el.transform.transform(seg.path[0]);
17884
+ const pN = el.transform.transform(seg.path[seg.path.length - 1]);
17885
+ const d0 = Math.hypot(p0.x - junctionPt.x, p0.y - junctionPt.y);
17886
+ const dN = Math.hypot(pN.x - junctionPt.x, pN.y - junctionPt.y);
17887
+ const far = d0 > dN ? p0 : pN;
17888
+ return { farX: far.x * scale, farY: -far.y * scale };
17825
17889
  }
17826
17890
  function segEndpointsSvg(el, seg, scale) {
17827
17891
  const p0 = el.transform.transform(seg.path[0]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skillpet/circuit",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
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",