@tscircuit/pcb-viewer 1.11.218 → 1.11.220

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.js CHANGED
@@ -11478,20 +11478,90 @@ var any_soup_element = any_circuit_element;
11478
11478
  expectTypesMatch(true);
11479
11479
  expectStringUnionsMatch(true);
11480
11480
 
11481
- // node_modules/@tscircuit/soup-util/dist/index.js
11481
+ // node_modules/@tscircuit/circuit-json-util/dist/index.js
11482
11482
  import { applyToPoint, decomposeTSR } from "transformation-matrix";
11483
- var su = (soup, options = {}) => {
11484
- let internalStore = soup._internal_store;
11483
+ import { translate } from "transformation-matrix";
11484
+ import { translate as translate2 } from "transformation-matrix";
11485
+ import { translate as translate3 } from "transformation-matrix";
11486
+ import { translate as translate4 } from "transformation-matrix";
11487
+ function connect(map, a, b) {
11488
+ if (!a || !b) return;
11489
+ let setA = map.get(a);
11490
+ if (!setA) {
11491
+ setA = /* @__PURE__ */ new Set();
11492
+ map.set(a, setA);
11493
+ }
11494
+ setA.add(b);
11495
+ let setB = map.get(b);
11496
+ if (!setB) {
11497
+ setB = /* @__PURE__ */ new Set();
11498
+ map.set(b, setB);
11499
+ }
11500
+ setB.add(a);
11501
+ }
11502
+ function buildSubtree(soup, opts) {
11503
+ if (!opts.subcircuit_id && !opts.source_group_id) return [...soup];
11504
+ const idMap = /* @__PURE__ */ new Map();
11505
+ for (const elm of soup) {
11506
+ const idVal = elm[`${elm.type}_id`];
11507
+ if (typeof idVal === "string") {
11508
+ idMap.set(idVal, elm);
11509
+ }
11510
+ }
11511
+ const adj = /* @__PURE__ */ new Map();
11512
+ for (const elm of soup) {
11513
+ const entries = Object.entries(elm);
11514
+ for (const [key, val] of entries) {
11515
+ if (key === "parent_source_group_id") continue;
11516
+ if (key.endsWith("_id") && typeof val === "string") {
11517
+ const other = idMap.get(val);
11518
+ connect(adj, elm, other);
11519
+ } else if (key.endsWith("_ids") && Array.isArray(val)) {
11520
+ for (const v of val) {
11521
+ if (typeof v === "string") {
11522
+ const other = idMap.get(v);
11523
+ connect(adj, elm, other);
11524
+ }
11525
+ }
11526
+ }
11527
+ }
11528
+ }
11529
+ const queue = [];
11530
+ const included = /* @__PURE__ */ new Set();
11531
+ for (const elm of soup) {
11532
+ if (opts.subcircuit_id && elm.subcircuit_id === opts.subcircuit_id || opts.source_group_id && (elm.source_group_id === opts.source_group_id || Array.isArray(elm.member_source_group_ids) && elm.member_source_group_ids.includes(
11533
+ opts.source_group_id
11534
+ ))) {
11535
+ queue.push(elm);
11536
+ included.add(elm);
11537
+ }
11538
+ }
11539
+ while (queue.length > 0) {
11540
+ const elm = queue.shift();
11541
+ const neighbors = adj.get(elm);
11542
+ if (!neighbors) continue;
11543
+ for (const n of neighbors) {
11544
+ if (!included.has(n)) {
11545
+ included.add(n);
11546
+ queue.push(n);
11547
+ }
11548
+ }
11549
+ }
11550
+ return soup.filter((e) => included.has(e));
11551
+ }
11552
+ var cju = (circuitJsonInput, options = {}) => {
11553
+ const circuitJson = circuitJsonInput;
11554
+ let internalStore = circuitJson._internal_store;
11485
11555
  if (!internalStore) {
11486
11556
  internalStore = {
11487
- counts: {}
11557
+ counts: {},
11558
+ editCount: 0
11488
11559
  };
11489
- soup._internal_store = internalStore;
11490
- for (const elm of soup) {
11560
+ circuitJson._internal_store = internalStore;
11561
+ for (const elm of circuitJson) {
11491
11562
  const type = elm.type;
11492
11563
  const idVal = elm[`${type}_id`];
11493
- if (!idVal)
11494
- continue;
11564
+ if (!idVal) continue;
11495
11565
  const idNum = Number.parseInt(idVal.split("_").pop());
11496
11566
  if (!Number.isNaN(idNum)) {
11497
11567
  internalStore.counts[type] = Math.max(
@@ -11504,12 +11574,23 @@ var su = (soup, options = {}) => {
11504
11574
  const su2 = new Proxy(
11505
11575
  {},
11506
11576
  {
11507
- get: (proxy_target, component_type) => {
11508
- if (component_type === "toArray") {
11509
- return () => soup;
11577
+ get: (proxy_target, prop) => {
11578
+ if (prop === "toArray") {
11579
+ return () => {
11580
+ ;
11581
+ circuitJson.editCount = internalStore.editCount;
11582
+ return circuitJson;
11583
+ };
11584
+ }
11585
+ if (prop === "editCount") {
11586
+ return internalStore.editCount;
11510
11587
  }
11588
+ if (prop === "subtree") {
11589
+ return (opts) => cju(buildSubtree(circuitJson, opts), options);
11590
+ }
11591
+ const component_type = prop;
11511
11592
  return {
11512
- get: (id) => soup.find(
11593
+ get: (id) => circuitJson.find(
11513
11594
  (e) => e.type === component_type && e[`${component_type}_id`] === id
11514
11595
  ),
11515
11596
  getUsing: (using) => {
@@ -11521,24 +11602,23 @@ var su = (soup, options = {}) => {
11521
11602
  }
11522
11603
  const join_key = keys[0];
11523
11604
  const join_type = join_key.replace("_id", "");
11524
- const joiner = soup.find(
11605
+ const joiner = circuitJson.find(
11525
11606
  (e) => e.type === join_type && e[join_key] === using[join_key]
11526
11607
  );
11527
- if (!joiner)
11528
- return null;
11529
- return soup.find(
11608
+ if (!joiner) return null;
11609
+ return circuitJson.find(
11530
11610
  (e) => e.type === component_type && e[`${component_type}_id`] === joiner[`${component_type}_id`]
11531
11611
  );
11532
11612
  },
11533
11613
  getWhere: (where) => {
11534
11614
  const keys = Object.keys(where);
11535
- return soup.find(
11615
+ return circuitJson.find(
11536
11616
  (e) => e.type === component_type && keys.every((key) => e[key] === where[key])
11537
11617
  );
11538
11618
  },
11539
11619
  list: (where) => {
11540
11620
  const keys = !where ? [] : Object.keys(where);
11541
- return soup.filter(
11621
+ return circuitJson.filter(
11542
11622
  (e) => e.type === component_type && keys.every((key) => e[key] === where[key])
11543
11623
  );
11544
11624
  },
@@ -11555,64 +11635,581 @@ var su = (soup, options = {}) => {
11555
11635
  const parser = dist_exports[component_type] ?? any_soup_element;
11556
11636
  parser.parse(newElm);
11557
11637
  }
11558
- soup.push(newElm);
11638
+ circuitJson.push(newElm);
11639
+ internalStore.editCount++;
11559
11640
  return newElm;
11560
11641
  },
11561
11642
  delete: (id) => {
11562
- const elm = soup.find(
11643
+ const elm = circuitJson.find(
11563
11644
  (e) => e[`${component_type}_id`] === id
11564
11645
  );
11565
- if (!elm)
11566
- return;
11567
- soup.splice(soup.indexOf(elm), 1);
11646
+ if (!elm) return;
11647
+ circuitJson.splice(circuitJson.indexOf(elm), 1);
11648
+ internalStore.editCount++;
11568
11649
  },
11569
11650
  update: (id, newProps) => {
11570
- const elm = soup.find(
11651
+ const elm = circuitJson.find(
11571
11652
  (e) => e.type === component_type && e[`${component_type}_id`] === id
11572
11653
  );
11573
- if (!elm)
11574
- return;
11654
+ if (!elm) return null;
11575
11655
  Object.assign(elm, newProps);
11656
+ internalStore.editCount++;
11576
11657
  return elm;
11577
11658
  },
11578
11659
  select: (selector) => {
11579
11660
  if (component_type === "source_component") {
11580
- return soup.find(
11661
+ return circuitJson.find(
11581
11662
  (e) => e.type === "source_component" && e.name === selector.replace(/\./g, "")
11582
11663
  );
11664
+ } else if (component_type === "pcb_port" || component_type === "source_port" || component_type === "schematic_port") {
11665
+ const [component_name, port_selector] = selector.replace(/\./g, "").split(/[\s\>]+/);
11666
+ const source_component = circuitJson.find(
11667
+ (e) => e.type === "source_component" && e.name === component_name
11668
+ );
11669
+ if (!source_component) return null;
11670
+ const source_port2 = circuitJson.find(
11671
+ (e) => e.type === "source_port" && e.source_component_id === source_component.source_component_id && (e.name === port_selector || (e.port_hints ?? []).includes(port_selector))
11672
+ );
11673
+ if (!source_port2) return null;
11674
+ if (component_type === "source_port") return source_port2;
11675
+ if (component_type === "pcb_port") {
11676
+ return circuitJson.find(
11677
+ (e) => e.type === "pcb_port" && e.source_port_id === source_port2.source_port_id
11678
+ );
11679
+ } else if (component_type === "schematic_port") {
11680
+ return circuitJson.find(
11681
+ (e) => e.type === "schematic_port" && e.source_port_id === source_port2.source_port_id
11682
+ );
11683
+ }
11684
+ }
11685
+ }
11686
+ };
11687
+ }
11688
+ }
11689
+ );
11690
+ return su2;
11691
+ };
11692
+ cju.unparsed = cju;
11693
+ var su = cju;
11694
+ function createIdKey(element) {
11695
+ const type = element.type;
11696
+ return `${type}:${element[`${type}_id`]}`;
11697
+ }
11698
+ var cjuIndexed = (soup, options = {}) => {
11699
+ let internalStore = soup._internal_store_indexed;
11700
+ if (!internalStore) {
11701
+ internalStore = {
11702
+ counts: {},
11703
+ editCount: 0,
11704
+ indexes: {}
11705
+ };
11706
+ for (const elm of soup) {
11707
+ const type = elm.type;
11708
+ const idVal = elm[`${type}_id`];
11709
+ if (!idVal) continue;
11710
+ const idNum = Number.parseInt(idVal.split("_").pop() || "");
11711
+ if (!Number.isNaN(idNum)) {
11712
+ internalStore.counts[type] = Math.max(
11713
+ internalStore.counts[type] ?? 0,
11714
+ idNum
11715
+ );
11716
+ }
11717
+ }
11718
+ const indexConfig = options.indexConfig || {};
11719
+ const indexes = internalStore.indexes;
11720
+ if (indexConfig.byId) {
11721
+ indexes.byId = /* @__PURE__ */ new Map();
11722
+ }
11723
+ if (indexConfig.byType) {
11724
+ indexes.byType = /* @__PURE__ */ new Map();
11725
+ }
11726
+ if (indexConfig.byRelation) {
11727
+ indexes.byRelation = /* @__PURE__ */ new Map();
11728
+ }
11729
+ if (indexConfig.bySubcircuit) {
11730
+ indexes.bySubcircuit = /* @__PURE__ */ new Map();
11731
+ }
11732
+ if (indexConfig.byCustomField && indexConfig.byCustomField.length > 0) {
11733
+ indexes.byCustomField = /* @__PURE__ */ new Map();
11734
+ for (const field of indexConfig.byCustomField) {
11735
+ indexes.byCustomField.set(field, /* @__PURE__ */ new Map());
11736
+ }
11737
+ }
11738
+ for (const element of soup) {
11739
+ if (indexConfig.byId) {
11740
+ const idKey = createIdKey(element);
11741
+ indexes.byId.set(idKey, element);
11742
+ }
11743
+ if (indexConfig.byType) {
11744
+ const elementsOfType = indexes.byType.get(element.type) || [];
11745
+ elementsOfType.push(element);
11746
+ indexes.byType.set(element.type, elementsOfType);
11747
+ }
11748
+ if (indexConfig.byRelation) {
11749
+ const elementEntries = Object.entries(element);
11750
+ for (const [key, value] of elementEntries) {
11751
+ if (key.endsWith("_id") && key !== `${element.type}_id` && typeof value === "string") {
11752
+ const relationTypeMap = indexes.byRelation.get(key) || /* @__PURE__ */ new Map();
11753
+ const relatedElements = relationTypeMap.get(value) || [];
11754
+ relatedElements.push(element);
11755
+ relationTypeMap.set(value, relatedElements);
11756
+ indexes.byRelation.set(key, relationTypeMap);
11757
+ }
11758
+ }
11759
+ }
11760
+ if (indexConfig.bySubcircuit && "subcircuit_id" in element) {
11761
+ const subcircuitId = element.subcircuit_id;
11762
+ if (subcircuitId && typeof subcircuitId === "string") {
11763
+ const subcircuitElements = indexes.bySubcircuit.get(subcircuitId) || [];
11764
+ subcircuitElements.push(element);
11765
+ indexes.bySubcircuit.set(subcircuitId, subcircuitElements);
11766
+ }
11767
+ }
11768
+ if (indexConfig.byCustomField && indexes.byCustomField) {
11769
+ for (const field of indexConfig.byCustomField) {
11770
+ if (field in element) {
11771
+ const fieldValue = element[field];
11772
+ if (fieldValue !== void 0 && (typeof fieldValue === "string" || typeof fieldValue === "number")) {
11773
+ const fieldValueStr = String(fieldValue);
11774
+ const fieldMap = indexes.byCustomField.get(field);
11775
+ const elementsWithFieldValue = fieldMap.get(fieldValueStr) || [];
11776
+ elementsWithFieldValue.push(element);
11777
+ fieldMap.set(fieldValueStr, elementsWithFieldValue);
11778
+ }
11779
+ }
11780
+ }
11781
+ }
11782
+ }
11783
+ ;
11784
+ soup._internal_store_indexed = internalStore;
11785
+ }
11786
+ const suIndexed = new Proxy(
11787
+ {},
11788
+ {
11789
+ get: (proxy_target, prop) => {
11790
+ if (prop === "toArray") {
11791
+ return () => {
11792
+ ;
11793
+ soup.editCount = internalStore.editCount;
11794
+ return soup;
11795
+ };
11796
+ }
11797
+ if (prop === "editCount") {
11798
+ return internalStore.editCount;
11799
+ }
11800
+ const component_type = prop;
11801
+ return {
11802
+ get: (id) => {
11803
+ const indexConfig = options.indexConfig || {};
11804
+ if (indexConfig.byId && internalStore.indexes.byId) {
11805
+ return internalStore.indexes.byId.get(
11806
+ `${component_type}:${id}`
11807
+ ) || null;
11808
+ }
11809
+ if (indexConfig.byType && internalStore.indexes.byType) {
11810
+ const elementsOfType = internalStore.indexes.byType.get(component_type) || [];
11811
+ return elementsOfType.find(
11812
+ (e) => e[`${component_type}_id`] === id
11813
+ ) || null;
11814
+ }
11815
+ return soup.find(
11816
+ (e) => e.type === component_type && e[`${component_type}_id`] === id
11817
+ ) || null;
11818
+ },
11819
+ getUsing: (using) => {
11820
+ const indexConfig = options.indexConfig || {};
11821
+ const keys = Object.keys(using);
11822
+ if (keys.length !== 1) {
11823
+ throw new Error(
11824
+ "getUsing requires exactly one key, e.g. { pcb_component_id }"
11825
+ );
11826
+ }
11827
+ const join_key = keys[0];
11828
+ const join_type = join_key.replace("_id", "");
11829
+ if (indexConfig.byRelation && internalStore.indexes.byRelation) {
11830
+ const relationMap = internalStore.indexes.byRelation.get(join_key);
11831
+ if (relationMap) {
11832
+ const relatedElements = relationMap.get(using[join_key]) || [];
11833
+ const joiner2 = relatedElements.find((e) => e.type === join_type);
11834
+ if (!joiner2) return null;
11835
+ const joinerId = joiner2[`${component_type}_id`];
11836
+ if (indexConfig.byId && internalStore.indexes.byId) {
11837
+ return internalStore.indexes.byId.get(
11838
+ `${component_type}:${joinerId}`
11839
+ ) || null;
11840
+ }
11841
+ if (indexConfig.byType && internalStore.indexes.byType) {
11842
+ const elementsOfType = internalStore.indexes.byType.get(component_type) || [];
11843
+ return elementsOfType.find(
11844
+ (e) => e[`${component_type}_id`] === joinerId
11845
+ ) || null;
11846
+ }
11847
+ return soup.find(
11848
+ (e) => e.type === component_type && e[`${component_type}_id`] === joinerId
11849
+ ) || null;
11850
+ }
11851
+ }
11852
+ const joiner = soup.find(
11853
+ (e) => e.type === join_type && e[join_key] === using[join_key]
11854
+ );
11855
+ if (!joiner) return null;
11856
+ return soup.find(
11857
+ (e) => e.type === component_type && e[`${component_type}_id`] === joiner[`${component_type}_id`]
11858
+ ) || null;
11859
+ },
11860
+ getWhere: (where) => {
11861
+ const indexConfig = options.indexConfig || {};
11862
+ const keys = Object.keys(where);
11863
+ if (keys.length === 1 && indexConfig.byCustomField && internalStore.indexes.byCustomField) {
11864
+ const field = keys[0];
11865
+ const fieldMap = internalStore.indexes.byCustomField.get(field);
11866
+ if (fieldMap) {
11867
+ const fieldValue = String(where[field]);
11868
+ const elementsWithFieldValue = fieldMap.get(fieldValue) || [];
11869
+ return elementsWithFieldValue.find(
11870
+ (e) => e.type === component_type
11871
+ ) || null;
11872
+ }
11873
+ }
11874
+ if ("subcircuit_id" in where && indexConfig.bySubcircuit && internalStore.indexes.bySubcircuit) {
11875
+ const subcircuitId = where.subcircuit_id;
11876
+ const subcircuitElements = internalStore.indexes.bySubcircuit.get(subcircuitId) || [];
11877
+ return subcircuitElements.find(
11878
+ (e) => e.type === component_type && keys.every((key) => e[key] === where[key])
11879
+ ) || null;
11880
+ }
11881
+ if (indexConfig.byType && internalStore.indexes.byType) {
11882
+ const elementsOfType = internalStore.indexes.byType.get(component_type) || [];
11883
+ return elementsOfType.find(
11884
+ (e) => keys.every((key) => e[key] === where[key])
11885
+ ) || null;
11886
+ }
11887
+ return soup.find(
11888
+ (e) => e.type === component_type && keys.every((key) => e[key] === where[key])
11889
+ ) || null;
11890
+ },
11891
+ list: (where) => {
11892
+ const indexConfig = options.indexConfig || {};
11893
+ const keys = !where ? [] : Object.keys(where);
11894
+ if (keys.length === 0 && indexConfig.byType && internalStore.indexes.byType) {
11895
+ return internalStore.indexes.byType.get(component_type) || [];
11896
+ }
11897
+ if (keys.length === 1 && keys[0] === "subcircuit_id" && indexConfig.bySubcircuit && internalStore.indexes.bySubcircuit) {
11898
+ const subcircuitId = where.subcircuit_id;
11899
+ const subcircuitElements = internalStore.indexes.bySubcircuit.get(subcircuitId) || [];
11900
+ return subcircuitElements.filter(
11901
+ (e) => e.type === component_type
11902
+ );
11903
+ }
11904
+ let elementsToFilter;
11905
+ if (indexConfig.byType && internalStore.indexes.byType) {
11906
+ elementsToFilter = internalStore.indexes.byType.get(component_type) || [];
11907
+ } else {
11908
+ elementsToFilter = soup.filter((e) => e.type === component_type);
11909
+ }
11910
+ if (keys.length > 0) {
11911
+ return elementsToFilter.filter(
11912
+ (e) => keys.every((key) => e[key] === where[key])
11913
+ );
11914
+ }
11915
+ return elementsToFilter;
11916
+ },
11917
+ insert: (elm) => {
11918
+ internalStore.counts[component_type] ??= -1;
11919
+ internalStore.counts[component_type]++;
11920
+ const index = internalStore.counts[component_type];
11921
+ const newElm = {
11922
+ type: component_type,
11923
+ [`${component_type}_id`]: `${component_type}_${index}`,
11924
+ ...elm
11925
+ };
11926
+ if (options.validateInserts) {
11927
+ const parser = dist_exports[component_type] ?? any_soup_element;
11928
+ parser.parse(newElm);
11929
+ }
11930
+ soup.push(newElm);
11931
+ internalStore.editCount++;
11932
+ const indexConfig = options.indexConfig || {};
11933
+ if (indexConfig.byId && internalStore.indexes.byId) {
11934
+ const idKey = createIdKey(newElm);
11935
+ internalStore.indexes.byId.set(idKey, newElm);
11936
+ }
11937
+ if (indexConfig.byType && internalStore.indexes.byType) {
11938
+ const elementsOfType = internalStore.indexes.byType.get(component_type) || [];
11939
+ elementsOfType.push(newElm);
11940
+ internalStore.indexes.byType.set(component_type, elementsOfType);
11941
+ }
11942
+ if (indexConfig.byRelation && internalStore.indexes.byRelation) {
11943
+ const elementEntries = Object.entries(newElm);
11944
+ for (const [key, value] of elementEntries) {
11945
+ if (key.endsWith("_id") && key !== `${newElm.type}_id` && typeof value === "string") {
11946
+ const relationTypeMap = internalStore.indexes.byRelation.get(key) || /* @__PURE__ */ new Map();
11947
+ const relatedElements = relationTypeMap.get(value) || [];
11948
+ relatedElements.push(newElm);
11949
+ relationTypeMap.set(value, relatedElements);
11950
+ internalStore.indexes.byRelation.set(key, relationTypeMap);
11951
+ }
11952
+ }
11953
+ }
11954
+ if (indexConfig.bySubcircuit && internalStore.indexes.bySubcircuit && "subcircuit_id" in newElm) {
11955
+ const subcircuitId = newElm.subcircuit_id;
11956
+ if (subcircuitId && typeof subcircuitId === "string") {
11957
+ const subcircuitElements = internalStore.indexes.bySubcircuit.get(subcircuitId) || [];
11958
+ subcircuitElements.push(newElm);
11959
+ internalStore.indexes.bySubcircuit.set(
11960
+ subcircuitId,
11961
+ subcircuitElements
11962
+ );
11963
+ }
11964
+ }
11965
+ if (indexConfig.byCustomField && internalStore.indexes.byCustomField) {
11966
+ for (const field of indexConfig.byCustomField) {
11967
+ if (field in newElm) {
11968
+ const fieldValue = newElm[field];
11969
+ if (fieldValue !== void 0 && (typeof fieldValue === "string" || typeof fieldValue === "number")) {
11970
+ const fieldValueStr = String(fieldValue);
11971
+ const fieldMap = internalStore.indexes.byCustomField.get(field);
11972
+ const elementsWithFieldValue = fieldMap.get(fieldValueStr) || [];
11973
+ elementsWithFieldValue.push(newElm);
11974
+ fieldMap.set(fieldValueStr, elementsWithFieldValue);
11975
+ }
11976
+ }
11977
+ }
11978
+ }
11979
+ return newElm;
11980
+ },
11981
+ delete: (id) => {
11982
+ const indexConfig = options.indexConfig || {};
11983
+ let elm;
11984
+ if (indexConfig.byId && internalStore.indexes.byId) {
11985
+ elm = internalStore.indexes.byId.get(`${component_type}:${id}`);
11986
+ } else if (indexConfig.byType && internalStore.indexes.byType) {
11987
+ const elementsOfType = internalStore.indexes.byType.get(component_type) || [];
11988
+ elm = elementsOfType.find(
11989
+ (e) => e[`${component_type}_id`] === id
11990
+ );
11991
+ } else {
11992
+ elm = soup.find((e) => e[`${component_type}_id`] === id);
11993
+ }
11994
+ if (!elm) return;
11995
+ const elmIndex = soup.indexOf(elm);
11996
+ if (elmIndex >= 0) {
11997
+ soup.splice(elmIndex, 1);
11998
+ internalStore.editCount++;
11999
+ }
12000
+ if (indexConfig.byId && internalStore.indexes.byId) {
12001
+ const idKey = createIdKey(elm);
12002
+ internalStore.indexes.byId.delete(idKey);
12003
+ }
12004
+ if (indexConfig.byType && internalStore.indexes.byType) {
12005
+ const elementsOfType = internalStore.indexes.byType.get(component_type) || [];
12006
+ const filteredElements = elementsOfType.filter(
12007
+ (e) => e[`${component_type}_id`] !== id
12008
+ );
12009
+ internalStore.indexes.byType.set(component_type, filteredElements);
12010
+ }
12011
+ if (indexConfig.byRelation && internalStore.indexes.byRelation) {
12012
+ for (const [
12013
+ relationKey,
12014
+ relationMap
12015
+ ] of internalStore.indexes.byRelation.entries()) {
12016
+ for (const [relationValue, elements] of relationMap.entries()) {
12017
+ const updatedElements = elements.filter((e) => e !== elm);
12018
+ if (updatedElements.length === 0) {
12019
+ relationMap.delete(relationValue);
12020
+ } else {
12021
+ relationMap.set(relationValue, updatedElements);
12022
+ }
12023
+ }
12024
+ }
12025
+ }
12026
+ if (indexConfig.bySubcircuit && internalStore.indexes.bySubcircuit && "subcircuit_id" in elm) {
12027
+ const subcircuitId = elm.subcircuit_id;
12028
+ if (subcircuitId) {
12029
+ const subcircuitElements = internalStore.indexes.bySubcircuit.get(subcircuitId) || [];
12030
+ const updatedElements = subcircuitElements.filter(
12031
+ (e) => e !== elm
12032
+ );
12033
+ if (updatedElements.length === 0) {
12034
+ internalStore.indexes.bySubcircuit.delete(subcircuitId);
12035
+ } else {
12036
+ internalStore.indexes.bySubcircuit.set(
12037
+ subcircuitId,
12038
+ updatedElements
12039
+ );
12040
+ }
12041
+ }
12042
+ }
12043
+ if (indexConfig.byCustomField && internalStore.indexes.byCustomField) {
12044
+ for (const fieldMap of internalStore.indexes.byCustomField.values()) {
12045
+ for (const [fieldValue, elements] of fieldMap.entries()) {
12046
+ const updatedElements = elements.filter((e) => e !== elm);
12047
+ if (updatedElements.length === 0) {
12048
+ fieldMap.delete(fieldValue);
12049
+ } else {
12050
+ fieldMap.set(fieldValue, updatedElements);
12051
+ }
12052
+ }
12053
+ }
12054
+ }
12055
+ },
12056
+ update: (id, newProps) => {
12057
+ const indexConfig = options.indexConfig || {};
12058
+ let elm;
12059
+ if (indexConfig.byId && internalStore.indexes.byId) {
12060
+ elm = internalStore.indexes.byId.get(`${component_type}:${id}`);
12061
+ } else if (indexConfig.byType && internalStore.indexes.byType) {
12062
+ const elementsOfType = internalStore.indexes.byType.get(component_type) || [];
12063
+ elm = elementsOfType.find(
12064
+ (e) => e[`${component_type}_id`] === id
12065
+ );
12066
+ } else {
12067
+ elm = soup.find(
12068
+ (e) => e.type === component_type && e[`${component_type}_id`] === id
12069
+ );
12070
+ }
12071
+ if (!elm) return null;
12072
+ if (indexConfig.byRelation && internalStore.indexes.byRelation) {
12073
+ const elementEntries = Object.entries(elm);
12074
+ for (const [key, value] of elementEntries) {
12075
+ if (key.endsWith("_id") && key !== `${elm.type}_id` && typeof value === "string") {
12076
+ if (key in newProps && newProps[key] !== value) {
12077
+ const relationTypeMap = internalStore.indexes.byRelation.get(key);
12078
+ if (relationTypeMap) {
12079
+ const relatedElements = relationTypeMap.get(value) || [];
12080
+ const updatedElements = relatedElements.filter(
12081
+ (e) => e !== elm
12082
+ );
12083
+ if (updatedElements.length === 0) {
12084
+ relationTypeMap.delete(value);
12085
+ } else {
12086
+ relationTypeMap.set(value, updatedElements);
12087
+ }
12088
+ }
12089
+ }
12090
+ }
12091
+ }
12092
+ }
12093
+ if (indexConfig.bySubcircuit && internalStore.indexes.bySubcircuit && "subcircuit_id" in elm && "subcircuit_id" in newProps) {
12094
+ const oldSubcircuitId = elm.subcircuit_id;
12095
+ const newSubcircuitId = newProps.subcircuit_id;
12096
+ if (oldSubcircuitId !== newSubcircuitId) {
12097
+ const subcircuitElements = internalStore.indexes.bySubcircuit.get(oldSubcircuitId) || [];
12098
+ const updatedElements = subcircuitElements.filter(
12099
+ (e) => e !== elm
12100
+ );
12101
+ if (updatedElements.length === 0) {
12102
+ internalStore.indexes.bySubcircuit.delete(oldSubcircuitId);
12103
+ } else {
12104
+ internalStore.indexes.bySubcircuit.set(
12105
+ oldSubcircuitId,
12106
+ updatedElements
12107
+ );
12108
+ }
12109
+ }
12110
+ }
12111
+ if (indexConfig.byCustomField && internalStore.indexes.byCustomField) {
12112
+ for (const field of indexConfig.byCustomField) {
12113
+ if (field in elm && field in newProps && elm[field] !== newProps[field]) {
12114
+ const fieldMap = internalStore.indexes.byCustomField.get(field);
12115
+ if (fieldMap) {
12116
+ const oldValue = String(elm[field]);
12117
+ const elements = fieldMap.get(oldValue) || [];
12118
+ const updatedElements = elements.filter((e) => e !== elm);
12119
+ if (updatedElements.length === 0) {
12120
+ fieldMap.delete(oldValue);
12121
+ } else {
12122
+ fieldMap.set(oldValue, updatedElements);
12123
+ }
12124
+ }
12125
+ }
12126
+ }
12127
+ }
12128
+ Object.assign(elm, newProps);
12129
+ internalStore.editCount++;
12130
+ if (indexConfig.byRelation && internalStore.indexes.byRelation) {
12131
+ const elementEntries = Object.entries(elm);
12132
+ for (const [key, value] of elementEntries) {
12133
+ if (key.endsWith("_id") && key !== `${elm.type}_id` && typeof value === "string") {
12134
+ if (key in newProps) {
12135
+ const relationTypeMap = internalStore.indexes.byRelation.get(key) || /* @__PURE__ */ new Map();
12136
+ const relatedElements = relationTypeMap.get(value) || [];
12137
+ if (!relatedElements.includes(elm)) {
12138
+ relatedElements.push(elm);
12139
+ relationTypeMap.set(value, relatedElements);
12140
+ internalStore.indexes.byRelation.set(key, relationTypeMap);
12141
+ }
12142
+ }
12143
+ }
12144
+ }
12145
+ }
12146
+ if (indexConfig.bySubcircuit && internalStore.indexes.bySubcircuit && "subcircuit_id" in elm && "subcircuit_id" in newProps) {
12147
+ const subcircuitId = elm.subcircuit_id;
12148
+ if (subcircuitId && typeof subcircuitId === "string") {
12149
+ const subcircuitElements = internalStore.indexes.bySubcircuit.get(subcircuitId) || [];
12150
+ if (!subcircuitElements.includes(elm)) {
12151
+ subcircuitElements.push(elm);
12152
+ internalStore.indexes.bySubcircuit.set(
12153
+ subcircuitId,
12154
+ subcircuitElements
12155
+ );
12156
+ }
12157
+ }
12158
+ }
12159
+ if (indexConfig.byCustomField && internalStore.indexes.byCustomField) {
12160
+ for (const field of indexConfig.byCustomField) {
12161
+ if (field in elm && field in newProps) {
12162
+ const fieldValue = elm[field];
12163
+ if (fieldValue !== void 0 && (typeof fieldValue === "string" || typeof fieldValue === "number")) {
12164
+ const fieldValueStr = String(fieldValue);
12165
+ const fieldMap = internalStore.indexes.byCustomField.get(field);
12166
+ const elementsWithFieldValue = fieldMap.get(fieldValueStr) || [];
12167
+ if (!elementsWithFieldValue.includes(elm)) {
12168
+ elementsWithFieldValue.push(elm);
12169
+ fieldMap.set(fieldValueStr, elementsWithFieldValue);
12170
+ }
12171
+ }
12172
+ }
12173
+ }
12174
+ }
12175
+ return elm;
12176
+ },
12177
+ select: (selector) => {
12178
+ if (component_type === "source_component") {
12179
+ return soup.find(
12180
+ (e) => e.type === "source_component" && e.name === selector.replace(/\./g, "")
12181
+ ) || null;
11583
12182
  } else if (component_type === "pcb_port" || component_type === "source_port" || component_type === "schematic_port") {
11584
12183
  const [component_name, port_selector] = selector.replace(/\./g, "").split(/[\s\>]+/);
11585
12184
  const source_component = soup.find(
11586
12185
  (e) => e.type === "source_component" && e.name === component_name
11587
12186
  );
11588
- if (!source_component)
11589
- return null;
12187
+ if (!source_component) return null;
11590
12188
  const source_port2 = soup.find(
11591
12189
  (e) => e.type === "source_port" && e.source_component_id === source_component.source_component_id && (e.name === port_selector || (e.port_hints ?? []).includes(port_selector))
11592
12190
  );
11593
- if (!source_port2)
11594
- return null;
12191
+ if (!source_port2) return null;
11595
12192
  if (component_type === "source_port")
11596
12193
  return source_port2;
11597
12194
  if (component_type === "pcb_port") {
11598
12195
  return soup.find(
11599
12196
  (e) => e.type === "pcb_port" && e.source_port_id === source_port2.source_port_id
11600
- );
12197
+ ) || null;
11601
12198
  } else if (component_type === "schematic_port") {
11602
12199
  return soup.find(
11603
12200
  (e) => e.type === "schematic_port" && e.source_port_id === source_port2.source_port_id
11604
- );
12201
+ ) || null;
11605
12202
  }
11606
12203
  }
12204
+ return null;
11607
12205
  }
11608
12206
  };
11609
12207
  }
11610
12208
  }
11611
12209
  );
11612
- return su2;
12210
+ return suIndexed;
11613
12211
  };
11614
- su.unparsed = su;
11615
- var su_default = su;
12212
+ cjuIndexed.unparsed = cjuIndexed;
11616
12213
  var getElementId = (elm) => {
11617
12214
  const type = elm.type;
11618
12215
  const id = elm[`${type}_id`];
@@ -11624,24 +12221,45 @@ var getBoundsOfPcbElements = (elements) => {
11624
12221
  let maxX = Number.NEGATIVE_INFINITY;
11625
12222
  let maxY = Number.NEGATIVE_INFINITY;
11626
12223
  for (const elm of elements) {
11627
- if (!elm.type.startsWith("pcb_"))
11628
- continue;
12224
+ if (!elm.type.startsWith("pcb_")) continue;
12225
+ let centerX;
12226
+ let centerY;
12227
+ let width;
12228
+ let height;
11629
12229
  if ("x" in elm && "y" in elm) {
11630
- minX = Math.min(minX, elm.x);
11631
- minY = Math.min(minY, elm.y);
11632
- maxX = Math.max(maxX, elm.x);
11633
- maxY = Math.max(maxY, elm.y);
11634
- if ("width" in elm) {
11635
- maxX = Math.max(maxX, elm.x + elm.width);
11636
- }
11637
- if ("height" in elm) {
11638
- maxY = Math.max(maxY, elm.y + elm.height);
12230
+ centerX = Number(elm.x);
12231
+ centerY = Number(elm.y);
12232
+ }
12233
+ if ("outer_diameter" in elm) {
12234
+ width = Number(elm.outer_diameter);
12235
+ height = Number(elm.outer_diameter);
12236
+ }
12237
+ if ("width" in elm) {
12238
+ width = Number(elm.width);
12239
+ }
12240
+ if ("height" in elm) {
12241
+ height = Number(elm.height);
12242
+ }
12243
+ if ("center" in elm) {
12244
+ centerX = elm.center.x;
12245
+ centerY = elm.center.y;
12246
+ }
12247
+ if (centerX !== void 0 && centerY !== void 0) {
12248
+ minX = Math.min(minX, centerX);
12249
+ minY = Math.min(minY, centerY);
12250
+ maxX = Math.max(maxX, centerX);
12251
+ maxY = Math.max(maxY, centerY);
12252
+ if (width !== void 0 && height !== void 0) {
12253
+ minX = Math.min(minX, centerX - width / 2);
12254
+ minY = Math.min(minY, centerY - height / 2);
12255
+ maxX = Math.max(maxX, centerX + width / 2);
12256
+ maxY = Math.max(maxY, centerY + height / 2);
11639
12257
  }
11640
12258
  if ("radius" in elm) {
11641
- minX = Math.min(minX, elm.x - elm.radius);
11642
- minY = Math.min(minY, elm.y - elm.radius);
11643
- maxX = Math.max(maxX, elm.x + elm.radius);
11644
- maxY = Math.max(maxY, elm.y + elm.radius);
12259
+ minX = Math.min(minX, centerX - elm.radius);
12260
+ minY = Math.min(minY, centerY - elm.radius);
12261
+ maxX = Math.max(maxX, centerX + elm.radius);
12262
+ maxY = Math.max(maxY, centerY + elm.radius);
11645
12263
  }
11646
12264
  } else if (elm.type === "pcb_trace") {
11647
12265
  for (const point2 of elm.route) {
@@ -11656,8 +12274,7 @@ var getBoundsOfPcbElements = (elements) => {
11656
12274
  };
11657
12275
  function stringHash(str) {
11658
12276
  let hash = 0;
11659
- if (str.length == 0)
11660
- return hash;
12277
+ if (str.length == 0) return hash;
11661
12278
  for (var i = 0; i < str.length; i++) {
11662
12279
  var char = str.charCodeAt(i);
11663
12280
  hash = (hash << 5) - hash + char;
@@ -11785,10 +12402,21 @@ var getDebugLayoutObject = (lo) => {
11785
12402
  width = Math.abs(lo.x1 - lo.x2);
11786
12403
  height = Math.abs(lo.y1 - lo.y2);
11787
12404
  }
12405
+ if (lo.points && Array.isArray(lo.points) && lo.points.length > 0) {
12406
+ const xCoords = lo.points.map((point2) => point2.x);
12407
+ const yCoords = lo.points.map((point2) => point2.y);
12408
+ const minX = Math.min(...xCoords);
12409
+ const maxX = Math.max(...xCoords);
12410
+ const minY = Math.min(...yCoords);
12411
+ const maxY = Math.max(...yCoords);
12412
+ x = (minX + maxX) / 2;
12413
+ y = (minY + maxY) / 2;
12414
+ width = maxX - minX;
12415
+ height = maxY - minY;
12416
+ }
11788
12417
  const title = lo.text || lo.name || lo.source?.text || lo.source?.name || "?";
11789
12418
  const content = lo;
11790
- if (x === void 0 || y === void 0)
11791
- return null;
12419
+ if (x === void 0 || y === void 0) return null;
11792
12420
  if (width === void 0) {
11793
12421
  if ("outer_diameter" in lo) {
11794
12422
  width = lo.outer_diameter;
@@ -11811,8 +12439,12 @@ var getDebugLayoutObject = (lo) => {
11811
12439
  };
11812
12440
  var isTruthy = (value) => Boolean(value);
11813
12441
  var findBoundsAndCenter = (elements) => {
11814
- const debugObjects = elements.filter((elm) => elm.type.startsWith("pcb_")).concat(
11815
- elements.filter((elm) => elm.type === "pcb_trace").flatMap((elm) => elm.route)
12442
+ const debugObjects = elements.filter(
12443
+ (elm) => elm.type.startsWith("pcb_") || elm.type.startsWith("schematic_")
12444
+ ).concat(
12445
+ elements.filter(
12446
+ (elm) => elm.type === "pcb_trace" || elm.type === "schematic_trace"
12447
+ ).flatMap((elm) => elm.route)
11816
12448
  ).map((elm) => getDebugLayoutObject(elm)).filter(isTruthy);
11817
12449
  if (debugObjects.length === 0)
11818
12450
  return { center: { x: 0, y: 0 }, width: 0, height: 0 };
@@ -12006,10 +12638,10 @@ var useMeasure_default = isBrowser && typeof window.ResizeObserver !== "undefine
12006
12638
  };
12007
12639
 
12008
12640
  // src/PCBViewer.tsx
12009
- import { compose as compose7, scale as scale5, translate as translate7 } from "transformation-matrix";
12641
+ import { compose as compose7, scale as scale5, translate as translate11 } from "transformation-matrix";
12010
12642
 
12011
12643
  // node_modules/use-mouse-matrix-transform/dist/chunk-TGYMZPTI.js
12012
- import { compose, translate, scale } from "transformation-matrix";
12644
+ import { compose, translate as translate5, scale } from "transformation-matrix";
12013
12645
  var computePinchTransform = ({
12014
12646
  initialTransform,
12015
12647
  initialTouch1,
@@ -12040,10 +12672,10 @@ var computePinchTransform = ({
12040
12672
  const deltaX = currentCenter.x - initialCenter.x;
12041
12673
  const deltaY = currentCenter.y - initialCenter.y;
12042
12674
  const pinchTransform = compose(
12043
- translate(deltaX, deltaY),
12044
- translate(initialCenter.x, initialCenter.y),
12675
+ translate5(deltaX, deltaY),
12676
+ translate5(initialCenter.x, initialCenter.y),
12045
12677
  scale(s, s),
12046
- translate(-initialCenter.x, -initialCenter.y),
12678
+ translate5(-initialCenter.x, -initialCenter.y),
12047
12679
  initialTransform
12048
12680
  );
12049
12681
  return pinchTransform;
@@ -12052,7 +12684,7 @@ var computePinchTransform = ({
12052
12684
  // node_modules/use-mouse-matrix-transform/dist/chunk-24EXXVA4.js
12053
12685
  import {
12054
12686
  identity,
12055
- translate as translate2,
12687
+ translate as translate6,
12056
12688
  compose as compose2,
12057
12689
  applyToPoint as applyToPoint2,
12058
12690
  scale as scale2
@@ -12129,7 +12761,7 @@ var useMouseMatrixTransform = (props = {}) => {
12129
12761
  if (props.shouldDrag && !props.shouldDrag(e))
12130
12762
  return;
12131
12763
  m1 = getMousePos(e);
12132
- const new_tf = compose2(translate2(m1.x - m0.x, m1.y - m0.y), init_tf);
12764
+ const new_tf = compose2(translate6(m1.x - m0.x, m1.y - m0.y), init_tf);
12133
12765
  setTransform(new_tf);
12134
12766
  init_tf = new_tf;
12135
12767
  md = false;
@@ -12141,7 +12773,7 @@ var useMouseMatrixTransform = (props = {}) => {
12141
12773
  if (props.shouldDrag && !props.shouldDrag(e))
12142
12774
  return;
12143
12775
  m1 = getMousePos(e);
12144
- const new_tf = compose2(translate2(m1.x - m0.x, m1.y - m0.y), init_tf);
12776
+ const new_tf = compose2(translate6(m1.x - m0.x, m1.y - m0.y), init_tf);
12145
12777
  setTransform(new_tf);
12146
12778
  }
12147
12779
  function handleMouseWheel(e) {
@@ -12151,9 +12783,9 @@ var useMouseMatrixTransform = (props = {}) => {
12151
12783
  return;
12152
12784
  const center = getMousePos(e);
12153
12785
  const new_tf = compose2(
12154
- translate2(center.x, center.y),
12786
+ translate6(center.x, center.y),
12155
12787
  scale2(1 - e.deltaY / 1e3, 1 - e.deltaY / 1e3),
12156
- translate2(-center.x, -center.y),
12788
+ translate6(-center.x, -center.y),
12157
12789
  init_tf
12158
12790
  );
12159
12791
  setTransform(new_tf);
@@ -12173,7 +12805,7 @@ var useMouseMatrixTransform = (props = {}) => {
12173
12805
  }
12174
12806
  md = false;
12175
12807
  m1 = getMousePos(e);
12176
- const new_tf = compose2(translate2(m1.x - m0.x, m1.y - m0.y), init_tf);
12808
+ const new_tf = compose2(translate6(m1.x - m0.x, m1.y - m0.y), init_tf);
12177
12809
  setTransform(new_tf);
12178
12810
  init_tf = new_tf;
12179
12811
  }
@@ -12211,7 +12843,7 @@ var useMouseMatrixTransform = (props = {}) => {
12211
12843
  const deltaX = currentTouch.x - dragDataRef.current.initialTouch.x;
12212
12844
  const deltaY = currentTouch.y - dragDataRef.current.initialTouch.y;
12213
12845
  const new_tf = compose2(
12214
- translate2(deltaX, deltaY),
12846
+ translate6(deltaX, deltaY),
12215
12847
  dragDataRef.current.initialTransform
12216
12848
  );
12217
12849
  setTransform(new_tf);
@@ -12238,7 +12870,7 @@ var useMouseMatrixTransform = (props = {}) => {
12238
12870
  const deltaX = finalTouchPos.x - dragDataRef.current.initialTouch.x;
12239
12871
  const deltaY = finalTouchPos.y - dragDataRef.current.initialTouch.y;
12240
12872
  const new_tf = compose2(
12241
- translate2(deltaX, deltaY),
12873
+ translate6(deltaX, deltaY),
12242
12874
  dragDataRef.current.initialTransform
12243
12875
  );
12244
12876
  setTransform(new_tf);
@@ -12596,7 +13228,7 @@ var convertElementToPrimitives = (element, allElements) => {
12596
13228
  const _parent_source_component = _parent_pcb_component && "source_component_id" in _parent_pcb_component ? allElements.find(
12597
13229
  (elm) => elm.type === "source_component" && elm.source_component_id === _parent_pcb_component.source_component_id
12598
13230
  ) : void 0;
12599
- const _source_port_id = "source_port_id" in element ? element.source_port_id : "pcb_port_id" in element ? su_default(allElements).pcb_port.get(element.pcb_port_id)?.source_port_id : void 0;
13231
+ const _source_port_id = "source_port_id" in element ? element.source_port_id : "pcb_port_id" in element ? su(allElements).pcb_port.get(element.pcb_port_id)?.source_port_id : void 0;
12600
13232
  const _source_port = _source_port_id ? allElements.find(
12601
13233
  (e) => e.type === "source_port" && e.source_port_id === _source_port_id
12602
13234
  ) : void 0;
@@ -13242,31 +13874,47 @@ var convertElementToPrimitives = (element, allElements) => {
13242
13874
  const pour = element;
13243
13875
  switch (pour.shape) {
13244
13876
  case "rect": {
13877
+ const { center, width, height, layer, rotation: rotation2 } = pour;
13245
13878
  return [
13246
13879
  {
13247
13880
  _pcb_drawing_object_id: getNewPcbDrawingObjectId(
13248
13881
  "pcb_copper_pour_rect"
13249
13882
  ),
13250
13883
  pcb_drawing_type: "rect",
13251
- x: pour.center.x,
13252
- y: pour.center.y,
13253
- w: pour.width,
13254
- h: pour.height,
13255
- layer: pour.layer,
13884
+ x: center.x,
13885
+ y: center.y,
13886
+ w: width,
13887
+ h: height,
13888
+ layer,
13256
13889
  _element: element,
13257
- ccw_rotation: pour.rotation
13890
+ ccw_rotation: rotation2
13258
13891
  }
13259
13892
  ];
13260
13893
  }
13261
13894
  case "polygon": {
13895
+ const { points, layer } = pour;
13262
13896
  return [
13263
13897
  {
13264
13898
  _pcb_drawing_object_id: getNewPcbDrawingObjectId(
13265
13899
  "pcb_copper_pour_polygon"
13266
13900
  ),
13267
13901
  pcb_drawing_type: "polygon",
13268
- points: pour.points,
13269
- layer: pour.layer,
13902
+ points,
13903
+ layer,
13904
+ _element: element
13905
+ }
13906
+ ];
13907
+ }
13908
+ case "brep": {
13909
+ const { brep_shape: brep_shape2, layer } = pour;
13910
+ return [
13911
+ {
13912
+ _pcb_drawing_object_id: getNewPcbDrawingObjectId(
13913
+ "pcb_copper_pour_brep"
13914
+ ),
13915
+ pcb_drawing_type: "polygon_with_arcs",
13916
+ brep_shape: brep_shape2,
13917
+ layer,
13270
13918
  _element: element
13271
13919
  }
13272
13920
  ];
@@ -13354,7 +14002,7 @@ import {
13354
14002
  compose as compose3,
13355
14003
  identity as identity2,
13356
14004
  inverse,
13357
- translate as translate3
14005
+ translate as translate7
13358
14006
  } from "transformation-matrix";
13359
14007
 
13360
14008
  // src/lib/colors.ts
@@ -13671,7 +14319,7 @@ var Drawer = class {
13671
14319
  );
13672
14320
  this.transform = identity2();
13673
14321
  this.transform.d *= -1;
13674
- this.transform = compose3(this.transform, translate3(0, -500));
14322
+ this.transform = compose3(this.transform, translate7(0, -500));
13675
14323
  this.lastPoint = { x: 0, y: 0 };
13676
14324
  this.equip({});
13677
14325
  }
@@ -13912,7 +14560,7 @@ var Drawer = class {
13912
14560
  ctx.lineTo(transformedPoints[i][0], transformedPoints[i][1]);
13913
14561
  }
13914
14562
  ctx.closePath();
13915
- ctx.fill();
14563
+ ctx.fill("evenodd");
13916
14564
  const lineWidth = scaleOnly(this.transform, this.aperture.size);
13917
14565
  ctx.lineWidth = lineWidth;
13918
14566
  ctx.stroke();
@@ -13976,6 +14624,7 @@ var Drawer = class {
13976
14624
  ctx.lineWidth = scaleOnly(transform, size2);
13977
14625
  ctx.lineCap = "round";
13978
14626
  if (mode === "add") {
14627
+ ctx.globalCompositeOperation = "source-over";
13979
14628
  let colorString = color2?.[0] === "#" || color2?.startsWith("rgb") ? color2 : LAYER_NAME_TO_COLOR[color2?.toLowerCase()] ? LAYER_NAME_TO_COLOR[color2?.toLowerCase()] : null;
13980
14629
  if (colorString === null) {
13981
14630
  console.warn(`Color mapping for "${color2}" not found`);
@@ -14017,19 +14666,88 @@ var Drawer = class {
14017
14666
  ctx.fillRect(x$ - size$ / 2, y$ - size$ / 2, size$, size$);
14018
14667
  this.lastPoint = { x, y };
14019
14668
  }
14669
+ polygonWithArcs(brep) {
14670
+ const ctx = this.getLayerCtx();
14671
+ const processRing = (ring2) => {
14672
+ if (ring2.vertices.length === 0) return;
14673
+ const startPoint = ring2.vertices[0];
14674
+ const t_start_point = applyToPoint3(this.transform, [
14675
+ startPoint.x,
14676
+ startPoint.y
14677
+ ]);
14678
+ ctx.moveTo(t_start_point[0], t_start_point[1]);
14679
+ for (let i = 0; i < ring2.vertices.length; i++) {
14680
+ const p1 = ring2.vertices[i];
14681
+ const p2 = ring2.vertices[(i + 1) % ring2.vertices.length];
14682
+ if (p1.bulge && p1.bulge !== 0) {
14683
+ const bulge = p1.bulge;
14684
+ const dx = p2.x - p1.x;
14685
+ const dy = p2.y - p1.y;
14686
+ const chord = Math.sqrt(dx * dx + dy * dy);
14687
+ if (chord < 1e-9) {
14688
+ const t_p2 = applyToPoint3(this.transform, [p2.x, p2.y]);
14689
+ ctx.lineTo(t_p2[0], t_p2[1]);
14690
+ continue;
14691
+ }
14692
+ const angle = 4 * Math.atan(bulge);
14693
+ const radius = Math.abs(chord / (2 * Math.sin(angle / 2)));
14694
+ const mx = (p1.x + p2.x) / 2;
14695
+ const my = (p1.y + p2.y) / 2;
14696
+ const norm_dx = dx / chord;
14697
+ const norm_dy = dy / chord;
14698
+ const perp_vx = -norm_dy;
14699
+ const perp_vy = norm_dx;
14700
+ const dist_to_center = Math.sqrt(
14701
+ Math.max(0, radius * radius - (chord / 2) ** 2)
14702
+ );
14703
+ const cx = mx + dist_to_center * perp_vx * Math.sign(bulge);
14704
+ const cy = my + dist_to_center * perp_vy * Math.sign(bulge);
14705
+ const startAngle = Math.atan2(p1.y - cy, p1.x - cx);
14706
+ let endAngle = Math.atan2(p2.y - cy, p2.x - cx);
14707
+ if (bulge > 0 && endAngle < startAngle) {
14708
+ endAngle += 2 * Math.PI;
14709
+ } else if (bulge < 0 && endAngle > startAngle) {
14710
+ endAngle -= 2 * Math.PI;
14711
+ }
14712
+ const t_center = applyToPoint3(this.transform, [cx, cy]);
14713
+ const t_radius = scaleOnly(this.transform, radius);
14714
+ ctx.arc(
14715
+ t_center[0],
14716
+ t_center[1],
14717
+ t_radius,
14718
+ -startAngle,
14719
+ -endAngle,
14720
+ bulge > 0
14721
+ );
14722
+ } else {
14723
+ const t_p2 = applyToPoint3(this.transform, [p2.x, p2.y]);
14724
+ ctx.lineTo(t_p2[0], t_p2[1]);
14725
+ }
14726
+ }
14727
+ ctx.closePath();
14728
+ };
14729
+ ctx.beginPath();
14730
+ processRing(brep.outer_ring);
14731
+ if (brep.inner_rings) {
14732
+ for (const inner_ring of brep.inner_rings) {
14733
+ processRing(inner_ring);
14734
+ }
14735
+ }
14736
+ ctx.fill("evenodd");
14737
+ }
14020
14738
  };
14021
14739
 
14022
14740
  // src/lib/util/rotate-text.ts
14023
- import { compose as compose4, translate as translate4, rotate, applyToPoint as applyToPoint4 } from "transformation-matrix";
14741
+ import { compose as compose4, translate as translate8, rotate, applyToPoint as applyToPoint4 } from "transformation-matrix";
14024
14742
  function rotateText(rotateTextParams) {
14025
14743
  const { lines, anchorPoint, ccwRotation } = rotateTextParams;
14026
14744
  if (!ccwRotation) return lines;
14027
14745
  const rad = ccwRotation * Math.PI / 180;
14028
14746
  const rotationMatrix = rotate(rad);
14029
14747
  const transform = compose4(
14030
- translate4(anchorPoint.x, anchorPoint.y),
14748
+ translate8(anchorPoint.x, anchorPoint.y),
14031
14749
  rotationMatrix,
14032
- translate4(-anchorPoint.x, -anchorPoint.y)
14750
+ translate8(-anchorPoint.x, -anchorPoint.y)
14033
14751
  );
14034
14752
  applyToPoint4(transform, anchorPoint);
14035
14753
  return lines.map((line) => ({
@@ -14334,17 +15052,25 @@ var drawPolygon = (drawer, polygon) => {
14334
15052
  });
14335
15053
  drawer.polygon(polygon.points);
14336
15054
  };
15055
+ var drawPolygonWithArcs = (drawer, p) => {
15056
+ drawer.equip({
15057
+ color: getColor(p),
15058
+ layer: p.layer
15059
+ });
15060
+ drawer.polygonWithArcs(p.brep_shape);
15061
+ };
14337
15062
  var drawPrimitive = (drawer, primitive) => {
14338
15063
  switch (primitive.pcb_drawing_type) {
14339
15064
  case "line":
14340
15065
  return drawLine(drawer, primitive);
14341
15066
  case "text":
14342
15067
  return drawText(drawer, primitive);
14343
- case "rect":
15068
+ case "rect": {
14344
15069
  if (primitive.ccw_rotation) {
14345
15070
  return drawRotatedRect(drawer, primitive);
14346
15071
  }
14347
15072
  return drawRect(drawer, primitive);
15073
+ }
14348
15074
  case "circle":
14349
15075
  return drawCircle(drawer, primitive);
14350
15076
  case "oval":
@@ -14356,6 +15082,8 @@ var drawPrimitive = (drawer, primitive) => {
14356
15082
  return drawPill(drawer, primitive);
14357
15083
  case "polygon":
14358
15084
  return drawPolygon(drawer, primitive);
15085
+ case "polygon_with_arcs":
15086
+ return drawPolygonWithArcs(drawer, primitive);
14359
15087
  }
14360
15088
  };
14361
15089
  var drawPrimitives = (drawer, primitives) => {
@@ -14457,7 +15185,7 @@ var import_svgson = __toESM(require_svgson_umd(), 1);
14457
15185
  var import_pretty = __toESM(require_pretty(), 1);
14458
15186
  import {
14459
15187
  compose as compose5,
14460
- translate as translate5,
15188
+ translate as translate9,
14461
15189
  scale as scale3,
14462
15190
  applyToPoint as applyToPoint5
14463
15191
  } from "transformation-matrix";
@@ -14466,7 +15194,7 @@ import {
14466
15194
  import {
14467
15195
  compose as compose6,
14468
15196
  scale as scale4,
14469
- translate as translate6,
15197
+ translate as translate10,
14470
15198
  applyToPoint as applyToPoint6
14471
15199
  } from "transformation-matrix";
14472
15200
  var defaultColors = [
@@ -14530,9 +15258,9 @@ function computeTransformFromViewbox(viewbox, canvasWidth, canvasHeight, options
14530
15258
  (canvasHeight - 2 * padding) / height
14531
15259
  );
14532
15260
  return compose6(
14533
- translate6(canvasWidth / 2, canvasHeight / 2),
15261
+ translate10(canvasWidth / 2, canvasHeight / 2),
14534
15262
  scale4(scale_factor, yFlip ? -scale_factor : scale_factor),
14535
- translate6(-(bounds.minX + width / 2), -(bounds.minY + height / 2))
15263
+ translate10(-(bounds.minX + width / 2), -(bounds.minY + height / 2))
14536
15264
  );
14537
15265
  }
14538
15266
  function getBounds(graphics) {
@@ -15502,7 +16230,7 @@ var EditTraceHintOverlay = ({
15502
16230
  toast.error(`pcb_port_id is null on the selected "${e2.type}"`);
15503
16231
  return;
15504
16232
  }
15505
- const pcb_port2 = su_default(soup).pcb_port.get(e2.pcb_port_id);
16233
+ const pcb_port2 = su(soup).pcb_port.get(e2.pcb_port_id);
15506
16234
  if (!pcb_port2) {
15507
16235
  toast.error(
15508
16236
  `Could not find associated pcb_port for "${e2.pcb_port_id}"`
@@ -15661,7 +16389,7 @@ var EditTraceHintOverlay = ({
15661
16389
  height: containerBounds?.height,
15662
16390
  children: soup.filter((e) => e.type === "pcb_trace_hint").map((e) => {
15663
16391
  const { route } = e;
15664
- const pcb_port2 = su_default(soup).pcb_port.get(e.pcb_port_id);
16392
+ const pcb_port2 = su(soup).pcb_port.get(e.pcb_port_id);
15665
16393
  const pcb_port_screen = applyToPoint10(transform, pcb_port2);
15666
16394
  return /* @__PURE__ */ jsxs7(Fragment2, { children: [
15667
16395
  /* @__PURE__ */ jsx9(
@@ -15926,7 +16654,7 @@ var ErrorOverlay = ({
15926
16654
  const { pcb_port_ids, pcb_trace_id } = el;
15927
16655
  const port1 = pcb_port_ids?.[0] ? portsMap.get(pcb_port_ids[0]) : void 0;
15928
16656
  const port2 = pcb_port_ids?.[1] ? portsMap.get(pcb_port_ids[1]) : void 0;
15929
- const trace = elements ? su_default(elements).pcb_trace.get(pcb_trace_id) : void 0;
16657
+ const trace = elements ? su(elements).pcb_trace.get(pcb_trace_id) : void 0;
15930
16658
  const errorId = el.pcb_trace_error_id || `error_${index}_${el.error_type}_${el.message?.slice(0, 20)}`;
15931
16659
  const isHighlighted = hoveredErrorId === errorId;
15932
16660
  if (port1 && port2) {
@@ -16265,7 +16993,7 @@ function getTraceOverlayInfo({
16265
16993
  elements
16266
16994
  }) {
16267
16995
  let text = primitiveElement.trace_length ? `${primitiveElement.trace_length.toFixed(3)}` : "";
16268
- const trace = su_default(elements).source_trace.get(
16996
+ const trace = su(elements).source_trace.get(
16269
16997
  primitiveElement?.source_trace_id
16270
16998
  );
16271
16999
  if (trace?.display_name) {
@@ -16881,7 +17609,7 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
16881
17609
  const ratsNestLines = useMemo4(() => {
16882
17610
  if (!soup || !isShowingRatsNest) return [];
16883
17611
  const getElementPosition = (id) => {
16884
- const element = su_default(soup)[id.replace(/_\d+$/, "")].get(id);
17612
+ const element = su(soup)[id.replace(/_\d+$/, "")].get(id);
16885
17613
  if (element && "x" in element && "y" in element) {
16886
17614
  return { x: element.x, y: element.y };
16887
17615
  }
@@ -16905,8 +17633,8 @@ var RatsNestOverlay = ({ transform, soup, children }) => {
16905
17633
  });
16906
17634
  return nearestPoint;
16907
17635
  };
16908
- const pcbPorts = su_default(soup).pcb_port.list();
16909
- const sourceTraces = su_default(soup).source_trace.list();
17636
+ const pcbPorts = su(soup).pcb_port.list();
17637
+ const sourceTraces = su(soup).source_trace.list();
16910
17638
  const lines = [];
16911
17639
  pcbPorts.forEach((port, index) => {
16912
17640
  const netId = idToNetMap[port.pcb_port_id];
@@ -16983,7 +17711,7 @@ import { css as css3 } from "@emotion/css";
16983
17711
  // package.json
16984
17712
  var package_default = {
16985
17713
  name: "@tscircuit/pcb-viewer",
16986
- version: "1.11.217",
17714
+ version: "1.11.219",
16987
17715
  main: "dist/index.js",
16988
17716
  type: "module",
16989
17717
  repository: "tscircuit/pcb-viewer",
@@ -17007,8 +17735,9 @@ var package_default = {
17007
17735
  "@semantic-release/npm": "^9.0.1",
17008
17736
  "@semantic-release/release-notes-generator": "^10.0.3",
17009
17737
  "@swc/core": "^1.4.12",
17738
+ "@tscircuit/circuit-json-util": "^0.0.67",
17010
17739
  "@tscircuit/eagle-xml-converter": "^1.0.0",
17011
- "@tscircuit/soup-util": "^0.0.41",
17740
+ "@types/bun": "latest",
17012
17741
  "@types/color": "^3.0.6",
17013
17742
  "@types/node": "18.7.23",
17014
17743
  "@types/react": "19",
@@ -17024,8 +17753,7 @@ var package_default = {
17024
17753
  "type-fest": "^3.0.0",
17025
17754
  typescript: "^5.4.4",
17026
17755
  "use-mouse-matrix-transform": "^1.3.4",
17027
- zod: "^3.23.5",
17028
- "@types/bun": "latest"
17756
+ zod: "^3.23.5"
17029
17757
  },
17030
17758
  peerDependencies: {
17031
17759
  react: "*",
@@ -17896,7 +18624,7 @@ var calculateCircuitJsonKey = (circuitJson) => {
17896
18624
 
17897
18625
  // src/PCBViewer.tsx
17898
18626
  import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
17899
- var defaultTransform = compose7(translate7(400, 300), scale5(40, -40));
18627
+ var defaultTransform = compose7(translate11(400, 300), scale5(40, -40));
17900
18628
  var PCBViewer = ({
17901
18629
  circuitJson,
17902
18630
  debugGraphics,
@@ -17944,9 +18672,9 @@ var PCBViewer = ({
17944
18672
  100
17945
18673
  ) * 0.75;
17946
18674
  const targetTransform = compose7(
17947
- translate7((elmBounds.width ?? 0) / 2, (elmBounds.height ?? 0) / 2),
18675
+ translate11((elmBounds.width ?? 0) / 2, (elmBounds.height ?? 0) / 2),
17948
18676
  scale5(scaleFactor, -scaleFactor, 0, 0),
17949
- translate7(-center.x, -center.y)
18677
+ translate11(-center.x, -center.y)
17950
18678
  );
17951
18679
  setTransform(targetTransform);
17952
18680
  return;