@tscircuit/core 0.0.97 → 0.0.99

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +139 -140
  2. package/dist/index.js +860 -843
  3. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -34,13 +34,12 @@ __export(components_exports, {
34
34
  TraceHint: () => TraceHint
35
35
  });
36
36
 
37
- // lib/components/primitive-components/Footprint.ts
38
- import { footprintProps } from "@tscircuit/props";
37
+ // lib/components/base-components/NormalComponent.ts
38
+ import { rotation } from "circuit-json";
39
+ import { fp } from "footprinter";
39
40
 
40
- // lib/components/base-components/PrimitiveComponent.ts
41
- import { z } from "zod";
42
- import { symbols } from "schematic-symbols";
43
- import "react";
41
+ // lib/fiber/create-instance-from-react-element.ts
42
+ import ReactReconciler from "react-reconciler";
44
43
 
45
44
  // lib/components/base-components/Renderable.ts
46
45
  var orderedRenderPhases = [
@@ -133,12 +132,169 @@ var Renderable = class {
133
132
  }
134
133
  };
135
134
 
135
+ // lib/fiber/catalogue.ts
136
+ var catalogue = {};
137
+ var extendCatalogue = (objects) => {
138
+ const altKeys = Object.fromEntries(
139
+ Object.entries(objects).map(([key, v]) => [key.toLowerCase(), v])
140
+ );
141
+ Object.assign(catalogue, objects);
142
+ Object.assign(catalogue, altKeys);
143
+ };
144
+
145
+ // lib/fiber/create-instance-from-react-element.ts
146
+ import { identity } from "transformation-matrix";
147
+ function prepare(object, state) {
148
+ const instance = object;
149
+ instance.__tsci = {
150
+ ...state
151
+ };
152
+ return object;
153
+ }
154
+ var hostConfig = {
155
+ supportsMutation: true,
156
+ createInstance(type, props) {
157
+ const target = catalogue[type];
158
+ if (!target) {
159
+ if (Object.keys(catalogue).length === 0) {
160
+ throw new Error(
161
+ "No components registered in catalogue, did you forget to import lib/register-catalogue in your test file?"
162
+ );
163
+ }
164
+ throw new Error(
165
+ `Unsupported component type (not registered in @tscircuit/core catalogue): "${type}" See CREATING_NEW_COMPONENTS.md`
166
+ );
167
+ }
168
+ const instance = prepare(new target(props), {});
169
+ return instance;
170
+ },
171
+ createTextInstance() {
172
+ return {};
173
+ },
174
+ appendInitialChild(parentInstance, child) {
175
+ parentInstance.add(child);
176
+ },
177
+ appendChild(parentInstance, child) {
178
+ parentInstance.add(child);
179
+ },
180
+ appendChildToContainer(container, child) {
181
+ container.add(child);
182
+ },
183
+ finalizeInitialChildren() {
184
+ return false;
185
+ },
186
+ prepareUpdate() {
187
+ return null;
188
+ },
189
+ shouldSetTextContent() {
190
+ return false;
191
+ },
192
+ getRootHostContext() {
193
+ return {};
194
+ },
195
+ getChildHostContext() {
196
+ return {};
197
+ },
198
+ prepareForCommit() {
199
+ return null;
200
+ },
201
+ resetAfterCommit() {
202
+ },
203
+ commitMount() {
204
+ },
205
+ commitUpdate() {
206
+ },
207
+ removeChild() {
208
+ },
209
+ clearContainer() {
210
+ },
211
+ supportsPersistence: false,
212
+ getPublicInstance(instance) {
213
+ return instance;
214
+ },
215
+ preparePortalMount(containerInfo) {
216
+ throw new Error("Function not implemented.");
217
+ },
218
+ scheduleTimeout(fn, delay) {
219
+ throw new Error("Function not implemented.");
220
+ },
221
+ cancelTimeout(id) {
222
+ throw new Error("Function not implemented.");
223
+ },
224
+ noTimeout: void 0,
225
+ isPrimaryRenderer: false,
226
+ getCurrentEventPriority() {
227
+ throw new Error("Function not implemented.");
228
+ },
229
+ getInstanceFromNode(node) {
230
+ throw new Error("Function not implemented.");
231
+ },
232
+ beforeActiveInstanceBlur() {
233
+ throw new Error("Function not implemented.");
234
+ },
235
+ afterActiveInstanceBlur() {
236
+ throw new Error("Function not implemented.");
237
+ },
238
+ prepareScopeUpdate: (scopeInstance, instance) => {
239
+ throw new Error("Function not implemented.");
240
+ },
241
+ getInstanceFromScope: (scopeInstance) => {
242
+ throw new Error("Function not implemented.");
243
+ },
244
+ detachDeletedInstance: (node) => {
245
+ throw new Error("Function not implemented.");
246
+ },
247
+ supportsHydration: false
248
+ };
249
+ var reconciler = ReactReconciler(hostConfig);
250
+ var createInstanceFromReactElement = (reactElm) => {
251
+ const rootContainer = {
252
+ children: [],
253
+ props: {
254
+ name: "$root"
255
+ },
256
+ add(instance) {
257
+ instance.parent = this;
258
+ this.children.push(instance);
259
+ },
260
+ computePcbGlobalTransform() {
261
+ return identity();
262
+ }
263
+ };
264
+ const container = reconciler.createContainer(
265
+ // TODO Replace with store like react-three-fiber
266
+ // https://github.com/pmndrs/react-three-fiber/blob/a457290856f57741bf8beef4f6ff9dbf4879c0a5/packages/fiber/src/core/index.tsx#L172
267
+ // https://github.com/pmndrs/react-three-fiber/blob/master/packages/fiber/src/core/store.ts#L168
268
+ rootContainer,
269
+ 0,
270
+ null,
271
+ false,
272
+ null,
273
+ "tsci",
274
+ (error) => {
275
+ console.log("Error in createContainer");
276
+ console.error(error);
277
+ },
278
+ null
279
+ );
280
+ reconciler.updateContainer(reactElm, container, null, () => {
281
+ });
282
+ const rootInstance = reconciler.getPublicRootInstance(
283
+ container
284
+ );
285
+ if (rootInstance) return rootInstance;
286
+ return rootContainer.children[0];
287
+ };
288
+
136
289
  // lib/components/base-components/PrimitiveComponent.ts
290
+ import { z } from "zod";
291
+ import { symbols } from "schematic-symbols";
292
+ import "react";
137
293
  import {
138
294
  applyToPoint,
139
295
  compose,
140
296
  flipY,
141
- identity,
297
+ identity as identity2,
142
298
  rotate,
143
299
  translate
144
300
  } from "transformation-matrix";
