@tscircuit/core 0.0.25 → 0.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -133,6 +133,14 @@ declare abstract class PrimitiveComponent<ZodProps extends ZodType = any> extend
133
133
  */
134
134
  computeSchematicGlobalTransform(): Matrix;
135
135
  getSchematicSymbol(variant?: "horz" | "vert" | null): SchSymbol | null;
136
+ /**
137
+ * Subcircuit groups have a prop called "layout" that can include manual
138
+ * placements for pcb components. These are typically added from an IDE
139
+ */
140
+ _getManualPlacementForComponent(component: PrimitiveComponent): {
141
+ x: number;
142
+ y: number;
143
+ } | null;
136
144
  getGlobalPcbPosition(): {
137
145
  x: number;
138
146
  y: number;
@@ -401,10 +409,19 @@ declare class Trace extends PrimitiveComponent<typeof traceProps> {
401
409
  ports?: undefined;
402
410
  portsWithSelectors?: undefined;
403
411
  };
404
- _findConnectedNets(): Array<{
405
- selector: string;
406
- net: Net;
407
- }>;
412
+ _findConnectedNets(): {
413
+ nets: Net[];
414
+ netsWithSelectors: Array<{
415
+ selector: string;
416
+ net: Net;
417
+ }>;
418
+ };
419
+ /**
420
+ * Get all the traces that are connected in any degree to this trace, this is
421
+ * used during autorouting to routes to pass through traces connected to the
422
+ * same net.
423
+ */
424
+ _getAllTracesConnectedToSameNet(): Trace[];
408
425
  /**
409
426
  * Determine if a trace is explicitly connected to a port (not via a net)
410
427
  */
@@ -413,6 +430,7 @@ declare class Trace extends PrimitiveComponent<typeof traceProps> {
413
430
  * Determine if a trace is explicitly connected to a net (not via a port)
414
431
  */
415
432
  _isExplicitlyConnectedToNet(net: Net): boolean;
433
+ doInitialCreateNetsFromProps(): void;
416
434
  doInitialSourceTraceRender(): void;
417
435
  doInitialPcbTraceRender(): void;
418
436
  doInitialSchematicTraceRender(): void;
package/dist/index.js CHANGED
@@ -274,7 +274,32 @@ var PrimitiveComponent = class extends Renderable {
274
274
  if (!config.schematicSymbolName) return null;
275
275
  return symbols[`${config.schematicSymbolName}_${variant}`] ?? null;
276
276
  }
277
+ /**
278
+ * Subcircuit groups have a prop called "layout" that can include manual
279
+ * placements for pcb components. These are typically added from an IDE
280
+ */
281
+ _getManualPlacementForComponent(component) {
282
+ if (!this.isSubcircuit) return null;
283
+ const layout = this.props.layout;
284
+ if (!layout) return null;
285
+ const placementConfig = layout.manual_pcb_placement_config;
286
+ if (!placementConfig) return null;
287
+ for (const position of placementConfig.positions) {
288
+ if (isMatchingSelector(component, position.selector)) {
289
+ const center = applyToPoint(
290
+ this.computePcbGlobalTransform(),
291
+ position.center
292
+ );
293
+ return center;
294
+ }
295
+ }
296
+ return null;
297
+ }
277
298
  getGlobalPcbPosition() {
299
+ const manualPlacement = this.getSubcircuit()._getManualPlacementForComponent(this);
300
+ if (manualPlacement) {
301
+ return manualPlacement;
302
+ }
278
303
  return applyToPoint(this.computePcbGlobalTransform(), { x: 0, y: 0 });
279
304
  }
280
305
  getGlobalSchematicPosition() {
@@ -1143,6 +1168,17 @@ var Net = class extends PrimitiveComponent {
1143
1168
  }
1144
1169
  };
1145
1170
 
1171
+ // lib/utils/components/createNetsFromProps.ts
1172
+ var createNetsFromProps = (component, props) => {
1173
+ for (const prop of props) {
1174
+ if (typeof prop === "string" && prop.startsWith("net.")) {
1175
+ if (!component.getSubcircuit().selectOne(prop)) {
1176
+ component.getSubcircuit().add(new Net({ name: prop.split("net.")[1] }));
1177
+ }
1178
+ }
1179
+ }
1180
+ };
1181
+
1146
1182
  // lib/components/base-components/NormalComponent.ts
1147
1183
  var NormalComponent = class extends PrimitiveComponent {
1148
1184
  reactSubtrees = [];
@@ -1365,17 +1401,7 @@ var NormalComponent = class extends PrimitiveComponent {
1365
1401
  return newPorts;
1366
1402
  }
1367
1403
  _createNetsFromProps(propsWithConnections) {
1368
- for (const prop of propsWithConnections) {
1369
- if (typeof prop === "string" && prop.startsWith("net.")) {
1370
- if (!this.getSubcircuit().selectOne(prop)) {
1371
- this.getSubcircuit().add(
1372
- new Net({
1373
- name: prop.split(".")[1]
1374
- })
1375
- );
1376
- }
1377
- }
1378
- }
1404
+ createNetsFromProps(this, propsWithConnections);
1379
1405
  }
1380
1406
  /**
1381
1407
  * Use data from our props to create ports for this component.
@@ -1469,7 +1495,6 @@ import "zod";
1469
1495
  import { traceProps } from "@tscircuit/props";
1470
1496
  import {
1471
1497
  IJumpAutorouter,
1472
- autoroute as autoroute2,
1473
1498
  getObstaclesFromSoup,
1474
1499
  markObstaclesAsConnected
1475
1500
  } from "@tscircuit/infgrid-ijump-astar";
@@ -1558,6 +1583,7 @@ function pdist(a, b) {
1558
1583
  return Math.hypot(a.x - b.x, a.y - b.y);
1559
1584
  }
1560
1585
  var mergeRoutes = (routes) => {
1586
+ if (routes.length === 1) return routes[0];
1561
1587
  if (routes.some((r) => r.length === 0)) {
1562
1588
  throw new Error("Cannot merge routes with zero length");
1563
1589
  }
@@ -1698,17 +1724,35 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
1698
1724
  };
1699
1725
  }
1700
1726
  _findConnectedNets() {
1701
- const nets = this.getTracePathNetSelectors().map((selector) => ({
1702
- selector,
1703
- net: this.getSubcircuit().selectOne(selector, { type: "net" })
1704
- }));
1705
- const undefinedNets = nets.filter((n) => !n.net);
1727
+ const netsWithSelectors = this.getTracePathNetSelectors().map(
1728
+ (selector) => ({
1729
+ selector,
1730
+ net: this.getSubcircuit().selectOne(selector, { type: "net" })
1731
+ })
1732
+ );
1733
+ const undefinedNets = netsWithSelectors.filter((n) => !n.net);
1706
1734
  if (undefinedNets.length > 0) {
1707
1735
  this.renderError(
1708
1736
  `Could not find net for selector "${undefinedNets[0].selector}" inside ${this}`
1709
1737
  );
1710
1738
  }
1711
- return nets;
1739
+ return { netsWithSelectors, nets: netsWithSelectors.map((n) => n.net) };
1740
+ }
1741
+ /**
1742
+ * Get all the traces that are connected in any degree to this trace, this is
1743
+ * used during autorouting to routes to pass through traces connected to the
1744
+ * same net.
1745
+ */
1746
+ _getAllTracesConnectedToSameNet() {
1747
+ const traces = this.getSubcircuit().selectAll("trace");
1748
+ const myNets = this._findConnectedNets().nets;
1749
+ const myPorts = this._findConnectedPorts().ports ?? [];
1750
+ return traces.filter((t) => {
1751
+ if (t === this) return false;
1752
+ const tNets = t._findConnectedNets().nets;
1753
+ const tPorts = t._findConnectedPorts().ports ?? [];
1754
+ return tNets.some((n) => myNets.includes(n)) || tPorts.some((p) => myPorts.includes(p));
1755
+ });
1712
1756
  }
1713
1757
  /**
1714
1758
  * Determine if a trace is explicitly connected to a port (not via a net)
@@ -1723,9 +1767,12 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
1723
1767
  * Determine if a trace is explicitly connected to a net (not via a port)
1724
1768
  */
1725
1769
  _isExplicitlyConnectedToNet(net) {
1726
- const nets = this._findConnectedNets().map((n) => n.net);
1770
+ const nets = this._findConnectedNets().nets;
1727
1771
  return nets.includes(net);
1728
1772
  }
1773
+ doInitialCreateNetsFromProps() {
1774
+ createNetsFromProps(this, this.getTracePathNetSelectors());
1775
+ }
1729
1776
  doInitialSourceTraceRender() {
1730
1777
  const { db } = this.project;
1731
1778
  const { _parsedProps: props, parent } = this;
@@ -1735,10 +1782,10 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
1735
1782
  }
1736
1783
  const { allPortsFound, portsWithSelectors: ports } = this._findConnectedPorts();
1737
1784
  if (!allPortsFound) return;
1738
- const nets = this._findConnectedNets();
1785
+ const nets = this._findConnectedNets().nets;
1739
1786
  const trace = db.source_trace.insert({
1740
1787
  connected_source_port_ids: ports.map((p) => p.port.source_port_id),
1741
- connected_source_net_ids: nets.map((n) => n.net.source_net_id)
1788
+ connected_source_net_ids: nets.map((n) => n.source_net_id)
1742
1789
  });
1743
1790
  this.source_trace_id = trace.source_trace_id;
1744
1791
  }
@@ -1749,7 +1796,7 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
1749
1796
  const { allPortsFound, ports } = this._findConnectedPorts();
1750
1797
  const portsConnectedOnPcbViaNet = [];
1751
1798
  if (!allPortsFound) return;
1752
- const nets = this._findConnectedNets();
1799
+ const nets = this._findConnectedNets().netsWithSelectors;
1753
1800
  if (ports.length === 0 && nets.length === 2) {
1754
1801
  this.renderError(
1755
1802
  `Trace connects two nets, we haven't implemented a way to route this yet`
@@ -1775,7 +1822,7 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
1775
1822
  return;
1776
1823
  }
1777
1824
  const pcbElements = db.toArray().filter(
1778
- (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"
1825
+ (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" || elm.type === "source_trace"
1779
1826
  );
1780
1827
  const source_trace = db.source_trace.get(this.source_trace_id);
1781
1828
  const hints = ports.flatMap(
@@ -1803,35 +1850,19 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
1803
1850
  if (isAlreadyRouted) {
1804
1851
  return;
1805
1852
  }
1853
+ let orderedRouteObjectives = [];
1806
1854
  if (pcbRouteHints.length === 0) {
1807
- const { solution } = autoroute2(
1808
- pcbElements.concat([
1809
- {
1810
- ...source_trace,
1811
- // manually override b/c some of the ports may be connected via nets
1812
- // so they don't appear properly in the source_trace, we don't need
1813
- // to do this if the algorithm correctly looks at connected_source_net_ids
1814
- connected_source_port_ids: ports.map((p) => p.source_port_id)
1815
- }
1816
- ])
1817
- );
1818
- const inputPcbTrace = solution[0];
1819
- if (!inputPcbTrace) {
1820
- console.log(
1821
- `Failed to find route from ${ports[0]} to ${ports[1]} (TODO render error!)`
1822
- );
1823
- return;
1824
- }
1825
- const pcb_trace2 = db.pcb_trace.insert(inputPcbTrace);
1826
- this.pcb_trace_id = pcb_trace2.pcb_trace_id;
1827
- this._portsRoutedOnPcb = ports;
1828
- return;
1855
+ orderedRouteObjectives = [
1856
+ portToObjective(ports[0]),
1857
+ portToObjective(ports[1])
1858
+ ];
1859
+ } else {
1860
+ orderedRouteObjectives = [
1861
+ portToObjective(ports[0]),
1862
+ ...pcbRouteHints,
1863
+ portToObjective(ports[1])
1864
+ ];
1829
1865
  }
1830
- const orderedRouteObjectives = [
1831
- portToObjective(ports[0]),
1832
- ...pcbRouteHints,
1833
- portToObjective(ports[1])
1834
- ];
1835
1866
  const candidateLayerCombinations = findPossibleTraceLayerCombinations(
1836
1867
  orderedRouteObjectives
1837
1868
  );
@@ -1846,6 +1877,16 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
1846
1877
  orderedRouteObjectives,
1847
1878
  this.source_trace_id
1848
1879
  );
1880
+ const allConnectedTraceIds = this._getAllTracesConnectedToSameNet().map(
1881
+ (t) => t.source_trace_id
1882
+ );
1883
+ for (const obstacle of obstacles) {
1884
+ if (obstacle.connectedTo.some(
1885
+ (connection) => allConnectedTraceIds.includes(connection)
1886
+ )) {
1887
+ obstacle.connectedTo.push(this.source_trace_id);
1888
+ }
1889
+ }
1849
1890
  const candidateLayerSelections = candidateLayerCombinations[0].layer_path;
1850
1891
  const orderedRoutePoints = orderedRouteObjectives.map((t, idx) => {
1851
1892
  if (t.via) {
@@ -1860,6 +1901,7 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
1860
1901
  for (const [a, b] of pairs(orderedRoutePoints)) {
1861
1902
  const BOUNDS_MARGIN = 2;
1862
1903
  const ijump = new IJumpAutorouter({
1904
+ OBSTACLE_MARGIN: 0.3,
1863
1905
  input: {
1864
1906
  obstacles,
1865
1907
  connections: [
@@ -1879,8 +1921,8 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
1879
1921
  });
1880
1922
  const traces = ijump.solveAndMapToTraces();
1881
1923
  if (traces.length === 0) {
1882
- this.renderError(
1883
- `Could not find a route between ${a.x}, ${a.y} and ${b.x}, ${b.y}`
1924
+ console.log(
1925
+ `Could not find a route between ${a} and ${b} for ${this} (TODO render error)`
1884
1926
  );
1885
1927
  return;
1886
1928
  }
@@ -1891,6 +1933,7 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
1891
1933
  route: mergeRoutes(routes),
1892
1934
  source_trace_id: this.source_trace_id
1893
1935
  });
1936
+ this._portsRoutedOnPcb = ports;
1894
1937
  this.pcb_trace_id = pcb_trace.pcb_trace_id;
1895
1938
  }
1896
1939
  doInitialSchematicTraceRender() {
@@ -17,6 +17,7 @@ import {
17
17
  import { getPortFromHints } from "lib/utils/getPortFromHints"
18
18
  import { createComponentsFromSoup } from "lib/utils/createComponentsFromSoup"
19
19
  import { Net } from "../primitive-components/Net"
20
+ import { createNetsFromProps } from "lib/utils/components/createNetsFromProps"
20
21
 
21
22
  export type PortMap<T extends string> = {
22
23
  [K in T]: Port
@@ -309,17 +310,7 @@ export class NormalComponent<
309
310
  }
310
311
 
311
312
  _createNetsFromProps(propsWithConnections: (string | undefined | null)[]) {
312
- for (const prop of propsWithConnections) {
313
- if (typeof prop === "string" && prop.startsWith("net.")) {
314
- if (!this.getSubcircuit().selectOne(prop)) {
315
- this.getSubcircuit().add(
316
- new Net({
317
- name: prop.split(".")[1],
318
- }),
319
- )
320
- }
321
- }
322
- }
313
+ createNetsFromProps(this, propsWithConnections)
323
314
  }
324
315
 
325
316
  /**
@@ -15,6 +15,7 @@ import {
15
15
  type Matrix,
16
16
  } from "transformation-matrix"
17
17
  import { isMatchingSelector } from "lib/utils/selector-matching"
18
+ import type { LayoutBuilder } from "@tscircuit/layout"
18
19
 
19
20
  export interface BaseComponentConfig {
20
21
  schematicSymbolName?: BaseSymbolName | null
@@ -168,7 +169,40 @@ export abstract class PrimitiveComponent<
168
169
  )
169
170
  }
170
171
 
172
+ /**
173
+ * Subcircuit groups have a prop called "layout" that can include manual
174
+ * placements for pcb components. These are typically added from an IDE
175
+ */
176
+ _getManualPlacementForComponent(
177
+ component: PrimitiveComponent,
178
+ ): { x: number; y: number } | null {
179
+ if (!this.isSubcircuit) return null
180
+
181
+ const layout: LayoutBuilder = this.props.layout
182
+ if (!layout) return null
183
+
184
+ const placementConfig = layout.manual_pcb_placement_config
185
+ if (!placementConfig) return null
186
+
187
+ for (const position of placementConfig.positions) {
188
+ if (isMatchingSelector(component, position.selector)) {
189
+ const center = applyToPoint(
190
+ this.computePcbGlobalTransform(),
191
+ position.center,
192
+ )
193
+ return center
194
+ }
195
+ }
196
+
197
+ return null
198
+ }
199
+
171
200
  getGlobalPcbPosition(): { x: number; y: number } {
201
+ const manualPlacement =
202
+ this.getSubcircuit()._getManualPlacementForComponent(this)
203
+ if (manualPlacement) {
204
+ return manualPlacement
205
+ }
172
206
  return applyToPoint(this.computePcbGlobalTransform(), { x: 0, y: 0 })
173
207
  }
174
208
 
@@ -22,12 +22,16 @@ import type {
22
22
  import { computeObstacleBounds } from "lib/utils/autorouting/computeObstacleBounds"
23
23
  import { projectPointInDirection } from "lib/utils/projectPointInDirection"
24
24
  import type { TraceHint } from "./TraceHint"
25
- import { findPossibleTraceLayerCombinations } from "lib/utils/autorouting/findPossibleTraceLayerCombinations"
25
+ import {
26
+ findPossibleTraceLayerCombinations,
27
+ type CandidateTraceLayerCombination,
28
+ } from "lib/utils/autorouting/findPossibleTraceLayerCombinations"
26
29
  import { pairs } from "lib/utils/pairs"
27
30
  import { mergeRoutes } from "lib/utils/autorouting/mergeRoutes"
28
31
  import type { Net } from "./Net"
29
32
  import { getClosest } from "lib/utils/getClosest"
30
33
  import { z } from "zod"
34
+ import { createNetsFromProps } from "lib/utils/components/createNetsFromProps"
31
35
 
32
36
  type PcbRouteObjective =
33
37
  | RouteHintPoint
@@ -144,20 +148,47 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
144
148
  }
145
149
  }
146
150
 
147
- _findConnectedNets(): Array<{ selector: string; net: Net }> {
148
- const nets = this.getTracePathNetSelectors().map((selector) => ({
149
- selector,
150
- net: this.getSubcircuit().selectOne(selector, { type: "net" }) as Net,
151
- }))
151
+ _findConnectedNets(): {
152
+ nets: Net[]
153
+ netsWithSelectors: Array<{ selector: string; net: Net }>
154
+ } {
155
+ const netsWithSelectors = this.getTracePathNetSelectors().map(
156
+ (selector) => ({
157
+ selector,
158
+ net: this.getSubcircuit().selectOne(selector, { type: "net" }) as Net,
159
+ }),
160
+ )
152
161
 
153
- const undefinedNets = nets.filter((n) => !n.net)
162
+ const undefinedNets = netsWithSelectors.filter((n) => !n.net)
154
163
  if (undefinedNets.length > 0) {
155
164
  this.renderError(
156
165
  `Could not find net for selector "${undefinedNets[0].selector}" inside ${this}`,
157
166
  )
158
167
  }
159
168
 
160
- return nets
169
+ return { netsWithSelectors, nets: netsWithSelectors.map((n) => n.net) }
170
+ }
171
+
172
+ /**
173
+ * Get all the traces that are connected in any degree to this trace, this is
174
+ * used during autorouting to routes to pass through traces connected to the
175
+ * same net.
176
+ */
177
+ _getAllTracesConnectedToSameNet(): Trace[] {
178
+ const traces = this.getSubcircuit().selectAll("trace") as Trace[]
179
+
180
+ const myNets = this._findConnectedNets().nets
181
+ const myPorts = this._findConnectedPorts().ports ?? []
182
+
183
+ return traces.filter((t) => {
184
+ if (t === this) return false
185
+ const tNets = t._findConnectedNets().nets
186
+ const tPorts = t._findConnectedPorts().ports ?? []
187
+ return (
188
+ tNets.some((n) => myNets.includes(n)) ||
189
+ tPorts.some((p) => myPorts.includes(p))
190
+ )
191
+ })
161
192
  }
162
193
 
163
194
  /**
@@ -175,10 +206,14 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
175
206
  * Determine if a trace is explicitly connected to a net (not via a port)
176
207
  */
177
208
  _isExplicitlyConnectedToNet(net: Net) {
178
- const nets = this._findConnectedNets().map((n) => n.net)
209
+ const nets = this._findConnectedNets().nets
179
210
  return nets.includes(net)
180
211
  }
181
212
 
213
+ doInitialCreateNetsFromProps(): void {
214
+ createNetsFromProps(this, this.getTracePathNetSelectors())
215
+ }
216
+
182
217
  doInitialSourceTraceRender(): void {
183
218
  const { db } = this.project!
184
219
  const { _parsedProps: props, parent } = this
@@ -192,11 +227,11 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
192
227
  this._findConnectedPorts()
193
228
  if (!allPortsFound) return
194
229
 
195
- const nets = this._findConnectedNets()
230
+ const nets = this._findConnectedNets().nets
196
231
 
197
232
  const trace = db.source_trace.insert({
198
233
  connected_source_port_ids: ports.map((p) => p.port.source_port_id!),
199
- connected_source_net_ids: nets.map((n) => n.net.source_net_id!),
234
+ connected_source_net_ids: nets.map((n) => n.source_net_id!),
200
235
  })
201
236
 
202
237
  this.source_trace_id = trace.source_trace_id
@@ -213,7 +248,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
213
248
 
214
249
  if (!allPortsFound) return
215
250
 
216
- const nets = this._findConnectedNets()
251
+ const nets = this._findConnectedNets().netsWithSelectors
217
252
 
218
253
  if (ports.length === 0 && nets.length === 2) {
219
254
  // Find the two optimal points to connect the two nets
@@ -254,7 +289,8 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
254
289
  elm.type === "pcb_plated_hole" ||
255
290
  elm.type === "pcb_hole" ||
256
291
  elm.type === "source_port" ||
257
- elm.type === "pcb_port",
292
+ elm.type === "pcb_port" ||
293
+ elm.type === "source_trace",
258
294
  )
259
295
 
260
296
  const source_trace = db.source_trace.get(this.source_trace_id!)!
@@ -296,44 +332,24 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
296
332
  return
297
333
  }
298
334
 
299
- if (pcbRouteHints.length === 0) {
300
- const { solution } = autoroute(
301
- pcbElements.concat([
302
- {
303
- ...source_trace,
304
- // manually override b/c some of the ports may be connected via nets
305
- // so they don't appear properly in the source_trace, we don't need
306
- // to do this if the algorithm correctly looks at connected_source_net_ids
307
- connected_source_port_ids: ports.map((p) => p.source_port_id!),
308
- } as SourceTrace,
309
- ]),
310
- )
311
- // TODO for some reason, the solution gets duplicated inside ijump-astar
312
- const inputPcbTrace = solution[0]
313
-
314
- if (!inputPcbTrace) {
315
- // TODO render error indicating we could not find a route
316
- console.log(
317
- `Failed to find route from ${ports[0]} to ${ports[1]} (TODO render error!)`,
318
- )
319
- return
320
- }
321
- const pcb_trace = db.pcb_trace.insert(inputPcbTrace as any)
335
+ let orderedRouteObjectives: PcbRouteObjective[] = []
322
336
 
323
- this.pcb_trace_id = pcb_trace.pcb_trace_id
324
- this._portsRoutedOnPcb = ports
325
- return
337
+ if (pcbRouteHints.length === 0) {
338
+ orderedRouteObjectives = [
339
+ portToObjective(ports[0]),
340
+ portToObjective(ports[1]),
341
+ ]
342
+ } else {
343
+ // When we have hints, we have to order the hints then route between each
344
+ // terminal of the trace and the hints
345
+ // TODO order based on proximity to ports
346
+ orderedRouteObjectives = [
347
+ portToObjective(ports[0]),
348
+ ...pcbRouteHints,
349
+ portToObjective(ports[1]),
350
+ ]
326
351
  }
327
352
 
328
- // When we have hints, we have to order the hints then route between each
329
- // terminal of the trace and the hints
330
- // TODO order based on proximity to ports
331
- const orderedRouteObjectives: PcbRouteObjective[] = [
332
- portToObjective(ports[0]),
333
- ...pcbRouteHints,
334
- portToObjective(ports[1]),
335
- ]
336
-
337
353
  // Hints can indicate where there should be a via, but the layer is allowed
338
354
  // to be unspecified, therefore we need to find possible layer combinations
339
355
  // to go to each hint and still route to the start and end points
@@ -356,6 +372,19 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
356
372
  this.source_trace_id!,
357
373
  )
358
374
 
375
+ const allConnectedTraceIds = this._getAllTracesConnectedToSameNet().map(
376
+ (t) => t.source_trace_id,
377
+ )
378
+ for (const obstacle of obstacles) {
379
+ if (
380
+ obstacle.connectedTo.some((connection) =>
381
+ allConnectedTraceIds.includes(connection),
382
+ )
383
+ ) {
384
+ obstacle.connectedTo.push(this.source_trace_id!)
385
+ }
386
+ }
387
+
359
388
  // TODO explore all candidate layer combinations if one fails
360
389
  const candidateLayerSelections = candidateLayerCombinations[0].layer_path
361
390
 
@@ -377,6 +406,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
377
406
  for (const [a, b] of pairs(orderedRoutePoints)) {
378
407
  const BOUNDS_MARGIN = 2 //mm
379
408
  const ijump = new IJumpAutorouter({
409
+ OBSTACLE_MARGIN: 0.3,
380
410
  input: {
381
411
  obstacles,
382
412
  connections: [
@@ -396,8 +426,8 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
396
426
  })
397
427
  const traces = ijump.solveAndMapToTraces()
398
428
  if (traces.length === 0) {
399
- this.renderError(
400
- `Could not find a route between ${a.x}, ${a.y} and ${b.x}, ${b.y}`,
429
+ console.log(
430
+ `Could not find a route between ${a} and ${b} for ${this} (TODO render error)`,
401
431
  )
402
432
  return
403
433
  }
@@ -410,6 +440,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
410
440
  route: mergeRoutes(routes),
411
441
  source_trace_id: this.source_trace_id!,
412
442
  })
443
+ this._portsRoutedOnPcb = ports
413
444
  this.pcb_trace_id = pcb_trace.pcb_trace_id
414
445
  }
415
446
 
@@ -13,7 +13,7 @@ const LAYER_SELECTION_PREFERENCE = ["top", "bottom", "inner1", "inner2"]
13
13
  * top -> bottom -> bottom -> top
14
14
  * bottom -> top -> top -> bottom
15
15
  */
16
- interface CandidateTraceLayerCombination {
16
+ export interface CandidateTraceLayerCombination {
17
17
  layer_path: string[]
18
18
  }
19
19
 
@@ -11,6 +11,7 @@ function pdist(a: any, b: any) {
11
11
  * reverse the next route and append it to the previous route.
12
12
  */
13
13
  export const mergeRoutes = (routes: PCBTrace["route"][]) => {
14
+ if (routes.length === 1) return routes[0]
14
15
  // routes = routes.filter((route) => route.length > 0)
15
16
  if (routes.some((r) => r.length === 0)) {
16
17
  throw new Error("Cannot merge routes with zero length")
@@ -0,0 +1,15 @@
1
+ import type { PrimitiveComponent } from "lib/components/base-components/PrimitiveComponent"
2
+ import { Net } from "lib/components/primitive-components/Net"
3
+
4
+ export const createNetsFromProps = (
5
+ component: PrimitiveComponent,
6
+ props: (string | undefined | null)[],
7
+ ) => {
8
+ for (const prop of props) {
9
+ if (typeof prop === "string" && prop.startsWith("net.")) {
10
+ if (!component.getSubcircuit().selectOne(prop)) {
11
+ component.getSubcircuit().add(new Net({ name: prop.split("net.")[1] }))
12
+ }
13
+ }
14
+ }
15
+ }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@tscircuit/core",
3
3
  "module": "index.ts",
4
4
  "type": "module",
5
- "version": "0.0.25",
5
+ "version": "0.0.27",
6
6
  "types": "dist/index.d.ts",
7
7
  "main": "dist/index.js",
8
8
  "files": [
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "devDependencies": {
18
18
  "@biomejs/biome": "^1.8.3",
19
- "@tscircuit/layout": "^0.0.27",
19
+ "@tscircuit/layout": "^0.0.28",
20
20
  "@tscircuit/log-soup": "^1.0.2",
21
21
  "@types/bun": "latest",
22
22
  "@types/react": "^18.3.3",