@tscircuit/core 0.0.145 → 0.0.147

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 +491 -38
  2. package/dist/index.js +793 -735
  3. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -25,6 +25,7 @@ __export(components_exports, {
25
25
  Net: () => Net,
26
26
  NetAlias: () => NetAlias,
27
27
  NormalComponent: () => NormalComponent,
28
+ PinHeader: () => PinHeader,
28
29
  PlatedHole: () => PlatedHole,
29
30
  Port: () => Port,
30
31
  PowerSource: () => PowerSource,
@@ -789,6 +790,20 @@ var PrimitiveComponent = class extends Renderable {
789
790
  }
790
791
  return descendants;
791
792
  }
793
+ /**
794
+ * Return the number of pins in this component, this is important for
795
+ * NormalComponents
796
+ */
797
+ _getPinCount() {
798
+ return 0;
799
+ }
800
+ /**
801
+ * If this component represents a SchematicBox (like a Chip), return the
802
+ * dimensions of the box, which allows computing the position of ports etc.
803
+ */
804
+ _getSchematicBoxDimensions() {
805
+ return null;
806
+ }
792
807
  // TODO we shouldn't need to override this, errors can be rendered and handled
793
808
  // by the Renderable class, however, the Renderable class currently doesn't
794
809
  // have access to the database or cleanup
@@ -1614,23 +1629,28 @@ var Port = class extends PrimitiveComponent {
1614
1629
  return this._getPcbCircuitJsonBounds().center;
1615
1630
  }
1616
1631
  _getGlobalSchematicPositionBeforeLayout() {
1617
- if (!this.schematicSymbolPortDef) {
1618
- return applyToPoint4(this.parent.computeSchematicGlobalTransform(), {
1619
- x: 0,
1620
- y: 0
1621
- });
1622
- }
1623
1632
  const symbol = this.parent?.getSchematicSymbol();
1624
- if (!symbol) {
1625
- console.warn(`Could not find parent symbol for ${this}`);
1626
- return { x: 0, y: 0 };
1627
- }
1628
- const offsetY = -0.045;
1629
- const transform = compose3(
1630
- this.parent.computeSchematicGlobalTransform(),
1631
- translate3(-symbol.center.x, -symbol.center.y + offsetY)
1632
- );
1633
- return applyToPoint4(transform, this.schematicSymbolPortDef);
1633
+ if (symbol && this.schematicSymbolPortDef) {
1634
+ const transform = compose3(
1635
+ this.parent.computeSchematicGlobalTransform(),
1636
+ translate3(-symbol.center.x, -symbol.center.y)
1637
+ );
1638
+ return applyToPoint4(transform, this.schematicSymbolPortDef);
1639
+ }
1640
+ const parentBoxDim = this?.parent?._getSchematicBoxDimensions();
1641
+ if (parentBoxDim && this.props.pinNumber !== void 0) {
1642
+ const localPortPosition = parentBoxDim.getPortPositionByPinNumber(
1643
+ this.props.pinNumber
1644
+ );
1645
+ if (!localPortPosition) {
1646
+ return { x: 0, y: 0 };
1647
+ }
1648
+ return applyToPoint4(
1649
+ this.parent.computeSchematicGlobalTransform(),
1650
+ localPortPosition
1651
+ );
1652
+ }
1653
+ return { x: 0, y: 0 };
1634
1654
  }
1635
1655
  _getGlobalSchematicPositionAfterLayout() {
1636
1656
  const { db } = this.root;
@@ -1748,17 +1768,16 @@ var Port = class extends PrimitiveComponent {
1748
1768
  const container = this.getPrimitiveContainer();
1749
1769
  if (!container) return;
1750
1770
  const containerCenter = container._getGlobalSchematicPositionBeforeLayout();
1751
- let center = this._getGlobalSchematicPositionBeforeLayout();
1752
- if ("schematicDimensions" in container && props.pinNumber !== void 0) {
1753
- const chipDims = container.schematicDimensions;
1754
- center = chipDims.getPortPositionByPinNumber(props.pinNumber);
1755
- center.x += containerCenter.x;
1756
- center.y += containerCenter.y;
1771
+ const portCenter = this._getGlobalSchematicPositionBeforeLayout();
1772
+ let localPortInfo = null;
1773
+ const containerDims = container._getSchematicBoxDimensions();
1774
+ if (containerDims && props.pinNumber !== void 0) {
1775
+ localPortInfo = containerDims.getPortPositionByPinNumber(props.pinNumber);
1757
1776
  }
1758
1777
  if (this.getSubcircuit().props._schDebugObjectsEnabled) {
1759
1778
  db.schematic_debug_object.insert({
1760
1779
  shape: "rect",
1761
- center,
1780
+ center: portCenter,
1762
1781
  size: {
1763
1782
  width: 0.1,
1764
1783
  height: 0.1
@@ -1766,12 +1785,16 @@ var Port = class extends PrimitiveComponent {
1766
1785
  label: "obstacle"
1767
1786
  });
1768
1787
  }
1769
- this.facingDirection = getRelativeDirection(containerCenter, center);
1788
+ this.facingDirection = getRelativeDirection(containerCenter, portCenter);
1770
1789
  const schematic_port = db.schematic_port.insert({
1771
1790
  schematic_component_id: this.parent?.schematic_component_id,
1772
- center,
1791
+ center: portCenter,
1773
1792
  source_port_id: this.source_port_id,
1774
- facing_direction: this.facingDirection
1793
+ facing_direction: this.facingDirection,
1794
+ distance_from_component_edge: localPortInfo?.distanceFromEdge,
1795
+ side_of_component: localPortInfo?.side,
1796
+ pin_number: props.pinNumber,
1797
+ true_ccw_index: localPortInfo?.trueIndex
1775
1798
  });
1776
1799
  this.schematic_port_id = schematic_port.schematic_port_id;
1777
1800
  }
@@ -1804,7 +1827,7 @@ import {
1804
1827
  isValidElement
1805
1828
  } from "react";
1806
1829
  import { symbols as symbols2 } from "schematic-symbols";
1807
- import { z as z4 } from "zod";
1830
+ import { z as z5 } from "zod";
1808
1831
 
1809
1832
  // lib/components/primitive-components/Footprint.ts
1810
1833
  import { footprintProps } from "@tscircuit/props";
@@ -2015,261 +2038,576 @@ var Footprint = class extends PrimitiveComponent {
2015
2038
  }
2016
2039
  };
2017
2040
 
2018
- // lib/components/base-components/NormalComponent.ts
2019
- var debug2 = Debug2("tscircuit:core");
2020
- var rotation3 = z4.object({
2021
- x: rotation,
2022
- y: rotation,
2023
- z: rotation
2024
- });
2025
- var NormalComponent = class extends PrimitiveComponent {
2026
- reactSubtrees = [];
2027
- isPrimitiveContainer = true;
2028
- constructor(props) {
2029
- super(props);
2030
- this._addChildrenFromStringFootprint();
2031
- this.initPorts();
2041
+ // lib/utils/schematic/getSizeOfSidesFromPortArrangement.ts
2042
+ var hasExplicitPinMapping = (pa) => {
2043
+ for (const side of [
2044
+ "leftSide",
2045
+ "rightSide",
2046
+ "topSide",
2047
+ "bottomSide"
2048
+ ]) {
2049
+ if (side in pa && typeof pa[side] === "number") {
2050
+ throw new Error(
2051
+ `A number was specified for "${side}", you probably meant to use "size" not "side"`
2052
+ );
2053
+ }
2032
2054
  }
2033
- /**
2034
- * Override this method for better control over the auto-discovery of ports.
2035
- *
2036
- * If you override this method just do something like:
2037
- * initPorts() {
2038
- * this.add(new Port({ pinNumber: 1, aliases: ["anode", "pos"] }))
2039
- * this.add(new Port({ pinNumber: 2, aliases: ["cathode", "neg"] }))
2040
- * }
2041
- *
2042
- * By default, we'll pull the ports from the first place we find them:
2043
- * 1. `config.schematicSymbolName`
2044
- * 2. `props.footprint`
2045
- *
2046
- */
2047
- initPorts() {
2048
- const { config } = this;
2049
- const portsToCreate = [];
2050
- const schPortArrangement = this._parsedProps.schPortArrangement;
2051
- if (schPortArrangement) {
2052
- for (const side in schPortArrangement) {
2053
- const pins = schPortArrangement[side].pins;
2054
- if (Array.isArray(pins)) {
2055
- for (const pinNumber of pins) {
2056
- portsToCreate.push(
2057
- new Port(
2058
- {
2059
- pinNumber
2060
- },
2061
- {
2062
- originDescription: `schPortArrangement:${side}`
2063
- }
2064
- )
2065
- );
2066
- }
2067
- }
2068
- }
2069
- const sides = ["left", "right", "top", "bottom"];
2070
- let pinNum = 1;
2071
- for (const side of sides) {
2072
- const size = schPortArrangement[`${side}Size`];
2073
- for (let i = 0; i < size; i++) {
2074
- portsToCreate.push(
2075
- new Port(
2076
- {
2077
- pinNumber: pinNum++
2078
- },
2079
- {
2080
- originDescription: `schPortArrangement:${side}`
2081
- }
2082
- )
2083
- );
2084
- }
2085
- }
2055
+ return "leftSide" in pa || "rightSide" in pa || "topSide" in pa || "bottomSide" in pa;
2056
+ };
2057
+ var getSizeOfSidesFromPortArrangement = (pa) => {
2058
+ if (hasExplicitPinMapping(pa)) {
2059
+ return {
2060
+ leftSize: pa.leftSide?.pins.length ?? 0,
2061
+ rightSize: pa.rightSide?.pins.length ?? 0,
2062
+ topSize: pa.topSide?.pins.length ?? 0,
2063
+ bottomSize: pa.bottomSide?.pins.length ?? 0
2064
+ };
2065
+ }
2066
+ const { leftSize = 0, rightSize = 0, topSize = 0, bottomSize = 0 } = pa;
2067
+ return { leftSize, rightSize, topSize, bottomSize };
2068
+ };
2069
+
2070
+ // lib/utils/schematic/getAllDimensionsForSchematicBox.ts
2071
+ function isExplicitPinMappingArrangement(arrangement) {
2072
+ return arrangement.leftSide !== void 0;
2073
+ }
2074
+ var getAllDimensionsForSchematicBox = (params) => {
2075
+ const componentMargin = params.schPinSpacing;
2076
+ let sidePinCounts = params.schPortArrangement ? getSizeOfSidesFromPortArrangement(params.schPortArrangement) : null;
2077
+ const sideLengths = {
2078
+ left: 0,
2079
+ right: 0,
2080
+ top: 0,
2081
+ bottom: 0
2082
+ };
2083
+ let pinCount = params.pinCount ?? null;
2084
+ if (pinCount === null) {
2085
+ if (sidePinCounts) {
2086
+ pinCount = sidePinCounts.leftSize + sidePinCounts.rightSize + sidePinCounts.topSize + sidePinCounts.bottomSize;
2087
+ } else {
2088
+ throw new Error("Could not determine pin count for the schematic box");
2086
2089
  }
2087
- const pinLabels = this._parsedProps.pinLabels;
2088
- if (pinLabels) {
2089
- for (let [pinNumber, label] of Object.entries(pinLabels)) {
2090
- pinNumber = pinNumber.replace("pin", "");
2091
- let existingPort = portsToCreate.find(
2092
- (p) => p._parsedProps.pinNumber === Number(pinNumber)
2093
- );
2094
- const primaryLabel = Array.isArray(label) ? label[0] : label;
2095
- const otherLabels = Array.isArray(label) ? label.slice(1) : [];
2096
- if (!existingPort) {
2097
- existingPort = new Port(
2098
- {
2099
- pinNumber: parseInt(pinNumber),
2100
- name: primaryLabel,
2101
- aliases: otherLabels
2102
- },
2103
- {
2104
- originDescription: `pinLabels:pin${pinNumber}`
2105
- }
2106
- );
2107
- portsToCreate.push(existingPort);
2090
+ }
2091
+ if (pinCount && !sidePinCounts) {
2092
+ const rightSize = Math.floor(pinCount / 2);
2093
+ sidePinCounts = {
2094
+ leftSize: pinCount - rightSize,
2095
+ rightSize,
2096
+ topSize: 0,
2097
+ bottomSize: 0
2098
+ };
2099
+ }
2100
+ if (!sidePinCounts) {
2101
+ sidePinCounts = {
2102
+ leftSize: 0,
2103
+ rightSize: 0,
2104
+ topSize: 0,
2105
+ bottomSize: 0
2106
+ };
2107
+ }
2108
+ const schWidth = params.schWidth ?? Math.max(sidePinCounts.topSize, sidePinCounts.bottomSize) * params.schPinSpacing + 2 * componentMargin;
2109
+ const schHeight = params.schHeight ?? Math.max(sidePinCounts.leftSize, sidePinCounts.rightSize) * params.schPinSpacing + 2 * componentMargin;
2110
+ const distributePorts = (sideSize, sideLength, totalMargin) => {
2111
+ if (sideSize <= 1) return 0;
2112
+ return (sideLength - totalMargin) / (sideSize - 1);
2113
+ };
2114
+ const orderedTruePorts = [];
2115
+ let truePinIndex = 0;
2116
+ const processSide = (side, pins, direction) => {
2117
+ const isVertical = side === "left" || side === "right";
2118
+ const sideLength = isVertical ? schHeight : schWidth;
2119
+ let totalMargin = 0;
2120
+ for (const pin of pins) {
2121
+ const pinStyle = params.schPinStyle?.[`pin${pin}`] ?? params.schPinStyle?.[pin];
2122
+ if (pinStyle) {
2123
+ if (isVertical) {
2124
+ totalMargin += (pinStyle.topMargin ?? 0) + (pinStyle.bottomMargin ?? 0);
2108
2125
  } else {
2109
- existingPort.externallyAddedAliases.push(primaryLabel, ...otherLabels);
2110
- existingPort.props.name = primaryLabel;
2126
+ totalMargin += (pinStyle.leftMargin ?? 0) + (pinStyle.rightMargin ?? 0);
2111
2127
  }
2112
2128
  }
2113
2129
  }
2114
- if (config.schematicSymbolName) {
2115
- const sym = symbols2[`${config.schematicSymbolName}_horz`];
2116
- if (!sym) return;
2117
- for (const symPort of sym.ports) {
2118
- const port = getPortFromHints(symPort.labels);
2119
- if (port) {
2120
- port.originDescription = `schematicSymbol:labels[0]:${symPort.labels[0]}`;
2121
- port.schematicSymbolPortDef = symPort;
2122
- portsToCreate.push(port);
2130
+ const spacing = distributePorts(
2131
+ pins.length,
2132
+ sideLength - 2 * componentMargin,
2133
+ totalMargin
2134
+ );
2135
+ let currentDistanceFromEdge = componentMargin;
2136
+ pins.forEach((pinNumber, index) => {
2137
+ const pinStyle = params.schPinStyle?.[`pin${pinNumber}`] ?? params.schPinStyle?.[pinNumber];
2138
+ if (pinStyle) {
2139
+ if (isVertical) {
2140
+ if (side === "left") {
2141
+ currentDistanceFromEdge += direction === "top-to-bottom" ? pinStyle.topMargin ?? 0 : pinStyle.bottomMargin ?? 0;
2142
+ } else {
2143
+ currentDistanceFromEdge += direction === "top-to-bottom" ? pinStyle.bottomMargin ?? 0 : pinStyle.topMargin ?? 0;
2144
+ }
2145
+ } else {
2146
+ if (side === "top") {
2147
+ currentDistanceFromEdge += direction === "left-to-right" ? pinStyle.leftMargin ?? 0 : pinStyle.rightMargin ?? 0;
2148
+ } else {
2149
+ currentDistanceFromEdge += direction === "left-to-right" ? pinStyle.rightMargin ?? 0 : pinStyle.leftMargin ?? 0;
2150
+ }
2123
2151
  }
2124
2152
  }
2125
- this.addAll(portsToCreate);
2126
- return;
2127
- }
2128
- const portsFromFootprint = this.getPortsFromFootprint();
2129
- for (const port of portsFromFootprint) {
2130
- if (!portsToCreate.some((p) => p.isMatchingAnyOf(port.getNameAndAliases()))) {
2131
- portsToCreate.push(port);
2153
+ orderedTruePorts.push({
2154
+ trueIndex: truePinIndex,
2155
+ pinNumber,
2156
+ side,
2157
+ distanceFromEdge: currentDistanceFromEdge,
2158
+ x: 0,
2159
+ y: 0
2160
+ });
2161
+ if (pinStyle) {
2162
+ if (isVertical) {
2163
+ if (side === "left") {
2164
+ currentDistanceFromEdge += direction === "top-to-bottom" ? pinStyle.bottomMargin ?? 0 : pinStyle.topMargin ?? 0;
2165
+ } else {
2166
+ currentDistanceFromEdge += direction === "top-to-bottom" ? pinStyle.topMargin ?? 0 : pinStyle.bottomMargin ?? 0;
2167
+ }
2168
+ } else {
2169
+ if (side === "top") {
2170
+ currentDistanceFromEdge += direction === "left-to-right" ? pinStyle.rightMargin ?? 0 : pinStyle.leftMargin ?? 0;
2171
+ } else {
2172
+ currentDistanceFromEdge += direction === "left-to-right" ? pinStyle.leftMargin ?? 0 : pinStyle.rightMargin ?? 0;
2173
+ }
2174
+ }
2132
2175
  }
2133
- }
2134
- if (portsToCreate.length > 0) {
2135
- this.addAll(portsToCreate);
2136
- }
2137
- }
2138
- _addChildrenFromStringFootprint() {
2139
- const { footprint } = this.props;
2140
- if (!footprint) return;
2141
- if (typeof footprint === "string") {
2142
- const fpSoup = fp.string(footprint).soup();
2143
- const fpComponents = createComponentsFromSoup(fpSoup);
2144
- this.addAll(fpComponents);
2145
- }
2146
- }
2147
- get portMap() {
2148
- return new Proxy(
2149
- {},
2150
- {
2151
- get: (target, prop) => {
2152
- const port = this.children.find(
2153
- (c) => c.componentName === "Port" && c.isMatchingNameOrAlias(prop)
2154
- );
2155
- if (!port) {
2156
- throw new Error(
2157
- `There was an issue finding the port "${prop.toString()}" inside of a ${this.componentName} component with name: "${this.props.name}". This is a bug in @tscircuit/core`
2158
- );
2159
- }
2160
- return port;
2161
- }
2162
- }
2163
- );
2164
- }
2165
- getInstanceForReactElement(element) {
2166
- for (const subtree of this.reactSubtrees) {
2167
- if (subtree.element === element) return subtree.component;
2168
- }
2169
- return null;
2170
- }
2171
- doInitialSourceRender() {
2172
- const ftype = this.config.sourceFtype;
2173
- if (!ftype) return;
2174
- const { db } = this.root;
2175
- const { _parsedProps: props } = this;
2176
- const source_component = db.source_component.insert({
2177
- ftype,
2178
- name: props.name,
2179
- manufacturer_part_number: props.manufacturerPartNumber ?? props.mfn,
2180
- supplier_part_numbers: props.supplierPartNumbers
2176
+ currentDistanceFromEdge += spacing;
2177
+ truePinIndex++;
2181
2178
  });
2182
- this.source_component_id = source_component.source_component_id;
2183
- }
2184
- /**
2185
- * Render the schematic component for this NormalComponent using the
2186
- * config.schematicSymbolName if it exists.
2187
- *
2188
- * You can override this method to do more complicated things.
2189
- */
2190
- doInitialSchematicComponentRender() {
2191
- const { db } = this.root;
2192
- const { schematicSymbolName } = this.config;
2193
- if (!schematicSymbolName) return;
2194
- const base_symbol_name = this.config.schematicSymbolName;
2195
- const symbol_name_horz = `${base_symbol_name}_horz`;
2196
- let symbol_name;
2197
- if (!base_symbol_name) {
2198
- throw new Error(
2199
- "No schematic symbol defined (schematicSymbolName not provided)"
2179
+ sideLengths[side] = sideLength;
2180
+ };
2181
+ if (params.schPortArrangement && isExplicitPinMappingArrangement(params.schPortArrangement)) {
2182
+ if (params.schPortArrangement.leftSide) {
2183
+ processSide(
2184
+ "left",
2185
+ params.schPortArrangement.leftSide.pins,
2186
+ params.schPortArrangement.leftSide.direction ?? "top-to-bottom"
2200
2187
  );
2201
2188
  }
2202
- if (base_symbol_name in symbols2) {
2203
- symbol_name = base_symbol_name;
2204
- } else if (symbol_name_horz in symbols2) {
2205
- symbol_name = symbol_name_horz;
2206
- } else {
2207
- throw new Error(`Could not find schematic-symbol: "${base_symbol_name}"`);
2189
+ if (params.schPortArrangement.rightSide) {
2190
+ processSide(
2191
+ "right",
2192
+ params.schPortArrangement.rightSide.pins,
2193
+ params.schPortArrangement.rightSide.direction ?? "bottom-to-top"
2194
+ );
2208
2195
  }
2209
- const symbol = symbols2[symbol_name];
2210
- if (!symbol) {
2211
- throw new Error(`Could not find schematic-symbol "${symbol_name}"`);
2196
+ if (params.schPortArrangement.topSide) {
2197
+ processSide(
2198
+ "top",
2199
+ params.schPortArrangement.topSide.pins,
2200
+ params.schPortArrangement.topSide.direction ?? "left-to-right"
2201
+ );
2212
2202
  }
2213
- const schematic_component2 = db.schematic_component.insert({
2214
- center: { x: this.props.schX ?? 0, y: this.props.schY ?? 0 },
2215
- rotation: this.props.schRotation ?? 0,
2216
- size: symbol.size,
2217
- source_component_id: this.source_component_id,
2218
- symbol_name
2219
- });
2220
- this.schematic_component_id = schematic_component2.schematic_component_id;
2203
+ if (params.schPortArrangement.bottomSide) {
2204
+ processSide(
2205
+ "bottom",
2206
+ params.schPortArrangement.bottomSide.pins,
2207
+ params.schPortArrangement.bottomSide.direction ?? "left-to-right"
2208
+ );
2209
+ }
2210
+ } else {
2211
+ const leftSide = Array.from(
2212
+ { length: sidePinCounts.leftSize },
2213
+ (_, i) => i + 1
2214
+ );
2215
+ const rightSide = Array.from(
2216
+ { length: sidePinCounts.rightSize },
2217
+ (_, i) => i + sidePinCounts.leftSize + 1
2218
+ );
2219
+ const topSide = Array.from(
2220
+ { length: sidePinCounts.topSize },
2221
+ (_, i) => i + sidePinCounts.leftSize + sidePinCounts.rightSize + 1
2222
+ );
2223
+ const bottomSide = Array.from(
2224
+ {
2225
+ length: pinCount - sidePinCounts.leftSize - sidePinCounts.rightSize - sidePinCounts.topSize
2226
+ },
2227
+ (_, i) => i + sidePinCounts.leftSize + sidePinCounts.rightSize + sidePinCounts.topSize + 1
2228
+ );
2229
+ processSide("left", leftSide, "top-to-bottom");
2230
+ processSide("right", rightSide, "top-to-bottom");
2231
+ processSide("top", topSide, "left-to-right");
2232
+ processSide("bottom", bottomSide, "left-to-right");
2221
2233
  }
2222
- doInitialPcbComponentRender() {
2223
- const { db } = this.root;
2224
- const { _parsedProps: props } = this;
2225
- const pcb_component = db.pcb_component.insert({
2226
- center: this._getGlobalPcbPositionBeforeLayout(),
2227
- // width/height are computed in the PcbComponentSizeCalculation phase
2228
- width: 0,
2229
- height: 0,
2230
- layer: props.layer ?? "top",
2231
- rotation: props.pcbRotation ?? 0,
2232
- source_component_id: this.source_component_id
2233
- });
2234
- this.pcb_component_id = pcb_component.pcb_component_id;
2234
+ const trueEdgePositions = {
2235
+ left: { x: -schWidth / 2 - 0.2, y: schHeight / 2 },
2236
+ bottom: { x: -schWidth / 2, y: schHeight / 2 + 0.2 },
2237
+ right: { x: schWidth / 2 + 0.2, y: -schHeight / 2 },
2238
+ top: { x: schWidth / 2, y: -schHeight / 2 - 0.2 }
2239
+ };
2240
+ const trueEdgeTraversalDirections = {
2241
+ left: { x: 0, y: -1 },
2242
+ right: { x: 0, y: 1 },
2243
+ top: { x: -1, y: 0 },
2244
+ bottom: { x: 1, y: 0 }
2245
+ };
2246
+ const truePortsWithPositions = orderedTruePorts.map((p) => {
2247
+ const { distanceFromEdge, side } = p;
2248
+ const edgePos = trueEdgePositions[side];
2249
+ const edgeDir = trueEdgeTraversalDirections[side];
2250
+ return {
2251
+ ...p,
2252
+ x: edgePos.x + distanceFromEdge * edgeDir.x,
2253
+ y: edgePos.y + distanceFromEdge * edgeDir.y
2254
+ };
2255
+ });
2256
+ return {
2257
+ getPortPositionByPinNumber(pinNumber) {
2258
+ const port = truePortsWithPositions.find(
2259
+ (p) => p.pinNumber.toString() === pinNumber.toString()
2260
+ );
2261
+ if (!port) {
2262
+ return null;
2263
+ }
2264
+ return port;
2265
+ },
2266
+ getSize() {
2267
+ return { width: schWidth, height: schHeight };
2268
+ },
2269
+ pinCount
2270
+ };
2271
+ };
2272
+
2273
+ // lib/soup/underscorifyPortArrangement.ts
2274
+ var underscorifyPortArrangement = (portArrangement) => {
2275
+ if (!portArrangement) return void 0;
2276
+ if ("leftSide" in portArrangement || "rightSide" in portArrangement || "topSide" in portArrangement || "bottomSide" in portArrangement) {
2277
+ return {
2278
+ left_side: portArrangement.leftSide,
2279
+ right_side: portArrangement.rightSide,
2280
+ top_side: portArrangement.topSide,
2281
+ bottom_side: portArrangement.bottomSide
2282
+ };
2235
2283
  }
2236
- /**
2237
- * At this stage, the smtpads/pcb primitives are placed, so we can compute
2238
- * the width/height of the component
2239
- */
2240
- doInitialPcbComponentSizeCalculation() {
2241
- if (!this.pcb_component_id) return;
2242
- const { db } = this.root;
2243
- const { _parsedProps: props } = this;
2244
- const bounds = getBoundsOfPcbComponents(this.children);
2245
- if (bounds.width === 0 || bounds.height === 0) return;
2246
- const center = {
2247
- x: (bounds.minX + bounds.maxX) / 2,
2248
- y: (bounds.minY + bounds.maxY) / 2
2284
+ if ("leftPinCount" in portArrangement || "rightPinCount" in portArrangement || "topPinCount" in portArrangement || "bottomPinCount" in portArrangement) {
2285
+ return {
2286
+ left_size: portArrangement.leftPinCount,
2287
+ right_size: portArrangement.rightPinCount,
2288
+ top_size: portArrangement.topPinCount,
2289
+ bottom_size: portArrangement.bottomPinCount
2249
2290
  };
2250
- db.pcb_component.update(this.pcb_component_id, {
2251
- center,
2252
- width: bounds.width,
2253
- height: bounds.height
2254
- });
2255
2291
  }
2256
- _renderReactSubtree(element) {
2292
+ if ("leftSize" in portArrangement || "rightSize" in portArrangement || "topSize" in portArrangement || "bottomSize" in portArrangement) {
2257
2293
  return {
2258
- element,
2259
- component: createInstanceFromReactElement(element)
2294
+ left_size: portArrangement.leftSize,
2295
+ right_size: portArrangement.rightSize,
2296
+ top_size: portArrangement.topSize,
2297
+ bottom_size: portArrangement.bottomSize
2260
2298
  };
2261
2299
  }
2262
- doInitialInitializePortsFromChildren() {
2300
+ return void 0;
2301
+ };
2302
+
2303
+ // lib/soup/underscorifyPinStyles.ts
2304
+ import "circuit-json";
2305
+ import "zod";
2306
+ var underscorifyPinStyles = (pinStyles) => {
2307
+ if (!pinStyles) return void 0;
2308
+ const underscorePinStyles = {};
2309
+ for (const [pinName, pinStyle] of Object.entries(pinStyles)) {
2310
+ underscorePinStyles[pinName] = {
2311
+ bottom_margin: pinStyle.bottomMargin,
2312
+ left_margin: pinStyle.leftMargin,
2313
+ right_margin: pinStyle.rightMargin,
2314
+ top_margin: pinStyle.topMargin
2315
+ };
2316
+ }
2317
+ return underscorePinStyles;
2318
+ };
2319
+
2320
+ // lib/components/base-components/NormalComponent.ts
2321
+ var debug2 = Debug2("tscircuit:core");
2322
+ var rotation3 = z5.object({
2323
+ x: rotation,
2324
+ y: rotation,
2325
+ z: rotation
2326
+ });
2327
+ var NormalComponent = class extends PrimitiveComponent {
2328
+ reactSubtrees = [];
2329
+ _impliedFootprint;
2330
+ isPrimitiveContainer = true;
2331
+ constructor(props) {
2332
+ super(props);
2333
+ this._addChildrenFromStringFootprint();
2263
2334
  this.initPorts();
2264
2335
  }
2265
- doInitialReactSubtreesRender() {
2266
- if (isReactElement(this.props.footprint)) {
2267
- if (this.reactSubtrees.some((rs) => rs.element === this.props.footprint))
2268
- return;
2269
- const subtree = this._renderReactSubtree(this.props.footprint);
2270
- this.reactSubtrees.push(subtree);
2271
- this.add(subtree.component);
2272
- }
2336
+ /**
2337
+ * Override this method for better control over the auto-discovery of ports.
2338
+ *
2339
+ * If you override this method just do something like:
2340
+ * initPorts() {
2341
+ * this.add(new Port({ pinNumber: 1, aliases: ["anode", "pos"] }))
2342
+ * this.add(new Port({ pinNumber: 2, aliases: ["cathode", "neg"] }))
2343
+ * }
2344
+ *
2345
+ * By default, we'll pull the ports from the first place we find them:
2346
+ * 1. `config.schematicSymbolName`
2347
+ * 2. `props.footprint`
2348
+ *
2349
+ */
2350
+ initPorts() {
2351
+ const { config } = this;
2352
+ const portsToCreate = [];
2353
+ const schPortArrangement = this._parsedProps.schPortArrangement;
2354
+ if (schPortArrangement) {
2355
+ for (const side in schPortArrangement) {
2356
+ const pins = schPortArrangement[side].pins;
2357
+ if (Array.isArray(pins)) {
2358
+ for (const pinNumber of pins) {
2359
+ portsToCreate.push(
2360
+ new Port(
2361
+ {
2362
+ pinNumber
2363
+ },
2364
+ {
2365
+ originDescription: `schPortArrangement:${side}`
2366
+ }
2367
+ )
2368
+ );
2369
+ }
2370
+ }
2371
+ }
2372
+ const sides = ["left", "right", "top", "bottom"];
2373
+ let pinNum = 1;
2374
+ for (const side of sides) {
2375
+ const size = schPortArrangement[`${side}Size`];
2376
+ for (let i = 0; i < size; i++) {
2377
+ portsToCreate.push(
2378
+ new Port(
2379
+ {
2380
+ pinNumber: pinNum++
2381
+ },
2382
+ {
2383
+ originDescription: `schPortArrangement:${side}`
2384
+ }
2385
+ )
2386
+ );
2387
+ }
2388
+ }
2389
+ }
2390
+ const pinLabels = this._parsedProps.pinLabels;
2391
+ if (pinLabels) {
2392
+ for (let [pinNumber, label] of Object.entries(pinLabels)) {
2393
+ pinNumber = pinNumber.replace("pin", "");
2394
+ let existingPort = portsToCreate.find(
2395
+ (p) => p._parsedProps.pinNumber === Number(pinNumber)
2396
+ );
2397
+ const primaryLabel = Array.isArray(label) ? label[0] : label;
2398
+ const otherLabels = Array.isArray(label) ? label.slice(1) : [];
2399
+ if (!existingPort) {
2400
+ existingPort = new Port(
2401
+ {
2402
+ pinNumber: parseInt(pinNumber),
2403
+ name: primaryLabel,
2404
+ aliases: otherLabels
2405
+ },
2406
+ {
2407
+ originDescription: `pinLabels:pin${pinNumber}`
2408
+ }
2409
+ );
2410
+ portsToCreate.push(existingPort);
2411
+ } else {
2412
+ existingPort.externallyAddedAliases.push(primaryLabel, ...otherLabels);
2413
+ existingPort.props.name = primaryLabel;
2414
+ }
2415
+ }
2416
+ }
2417
+ if (config.schematicSymbolName) {
2418
+ const sym = symbols2[`${config.schematicSymbolName}_horz`];
2419
+ if (!sym) return;
2420
+ for (const symPort of sym.ports) {
2421
+ const port = getPortFromHints(symPort.labels);
2422
+ if (port) {
2423
+ port.originDescription = `schematicSymbol:labels[0]:${symPort.labels[0]}`;
2424
+ port.schematicSymbolPortDef = symPort;
2425
+ portsToCreate.push(port);
2426
+ }
2427
+ }
2428
+ this.addAll(portsToCreate);
2429
+ return;
2430
+ }
2431
+ const portsFromFootprint = this.getPortsFromFootprint();
2432
+ for (const port of portsFromFootprint) {
2433
+ if (!portsToCreate.some((p) => p.isMatchingAnyOf(port.getNameAndAliases()))) {
2434
+ portsToCreate.push(port);
2435
+ }
2436
+ }
2437
+ if (portsToCreate.length > 0) {
2438
+ this.addAll(portsToCreate);
2439
+ }
2440
+ }
2441
+ _getImpliedFootprintString() {
2442
+ return null;
2443
+ }
2444
+ _addChildrenFromStringFootprint() {
2445
+ let { footprint } = this.props;
2446
+ footprint ??= this._getImpliedFootprintString?.();
2447
+ if (!footprint) return;
2448
+ if (typeof footprint === "string") {
2449
+ const fpSoup = fp.string(footprint).soup();
2450
+ const fpComponents = createComponentsFromSoup(fpSoup);
2451
+ this.addAll(fpComponents);
2452
+ }
2453
+ }
2454
+ get portMap() {
2455
+ return new Proxy(
2456
+ {},
2457
+ {
2458
+ get: (target, prop) => {
2459
+ const port = this.children.find(
2460
+ (c) => c.componentName === "Port" && c.isMatchingNameOrAlias(prop)
2461
+ );
2462
+ if (!port) {
2463
+ throw new Error(
2464
+ `There was an issue finding the port "${prop.toString()}" inside of a ${this.componentName} component with name: "${this.props.name}". This is a bug in @tscircuit/core`
2465
+ );
2466
+ }
2467
+ return port;
2468
+ }
2469
+ }
2470
+ );
2471
+ }
2472
+ getInstanceForReactElement(element) {
2473
+ for (const subtree of this.reactSubtrees) {
2474
+ if (subtree.element === element) return subtree.component;
2475
+ }
2476
+ return null;
2477
+ }
2478
+ doInitialSourceRender() {
2479
+ const ftype = this.config.sourceFtype;
2480
+ if (!ftype) return;
2481
+ const { db } = this.root;
2482
+ const { _parsedProps: props } = this;
2483
+ const source_component = db.source_component.insert({
2484
+ ftype,
2485
+ name: props.name,
2486
+ manufacturer_part_number: props.manufacturerPartNumber ?? props.mfn,
2487
+ supplier_part_numbers: props.supplierPartNumbers
2488
+ });
2489
+ this.source_component_id = source_component.source_component_id;
2490
+ }
2491
+ /**
2492
+ * Render the schematic component for this NormalComponent using the
2493
+ * config.schematicSymbolName if it exists, or create a generic box if
2494
+ * no symbol is defined.
2495
+ *
2496
+ * You can override this method to do more complicated things.
2497
+ */
2498
+ doInitialSchematicComponentRender() {
2499
+ const { schematicSymbolName } = this.config;
2500
+ if (schematicSymbolName) {
2501
+ return this._doInitialSchematicComponentRenderWithSymbol();
2502
+ }
2503
+ const dimensions = this._getSchematicBoxDimensions();
2504
+ if (dimensions) {
2505
+ return this._doInitialSchematicComponentRenderWithSchematicBoxDimensions();
2506
+ }
2507
+ }
2508
+ _doInitialSchematicComponentRenderWithSymbol() {
2509
+ const { db } = this.root;
2510
+ const base_symbol_name = this.config.schematicSymbolName;
2511
+ const symbol_name_horz = `${base_symbol_name}_horz`;
2512
+ let symbol_name;
2513
+ if (!base_symbol_name) {
2514
+ throw new Error(
2515
+ "No schematic symbol defined (schematicSymbolName not provided)"
2516
+ );
2517
+ }
2518
+ if (base_symbol_name in symbols2) {
2519
+ symbol_name = base_symbol_name;
2520
+ } else if (symbol_name_horz in symbols2) {
2521
+ symbol_name = symbol_name_horz;
2522
+ } else {
2523
+ throw new Error(`Could not find schematic-symbol: "${base_symbol_name}"`);
2524
+ }
2525
+ const symbol = symbols2[symbol_name];
2526
+ if (symbol) {
2527
+ const schematic_component2 = db.schematic_component.insert({
2528
+ center: { x: this.props.schX ?? 0, y: this.props.schY ?? 0 },
2529
+ rotation: this.props.schRotation ?? 0,
2530
+ size: symbol.size,
2531
+ source_component_id: this.source_component_id,
2532
+ symbol_name
2533
+ });
2534
+ this.schematic_component_id = schematic_component2.schematic_component_id;
2535
+ }
2536
+ }
2537
+ _doInitialSchematicComponentRenderWithSchematicBoxDimensions() {
2538
+ const { db } = this.root;
2539
+ const { _parsedProps: props } = this;
2540
+ const dimensions = this._getSchematicBoxDimensions();
2541
+ const primaryPortLabels = {};
2542
+ for (const [port, label] of Object.entries(props.pinLabels ?? {})) {
2543
+ primaryPortLabels[port] = Array.isArray(label) ? label[0] : label;
2544
+ }
2545
+ const schematic_component2 = db.schematic_component.insert({
2546
+ center: { x: props.schX ?? 0, y: props.schY ?? 0 },
2547
+ rotation: props.schRotation ?? 0,
2548
+ size: dimensions.getSize(),
2549
+ port_arrangement: underscorifyPortArrangement(
2550
+ props.schPortArrangement
2551
+ ),
2552
+ pin_spacing: props.schPinSpacing ?? 0.2,
2553
+ // @ts-ignore soup needs to support distance for pin_styles
2554
+ pin_styles: underscorifyPinStyles(props.schPinStyle),
2555
+ port_labels: primaryPortLabels,
2556
+ source_component_id: this.source_component_id
2557
+ });
2558
+ this.schematic_component_id = schematic_component2.schematic_component_id;
2559
+ }
2560
+ doInitialPcbComponentRender() {
2561
+ const { db } = this.root;
2562
+ const { _parsedProps: props } = this;
2563
+ const pcb_component = db.pcb_component.insert({
2564
+ center: this._getGlobalPcbPositionBeforeLayout(),
2565
+ // width/height are computed in the PcbComponentSizeCalculation phase
2566
+ width: 0,
2567
+ height: 0,
2568
+ layer: props.layer ?? "top",
2569
+ rotation: props.pcbRotation ?? 0,
2570
+ source_component_id: this.source_component_id
2571
+ });
2572
+ this.pcb_component_id = pcb_component.pcb_component_id;
2573
+ }
2574
+ /**
2575
+ * At this stage, the smtpads/pcb primitives are placed, so we can compute
2576
+ * the width/height of the component
2577
+ */
2578
+ doInitialPcbComponentSizeCalculation() {
2579
+ if (!this.pcb_component_id) return;
2580
+ const { db } = this.root;
2581
+ const { _parsedProps: props } = this;
2582
+ const bounds = getBoundsOfPcbComponents(this.children);
2583
+ if (bounds.width === 0 || bounds.height === 0) return;
2584
+ const center = {
2585
+ x: (bounds.minX + bounds.maxX) / 2,
2586
+ y: (bounds.minY + bounds.maxY) / 2
2587
+ };
2588
+ db.pcb_component.update(this.pcb_component_id, {
2589
+ center,
2590
+ width: bounds.width,
2591
+ height: bounds.height
2592
+ });
2593
+ }
2594
+ _renderReactSubtree(element) {
2595
+ return {
2596
+ element,
2597
+ component: createInstanceFromReactElement(element)
2598
+ };
2599
+ }
2600
+ doInitialInitializePortsFromChildren() {
2601
+ this.initPorts();
2602
+ }
2603
+ doInitialReactSubtreesRender() {
2604
+ if (isReactElement(this.props.footprint)) {
2605
+ if (this.reactSubtrees.some((rs) => rs.element === this.props.footprint))
2606
+ return;
2607
+ const subtree = this._renderReactSubtree(this.props.footprint);
2608
+ this.reactSubtrees.push(subtree);
2609
+ this.add(subtree.component);
2610
+ }
2273
2611
  }
2274
2612
  _hasExistingPortExactly(port1) {
2275
2613
  const existingPorts = this.children.filter(
@@ -2420,8 +2758,37 @@ var NormalComponent = class extends PrimitiveComponent {
2420
2758
  height: pcb_component.height
2421
2759
  };
2422
2760
  }
2423
- doInitialCadModelRender() {
2424
- const { db } = this.root;
2761
+ _getPinCount() {
2762
+ const schPortArrangement = this._getSchematicPortArrangement();
2763
+ const pinCountFromSchArrangement = (schPortArrangement?.leftSize ?? 0) + (schPortArrangement?.rightSize ?? 0) + (schPortArrangement?.topSize ?? 0) + (schPortArrangement?.bottomSize ?? 0);
2764
+ const pinCount = pinCountFromSchArrangement || this.getPortsFromFootprint().length;
2765
+ return pinCount;
2766
+ }
2767
+ /**
2768
+ * Override the schematic port arrangement if you want to customize where pins
2769
+ * appear on a schematic box, e.g. for a pin header
2770
+ */
2771
+ _getSchematicPortArrangement() {
2772
+ return this._parsedProps.schPortArrangement;
2773
+ }
2774
+ _getSchematicBoxDimensions() {
2775
+ if (this.getSchematicSymbol()) return null;
2776
+ if (!this.config.shouldRenderAsSchematicBox) return null;
2777
+ const { _parsedProps: props } = this;
2778
+ const pinCount = this._getPinCount();
2779
+ const pinSpacing = props.schPinSpacing ?? 0.2;
2780
+ const dimensions = getAllDimensionsForSchematicBox({
2781
+ schWidth: props.schWidth,
2782
+ schHeight: props.schHeight,
2783
+ schPinSpacing: pinSpacing,
2784
+ schPinStyle: props.schPinStyle,
2785
+ pinCount,
2786
+ schPortArrangement: this._getSchematicPortArrangement()
2787
+ });
2788
+ return dimensions;
2789
+ }
2790
+ doInitialCadModelRender() {
2791
+ const { db } = this.root;
2425
2792
  const { boardThickness = 0 } = this.root?._getBoard() ?? {};
2426
2793
  const cadModel = this._parsedProps.cadModel;
2427
2794
  if (!this.pcb_component_id) return;
@@ -3425,421 +3792,126 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
3425
3792
  ...position,
3426
3793
  layer: "top"
3427
3794
  }));
3428
- const bounds = computeObstacleBounds(obstacles);
3429
- const simpleRouteJsonInput = {
3430
- minTraceWidth: 0.1,
3431
- obstacles,
3432
- connections: [connection],
3433
- bounds,
3434
- layerCount: 1
3435
- };
3436
- let Autorouter = MultilayerIjump;
3437
- if (this.getSubcircuit().props._schDirectLineRoutingEnabled) {
3438
- Autorouter = DirectLineRouter;
3439
- }
3440
- const autorouter = new Autorouter({
3441
- input: simpleRouteJsonInput,
3442
- OBSTACLE_MARGIN: 0.1,
3443
- isRemovePathLoopsEnabled: true
3444
- });
3445
- const results = autorouter.solveAndMapToTraces();
3446
- if (results.length === 0) return;
3447
- const [result] = results;
3448
- const { route } = result;
3449
- const edges = [];
3450
- for (let i = 0; i < route.length - 1; i++) {
3451
- edges.push({
3452
- from: route[i],
3453
- to: route[i + 1]
3454
- });
3455
- }
3456
- const STUB_LENGTH = 0.15;
3457
- edges.unshift({
3458
- from: {
3459
- ...projectPointInDirection(
3460
- route[0],
3461
- portsWithPosition[0].facingDirection,
3462
- STUB_LENGTH
3463
- )
3464
- },
3465
- to: route[0],
3466
- from_schematic_port_id: portsWithPosition[0].schematic_port_id
3467
- });
3468
- edges.push({
3469
- from: route[route.length - 1],
3470
- to: {
3471
- ...projectPointInOppositeDirection(
3472
- route[route.length - 1],
3473
- portsWithPosition[1].facingDirection,
3474
- STUB_LENGTH
3475
- )
3476
- },
3477
- from_schematic_port_id: portsWithPosition[1].schematic_port_id
3478
- });
3479
- const trace = db.schematic_trace.insert({
3480
- source_trace_id: this.source_trace_id,
3481
- edges
3482
- });
3483
- this.schematic_trace_id = trace.schematic_trace_id;
3484
- }
3485
- };
3486
-
3487
- // lib/components/normal-components/Capacitor.ts
3488
- var Capacitor = class extends NormalComponent {
3489
- // @ts-ignore (cause the symbolName is string and not fixed)
3490
- get config() {
3491
- return {
3492
- componentName: "Capacitor",
3493
- schematicSymbolName: this.props.symbolName ?? "capacitor_horz",
3494
- zodProps: capacitorProps,
3495
- sourceFtype: FTYPE.simple_capacitor
3496
- };
3497
- }
3498
- initPorts() {
3499
- this.add(new Port({ name: "pin1", pinNumber: 1, aliases: ["anode", "pos", "left"] }));
3500
- this.add(new Port({ name: "pin2", pinNumber: 2, aliases: ["cathode", "neg", "right"] }));
3501
- }
3502
- doInitialCreateNetsFromProps() {
3503
- this._createNetsFromProps([
3504
- this.props.decouplingFor,
3505
- this.props.decouplingTo
3506
- ]);
3507
- }
3508
- doInitialCreateTracesFromProps() {
3509
- if (this.props.decouplingFor && this.props.decouplingTo) {
3510
- this.add(
3511
- new Trace({
3512
- from: `${this.getSubcircuitSelector()} > port.1`,
3513
- to: this.props.decouplingFor
3514
- })
3515
- );
3516
- this.add(
3517
- new Trace({
3518
- from: `${this.getSubcircuitSelector()} > port.2`,
3519
- to: this.props.decouplingTo
3520
- })
3521
- );
3522
- }
3523
- }
3524
- doInitialSourceRender() {
3525
- const { db } = this.root;
3526
- const { _parsedProps: props } = this;
3527
- const source_component = db.source_component.insert({
3528
- ftype: "simple_capacitor",
3529
- name: props.name,
3530
- // @ts-ignore
3531
- manufacturer_part_number: props.manufacturerPartNumber ?? props.mfn,
3532
- supplier_part_numbers: props.supplierPartNumbers,
3533
- capacitance: props.capacitance
3534
- });
3535
- this.source_component_id = source_component.source_component_id;
3536
- }
3537
- };
3538
-
3539
- // lib/components/normal-components/Chip.ts
3540
- import { chipProps } from "@tscircuit/props";
3541
-
3542
- // lib/soup/underscorifyPinStyles.ts
3543
- import "circuit-json";
3544
- import "zod";
3545
- var underscorifyPinStyles = (pinStyles) => {
3546
- if (!pinStyles) return void 0;
3547
- const underscorePinStyles = {};
3548
- for (const [pinName, pinStyle] of Object.entries(pinStyles)) {
3549
- underscorePinStyles[pinName] = {
3550
- bottom_margin: pinStyle.bottomMargin,
3551
- left_margin: pinStyle.leftMargin,
3552
- right_margin: pinStyle.rightMargin,
3553
- top_margin: pinStyle.topMargin
3554
- };
3555
- }
3556
- return underscorePinStyles;
3557
- };
3558
-
3559
- // lib/soup/underscorifyPortArrangement.ts
3560
- var underscorifyPortArrangement = (portArrangement) => {
3561
- if (!portArrangement) return void 0;
3562
- if ("leftSide" in portArrangement || "rightSide" in portArrangement || "topSide" in portArrangement || "bottomSide" in portArrangement) {
3563
- return {
3564
- left_side: portArrangement.leftSide,
3565
- right_side: portArrangement.rightSide,
3566
- top_side: portArrangement.topSide,
3567
- bottom_side: portArrangement.bottomSide
3568
- };
3569
- }
3570
- if ("leftPinCount" in portArrangement || "rightPinCount" in portArrangement || "topPinCount" in portArrangement || "bottomPinCount" in portArrangement) {
3571
- return {
3572
- left_size: portArrangement.leftPinCount,
3573
- right_size: portArrangement.rightPinCount,
3574
- top_size: portArrangement.topPinCount,
3575
- bottom_size: portArrangement.bottomPinCount
3576
- };
3577
- }
3578
- if ("leftSize" in portArrangement || "rightSize" in portArrangement || "topSize" in portArrangement || "bottomSize" in portArrangement) {
3579
- return {
3580
- left_size: portArrangement.leftSize,
3581
- right_size: portArrangement.rightSize,
3582
- top_size: portArrangement.topSize,
3583
- bottom_size: portArrangement.bottomSize
3584
- };
3585
- }
3586
- return void 0;
3587
- };
3588
-
3589
- // lib/utils/schematic/getSizeOfSidesFromPortArrangement.ts
3590
- var hasExplicitPinMapping = (pa) => {
3591
- for (const side of [
3592
- "leftSide",
3593
- "rightSide",
3594
- "topSide",
3595
- "bottomSide"
3596
- ]) {
3597
- if (side in pa && typeof pa[side] === "number") {
3598
- throw new Error(
3599
- `A number was specified for "${side}", you probably meant to use "size" not "side"`
3600
- );
3601
- }
3602
- }
3603
- return "leftSide" in pa || "rightSide" in pa || "topSide" in pa || "bottomSide" in pa;
3604
- };
3605
- var getSizeOfSidesFromPortArrangement = (pa) => {
3606
- if (hasExplicitPinMapping(pa)) {
3607
- return {
3608
- leftSize: pa.leftSide?.pins.length ?? 0,
3609
- rightSize: pa.rightSide?.pins.length ?? 0,
3610
- topSize: pa.topSide?.pins.length ?? 0,
3611
- bottomSize: pa.bottomSide?.pins.length ?? 0
3612
- };
3613
- }
3614
- const { leftSize = 0, rightSize = 0, topSize = 0, bottomSize = 0 } = pa;
3615
- return { leftSize, rightSize, topSize, bottomSize };
3616
- };
3617
-
3618
- // lib/utils/schematic/getAllDimensionsForSchematicBox.ts
3619
- function isExplicitPinMappingArrangement(arrangement) {
3620
- return arrangement.leftSide !== void 0;
3621
- }
3622
- var getAllDimensionsForSchematicBox = (params) => {
3623
- const componentMargin = params.schPinSpacing;
3624
- let sidePinCounts = params.schPortArrangement ? getSizeOfSidesFromPortArrangement(params.schPortArrangement) : null;
3625
- const sideLengths = {
3626
- left: 0,
3627
- right: 0,
3628
- top: 0,
3629
- bottom: 0
3630
- };
3631
- let pinCount = params.pinCount ?? null;
3632
- if (pinCount === null) {
3633
- if (sidePinCounts) {
3634
- pinCount = sidePinCounts.leftSize + sidePinCounts.rightSize + sidePinCounts.topSize + sidePinCounts.bottomSize;
3635
- } else {
3636
- throw new Error("Could not determine pin count for the schematic box");
3637
- }
3638
- }
3639
- if (pinCount && !sidePinCounts) {
3640
- const rightSize = Math.floor(pinCount / 2);
3641
- sidePinCounts = {
3642
- leftSize: pinCount - rightSize,
3643
- rightSize,
3644
- topSize: 0,
3645
- bottomSize: 0
3646
- };
3647
- }
3648
- if (!sidePinCounts) {
3649
- sidePinCounts = {
3650
- leftSize: 0,
3651
- rightSize: 0,
3652
- topSize: 0,
3653
- bottomSize: 0
3654
- };
3655
- }
3656
- const schWidth = params.schWidth ?? Math.max(sidePinCounts.topSize, sidePinCounts.bottomSize) * params.schPinSpacing + 2 * componentMargin;
3657
- const schHeight = params.schHeight ?? Math.max(sidePinCounts.leftSize, sidePinCounts.rightSize) * params.schPinSpacing + 2 * componentMargin;
3658
- const distributePorts = (sideSize, sideLength, totalMargin) => {
3659
- if (sideSize <= 1) return 0;
3660
- return (sideLength - totalMargin) / (sideSize - 1);
3661
- };
3662
- const orderedTruePorts = [];
3663
- let truePinIndex = 0;
3664
- const processSide = (side, pins, direction) => {
3665
- const isVertical = side === "left" || side === "right";
3666
- const sideLength = isVertical ? schHeight : schWidth;
3667
- let totalMargin = 0;
3668
- for (const pin of pins) {
3669
- const pinStyle = params.schPinStyle?.[`pin${pin}`] ?? params.schPinStyle?.[pin];
3670
- if (pinStyle) {
3671
- if (isVertical) {
3672
- totalMargin += (pinStyle.topMargin ?? 0) + (pinStyle.bottomMargin ?? 0);
3673
- } else {
3674
- totalMargin += (pinStyle.leftMargin ?? 0) + (pinStyle.rightMargin ?? 0);
3675
- }
3676
- }
3677
- }
3678
- const spacing = distributePorts(
3679
- pins.length,
3680
- sideLength - 2 * componentMargin,
3681
- totalMargin
3682
- );
3683
- let currentDistanceFromEdge = componentMargin;
3684
- pins.forEach((pinNumber, index) => {
3685
- const pinStyle = params.schPinStyle?.[`pin${pinNumber}`] ?? params.schPinStyle?.[pinNumber];
3686
- if (pinStyle) {
3687
- if (isVertical) {
3688
- if (side === "left") {
3689
- currentDistanceFromEdge += direction === "top-to-bottom" ? pinStyle.topMargin ?? 0 : pinStyle.bottomMargin ?? 0;
3690
- } else {
3691
- currentDistanceFromEdge += direction === "top-to-bottom" ? pinStyle.bottomMargin ?? 0 : pinStyle.topMargin ?? 0;
3692
- }
3693
- } else {
3694
- if (side === "top") {
3695
- currentDistanceFromEdge += direction === "left-to-right" ? pinStyle.leftMargin ?? 0 : pinStyle.rightMargin ?? 0;
3696
- } else {
3697
- currentDistanceFromEdge += direction === "left-to-right" ? pinStyle.rightMargin ?? 0 : pinStyle.leftMargin ?? 0;
3698
- }
3699
- }
3700
- }
3701
- orderedTruePorts.push({
3702
- trueIndex: truePinIndex,
3703
- pinNumber,
3704
- side,
3705
- distanceFromEdge: currentDistanceFromEdge,
3706
- x: 0,
3707
- y: 0
3708
- });
3709
- if (pinStyle) {
3710
- if (isVertical) {
3711
- if (side === "left") {
3712
- currentDistanceFromEdge += direction === "top-to-bottom" ? pinStyle.bottomMargin ?? 0 : pinStyle.topMargin ?? 0;
3713
- } else {
3714
- currentDistanceFromEdge += direction === "top-to-bottom" ? pinStyle.topMargin ?? 0 : pinStyle.bottomMargin ?? 0;
3715
- }
3716
- } else {
3717
- if (side === "top") {
3718
- currentDistanceFromEdge += direction === "left-to-right" ? pinStyle.rightMargin ?? 0 : pinStyle.leftMargin ?? 0;
3719
- } else {
3720
- currentDistanceFromEdge += direction === "left-to-right" ? pinStyle.leftMargin ?? 0 : pinStyle.rightMargin ?? 0;
3721
- }
3722
- }
3723
- }
3724
- currentDistanceFromEdge += spacing;
3725
- truePinIndex++;
3726
- });
3727
- sideLengths[side] = sideLength;
3728
- };
3729
- if (params.schPortArrangement && isExplicitPinMappingArrangement(params.schPortArrangement)) {
3730
- if (params.schPortArrangement.leftSide) {
3731
- processSide(
3732
- "left",
3733
- params.schPortArrangement.leftSide.pins,
3734
- params.schPortArrangement.leftSide.direction ?? "top-to-bottom"
3735
- );
3736
- }
3737
- if (params.schPortArrangement.rightSide) {
3738
- processSide(
3739
- "right",
3740
- params.schPortArrangement.rightSide.pins,
3741
- params.schPortArrangement.rightSide.direction ?? "bottom-to-top"
3742
- );
3743
- }
3744
- if (params.schPortArrangement.topSide) {
3745
- processSide(
3746
- "top",
3747
- params.schPortArrangement.topSide.pins,
3748
- params.schPortArrangement.topSide.direction ?? "left-to-right"
3749
- );
3795
+ const bounds = computeObstacleBounds(obstacles);
3796
+ const simpleRouteJsonInput = {
3797
+ minTraceWidth: 0.1,
3798
+ obstacles,
3799
+ connections: [connection],
3800
+ bounds,
3801
+ layerCount: 1
3802
+ };
3803
+ let Autorouter = MultilayerIjump;
3804
+ if (this.getSubcircuit().props._schDirectLineRoutingEnabled) {
3805
+ Autorouter = DirectLineRouter;
3750
3806
  }
3751
- if (params.schPortArrangement.bottomSide) {
3752
- processSide(
3753
- "bottom",
3754
- params.schPortArrangement.bottomSide.pins,
3755
- params.schPortArrangement.bottomSide.direction ?? "left-to-right"
3756
- );
3807
+ const autorouter = new Autorouter({
3808
+ input: simpleRouteJsonInput,
3809
+ OBSTACLE_MARGIN: 0.1,
3810
+ isRemovePathLoopsEnabled: true
3811
+ });
3812
+ const results = autorouter.solveAndMapToTraces();
3813
+ if (results.length === 0) return;
3814
+ const [result] = results;
3815
+ const { route } = result;
3816
+ const edges = [];
3817
+ for (let i = 0; i < route.length - 1; i++) {
3818
+ edges.push({
3819
+ from: route[i],
3820
+ to: route[i + 1]
3821
+ });
3757
3822
  }
3758
- } else {
3759
- const leftSide = Array.from(
3760
- { length: sidePinCounts.leftSize },
3761
- (_, i) => i + 1
3762
- );
3763
- const rightSide = Array.from(
3764
- { length: sidePinCounts.rightSize },
3765
- (_, i) => i + sidePinCounts.leftSize + 1
3766
- );
3767
- const topSide = Array.from(
3768
- { length: sidePinCounts.topSize },
3769
- (_, i) => i + sidePinCounts.leftSize + sidePinCounts.rightSize + 1
3770
- );
3771
- const bottomSide = Array.from(
3772
- {
3773
- length: pinCount - sidePinCounts.leftSize - sidePinCounts.rightSize - sidePinCounts.topSize
3823
+ const STUB_LENGTH = 0.15;
3824
+ edges.unshift({
3825
+ from: {
3826
+ ...projectPointInDirection(
3827
+ route[0],
3828
+ portsWithPosition[0].facingDirection,
3829
+ STUB_LENGTH
3830
+ )
3774
3831
  },
3775
- (_, i) => i + sidePinCounts.leftSize + sidePinCounts.rightSize + sidePinCounts.topSize + 1
3776
- );
3777
- processSide("left", leftSide, "top-to-bottom");
3778
- processSide("right", rightSide, "top-to-bottom");
3779
- processSide("top", topSide, "left-to-right");
3780
- processSide("bottom", bottomSide, "left-to-right");
3832
+ to: route[0],
3833
+ from_schematic_port_id: portsWithPosition[0].schematic_port_id
3834
+ });
3835
+ edges.push({
3836
+ from: route[route.length - 1],
3837
+ to: {
3838
+ ...projectPointInOppositeDirection(
3839
+ route[route.length - 1],
3840
+ portsWithPosition[1].facingDirection,
3841
+ STUB_LENGTH
3842
+ )
3843
+ },
3844
+ from_schematic_port_id: portsWithPosition[1].schematic_port_id
3845
+ });
3846
+ const trace = db.schematic_trace.insert({
3847
+ source_trace_id: this.source_trace_id,
3848
+ edges
3849
+ });
3850
+ this.schematic_trace_id = trace.schematic_trace_id;
3781
3851
  }
3782
- const trueEdgePositions = {
3783
- left: { x: -schWidth / 2 - 0.2, y: schHeight / 2 },
3784
- bottom: { x: -schWidth / 2, y: schHeight / 2 + 0.2 },
3785
- right: { x: schWidth / 2 + 0.2, y: -schHeight / 2 },
3786
- top: { x: schWidth / 2, y: -schHeight / 2 - 0.2 }
3787
- };
3788
- const trueEdgeTraversalDirections = {
3789
- left: { x: 0, y: -1 },
3790
- right: { x: 0, y: 1 },
3791
- top: { x: -1, y: 0 },
3792
- bottom: { x: 1, y: 0 }
3793
- };
3794
- const truePortsWithPositions = orderedTruePorts.map((p) => {
3795
- const { distanceFromEdge, side } = p;
3796
- if (side === "center") {
3797
- return {
3798
- ...p,
3799
- x: 0,
3800
- y: 0
3801
- };
3802
- }
3803
- const edgePos = trueEdgePositions[side];
3804
- const edgeDir = trueEdgeTraversalDirections[side];
3852
+ };
3853
+
3854
+ // lib/components/normal-components/Capacitor.ts
3855
+ var Capacitor = class extends NormalComponent {
3856
+ // @ts-ignore (cause the symbolName is string and not fixed)
3857
+ get config() {
3805
3858
  return {
3806
- ...p,
3807
- x: edgePos.x + distanceFromEdge * edgeDir.x,
3808
- y: edgePos.y + distanceFromEdge * edgeDir.y
3859
+ componentName: "Capacitor",
3860
+ schematicSymbolName: this.props.symbolName ?? "capacitor_horz",
3861
+ zodProps: capacitorProps,
3862
+ sourceFtype: FTYPE.simple_capacitor
3809
3863
  };
3810
- });
3811
- return {
3812
- getPortPositionByPinNumber(pinNumber) {
3813
- const port = truePortsWithPositions.find(
3814
- (p) => p.pinNumber.toString() === pinNumber.toString()
3864
+ }
3865
+ initPorts() {
3866
+ this.add(new Port({ name: "pin1", pinNumber: 1, aliases: ["anode", "pos", "left"] }));
3867
+ this.add(new Port({ name: "pin2", pinNumber: 2, aliases: ["cathode", "neg", "right"] }));
3868
+ }
3869
+ doInitialCreateNetsFromProps() {
3870
+ this._createNetsFromProps([
3871
+ this.props.decouplingFor,
3872
+ this.props.decouplingTo
3873
+ ]);
3874
+ }
3875
+ doInitialCreateTracesFromProps() {
3876
+ if (this.props.decouplingFor && this.props.decouplingTo) {
3877
+ this.add(
3878
+ new Trace({
3879
+ from: `${this.getSubcircuitSelector()} > port.1`,
3880
+ to: this.props.decouplingFor
3881
+ })
3815
3882
  );
3816
- if (!port) {
3817
- return {
3818
- trueIndex: -1,
3819
- // Use -1 for non-existent ports
3820
- pinNumber,
3821
- side: "center",
3822
- distanceFromEdge: 0,
3823
- x: 0,
3824
- y: 0
3825
- };
3826
- }
3827
- return port;
3828
- },
3829
- getSize() {
3830
- return { width: schWidth, height: schHeight };
3831
- },
3832
- pinCount
3833
- };
3883
+ this.add(
3884
+ new Trace({
3885
+ from: `${this.getSubcircuitSelector()} > port.2`,
3886
+ to: this.props.decouplingTo
3887
+ })
3888
+ );
3889
+ }
3890
+ }
3891
+ doInitialSourceRender() {
3892
+ const { db } = this.root;
3893
+ const { _parsedProps: props } = this;
3894
+ const source_component = db.source_component.insert({
3895
+ ftype: "simple_capacitor",
3896
+ name: props.name,
3897
+ // @ts-ignore
3898
+ manufacturer_part_number: props.manufacturerPartNumber ?? props.mfn,
3899
+ supplier_part_numbers: props.supplierPartNumbers,
3900
+ capacitance: props.capacitance
3901
+ });
3902
+ this.source_component_id = source_component.source_component_id;
3903
+ }
3834
3904
  };
3835
3905
 
3836
3906
  // lib/components/normal-components/Chip.ts
3907
+ import { chipProps } from "@tscircuit/props";
3837
3908
  var Chip = class extends NormalComponent {
3838
- schematicDimensions = null;
3909
+ schematicBoxDimensions = null;
3839
3910
  get config() {
3840
3911
  return {
3841
3912
  componentName: "Chip",
3842
- zodProps: chipProps
3913
+ zodProps: chipProps,
3914
+ shouldRenderAsSchematicBox: true
3843
3915
  };
3844
3916
  }
3845
3917
  doInitialSourceRender() {
@@ -3853,43 +3925,6 @@ var Chip = class extends NormalComponent {
3853
3925
  });
3854
3926
  this.source_component_id = source_component.source_component_id;
3855
3927
  }
3856
- doInitialSchematicComponentRender() {
3857
- const { db } = this.root;
3858
- const { _parsedProps: props } = this;
3859
- const pinCountFromSchArrangement = (props.schPortArrangement?.leftSize ?? 0) + (props.schPortArrangement?.rightSize ?? 0) + (props.schPortArrangement?.topSize ?? 0) + (props.schPortArrangement?.bottomSize ?? 0);
3860
- const pinCount = pinCountFromSchArrangement || this.getPortsFromFootprint().length;
3861
- const pinSpacing = props.schPinSpacing ?? 0.2;
3862
- const dimensions = getAllDimensionsForSchematicBox({
3863
- schWidth: props.schWidth,
3864
- schHeight: props.schHeight,
3865
- schPinSpacing: pinSpacing,
3866
- schPinStyle: props.schPinStyle,
3867
- pinCount,
3868
- // @ts-ignore there's a subtley in the definition difference with
3869
- // leftSide/rightSide/topSide/bottomSide in how the direction is defined
3870
- // that doesn't really matter
3871
- schPortArrangement: props.schPortArrangement
3872
- });
3873
- this.schematicDimensions = dimensions;
3874
- const primaryPortLabels = {};
3875
- for (const [port, label] of Object.entries(props.pinLabels ?? {})) {
3876
- primaryPortLabels[port] = Array.isArray(label) ? label[0] : label;
3877
- }
3878
- const schematic_component2 = db.schematic_component.insert({
3879
- center: { x: props.schX ?? 0, y: props.schY ?? 0 },
3880
- rotation: props.schRotation ?? 0,
3881
- size: dimensions.getSize(),
3882
- port_arrangement: underscorifyPortArrangement(
3883
- props.schPortArrangement
3884
- ),
3885
- pin_spacing: pinSpacing,
3886
- // @ts-ignore soup needs to support distance for pin_styles
3887
- pin_styles: underscorifyPinStyles(props.schPinStyle),
3888
- port_labels: primaryPortLabels,
3889
- source_component_id: this.source_component_id
3890
- });
3891
- this.schematic_component_id = schematic_component2.schematic_component_id;
3892
- }
3893
3928
  doInitialPcbComponentRender() {
3894
3929
  const { db } = this.root;
3895
3930
  const { _parsedProps: props } = this;
@@ -3940,7 +3975,8 @@ var Jumper = class extends NormalComponent {
3940
3975
  get config() {
3941
3976
  return {
3942
3977
  componentName: "Jumper",
3943
- zodProps: jumperProps
3978
+ zodProps: jumperProps,
3979
+ shouldRenderAsSchematicBox: true
3944
3980
  };
3945
3981
  }
3946
3982
  doInitialSourceRender() {
@@ -3955,41 +3991,6 @@ var Jumper = class extends NormalComponent {
3955
3991
  });
3956
3992
  this.source_component_id = source_component.source_component_id;
3957
3993
  }
3958
- doInitialSchematicComponentRender() {
3959
- const { db } = this.root;
3960
- const { _parsedProps: props } = this;
3961
- const ports = this.children.filter((child) => child instanceof Port);
3962
- const pinSpacing = props.schPinSpacing ?? 0.2;
3963
- const dimensions = getAllDimensionsForSchematicBox({
3964
- schWidth: props.schWidth,
3965
- schHeight: props.schHeight,
3966
- schPinSpacing: pinSpacing,
3967
- schPinStyle: props.schPinStyle,
3968
- pinCount: ports.length,
3969
- // @ts-ignore there's a subtley in the definition difference with
3970
- // leftSide/rightSide/topSide/bottomSide in how the direction is defined
3971
- // that doesn't really matter
3972
- schPortArrangement: {
3973
- // TODO use schematic direction or schPortArrangement
3974
- rightSize: ports.length
3975
- }
3976
- });
3977
- this.schematicDimensions = dimensions;
3978
- const schematic_component2 = db.schematic_component.insert({
3979
- center: { x: props.schX ?? 0, y: props.schY ?? 0 },
3980
- rotation: props.schRotation ?? 0,
3981
- size: dimensions.getSize(),
3982
- port_arrangement: underscorifyPortArrangement(
3983
- props.schPortArrangement
3984
- ),
3985
- pin_spacing: pinSpacing,
3986
- // @ts-ignore soup needs to support distance for pin_styles
3987
- pin_styles: underscorifyPinStyles(props.schPinStyle),
3988
- port_labels: props.pinLabels,
3989
- source_component_id: this.source_component_id
3990
- });
3991
- this.schematic_component_id = schematic_component2.schematic_component_id;
3992
- }
3993
3994
  doInitialPcbComponentRender() {
3994
3995
  const { db } = this.root;
3995
3996
  const { _parsedProps: props } = this;
@@ -4488,6 +4489,62 @@ var Battery = class extends NormalComponent {
4488
4489
  }
4489
4490
  };
4490
4491
 
4492
+ // lib/components/normal-components/PinHeader.ts
4493
+ import { pinHeaderProps } from "@tscircuit/props";
4494
+ var PinHeader = class extends NormalComponent {
4495
+ get config() {
4496
+ return {
4497
+ componentName: "PinHeader",
4498
+ zodProps: pinHeaderProps,
4499
+ shouldRenderAsSchematicBox: true
4500
+ };
4501
+ }
4502
+ _getImpliedFootprintString() {
4503
+ const pinCount = this._parsedProps.pinCount ?? this._parsedProps.pinLabels?.length ?? 0;
4504
+ const holeDiameter = this._parsedProps.holeDiameter;
4505
+ const platedDiameter = this._parsedProps.platedDiameter;
4506
+ const pitch = this._parsedProps.pitch;
4507
+ if (pinCount > 0 && pitch) {
4508
+ if (!holeDiameter && !platedDiameter) {
4509
+ return `pinrow${pinCount}_p${pitch}`;
4510
+ }
4511
+ return `pinrow${pinCount}_p${pitch}_id${holeDiameter}_od${platedDiameter}`;
4512
+ }
4513
+ return null;
4514
+ }
4515
+ initPorts() {
4516
+ const pinCount = this._parsedProps.pinCount ?? this._parsedProps.pinLabels?.length ?? 1;
4517
+ for (let i = 1; i <= pinCount; i++) {
4518
+ this.add(
4519
+ new Port({
4520
+ name: `pin${i}`,
4521
+ pinNumber: i,
4522
+ aliases: []
4523
+ })
4524
+ );
4525
+ }
4526
+ }
4527
+ _getSchematicPortArrangement() {
4528
+ return {
4529
+ leftSize: 0,
4530
+ rightSize: this._parsedProps.pinCount ?? 1
4531
+ };
4532
+ }
4533
+ doInitialSourceRender() {
4534
+ const { db } = this.root;
4535
+ const { _parsedProps: props } = this;
4536
+ const source_component = db.source_component.insert({
4537
+ ftype: "simple_chip",
4538
+ name: props.name,
4539
+ // manufacturer_part_number: props.,
4540
+ supplier_part_numbers: props.supplierPartNumbers
4541
+ // gender: props.gender,
4542
+ // pitch: props.pitch,
4543
+ });
4544
+ this.source_component_id = source_component.source_component_id;
4545
+ }
4546
+ };
4547
+
4491
4548
  // lib/components/normal-components/Inductor.ts
4492
4549
  import { inductorProps } from "@tscircuit/props";
4493
4550
  var Inductor = class extends NormalComponent {
@@ -4783,6 +4840,7 @@ export {
4783
4840
  Net,
4784
4841
  NetAlias,
4785
4842
  NormalComponent,
4843
+ PinHeader,
4786
4844
  PlatedHole,
4787
4845
  Port,
4788
4846
  PowerSource,