@@ -267,7 +423,7 @@ var PrimitiveComponent = class extends Renderable {
267
423
  const manualPlacement = this.getSubcircuit()._getManualPlacementForComponent(this);
268
424
  if (manualPlacement && this.props.pcbX === void 0 && this.props.pcbY === void 0) {
269
425
  return compose(
270
- this.parent?._computePcbGlobalTransformBeforeLayout() ?? identity(),
426
+ this.parent?._computePcbGlobalTransformBeforeLayout() ?? identity2(),
271
427
  compose(
272
428
  translate(manualPlacement.x, manualPlacement.y),
273
429
  rotate((props.pcbRotation ?? 0) * Math.PI / 180)
@@ -286,7 +442,7 @@ var PrimitiveComponent = class extends Renderable {
286
442
  translate(-containerCenter.x, -containerCenter.y)
287
443
  );
288
444
  return compose(
289
- this.parent?._computePcbGlobalTransformBeforeLayout() ?? identity(),
445
+ this.parent?._computePcbGlobalTransformBeforeLayout() ?? identity2(),
290
446
  flipY(),
291
447
  this.computePcbPropsTransform()
292
448
  );
@@ -294,7 +450,7 @@ var PrimitiveComponent = class extends Renderable {
294
450
  }
295
451
  }
296
452
  return compose(
297
- this.parent?._computePcbGlobalTransformBeforeLayout() ?? identity(),
453
+ this.parent?._computePcbGlobalTransformBeforeLayout() ?? identity2(),
298
454
  this.computePcbPropsTransform()
299
455
  );
300
456
  }
@@ -356,7 +512,7 @@ var PrimitiveComponent = class extends Renderable {
356
512
  */
357
513
  computeSchematicGlobalTransform() {
358
514
  return compose(
359
- this.parent?.computeSchematicGlobalTransform?.() ?? identity(),
515
+ this.parent?.computeSchematicGlobalTransform?.() ?? identity2(),
360
516
  this.computeSchematicPropsTransform()
361
517
  );
362
518
  }
@@ -566,603 +722,166 @@ var PrimitiveComponent = class extends Renderable {
566
722
  }
567
723
  };
568
724
 
569
- // lib/components/primitive-components/Footprint.ts
570
- import * as kiwi from "@lume/kiwi";
571
- import Debug from "debug";
572
- var debug = Debug("tscircuit:core:footprint");
573
- var Footprint = class extends PrimitiveComponent {
725
+ // lib/components/primitive-components/Net.ts
726
+ import { z as z2 } from "zod";
727
+
728
+ // lib/utils/pairs.ts
729
+ function pairs(arr) {
730
+ const result = [];
731
+ for (let i = 0; i < arr.length - 1; i++) {
732
+ result.push([arr[i], arr[i + 1]]);
733
+ }
734
+ return result;
735
+ }
736
+
737
+ // lib/components/primitive-components/Net.ts
738
+ import { autoroute } from "@tscircuit/infgrid-ijump-astar";
739
+ var netProps = z2.object({
740
+ name: z2.string()
741
+ });
742
+ var Net = class extends PrimitiveComponent {
743
+ source_net_id;
574
744
  get config() {
575
745
  return {
576
- componentName: "Footprint",
577
- zodProps: footprintProps
746
+ componentName: "Net",
747
+ zodProps: netProps
578
748
  };
579
749
  }
750
+ getPortSelector() {
751
+ return `net.${this.props.name}`;
752
+ }
753
+ doInitialSourceRender() {
754
+ const { db } = this.root;
755
+ const { _parsedProps: props } = this;
756
+ const net = db.source_net.insert({
757
+ name: props.name,
758
+ member_source_group_ids: []
759
+ });
760
+ this.source_net_id = net.source_net_id;
761
+ }
580
762
  /**
581
- * A footprint is a constrainedlayout, the db elements are adjusted according
582
- * to any constraints that are defined.
763
+ * Get all ports connected to this net.
764
+ *
765
+ * TODO currently we're not checking for indirect connections (traces that are
766
+ * connected to other traces that are in turn connected to the net)
583
767
  */
584
- doInitialPcbFootprintLayout() {
585
- const constraints = this.children.filter(
586
- (child) => child.componentName === "Constraint"
587
- );
588
- if (constraints.length === 0) return;
589
- const { isFlipped } = this._getPcbPrimitiveFlippedHelpers();
590
- const maybeFlipLeftRight = (props) => {
591
- if (isFlipped) {
592
- if ("left" in props && "right" in props) {
593
- return {
594
- ...props,
595
- left: props.right,
596
- right: props.left
597
- };
768
+ getAllConnectedPorts() {
769
+ const allPorts = this.getSubcircuit().selectAll("port");
770
+ const connectedPorts = [];
771
+ for (const port of allPorts) {
772
+ const traces = port._getDirectlyConnectedTraces();
773
+ for (const trace of traces) {
774
+ if (trace._isExplicitlyConnectedToNet(this)) {
775
+ connectedPorts.push(port);
776
+ break;
598
777
  }
599
778
  }
600
- return props;
601
- };
602
- const involvedComponents = constraints.flatMap(
603
- (constraint) => constraint._getAllReferencedComponents().componentsWithSelectors
604
- ).map(({ component, selector, componentSelector, edge }) => ({
605
- component,
606
- selector,
607
- componentSelector,
608
- edge,
609
- bounds: component._getPcbCircuitJsonBounds()
610
- }));
611
- if (involvedComponents.some((c) => c.edge)) {
612
- throw new Error(
613
- "edge constraints not implemented yet for footprint layout, contributions welcome!"
614
- );
615
779
  }
616
- function getComponentDetails(selector) {
617
- return involvedComponents.find(({ selector: s }) => s === selector);
618
- }
619
- const solver = new kiwi.Solver();
620
- const kVars = {};
621
- function getKVar(name) {
622
- if (!(name in kVars)) {
623
- kVars[name] = new kiwi.Variable(name);
624
- solver.addEditVariable(kVars[name], kiwi.Strength.weak);
780
+ return connectedPorts;
781
+ }
782
+ /**
783
+ * Get all traces that are directly connected to this net, i.e. they list
784
+ * this net in their path, from, or to props
785
+ */
786
+ _getAllDirectlyConnectedTraces() {
787
+ const allTraces = this.getSubcircuit().selectAll("trace");
788
+ const connectedTraces = [];
789
+ for (const trace of allTraces) {
790
+ if (trace._isExplicitlyConnectedToNet(this)) {
791
+ connectedTraces.push(trace);
625
792
  }
626
- return kVars[name];
627
793
  }
628
- for (const { selector, bounds: bounds2 } of involvedComponents) {
629
- const kvx = getKVar(`${selector}_x`);
630
- const kvy = getKVar(`${selector}_y`);
631
- solver.suggestValue(kvx, bounds2.center.x);
632
- solver.suggestValue(kvy, bounds2.center.y);
794
+ return connectedTraces;
795
+ }
796
+ /**
797
+ * Add PCB Traces to connect net islands together. A net island is a set of
798
+ * ports that are connected to each other. If a there are multiple net islands
799
+ * that means that the net is not fully connected and we need to add traces
800
+ * such that the nets are fully connected
801
+ */
802
+ doInitialPcbRouteNetIslands() {
803
+ const { db } = this.root;
804
+ const { _parsedProps: props } = this;
805
+ const traces = this._getAllDirectlyConnectedTraces().filter(
806
+ (trace) => (trace._portsRoutedOnPcb?.length ?? 0) > 0
807
+ );
808
+ const islands = [];
809
+ for (const trace of traces) {
810
+ const tracePorts = trace._portsRoutedOnPcb;
811
+ const traceIsland = islands.find(
812
+ (island) => tracePorts.some((port) => island.ports.includes(port))
813
+ );
814
+ if (!traceIsland) {
815
+ islands.push({ ports: [...tracePorts], traces: [trace] });
816
+ continue;
817
+ }
818
+ traceIsland.traces.push(trace);
819
+ traceIsland.ports.push(...tracePorts);
633
820
  }
634
- for (const constraint of constraints) {
635
- const props = constraint._parsedProps;
636
- if ("xDist" in props) {
637
- const { xDist, left, right, edgeToEdge, centerToCenter } = maybeFlipLeftRight(props);
638
- const leftVar = getKVar(`${left}_x`);
639
- const rightVar = getKVar(`${right}_x`);
640
- const leftBounds = getComponentDetails(left)?.bounds;
641
- const rightBounds = getComponentDetails(right)?.bounds;
642
- if (centerToCenter) {
643
- const expr = new kiwi.Expression(rightVar, [-1, leftVar]);
644
- solver.addConstraint(
645
- new kiwi.Constraint(
646
- expr,
647
- kiwi.Operator.Eq,
648
- props.xDist,
649
- kiwi.Strength.required
650
- )
651
- );
652
- } else if (edgeToEdge) {
653
- const expr = new kiwi.Expression(
654
- rightVar,
655
- -rightBounds.width / 2,
656
- [-1, leftVar],
657
- -leftBounds.width / 2
658
- );
659
- solver.addConstraint(
660
- new kiwi.Constraint(
661
- expr,
662
- kiwi.Operator.Eq,
663
- props.xDist,
664
- kiwi.Strength.required
665
- )
666
- );
667
- }
668
- } else if ("yDist" in props) {
669
- const { yDist, top, bottom, edgeToEdge, centerToCenter } = props;
670
- const topVar = getKVar(`${top}_y`);
671
- const bottomVar = getKVar(`${bottom}_y`);
672
- const topBounds = getComponentDetails(top)?.bounds;
673
- const bottomBounds = getComponentDetails(bottom)?.bounds;
674
- if (centerToCenter) {
675
- const expr = new kiwi.Expression(topVar, [-1, bottomVar]);
676
- solver.addConstraint(
677
- new kiwi.Constraint(
678
- expr,
679
- kiwi.Operator.Eq,
680
- props.yDist,
681
- kiwi.Strength.required
682
- )
683
- );
684
- } else if (edgeToEdge) {
685
- const expr = new kiwi.Expression(
686
- topVar,
687
- topBounds.height / 2,
688
- [-1, bottomVar],
689
- -bottomBounds.height / 2
690
- );
691
- solver.addConstraint(
692
- new kiwi.Constraint(
693
- expr,
694
- kiwi.Operator.Eq,
695
- props.yDist,
696
- kiwi.Strength.required
697
- )
821
+ if (islands.length === 0) {
822
+ return;
823
+ }
824
+ const islandPairs = pairs(islands);
825
+ for (const [A, B] of islandPairs) {
826
+ const Apositions = A.ports.map(
827
+ (port) => port._getGlobalPcbPositionBeforeLayout()
828
+ );
829
+ const Bpositions = B.ports.map(
830
+ (port) => port._getGlobalPcbPositionBeforeLayout()
831
+ );
832
+ let closestDist = Infinity;
833
+ let closestPair = [-1, -1];
834
+ for (let i = 0; i < Apositions.length; i++) {
835
+ const Apos = Apositions[i];
836
+ for (let j = 0; j < Bpositions.length; j++) {
837
+ const Bpos = Bpositions[j];
838
+ const dist = Math.sqrt(
839
+ (Apos.x - Bpos.x) ** 2 + (Apos.y - Bpos.y) ** 2
698
840
  );
841
+ if (dist < closestDist) {
842
+ closestDist = dist;
843
+ closestPair = [i, j];
844
+ }
699
845
  }
700
- } else if ("sameY" in props) {
701
- const { for: selectors } = props;
702
- if (selectors.length < 2) continue;
703
- const vars = selectors.map((selector) => getKVar(`${selector}_y`));
704
- const expr = new kiwi.Expression(...vars.slice(1));
705
- solver.addConstraint(
706
- new kiwi.Constraint(
707
- expr,
708
- kiwi.Operator.Eq,
709
- vars[0],
710
- kiwi.Strength.required
711
- )
712
- );
713
- } else if ("sameX" in props) {
714
- const { for: selectors } = props;
715
- if (selectors.length < 2) continue;
716
- const vars = selectors.map((selector) => getKVar(`${selector}_x`));
717
- const expr = new kiwi.Expression(...vars.slice(1));
718
- solver.addConstraint(
719
- new kiwi.Constraint(
720
- expr,
721
- kiwi.Operator.Eq,
722
- vars[0],
723
- kiwi.Strength.required
724
- )
725
- );
726
846
  }
727
- }
728
- solver.updateVariables();
729
- if (debug.enabled) {
730
- console.log("Solution to layout constraints:");
731
- console.table(
732
- Object.entries(kVars).map(([key, kvar]) => ({
733
- var: key,
734
- val: kvar.value()
735
- }))
736
- );
737
- }
738
- const bounds = {
739
- left: Infinity,
740
- right: -Infinity,
741
- top: -Infinity,
742
- bottom: Infinity
743
- };
744
- for (const {
745
- selector,
746
- bounds: { width, height }
747
- } of involvedComponents) {
748
- const kvx = getKVar(`${selector}_x`);
749
- const kvy = getKVar(`${selector}_y`);
750
- const newLeft = kvx.value() - width / 2;
751
- const newRight = kvx.value() + width / 2;
752
- const newTop = kvy.value() + height / 2;
753
- const newBottom = kvy.value() - height / 2;
754
- bounds.left = Math.min(bounds.left, newLeft);
755
- bounds.right = Math.max(bounds.right, newRight);
756
- bounds.top = Math.max(bounds.top, newTop);
757
- bounds.bottom = Math.min(bounds.bottom, newBottom);
758
- }
759
- const globalOffset = {
760
- x: -(bounds.right + bounds.left) / 2,
761
- y: -(bounds.top + bounds.bottom) / 2
762
- };
763
- const containerPos = this.getPrimitiveContainer()._getGlobalPcbPositionBeforeLayout();
764
- globalOffset.x += containerPos.x;
765
- globalOffset.y += containerPos.y;
766
- for (const { component, selector } of involvedComponents) {
767
- const kvx = getKVar(`${selector}_x`);
768
- const kvy = getKVar(`${selector}_y`);
769
- component._setPositionFromLayout({
770
- x: kvx.value() + globalOffset.x,
771
- y: kvy.value() + globalOffset.y
772
- });
773
- }
774
- }
775
- };
776
-
777
- // lib/components/base-components/NormalComponent.ts
778
- import { z as z4 } from "zod";
779
-
780
- // lib/utils/get-relative-direction.ts
781
- function getRelativeDirection(pointA, pointB) {
782
- const dx = pointB.x - pointA.x;
783
- const dy = pointB.y - pointA.y;
784
- if (Math.abs(dx) > Math.abs(dy)) {
785
- return dx > 0 ? "right" : "left";
786
- }
787
- return dy > 0 ? "down" : "up";
788
- }
789
-
790
- // lib/components/primitive-components/Port.ts
791
- import "schematic-symbols";
792
- import { applyToPoint as applyToPoint2, compose as compose2, translate as translate2 } from "transformation-matrix";
793
- import { z as z2 } from "zod";
794
- var portProps = z2.object({
795
- name: z2.string().optional(),
796
- pinNumber: z2.number().optional(),
797
- aliases: z2.array(z2.string()).optional()
798
- });
799
- var Port = class extends PrimitiveComponent {
800
- source_port_id = null;
801
- pcb_port_id = null;
802
- schematic_port_id = null;
803
- schematicSymbolPortDef = null;
804
- matchedComponents;
805
- facingDirection = null;
806
- get config() {
807
- return {
808
- componentName: "Port",
809
- zodProps: portProps
810
- };
811
- }
812
- constructor(props) {
813
- if (!props.name && props.pinNumber) props.name = `pin${props.pinNumber}`;
814
- if (!props.name) {
815
- throw new Error("Port must have a name or a pinNumber");
816
- }
817
- super(props);
818
- this.matchedComponents = [];
819
- }
820
- _getGlobalPcbPositionBeforeLayout() {
821
- const matchedPcbElm = this.matchedComponents.find((c) => c.isPcbPrimitive);
822
- if (!matchedPcbElm) {
823
- throw new Error(
824
- `Port ${this} has no matched pcb component, can't get global schematic position`
847
+ const Aport = A.ports[closestPair[0]];
848
+ const Bport = B.ports[closestPair[1]];
849
+ const pcbElements = db.toArray().filter(
850
+ (elm) => elm.type === "pcb_smtpad" || elm.type === "pcb_trace" || elm.type === "pcb_plated_hole" || elm.type === "pcb_hole" || elm.type === "source_port" || elm.type === "pcb_port"
825
851
  );
826
- }
827
- return matchedPcbElm?._getGlobalPcbPositionBeforeLayout() ?? { x: 0, y: 0 };
828
- }
829
- _getPcbCircuitJsonBounds() {
830
- if (!this.pcb_port_id) {
831
- return super._getPcbCircuitJsonBounds();
832
- }
833
- const { db } = this.root;
834
- const pcb_port = db.pcb_port.get(this.pcb_port_id);
835
- return {
836
- center: { x: pcb_port.x, y: pcb_port.y },
837
- bounds: { left: 0, top: 0, right: 0, bottom: 0 },
838
- width: 0,
839
- height: 0
840
- };
841
- }
842
- _getGlobalPcbPositionAfterLayout() {
843
- return this._getPcbCircuitJsonBounds().center;
844
- }
845
- _getGlobalSchematicPositionBeforeLayout() {
846
- if (!this.schematicSymbolPortDef) {
847
- return applyToPoint2(this.parent.computeSchematicGlobalTransform(), {
848
- x: 0,
849
- y: 0
850
- });
851
- }
852
- const symbol = this.parent?.getSchematicSymbol();
853
- if (!symbol) {
854
- console.warn(`Could not find parent symbol for ${this}`);
855
- return { x: 0, y: 0 };
856
- }
857
- const transform = compose2(
858
- this.parent.computeSchematicGlobalTransform(),
859
- translate2(-symbol.center.x, -symbol.center.y)
860
- );
861
- return applyToPoint2(transform, this.schematicSymbolPortDef);
862
- }
863
- /**
864
- * Smtpads and platedholes call this method to register themselves as a match
865
- * for this port. All the matching is done by primitives other than the Port,
866
- * but everyone registers themselves as a match with their Port.
867
- */
868
- registerMatch(component) {
869
- this.matchedComponents.push(component);
870
- }
871
- getNameAndAliases() {
872
- const { _parsedProps: props } = this;
873
- return Array.from(
874
- /* @__PURE__ */ new Set([
875
- ...props.aliases ?? [],
876
- props.name,
877
- ...typeof props.pinNumber === "number" ? [`pin${props.pinNumber}`, props.pinNumber.toString()] : [],
878
- ...this.externallyAddedAliases
879
- ])
880
- );
881
- }
882
- isMatchingPort(port) {
883
- return this.isMatchingAnyOf(port.getNameAndAliases());
884
- }
885
- getPortSelector() {
886
- return `.${this.parent?.props.name} > port.${this.props.name}`;
887
- }
888
- getAvailablePcbLayers() {
889
- return Array.from(
890
- new Set(this.matchedComponents.flatMap((c) => c.getAvailablePcbLayers()))
891
- );
892
- }
893
- /**
894
- * Return traces that are explicitly connected to this port (not via a net)
895
- */
896
- _getDirectlyConnectedTraces() {
897
- const allSubcircuitTraces = this.getSubcircuit().selectAll(
898
- "trace"
899
- );
900
- const connectedTraces = allSubcircuitTraces.filter(
901
- (trace) => trace._isExplicitlyConnectedToPort(this)
902
- );
903
- return connectedTraces;
904
- }
905
- doInitialSourceRender() {
906
- const { db } = this.root;
907
- const { _parsedProps: props } = this;
908
- const port_hints = this.getNameAndAliases();
909
- const source_port = db.source_port.insert({
910
- name: props.name,
911
- pin_number: props.pinNumber,
912
- port_hints,
913
- source_component_id: this.parent?.source_component_id
914
- });
915
- this.source_port_id = source_port.source_port_id;
916
- }
917
- doInitialSourceParentAttachment() {
918
- const { db } = this.root;
919
- if (!this.parent?.source_component_id) {
920
- throw new Error(
921
- `${this.getString()} has no parent source component (parent: ${this.parent?.getString()})`
922
- );
923
- }
924
- db.source_port.update(this.source_port_id, {
925
- source_component_id: this.parent?.source_component_id
926
- });
927
- this.source_component_id = this.parent?.source_component_id;
928
- }
929
- doInitialPcbPortRender() {
930
- const { db } = this.root;
931
- const { matchedComponents } = this;
932
- if (!this.parent?.pcb_component_id) {
933
- throw new Error(
934
- `${this.getString()} has no parent pcb component, cannot render pcb_port (parent: ${this.parent?.getString()})`
935
- );
936
- }
937
- const pcbMatches = matchedComponents.filter((c) => c.isPcbPrimitive);
938
- if (pcbMatches.length === 0) return;
939
- if (pcbMatches.length > 1) {
940
- throw new Error(
941
- `${this.getString()} has multiple pcb matches, unclear how to place pcb_port: ${pcbMatches.map((c) => c.getString()).join(", ")}`
942
- );
943
- }
944
- const pcbMatch = pcbMatches[0];
945
- if ("_getPcbCircuitJsonBounds" in pcbMatch) {
946
- const pcb_port = db.pcb_port.insert({
947
- pcb_component_id: this.parent?.pcb_component_id,
948
- layers: this.getAvailablePcbLayers(),
949
- ...pcbMatch._getPcbCircuitJsonBounds().center,
950
- source_port_id: this.source_port_id
951
- });
952
- this.pcb_port_id = pcb_port.pcb_port_id;
953
- } else {
954
- throw new Error(
955
- `${pcbMatch.getString()} does not have a _getGlobalPcbPositionBeforeLayout method (needed for pcb_port placement)`
956
- );
957
- }
958
- }
959
- doInitialSchematicPortRender() {
960
- const { db } = this.root;
961
- const { _parsedProps: props } = this;
962
- const container = this.getPrimitiveContainer();
963
- if (!container) return;
964
- let center = this._getGlobalSchematicPositionBeforeLayout();
965
- if ("schematicDimensions" in container && props.pinNumber !== void 0) {
966
- const chipDims = container.schematicDimensions;
967
- center = chipDims.getPortPositionByPinNumber(props.pinNumber);
968
- }
969
- const containerCenter = container._getGlobalSchematicPositionBeforeLayout();
970
- this.facingDirection = getRelativeDirection(containerCenter, center);
971
- const schematic_port = db.schematic_port.insert({
972
- schematic_component_id: this.parent?.schematic_component_id,
973
- center,
974
- source_port_id: this.source_port_id,
975
- facing_direction: this.facingDirection
976
- });
977
- this.schematic_port_id = schematic_port.schematic_port_id;
978
- }
979
- _setPositionFromLayout(newCenter) {
980
- const { db } = this.root;
981
- if (!this.pcb_port_id) return;
982
- db.pcb_port.update(this.pcb_port_id, {
983
- x: newCenter.x,
984
- y: newCenter.y
985
- });
986
- }
987
- _hasMatchedPcbPrimitive() {
988
- return this.matchedComponents.some((c) => c.isPcbPrimitive);
989
- }
990
- };
991
-
992
- // lib/components/base-components/NormalComponent.ts
993
- import { symbols as symbols2 } from "schematic-symbols";
994
- import { fp } from "footprinter";
995
- import {
996
- isValidElement as isReactElement2,
997
- isValidElement
998
- } from "react";
999
-
1000
- // lib/fiber/create-instance-from-react-element.ts
1001
- import ReactReconciler from "react-reconciler";
1002
-
1003
- // lib/fiber/catalogue.ts
1004
- var catalogue = {};
1005
- var extendCatalogue = (objects) => {
1006
- const altKeys = Object.fromEntries(
1007
- Object.entries(objects).map(([key, v]) => [key.toLowerCase(), v])
1008
- );
1009
- Object.assign(catalogue, objects);
1010
- Object.assign(catalogue, altKeys);
1011
- };
1012
-
1013
- // lib/fiber/create-instance-from-react-element.ts
1014
- import { identity as identity2 } from "transformation-matrix";
1015
- function prepare(object, state) {
1016
- const instance = object;
1017
- instance.__tsci = {
1018
- ...state
1019
- };
1020
- return object;
1021
- }
1022
- var hostConfig = {
1023
- supportsMutation: true,
1024
- createInstance(type, props) {
1025
- const target = catalogue[type];
1026
- if (!target) {
1027
- if (Object.keys(catalogue).length === 0) {
1028
- throw new Error(
1029
- "No components registered in catalogue, did you forget to import lib/register-catalogue in your test file?"
1030
- );
1031
- }
1032
- throw new Error(
1033
- `Unsupported component type (not registered in @tscircuit/core catalogue): "${type}" See CREATING_NEW_COMPONENTS.md`
852
+ const { solution } = autoroute(
853
+ pcbElements.concat([
854
+ {
855
+ type: "source_trace",
856
+ source_trace_id: "__net_trace_tmp",
857
+ connected_source_port_ids: [
858
+ Aport.source_port_id,
859
+ Bport.source_port_id
860
+ ]
861
+ }
862
+ ])
863
+ // Remove as any when autorouting-dataset has been updated
1034
864
  );
1035
- }
1036
- const instance = prepare(new target(props), {});
1037
- return instance;
1038
- },
1039
- createTextInstance() {
1040
- return {};
1041
- },
1042
- appendInitialChild(parentInstance, child) {
1043
- parentInstance.add(child);
1044
- },
1045
- appendChild(parentInstance, child) {
1046
- parentInstance.add(child);
1047
- },
1048
- appendChildToContainer(container, child) {
1049
- container.add(child);
1050
- },
1051
- finalizeInitialChildren() {
1052
- return false;
1053
- },
1054
- prepareUpdate() {
1055
- return null;
1056
- },
1057
- shouldSetTextContent() {
1058
- return false;
1059
- },
1060
- getRootHostContext() {
1061
- return {};
1062
- },
1063
- getChildHostContext() {
1064
- return {};
1065
- },
1066
- prepareForCommit() {
1067
- return null;
1068
- },
1069
- resetAfterCommit() {
1070
- },
1071
- commitMount() {
1072
- },
1073
- commitUpdate() {
1074
- },
1075
- removeChild() {
1076
- },
1077
- clearContainer() {
1078
- },
1079
- supportsPersistence: false,
1080
- getPublicInstance(instance) {
1081
- return instance;
1082
- },
1083
- preparePortalMount(containerInfo) {
1084
- throw new Error("Function not implemented.");
1085
- },
1086
- scheduleTimeout(fn, delay) {
1087
- throw new Error("Function not implemented.");
1088
- },
1089
- cancelTimeout(id) {
1090
- throw new Error("Function not implemented.");
1091
- },
1092
- noTimeout: void 0,
1093
- isPrimaryRenderer: false,
1094
- getCurrentEventPriority() {
1095
- throw new Error("Function not implemented.");
1096
- },
1097
- getInstanceFromNode(node) {
1098
- throw new Error("Function not implemented.");
1099
- },
1100
- beforeActiveInstanceBlur() {
1101
- throw new Error("Function not implemented.");
1102
- },
1103
- afterActiveInstanceBlur() {
1104
- throw new Error("Function not implemented.");
1105
- },
1106
- prepareScopeUpdate: (scopeInstance, instance) => {
1107
- throw new Error("Function not implemented.");
1108
- },
1109
- getInstanceFromScope: (scopeInstance) => {
1110
- throw new Error("Function not implemented.");
1111
- },
1112
- detachDeletedInstance: (node) => {
1113
- throw new Error("Function not implemented.");
1114
- },
1115
- supportsHydration: false
1116
- };
1117
- var reconciler = ReactReconciler(hostConfig);
1118
- var createInstanceFromReactElement = (reactElm) => {
1119
- const rootContainer = {
1120
- children: [],
1121
- props: {
1122
- name: "$root"
1123
- },
1124
- add(instance) {
1125
- instance.parent = this;
1126
- this.children.push(instance);
1127
- },
1128
- computePcbGlobalTransform() {
1129
- return identity2();
1130
- }
1131
- };
1132
- const container = reconciler.createContainer(
1133
- // TODO Replace with store like react-three-fiber
1134
- // https://github.com/pmndrs/react-three-fiber/blob/a457290856f57741bf8beef4f6ff9dbf4879c0a5/packages/fiber/src/core/index.tsx#L172
1135
- // https://github.com/pmndrs/react-three-fiber/blob/master/packages/fiber/src/core/store.ts#L168
1136
- rootContainer,
1137
- 0,
1138
- null,
1139
- false,
1140
- null,
1141
- "tsci",
1142
- (error) => {
1143
- console.log("Error in createContainer");
1144
- console.error(error);
1145
- },
1146
- null
1147
- );
1148
- reconciler.updateContainer(reactElm, container, null, () => {
1149
- });
1150
- const rootInstance = reconciler.getPublicRootInstance(
1151
- container
1152
- );
1153
- if (rootInstance) return rootInstance;
1154
- return rootContainer.children[0];
865
+ const trace = solution[0];
866
+ if (!trace) {
867
+ this.renderError("Failed to route net islands");
868
+ return;
869
+ }
870
+ db.pcb_trace.insert(trace);
871
+ }
872
+ }
1155
873
  };
1156
874
 
1157
- // lib/utils/getPortFromHints.ts
1158
- function getPortFromHints(hints) {
1159
- const pinNumber = hints.find((p) => /^(pin)?\d+$/.test(p));
1160
- if (!pinNumber) return null;
1161
- return new Port({
1162
- pinNumber: Number.parseInt(pinNumber.replace(/^pin/, "")),
1163
- aliases: hints.filter((p) => p !== pinNumber)
1164
- });
1165
- }
875
+ // lib/utils/components/createNetsFromProps.ts
876
+ var createNetsFromProps = (component, props) => {
877
+ for (const prop of props) {
878
+ if (typeof prop === "string" && prop.startsWith("net.")) {
879
+ if (!component.getSubcircuit().selectOne(prop)) {
880
+ component.getSubcircuit().add(new Net({ name: prop.split("net.")[1] }));
881
+ }
882
+ }
883
+ }
884
+ };
1166
885
 
1167
886
  // lib/components/primitive-components/SmtPad.ts
1168
887
  import { smtPadProps } from "@tscircuit/props";
@@ -1301,7 +1020,7 @@ var SmtPad = class extends PrimitiveComponent {
1301
1020
 
1302
1021
  // lib/components/primitive-components/SilkscreenPath.ts
1303
1022
  import { silkscreenPathProps } from "@tscircuit/props";
1304
- import { applyToPoint as applyToPoint4 } from "transformation-matrix";
1023
+ import { applyToPoint as applyToPoint3 } from "transformation-matrix";
1305
1024
  var SilkscreenPath = class extends PrimitiveComponent {
1306
1025
  pcb_silkscreen_path_id = null;
1307
1026
  get config() {
@@ -1324,7 +1043,7 @@ var SilkscreenPath = class extends PrimitiveComponent {
1324
1043
  pcb_component_id: this.parent?.pcb_component_id,
1325
1044
  layer,
1326
1045
  route: props.route.map((p) => {
1327
- const transformedPosition = applyToPoint4(transform, {
1046
+ const transformedPosition = applyToPoint3(transform, {
1328
1047
  x: p.x,
1329
1048
  y: p.y
1330
1049
  });
@@ -1554,208 +1273,485 @@ var createComponentsFromSoup = (soup) => {
1554
1273
  );
1555
1274
  }
1556
1275
  }
1557
- return components;
1276
+ return components;
1277
+ };
1278
+
1279
+ // lib/utils/get-bounds-of-pcb-components.ts
1280
+ function getBoundsOfPcbComponents(components) {
1281
+ let minX = Infinity;
1282
+ let minY = Infinity;
1283
+ let maxX = -Infinity;
1284
+ let maxY = -Infinity;
1285
+ for (const child of components) {
1286
+ if (child.isPcbPrimitive) {
1287
+ const { x, y } = child._getGlobalPcbPositionBeforeLayout();
1288
+ const { width: width2, height: height2 } = child.getPcbSize();
1289
+ minX = Math.min(minX, x - width2 / 2);
1290
+ minY = Math.min(minY, y - height2 / 2);
1291
+ maxX = Math.max(maxX, x + width2 / 2);
1292
+ maxY = Math.max(maxY, y + height2 / 2);
1293
+ } else if (child.componentName === "Footprint") {
1294
+ const childBounds = getBoundsOfPcbComponents(child.children);
1295
+ minX = Math.min(minX, childBounds.minX);
1296
+ minY = Math.min(minY, childBounds.minY);
1297
+ maxX = Math.max(maxX, childBounds.maxX);
1298
+ maxY = Math.max(maxY, childBounds.maxY);
1299
+ }
1300
+ }
1301
+ let width = maxX - minX;
1302
+ let height = maxY - minY;
1303
+ if (width < 0) width = 0;
1304
+ if (height < 0) height = 0;
1305
+ return {
1306
+ minX,
1307
+ minY,
1308
+ maxX,
1309
+ maxY,
1310
+ width,
1311
+ height
1312
+ };
1313
+ }
1314
+
1315
+ // lib/utils/get-relative-direction.ts
1316
+ function getRelativeDirection(pointA, pointB) {
1317
+ const dx = pointB.x - pointA.x;
1318
+ const dy = pointB.y - pointA.y;
1319
+ if (Math.abs(dx) > Math.abs(dy)) {
1320
+ return dx > 0 ? "right" : "left";
1321
+ }
1322
+ return dy > 0 ? "down" : "up";
1323
+ }
1324
+
1325
+ // lib/components/primitive-components/Port.ts
1326
+ import "schematic-symbols";
1327
+ import { applyToPoint as applyToPoint4, compose as compose3, translate as translate3 } from "transformation-matrix";
1328
+ import { z as z3 } from "zod";
1329
+ var portProps = z3.object({
1330
+ name: z3.string().optional(),
1331
+ pinNumber: z3.number().optional(),
1332
+ aliases: z3.array(z3.string()).optional()
1333
+ });
1334
+ var Port = class extends PrimitiveComponent {
1335
+ source_port_id = null;
1336
+ pcb_port_id = null;
1337
+ schematic_port_id = null;
1338
+ schematicSymbolPortDef = null;
1339
+ matchedComponents;
1340
+ facingDirection = null;
1341
+ get config() {
1342
+ return {
1343
+ componentName: "Port",
1344
+ zodProps: portProps
1345
+ };
1346
+ }
1347
+ constructor(props) {
1348
+ if (!props.name && props.pinNumber) props.name = `pin${props.pinNumber}`;
1349
+ if (!props.name) {
1350
+ throw new Error("Port must have a name or a pinNumber");
1351
+ }
1352
+ super(props);
1353
+ this.matchedComponents = [];
1354
+ }
1355
+ _getGlobalPcbPositionBeforeLayout() {
1356
+ const matchedPcbElm = this.matchedComponents.find((c) => c.isPcbPrimitive);
1357
+ if (!matchedPcbElm) {
1358
+ throw new Error(
1359
+ `Port ${this} has no matched pcb component, can't get global schematic position`
1360
+ );
1361
+ }
1362
+ return matchedPcbElm?._getGlobalPcbPositionBeforeLayout() ?? { x: 0, y: 0 };
1363
+ }
1364
+ _getPcbCircuitJsonBounds() {
1365
+ if (!this.pcb_port_id) {
1366
+ return super._getPcbCircuitJsonBounds();
1367
+ }
1368
+ const { db } = this.root;
1369
+ const pcb_port = db.pcb_port.get(this.pcb_port_id);
1370
+ return {
1371
+ center: { x: pcb_port.x, y: pcb_port.y },
1372
+ bounds: { left: 0, top: 0, right: 0, bottom: 0 },
1373
+ width: 0,
1374
+ height: 0
1375
+ };
1376
+ }
1377
+ _getGlobalPcbPositionAfterLayout() {
1378
+ return this._getPcbCircuitJsonBounds().center;
1379
+ }
1380
+ _getGlobalSchematicPositionBeforeLayout() {
1381
+ if (!this.schematicSymbolPortDef) {
1382
+ return applyToPoint4(this.parent.computeSchematicGlobalTransform(), {
1383
+ x: 0,
1384
+ y: 0
1385
+ });
1386
+ }
1387
+ const symbol = this.parent?.getSchematicSymbol();
1388
+ if (!symbol) {
1389
+ console.warn(`Could not find parent symbol for ${this}`);
1390
+ return { x: 0, y: 0 };
1391
+ }
1392
+ const transform = compose3(
1393
+ this.parent.computeSchematicGlobalTransform(),
1394
+ translate3(-symbol.center.x, -symbol.center.y)
1395
+ );
1396
+ return applyToPoint4(transform, this.schematicSymbolPortDef);
1397
+ }
1398
+ /**
1399
+ * Smtpads and platedholes call this method to register themselves as a match
1400
+ * for this port. All the matching is done by primitives other than the Port,
1401
+ * but everyone registers themselves as a match with their Port.
1402
+ */
1403
+ registerMatch(component) {
1404
+ this.matchedComponents.push(component);
1405
+ }
1406
+ getNameAndAliases() {
1407
+ const { _parsedProps: props } = this;
1408
+ return Array.from(
1409
+ /* @__PURE__ */ new Set([
1410
+ ...props.aliases ?? [],
1411
+ props.name,
1412
+ ...typeof props.pinNumber === "number" ? [`pin${props.pinNumber}`, props.pinNumber.toString()] : [],
1413
+ ...this.externallyAddedAliases
1414
+ ])
1415
+ );
1416
+ }
1417
+ isMatchingPort(port) {
1418
+ return this.isMatchingAnyOf(port.getNameAndAliases());
1419
+ }
1420
+ getPortSelector() {
1421
+ return `.${this.parent?.props.name} > port.${this.props.name}`;
1422
+ }
1423
+ getAvailablePcbLayers() {
1424
+ return Array.from(
1425
+ new Set(this.matchedComponents.flatMap((c) => c.getAvailablePcbLayers()))
1426
+ );
1427
+ }
1428
+ /**
1429
+ * Return traces that are explicitly connected to this port (not via a net)
1430
+ */
1431
+ _getDirectlyConnectedTraces() {
1432
+ const allSubcircuitTraces = this.getSubcircuit().selectAll(
1433
+ "trace"
1434
+ );
1435
+ const connectedTraces = allSubcircuitTraces.filter(
1436
+ (trace) => trace._isExplicitlyConnectedToPort(this)
1437
+ );
1438
+ return connectedTraces;
1439
+ }
1440
+ doInitialSourceRender() {
1441
+ const { db } = this.root;
1442
+ const { _parsedProps: props } = this;
1443
+ const port_hints = this.getNameAndAliases();
1444
+ const source_port = db.source_port.insert({
1445
+ name: props.name,
1446
+ pin_number: props.pinNumber,
1447
+ port_hints,
1448
+ source_component_id: this.parent?.source_component_id
1449
+ });
1450
+ this.source_port_id = source_port.source_port_id;
1451
+ }
1452
+ doInitialSourceParentAttachment() {
1453
+ const { db } = this.root;
1454
+ if (!this.parent?.source_component_id) {
1455
+ throw new Error(
1456
+ `${this.getString()} has no parent source component (parent: ${this.parent?.getString()})`
1457
+ );
1458
+ }
1459
+ db.source_port.update(this.source_port_id, {
1460
+ source_component_id: this.parent?.source_component_id
1461
+ });
1462
+ this.source_component_id = this.parent?.source_component_id;
1463
+ }
1464
+ doInitialPcbPortRender() {
1465
+ const { db } = this.root;
1466
+ const { matchedComponents } = this;
1467
+ if (!this.parent?.pcb_component_id) {
1468
+ throw new Error(
1469
+ `${this.getString()} has no parent pcb component, cannot render pcb_port (parent: ${this.parent?.getString()})`
1470
+ );
1471
+ }
1472
+ const pcbMatches = matchedComponents.filter((c) => c.isPcbPrimitive);
1473
+ if (pcbMatches.length === 0) return;
1474
+ if (pcbMatches.length > 1) {
1475
+ throw new Error(
1476
+ `${this.getString()} has multiple pcb matches, unclear how to place pcb_port: ${pcbMatches.map((c) => c.getString()).join(", ")}`
1477
+ );
1478
+ }
1479
+ const pcbMatch = pcbMatches[0];
1480
+ if ("_getPcbCircuitJsonBounds" in pcbMatch) {
1481
+ const pcb_port = db.pcb_port.insert({
1482
+ pcb_component_id: this.parent?.pcb_component_id,
1483
+ layers: this.getAvailablePcbLayers(),
1484
+ ...pcbMatch._getPcbCircuitJsonBounds().center,
1485
+ source_port_id: this.source_port_id
1486
+ });
1487
+ this.pcb_port_id = pcb_port.pcb_port_id;
1488
+ } else {
1489
+ throw new Error(
1490
+ `${pcbMatch.getString()} does not have a _getGlobalPcbPositionBeforeLayout method (needed for pcb_port placement)`
1491
+ );
1492
+ }
1493
+ }
1494
+ doInitialSchematicPortRender() {
1495
+ const { db } = this.root;
1496
+ const { _parsedProps: props } = this;
1497
+ const container = this.getPrimitiveContainer();
1498
+ if (!container) return;
1499
+ let center = this._getGlobalSchematicPositionBeforeLayout();
1500
+ if ("schematicDimensions" in container && props.pinNumber !== void 0) {
1501
+ const chipDims = container.schematicDimensions;
1502
+ center = chipDims.getPortPositionByPinNumber(props.pinNumber);
1503
+ }
1504
+ const containerCenter = container._getGlobalSchematicPositionBeforeLayout();
1505
+ this.facingDirection = getRelativeDirection(containerCenter, center);
1506
+ const schematic_port = db.schematic_port.insert({
1507
+ schematic_component_id: this.parent?.schematic_component_id,
1508
+ center,
1509
+ source_port_id: this.source_port_id,
1510
+ facing_direction: this.facingDirection
1511
+ });
1512
+ this.schematic_port_id = schematic_port.schematic_port_id;
1513
+ }
1514
+ _setPositionFromLayout(newCenter) {
1515
+ const { db } = this.root;
1516
+ if (!this.pcb_port_id) return;
1517
+ db.pcb_port.update(this.pcb_port_id, {
1518
+ x: newCenter.x,
1519
+ y: newCenter.y
1520
+ });
1521
+ }
1522
+ _hasMatchedPcbPrimitive() {
1523
+ return this.matchedComponents.some((c) => c.isPcbPrimitive);
1524
+ }
1558
1525
  };
1559
1526
 
1560
- // lib/components/primitive-components/Net.ts
1561
- import { z as z3 } from "zod";
1562
-
1563
- // lib/utils/pairs.ts
1564
- function pairs(arr) {
1565
- const result = [];
1566
- for (let i = 0; i < arr.length - 1; i++) {
1567
- result.push([arr[i], arr[i + 1]]);
1568
- }
1569
- return result;
1527
+ // lib/utils/getPortFromHints.ts
1528
+ function getPortFromHints(hints) {
1529
+ const pinNumber = hints.find((p) => /^(pin)?\d+$/.test(p));
1530
+ if (!pinNumber) return null;
1531
+ return new Port({
1532
+ pinNumber: Number.parseInt(pinNumber.replace(/^pin/, "")),
1533
+ aliases: hints.filter((p) => p !== pinNumber)
1534
+ });
1570
1535
  }
1571
1536
 
1572
- // lib/components/primitive-components/Net.ts
1573
- import { autoroute } from "@tscircuit/infgrid-ijump-astar";
1574
- var netProps = z3.object({
1575
- name: z3.string()
1576
- });
1577
- var Net = class extends PrimitiveComponent {
1578
- source_net_id;
1537
+ // lib/components/base-components/NormalComponent.ts
1538
+ import {
1539
+ isValidElement as isReactElement2,
1540
+ isValidElement
1541
+ } from "react";
1542
+ import { symbols as symbols2 } from "schematic-symbols";
1543
+ import { z as z4 } from "zod";
1544
+
1545
+ // lib/components/primitive-components/Footprint.ts
1546
+ import { footprintProps } from "@tscircuit/props";
1547
+ import * as kiwi from "@lume/kiwi";
1548
+ import Debug from "debug";
1549
+ var debug = Debug("tscircuit:core:footprint");
1550
+ var Footprint = class extends PrimitiveComponent {
1579
1551
  get config() {
1580
1552
  return {
1581
- componentName: "Net",
1582
- zodProps: netProps
1553
+ componentName: "Footprint",
1554
+ zodProps: footprintProps
1583
1555
  };
1584
1556
  }
1585
- getPortSelector() {
1586
- return `net.${this.props.name}`;
1587
- }
1588
- doInitialSourceRender() {
1589
- const { db } = this.root;
1590
- const { _parsedProps: props } = this;
1591
- const net = db.source_net.insert({
1592
- name: props.name,
1593
- member_source_group_ids: []
1594
- });
1595
- this.source_net_id = net.source_net_id;
1596
- }
1597
1557
  /**
1598
- * Get all ports connected to this net.
1599
- *
1600
- * TODO currently we're not checking for indirect connections (traces that are
1601
- * connected to other traces that are in turn connected to the net)
1558
+ * A footprint is a constrainedlayout, the db elements are adjusted according
1559
+ * to any constraints that are defined.
1602
1560
  */
1603
- getAllConnectedPorts() {
1604
- const allPorts = this.getSubcircuit().selectAll("port");
1605
- const connectedPorts = [];
1606
- for (const port of allPorts) {
1607
- const traces = port._getDirectlyConnectedTraces();
1608
- for (const trace of traces) {
1609
- if (trace._isExplicitlyConnectedToNet(this)) {
1610
- connectedPorts.push(port);
1611
- break;
1561
+ doInitialPcbFootprintLayout() {
1562
+ const constraints = this.children.filter(
1563
+ (child) => child.componentName === "Constraint"
1564
+ );
1565
+ if (constraints.length === 0) return;
1566
+ const { isFlipped } = this._getPcbPrimitiveFlippedHelpers();
1567
+ const maybeFlipLeftRight = (props) => {
1568
+ if (isFlipped) {
1569
+ if ("left" in props && "right" in props) {
1570
+ return {
1571
+ ...props,
1572
+ left: props.right,
1573
+ right: props.left
1574
+ };
1612
1575
  }
1613
1576
  }
1577
+ return props;
1578
+ };
1579
+ const involvedComponents = constraints.flatMap(
1580
+ (constraint) => constraint._getAllReferencedComponents().componentsWithSelectors
1581
+ ).map(({ component, selector, componentSelector, edge }) => ({
1582
+ component,
1583
+ selector,
1584
+ componentSelector,
1585
+ edge,
1586
+ bounds: component._getPcbCircuitJsonBounds()
1587
+ }));
1588
+ if (involvedComponents.some((c) => c.edge)) {
1589
+ throw new Error(
1590
+ "edge constraints not implemented yet for footprint layout, contributions welcome!"
1591
+ );
1614
1592
  }
1615
- return connectedPorts;
1616
- }
1617
- /**
1618
- * Get all traces that are directly connected to this net, i.e. they list
1619
- * this net in their path, from, or to props
1620
- */
1621
- _getAllDirectlyConnectedTraces() {
1622
- const allTraces = this.getSubcircuit().selectAll("trace");
1623
- const connectedTraces = [];
1624
- for (const trace of allTraces) {
1625
- if (trace._isExplicitlyConnectedToNet(this)) {
1626
- connectedTraces.push(trace);
1627
- }
1593
+ function getComponentDetails(selector) {
1594
+ return involvedComponents.find(({ selector: s }) => s === selector);
1628
1595
  }
1629
- return connectedTraces;
1630
- }
1631
- /**
1632
- * Add PCB Traces to connect net islands together. A net island is a set of
1633
- * ports that are connected to each other. If a there are multiple net islands
1634
- * that means that the net is not fully connected and we need to add traces
1635
- * such that the nets are fully connected
1636
- */
1637
- doInitialPcbRouteNetIslands() {
1638
- const { db } = this.root;
1639
- const { _parsedProps: props } = this;
1640
- const traces = this._getAllDirectlyConnectedTraces().filter(
1641
- (trace) => (trace._portsRoutedOnPcb?.length ?? 0) > 0
1642
- );
1643
- const islands = [];
1644
- for (const trace of traces) {
1645
- const tracePorts = trace._portsRoutedOnPcb;
1646
- const traceIsland = islands.find(
1647
- (island) => tracePorts.some((port) => island.ports.includes(port))
1648
- );
1649
- if (!traceIsland) {
1650
- islands.push({ ports: [...tracePorts], traces: [trace] });
1651
- continue;
1596
+ const solver = new kiwi.Solver();
1597
+ const kVars = {};
1598
+ function getKVar(name) {
1599
+ if (!(name in kVars)) {
1600
+ kVars[name] = new kiwi.Variable(name);
1601
+ solver.addEditVariable(kVars[name], kiwi.Strength.weak);
1652
1602
  }
1653
- traceIsland.traces.push(trace);
1654
- traceIsland.ports.push(...tracePorts);
1603
+ return kVars[name];
1655
1604
  }
1656
- if (islands.length === 0) {
1657
- return;
1605
+ for (const { selector, bounds: bounds2 } of involvedComponents) {
1606
+ const kvx = getKVar(`${selector}_x`);
1607
+ const kvy = getKVar(`${selector}_y`);
1608
+ solver.suggestValue(kvx, bounds2.center.x);
1609
+ solver.suggestValue(kvy, bounds2.center.y);
1658
1610
  }
1659
- const islandPairs = pairs(islands);
1660
- for (const [A, B] of islandPairs) {
1661
- const Apositions = A.ports.map(
1662
- (port) => port._getGlobalPcbPositionBeforeLayout()
1663
- );
1664
- const Bpositions = B.ports.map(
1665
- (port) => port._getGlobalPcbPositionBeforeLayout()
1666
- );
1667
- let closestDist = Infinity;
1668
- let closestPair = [-1, -1];
1669
- for (let i = 0; i < Apositions.length; i++) {
1670
- const Apos = Apositions[i];
1671
- for (let j = 0; j < Bpositions.length; j++) {
1672
- const Bpos = Bpositions[j];
1673
- const dist = Math.sqrt(
1674
- (Apos.x - Bpos.x) ** 2 + (Apos.y - Bpos.y) ** 2
1611
+ for (const constraint of constraints) {
1612
+ const props = constraint._parsedProps;
1613
+ if ("xDist" in props) {
1614
+ const { xDist, left, right, edgeToEdge, centerToCenter } = maybeFlipLeftRight(props);
1615
+ const leftVar = getKVar(`${left}_x`);
1616
+ const rightVar = getKVar(`${right}_x`);
1617
+ const leftBounds = getComponentDetails(left)?.bounds;
1618
+ const rightBounds = getComponentDetails(right)?.bounds;
1619
+ if (centerToCenter) {
1620
+ const expr = new kiwi.Expression(rightVar, [-1, leftVar]);
1621
+ solver.addConstraint(
1622
+ new kiwi.Constraint(
1623
+ expr,
1624
+ kiwi.Operator.Eq,
1625
+ props.xDist,
1626
+ kiwi.Strength.required
1627
+ )
1628
+ );
1629
+ } else if (edgeToEdge) {
1630
+ const expr = new kiwi.Expression(
1631
+ rightVar,
1632
+ -rightBounds.width / 2,
1633
+ [-1, leftVar],
1634
+ -leftBounds.width / 2
1635
+ );
1636
+ solver.addConstraint(
1637
+ new kiwi.Constraint(
1638
+ expr,
1639
+ kiwi.Operator.Eq,
1640
+ props.xDist,
1641
+ kiwi.Strength.required
1642
+ )
1643
+ );
1644
+ }
1645
+ } else if ("yDist" in props) {
1646
+ const { yDist, top, bottom, edgeToEdge, centerToCenter } = props;
1647
+ const topVar = getKVar(`${top}_y`);
1648
+ const bottomVar = getKVar(`${bottom}_y`);
1649
+ const topBounds = getComponentDetails(top)?.bounds;
1650
+ const bottomBounds = getComponentDetails(bottom)?.bounds;
1651
+ if (centerToCenter) {
1652
+ const expr = new kiwi.Expression(topVar, [-1, bottomVar]);
1653
+ solver.addConstraint(
1654
+ new kiwi.Constraint(
1655
+ expr,
1656
+ kiwi.Operator.Eq,
1657
+ props.yDist,
1658
+ kiwi.Strength.required
1659
+ )
1660
+ );
1661
+ } else if (edgeToEdge) {
1662
+ const expr = new kiwi.Expression(
1663
+ topVar,
1664
+ topBounds.height / 2,
1665
+ [-1, bottomVar],
1666
+ -bottomBounds.height / 2
1667
+ );
1668
+ solver.addConstraint(
1669
+ new kiwi.Constraint(
1670
+ expr,
1671
+ kiwi.Operator.Eq,
1672
+ props.yDist,
1673
+ kiwi.Strength.required
1674
+ )
1675
1675
  );
1676
- if (dist < closestDist) {
1677
- closestDist = dist;
1678
- closestPair = [i, j];
1679
- }
1680
1676
  }
1677
+ } else if ("sameY" in props) {
1678
+ const { for: selectors } = props;
1679
+ if (selectors.length < 2) continue;
1680
+ const vars = selectors.map((selector) => getKVar(`${selector}_y`));
1681
+ const expr = new kiwi.Expression(...vars.slice(1));
1682
+ solver.addConstraint(
1683
+ new kiwi.Constraint(
1684
+ expr,
1685
+ kiwi.Operator.Eq,
1686
+ vars[0],
1687
+ kiwi.Strength.required
1688
+ )
1689
+ );
1690
+ } else if ("sameX" in props) {
1691
+ const { for: selectors } = props;
1692
+ if (selectors.length < 2) continue;
1693
+ const vars = selectors.map((selector) => getKVar(`${selector}_x`));
1694
+ const expr = new kiwi.Expression(...vars.slice(1));
1695
+ solver.addConstraint(
1696
+ new kiwi.Constraint(
1697
+ expr,
1698
+ kiwi.Operator.Eq,
1699
+ vars[0],
1700
+ kiwi.Strength.required
1701
+ )
1702
+ );
1681
1703
  }
1682
- const Aport = A.ports[closestPair[0]];
1683
- const Bport = B.ports[closestPair[1]];
1684
- const pcbElements = db.toArray().filter(
1685
- (elm) => elm.type === "pcb_smtpad" || elm.type === "pcb_trace" || elm.type === "pcb_plated_hole" || elm.type === "pcb_hole" || elm.type === "source_port" || elm.type === "pcb_port"
1686
- );
1687
- const { solution } = autoroute(
1688
- pcbElements.concat([
1689
- {
1690
- type: "source_trace",
1691
- source_trace_id: "__net_trace_tmp",
1692
- connected_source_port_ids: [
1693
- Aport.source_port_id,
1694
- Bport.source_port_id
1695
- ]
1696
- }
1697
- ])
1698
- // Remove as any when autorouting-dataset has been updated
1704
+ }
1705
+ solver.updateVariables();
1706
+ if (debug.enabled) {
1707
+ console.log("Solution to layout constraints:");
1708
+ console.table(
1709
+ Object.entries(kVars).map(([key, kvar]) => ({
1710
+ var: key,
1711
+ val: kvar.value()
1712
+ }))
1699
1713
  );
1700
- const trace = solution[0];
1701
- if (!trace) {
1702
- this.renderError("Failed to route net islands");
1703
- return;
1704
- }
1705
- db.pcb_trace.insert(trace);
1706
1714
  }
1707
- }
1708
- };
1709
-
1710
- // lib/utils/components/createNetsFromProps.ts
1711
- var createNetsFromProps = (component, props) => {
1712
- for (const prop of props) {
1713
- if (typeof prop === "string" && prop.startsWith("net.")) {
1714
- if (!component.getSubcircuit().selectOne(prop)) {
1715
- component.getSubcircuit().add(new Net({ name: prop.split("net.")[1] }));
1716
- }
1715
+ const bounds = {
1716
+ left: Infinity,
1717
+ right: -Infinity,
1718
+ top: -Infinity,
1719
+ bottom: Infinity
1720
+ };
1721
+ for (const {
1722
+ selector,
1723
+ bounds: { width, height }
1724
+ } of involvedComponents) {
1725
+ const kvx = getKVar(`${selector}_x`);
1726
+ const kvy = getKVar(`${selector}_y`);
1727
+ const newLeft = kvx.value() - width / 2;
1728
+ const newRight = kvx.value() + width / 2;
1729
+ const newTop = kvy.value() + height / 2;
1730
+ const newBottom = kvy.value() - height / 2;
1731
+ bounds.left = Math.min(bounds.left, newLeft);
1732
+ bounds.right = Math.max(bounds.right, newRight);
1733
+ bounds.top = Math.max(bounds.top, newTop);
1734
+ bounds.bottom = Math.min(bounds.bottom, newBottom);
1717
1735
  }
1718
- }
1719
- };
1720
-
1721
- // lib/utils/get-bounds-of-pcb-components.ts
1722
- function getBoundsOfPcbComponents(components) {
1723
- let minX = Infinity;
1724
- let minY = Infinity;
1725
- let maxX = -Infinity;
1726
- let maxY = -Infinity;
1727
- for (const child of components) {
1728
- if (child.isPcbPrimitive) {
1729
- const { x, y } = child._getGlobalPcbPositionBeforeLayout();
1730
- const { width: width2, height: height2 } = child.getPcbSize();
1731
- minX = Math.min(minX, x - width2 / 2);
1732
- minY = Math.min(minY, y - height2 / 2);
1733
- maxX = Math.max(maxX, x + width2 / 2);
1734
- maxY = Math.max(maxY, y + height2 / 2);
1735
- } else if (child.componentName === "Footprint") {
1736
- const childBounds = getBoundsOfPcbComponents(child.children);
1737
- minX = Math.min(minX, childBounds.minX);
1738
- minY = Math.min(minY, childBounds.minY);
1739
- maxX = Math.max(maxX, childBounds.maxX);
1740
- maxY = Math.max(maxY, childBounds.maxY);
1736
+ const globalOffset = {
1737
+ x: -(bounds.right + bounds.left) / 2,
1738
+ y: -(bounds.top + bounds.bottom) / 2
1739
+ };
1740
+ const containerPos = this.getPrimitiveContainer()._getGlobalPcbPositionBeforeLayout();
1741
+ globalOffset.x += containerPos.x;
1742
+ globalOffset.y += containerPos.y;
1743
+ for (const { component, selector } of involvedComponents) {
1744
+ const kvx = getKVar(`${selector}_x`);
1745
+ const kvy = getKVar(`${selector}_y`);
1746
+ component._setPositionFromLayout({
1747
+ x: kvx.value() + globalOffset.x,
1748
+ y: kvy.value() + globalOffset.y
1749
+ });
1741
1750
  }
1742
1751
  }
1743
- let width = maxX - minX;
1744
- let height = maxY - minY;
1745
- if (width < 0) width = 0;
1746
- if (height < 0) height = 0;
1747
- return {
1748
- minX,
1749
- minY,
1750
- maxX,
1751
- maxY,
1752
- width,
1753
- height
1754
- };
1755
- }
1752
+ };
1756
1753
 
1757
1754
  // lib/components/base-components/NormalComponent.ts
1758
- import { rotation } from "circuit-json";
1759
1755
  var rotation3 = z4.object({
1760
1756
  x: rotation,
1761
1757
  y: rotation,
@@ -1786,6 +1782,36 @@ var NormalComponent = class extends PrimitiveComponent {
1786
1782
  initPorts() {
1787
1783
  const { config } = this;
1788
1784
  const portsToCreate = [];
1785
+ const schPortArrangement = this._parsedProps.schPortArrangement;
1786
+ if (schPortArrangement) {
1787
+ for (const side in schPortArrangement) {
1788
+ const pins = schPortArrangement[side].pins;
1789
+ if (Array.isArray(pins)) {
1790
+ for (const pinNumber of pins) {
1791
+ portsToCreate.push(new Port({ pinNumber }));
1792
+ }
1793
+ }
1794
+ }
1795
+ }
1796
+ const pinLabels = this._parsedProps.pinLabels;
1797
+ if (pinLabels) {
1798
+ for (let [pinNumber, label] of Object.entries(pinLabels)) {
1799
+ pinNumber = pinNumber.replace("pin", "");
1800
+ let existingPort = portsToCreate.find(
1801
+ (p) => p._parsedProps.pinNumber === Number(pinNumber)
1802
+ );
1803
+ if (!existingPort) {
1804
+ existingPort = new Port({
1805
+ pinNumber: parseInt(pinNumber),
1806
+ name: label
1807
+ });
1808
+ portsToCreate.push(existingPort);
1809
+ } else {
1810
+ existingPort.externallyAddedAliases.push(label);
1811
+ existingPort.props.name = label;
1812
+ }
1813
+ }
1814
+ }
1789
1815
  if (config.schematicSymbolName) {
1790
1816
  const sym = symbols2[`${config.schematicSymbolName}_horz`];
1791
1817
  if (!sym) return;
@@ -1800,21 +1826,9 @@ var NormalComponent = class extends PrimitiveComponent {
1800
1826
  return;
1801
1827
  }
1802
1828
  const portsFromFootprint = this.getPortsFromFootprint();
1803
- portsToCreate.push(...portsFromFootprint);
1804
- const pinLabels = this._parsedProps.pinLabels;
1805
- if (pinLabels) {
1806
- for (let [pinNumber, label] of Object.entries(pinLabels)) {
1807
- pinNumber = pinNumber.replace("pin", "");
1808
- const existingPort = portsToCreate.find(
1809
- (p) => p._parsedProps.pinNumber === Number(pinNumber)
1810
- );
1811
- if (!existingPort) {
1812
- throw new Error(
1813
- `Could not find port for pin number ${pinNumber} in chip ${this.getString()}`
1814
- );
1815
- }
1816
- existingPort.externallyAddedAliases.push(label);
1817
- existingPort.props.name = label;
1829
+ for (const port of portsFromFootprint) {
1830
+ if (!portsToCreate.some((p) => p.isMatchingAnyOf(port.getNameAndAliases()))) {
1831
+ portsToCreate.push(port);
1818
1832
  }
1819
1833
  }
1820
1834
  if (portsToCreate.length > 0) {
@@ -2996,20 +3010,27 @@ var FTYPE = stringProxy;
2996
3010
 
2997
3011
  // lib/components/normal-components/Diode.ts
2998
3012
  var Diode = class extends NormalComponent {
2999
- pin1 = this.portMap.pin1;
3000
- pin2 = this.portMap.pin2;
3013
+ // @ts-ignore
3001
3014
  get config() {
3002
3015
  return {
3003
- // schematicSymbolName: "diode" as BaseSymbolName,
3016
+ schematicSymbolName: this.props.symbolName ?? "diode_horz",
3004
3017
  componentName: "Diode",
3005
3018
  zodProps: diodeProps,
3006
3019
  sourceFtype: "simple_diode"
3007
3020
  };
3008
3021
  }
3009
3022
  initPorts() {
3010
- this.add(new Port({ name: "pin1", aliases: ["1", "pin1"] }));
3011
- this.add(new Port({ name: "pin2", aliases: ["2", "pin2"] }));
3023
+ this.add(
3024
+ new Port({ name: "pin1", pinNumber: 1, aliases: ["anode", "pos"] })
3025
+ );
3026
+ this.add(
3027
+ new Port({ name: "pin2", pinNumber: 2, aliases: ["cathode", "neg"] })
3028
+ );
3012
3029
  }
3030
+ pos = this.portMap.pin1;
3031
+ anode = this.portMap.pin1;
3032
+ neg = this.portMap.pin2;
3033
+ cathode = this.portMap.pin2;
3013
3034
  };
3014
3035
 
3015
3036
  // lib/components/normal-components/Capacitor.ts
@@ -3068,8 +3089,52 @@ var Capacitor = class extends NormalComponent {
3068
3089
  // lib/components/normal-components/Chip.ts
3069
3090
  import { chipProps } from "@tscircuit/props";
3070
3091
 
3071
- // lib/utils/schematic/getAllDimensionsForSchematicBox.ts
3092
+ // lib/soup/underscorifyPinStyles.ts
3072
3093
  import "circuit-json";
3094
+ import "zod";
3095
+ var underscorifyPinStyles = (pinStyles) => {
3096
+ if (!pinStyles) return void 0;
3097
+ const underscorePinStyles = {};
3098
+ for (const [pinName, pinStyle] of Object.entries(pinStyles)) {
3099
+ underscorePinStyles[pinName] = {
3100
+ bottom_margin: pinStyle.bottomMargin,
3101
+ left_margin: pinStyle.leftMargin,
3102
+ right_margin: pinStyle.rightMargin,
3103
+ top_margin: pinStyle.topMargin
3104
+ };
3105
+ }
3106
+ return underscorePinStyles;
3107
+ };
3108
+
3109
+ // lib/soup/underscorifyPortArrangement.ts
3110
+ var underscorifyPortArrangement = (portArrangement) => {
3111
+ if (!portArrangement) return void 0;
3112
+ if ("leftSide" in portArrangement || "rightSide" in portArrangement || "topSide" in portArrangement || "bottomSide" in portArrangement) {
3113
+ return {
3114
+ left_side: portArrangement.leftSide,
3115
+ right_side: portArrangement.rightSide,
3116
+ top_side: portArrangement.topSide,
3117
+ bottom_side: portArrangement.bottomSide
3118
+ };
3119
+ }
3120
+ if ("leftPinCount" in portArrangement || "rightPinCount" in portArrangement || "topPinCount" in portArrangement || "bottomPinCount" in portArrangement) {
3121
+ return {
3122
+ left_size: portArrangement.leftPinCount,
3123
+ right_size: portArrangement.rightPinCount,
3124
+ top_size: portArrangement.topPinCount,
3125
+ bottom_size: portArrangement.bottomPinCount
3126
+ };
3127
+ }
3128
+ if ("leftSize" in portArrangement || "rightSize" in portArrangement || "topSize" in portArrangement || "bottomSize" in portArrangement) {
3129
+ return {
3130
+ left_size: portArrangement.leftSize,
3131
+ right_size: portArrangement.rightSize,
3132
+ top_size: portArrangement.topSize,
3133
+ bottom_size: portArrangement.bottomSize
3134
+ };
3135
+ }
3136
+ return void 0;
3137
+ };
3073
3138
 
3074
3139
  // lib/utils/schematic/getSizeOfSidesFromPortArrangement.ts
3075
3140
  var hasExplicitPinMapping = (pa) => {
@@ -3101,8 +3166,9 @@ var getSizeOfSidesFromPortArrangement = (pa) => {
3101
3166
  };
3102
3167
 
3103
3168
  // lib/utils/schematic/getAllDimensionsForSchematicBox.ts
3104
- import "@tscircuit/props";
3105
- import "zod";
3169
+ function isExplicitPinMappingArrangement(arrangement) {
3170
+ return arrangement.leftSide !== void 0;
3171
+ }
3106
3172
  var getAllDimensionsForSchematicBox = (params) => {
3107
3173
  const portDistanceFromEdge = params.portDistanceFromEdge ?? params.schPinSpacing * 2;
3108
3174
  let sidePinCounts = params.schPortArrangement ? getSizeOfSidesFromPortArrangement(params.schPortArrangement) : null;
@@ -3141,7 +3207,7 @@ var getAllDimensionsForSchematicBox = (params) => {
3141
3207
  let currentDistanceFromEdge = 0;
3142
3208
  let truePinIndex = 0;
3143
3209
  for (let sideIndex = 0; sideIndex < sidePinCounts.leftSize; sideIndex++) {
3144
- const pinNumber = truePinIndex + 1;
3210
+ const pinNumber = params.schPortArrangement && isExplicitPinMappingArrangement(params.schPortArrangement) ? params.schPortArrangement?.leftSide?.pins[sideIndex] : truePinIndex + 1;
3145
3211
  const pinStyle = params.schPinStyle?.[`pin${pinNumber}`] ?? params.schPinStyle?.[pinNumber];
3146
3212
  if (pinStyle?.topMargin) {
3147
3213
  currentDistanceFromEdge += pinStyle.topMargin;
@@ -3165,7 +3231,7 @@ var getAllDimensionsForSchematicBox = (params) => {
3165
3231
  }
3166
3232
  currentDistanceFromEdge = 0;
3167
3233
  for (let sideIndex = 0; sideIndex < sidePinCounts.bottomSize; sideIndex++) {
3168
- const pinNumber = truePinIndex + 1;
3234
+ const pinNumber = params.schPortArrangement && isExplicitPinMappingArrangement(params.schPortArrangement) ? params.schPortArrangement.bottomSide?.pins[sideIndex] : truePinIndex + 1;
3169
3235
  const pinStyle = params.schPinStyle?.[`pin${pinNumber}`] ?? params.schPinStyle?.[pinNumber];
3170
3236
  if (pinStyle?.leftMargin) {
3171
3237
  currentDistanceFromEdge += pinStyle.leftMargin;
@@ -3189,7 +3255,7 @@ var getAllDimensionsForSchematicBox = (params) => {
3189
3255
  }
3190
3256
  currentDistanceFromEdge = 0;
3191
3257
  for (let sideIndex = 0; sideIndex < sidePinCounts.rightSize; sideIndex++) {
3192
- const pinNumber = truePinIndex + 1;
3258
+ const pinNumber = params.schPortArrangement && isExplicitPinMappingArrangement(params.schPortArrangement) ? params.schPortArrangement.rightSide?.pins[sideIndex] : truePinIndex + 1;
3193
3259
  const pinStyle = params.schPinStyle?.[`pin${pinNumber}`] ?? params.schPinStyle?.[pinNumber];
3194
3260
  if (pinStyle?.bottomMargin) {
3195
3261
  currentDistanceFromEdge += pinStyle.bottomMargin;
@@ -3213,7 +3279,7 @@ var getAllDimensionsForSchematicBox = (params) => {
3213
3279
  }
3214
3280
  currentDistanceFromEdge = 0;
3215
3281
  for (let sideIndex = 0; sideIndex < sidePinCounts.topSize; sideIndex++) {
3216
- const pinNumber = truePinIndex + 1;
3282
+ const pinNumber = params.schPortArrangement && isExplicitPinMappingArrangement(params.schPortArrangement) ? params.schPortArrangement.topSide?.pins[sideIndex] : truePinIndex + 1;
3217
3283
  const pinStyle = params.schPinStyle?.[`pin${pinNumber}`] ?? params.schPinStyle?.[pinNumber];
3218
3284
  if (pinStyle?.rightMargin) {
3219
3285
  currentDistanceFromEdge += pinStyle.rightMargin;
@@ -3293,9 +3359,7 @@ var getAllDimensionsForSchematicBox = (params) => {
3293
3359
  (p) => p.pinNumber.toString() === pinNumber.toString()
3294
3360
  );
3295
3361
  if (!port) {
3296
- throw new Error(
3297
- `Could not find port for pin number ${pinNumber}, available pins: ${truePortsWithPositions.map((tp) => tp.pinNumber).join(", ")}`
3298
- );
3362
+ return { x: 0, y: 0 };
3299
3363
  }
3300
3364
  return port;
3301
3365
  },
@@ -3306,53 +3370,6 @@ var getAllDimensionsForSchematicBox = (params) => {
3306
3370
  };
3307
3371
  };
3308
3372
 
3309
- // lib/soup/underscorifyPortArrangement.ts
3310
- var underscorifyPortArrangement = (portArrangement) => {
3311
- if (!portArrangement) return void 0;
3312
- if ("leftSide" in portArrangement || "rightSide" in portArrangement || "topSide" in portArrangement || "bottomSide" in portArrangement) {
3313
- return {
3314
- left_side: portArrangement.leftSide,
3315
- right_side: portArrangement.rightSide,
3316
- top_side: portArrangement.topSide,
3317
- bottom_side: portArrangement.bottomSide
3318
- };
3319
- }
3320
- if ("leftPinCount" in portArrangement || "rightPinCount" in portArrangement || "topPinCount" in portArrangement || "bottomPinCount" in portArrangement) {
3321
- return {
3322
- left_size: portArrangement.leftPinCount,
3323
- right_size: portArrangement.rightPinCount,
3324
- top_size: portArrangement.topPinCount,
3325
- bottom_size: portArrangement.bottomPinCount
3326
- };
3327
- }
3328
- if ("leftSize" in portArrangement || "rightSize" in portArrangement || "topSize" in portArrangement || "bottomSize" in portArrangement) {
3329
- return {
3330
- left_size: portArrangement.leftSize,
3331
- right_size: portArrangement.rightSize,
3332
- top_size: portArrangement.topSize,
3333
- bottom_size: portArrangement.bottomSize
3334
- };
3335
- }
3336
- return void 0;
3337
- };
3338
-
3339
- // lib/soup/underscorifyPinStyles.ts
3340
- import "circuit-json";
3341
- import "zod";
3342
- var underscorifyPinStyles = (pinStyles) => {
3343
- if (!pinStyles) return void 0;
3344
- const underscorePinStyles = {};
3345
- for (const [pinName, pinStyle] of Object.entries(pinStyles)) {
3346
- underscorePinStyles[pinName] = {
3347
- bottom_margin: pinStyle.bottomMargin,
3348
- left_margin: pinStyle.leftMargin,
3349
- right_margin: pinStyle.rightMargin,
3350
- top_margin: pinStyle.topMargin
3351
- };
3352
- }
3353
- return underscorePinStyles;
3354
- };
3355
-
3356
3373
  // lib/components/normal-components/Chip.ts
3357
3374
  var Chip = class extends NormalComponent {
3358
3375
  schematicDimensions = null;