@tscircuit/core 0.0.48 → 0.0.50

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
@@ -3771,7 +3771,7 @@ declare class PlatedHole extends PrimitiveComponent<typeof platedHoleProps> {
3771
3771
  }>]>, "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6", "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6" | {
3772
3772
  name: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6";
3773
3773
  }>>;
3774
- }, "pcbRotation" | "layer">, {
3774
+ }, "layer" | "pcbRotation">, {
3775
3775
  name: zod.ZodOptional<zod.ZodString>;
3776
3776
  shape: zod.ZodLiteral<"circle">;
3777
3777
  holeDiameter: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
@@ -3806,7 +3806,7 @@ declare class PlatedHole extends PrimitiveComponent<typeof platedHoleProps> {
3806
3806
  }>]>, "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6", "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6" | {
3807
3807
  name: "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6";
3808
3808
  }>>;
3809
- }, "pcbRotation" | "layer">, {
3809
+ }, "layer" | "pcbRotation">, {
3810
3810
  name: zod.ZodOptional<zod.ZodString>;
3811
3811
  shape: zod.ZodLiteral<"oval">;
3812
3812
  outerWidth: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
package/dist/index.js CHANGED
@@ -2439,6 +2439,7 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
2439
2439
  this.renderError(
2440
2440
  `Could not find a common layer (using hints) for trace ${this.getString()}`
2441
2441
  );
2442
+ return;
2442
2443
  }
2443
2444
  const obstacles = getObstaclesFromSoup(this.root.db.toArray());
2444
2445
  markObstaclesAsConnected(
@@ -2468,6 +2469,7 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
2468
2469
  });
2469
2470
  const routes = [];
2470
2471
  for (const [a, b] of pairs(orderedRoutePoints)) {
2472
+ const dominantLayer = "via_to_layer" in a ? a.via_to_layer : null;
2471
2473
  const BOUNDS_MARGIN = 2;
2472
2474
  const ijump = new IJumpAutorouter({
2473
2475
  OBSTACLE_MARGIN: 0.3,
@@ -2476,10 +2478,13 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
2476
2478
  connections: [
2477
2479
  {
2478
2480
  name: this.source_trace_id,
2479
- pointsToConnect: [a, b]
2481
+ pointsToConnect: [
2482
+ { ...a, layer: dominantLayer ?? "top" },
2483
+ { ...b, layer: dominantLayer ?? "top" }
2484
+ ]
2480
2485
  }
2481
2486
  ],
2482
- layerCount: 1,
2487
+ layerCount: 2,
2483
2488
  bounds: {
2484
2489
  minX: Math.min(a.x, b.x) - BOUNDS_MARGIN,
2485
2490
  maxX: Math.max(a.x, b.x) + BOUNDS_MARGIN,
@@ -2490,16 +2495,53 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
2490
2495
  });
2491
2496
  const traces = ijump.solveAndMapToTraces();
2492
2497
  if (traces.length === 0) {
2493
- console.log(
2494
- `Could not find a route between ${a} and ${b} for ${this} (TODO render error)`
2498
+ this.renderError(
2499
+ `Could not find a route between ${JSON.stringify(a)} and ${JSON.stringify(b)} for ${this}`
2495
2500
  );
2496
2501
  return;
2497
2502
  }
2498
2503
  const [trace] = traces;
2504
+ if (dominantLayer) {
2505
+ trace.route = trace.route.map((p) => {
2506
+ if (p.route_type === "wire") {
2507
+ p.layer = dominantLayer;
2508
+ }
2509
+ return p;
2510
+ });
2511
+ }
2499
2512
  routes.push(trace.route);
2500
2513
  }
2514
+ const mergedRoute = mergeRoutes(routes);
2515
+ const routeWithVias = mergedRoute.flatMap((point, index) => {
2516
+ const nextPoint = mergedRoute[index + 1];
2517
+ if (!nextPoint) return [point];
2518
+ if (point.route_type === "wire" && nextPoint.route_type === "wire" && point.layer !== nextPoint.layer) {
2519
+ return [
2520
+ {
2521
+ route_type: "via",
2522
+ from_layer: point.layer,
2523
+ to_layer: nextPoint.layer,
2524
+ x: nextPoint.x,
2525
+ y: nextPoint.y
2526
+ },
2527
+ point
2528
+ ];
2529
+ }
2530
+ return [point];
2531
+ });
2532
+ for (const via of routeWithVias.filter((p) => p.route_type === "via")) {
2533
+ db.pcb_via.insert({
2534
+ x: via.x,
2535
+ y: via.y,
2536
+ hole_diameter: 0.3,
2537
+ outer_diameter: 0.6,
2538
+ layers: [via.from_layer, via.to_layer],
2539
+ from_layer: via.from_layer,
2540
+ to_layer: via.to_layer
2541
+ });
2542
+ }
2501
2543
  const pcb_trace = db.pcb_trace.insert({
2502
- route: mergeRoutes(routes),
2544
+ route: routeWithVias,
2503
2545
  source_trace_id: this.source_trace_id
2504
2546
  });
2505
2547
  this._portsRoutedOnPcb = ports;
@@ -2520,6 +2562,7 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
2520
2562
  if (elm.type === "schematic_component") {
2521
2563
  obstacles.push({
2522
2564
  type: "rect",
2565
+ layers: ["top"],
2523
2566
  center: elm.center,
2524
2567
  width: elm.size.width,
2525
2568
  height: elm.size.height,
@@ -2528,13 +2571,14 @@ searched component ${targetComponent.getString()}, which has ports: ${targetComp
2528
2571
  }
2529
2572
  }
2530
2573
  for (const { port } of ports) {
2531
- connection.pointsToConnect.push(
2532
- projectPointInDirection(
2574
+ connection.pointsToConnect.push({
2575
+ ...projectPointInDirection(
2533
2576
  port._getGlobalSchematicPositionBeforeLayout(),
2534
2577
  port.facingDirection,
2535
2578
  0.1501
2536
- )
2537
- );
2579
+ ),
2580
+ layer: "top"
2581
+ });
2538
2582
  }
2539
2583
  const bounds = computeObstacleBounds(obstacles);
2540
2584
  const simpleRouteJsonInput = {
@@ -9,6 +9,7 @@ import {
9
9
  } from "@tscircuit/infgrid-ijump-astar"
10
10
  import type {
11
11
  AnySoupElement,
12
+ LayerRef,
12
13
  PCBTrace,
13
14
  RouteHintPoint,
14
15
  SchematicTrace,
@@ -366,6 +367,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
366
367
  this.renderError(
367
368
  `Could not find a common layer (using hints) for trace ${this.getString()}`,
368
369
  )
370
+ return
369
371
  }
370
372
 
371
373
  // Cache the PCB obstacles, they'll be needed for each segment between
@@ -409,7 +411,10 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
409
411
 
410
412
  const routes: PCBTrace["route"][] = []
411
413
  for (const [a, b] of pairs(orderedRoutePoints)) {
414
+ const dominantLayer =
415
+ "via_to_layer" in a ? (a.via_to_layer as LayerRef) : null
412
416
  const BOUNDS_MARGIN = 2 //mm
417
+
413
418
  const ijump = new IJumpAutorouter({
414
419
  OBSTACLE_MARGIN: 0.3,
415
420
  input: {
@@ -417,10 +422,13 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
417
422
  connections: [
418
423
  {
419
424
  name: this.source_trace_id!,
420
- pointsToConnect: [a, b],
425
+ pointsToConnect: [
426
+ { ...a, layer: dominantLayer ?? "top" },
427
+ { ...b, layer: dominantLayer ?? "top" },
428
+ ],
421
429
  },
422
430
  ],
423
- layerCount: 1,
431
+ layerCount: 2,
424
432
  bounds: {
425
433
  minX: Math.min(a.x, b.x) - BOUNDS_MARGIN,
426
434
  maxX: Math.max(a.x, b.x) + BOUNDS_MARGIN,
@@ -431,18 +439,67 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
431
439
  })
432
440
  const traces = ijump.solveAndMapToTraces()
433
441
  if (traces.length === 0) {
434
- console.log(
435
- `Could not find a route between ${a} and ${b} for ${this} (TODO render error)`,
442
+ this.renderError(
443
+ `Could not find a route between ${JSON.stringify(a)} and ${JSON.stringify(b)} for ${this}`,
436
444
  )
437
445
  return
438
446
  }
439
447
  // TODO ijump returns multiple traces for some reason
440
448
  const [trace] = traces as PCBTrace[]
449
+ // TODO ijump always returns on top layer and has no multilayer support
450
+ // https://github.com/tscircuit/autorouting-dataset/issues/35
451
+ // For now, we'll move the trace to whatever layer the first point
452
+ // specifies we should have "via'd" to
453
+ if (dominantLayer) {
454
+ trace.route = trace.route.map((p) => {
455
+ if (p.route_type === "wire") {
456
+ p.layer = dominantLayer
457
+ }
458
+ return p
459
+ })
460
+ }
441
461
  routes.push(trace.route)
442
462
  }
443
463
 
464
+ const mergedRoute = mergeRoutes(routes)
465
+
466
+ // Add vias where layer changes occur
467
+ const routeWithVias = mergedRoute.flatMap((point, index) => {
468
+ const nextPoint = mergedRoute[index + 1]
469
+ if (!nextPoint) return [point]
470
+ if (
471
+ point.route_type === "wire" &&
472
+ nextPoint.route_type === "wire" &&
473
+ point.layer !== nextPoint.layer
474
+ ) {
475
+ return [
476
+ {
477
+ route_type: "via",
478
+ from_layer: point.layer,
479
+ to_layer: nextPoint.layer,
480
+ x: nextPoint.x,
481
+ y: nextPoint.y,
482
+ } as (typeof mergedRoute)[number],
483
+ point,
484
+ ]
485
+ }
486
+ return [point]
487
+ })
488
+
489
+ for (const via of routeWithVias.filter((p) => p.route_type === "via")) {
490
+ db.pcb_via.insert({
491
+ x: via.x,
492
+ y: via.y,
493
+ hole_diameter: 0.3,
494
+ outer_diameter: 0.6,
495
+ layers: [via.from_layer as LayerRef, via.to_layer as LayerRef],
496
+ from_layer: via.from_layer as LayerRef,
497
+ to_layer: via.to_layer as LayerRef,
498
+ })
499
+ }
500
+
444
501
  const pcb_trace = db.pcb_trace.insert({
445
- route: mergeRoutes(routes),
502
+ route: routeWithVias,
446
503
  source_trace_id: this.source_trace_id!,
447
504
  })
448
505
  this._portsRoutedOnPcb = ports
@@ -470,6 +527,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
470
527
  if (elm.type === "schematic_component") {
471
528
  obstacles.push({
472
529
  type: "rect",
530
+ layers: ["top"],
473
531
  center: elm.center,
474
532
  width: elm.size.width,
475
533
  height: elm.size.height,
@@ -479,13 +537,14 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
479
537
  }
480
538
 
481
539
  for (const { port } of ports) {
482
- connection.pointsToConnect.push(
483
- projectPointInDirection(
540
+ connection.pointsToConnect.push({
541
+ ...projectPointInDirection(
484
542
  port._getGlobalSchematicPositionBeforeLayout(),
485
543
  port.facingDirection!,
486
544
  0.1501,
487
545
  ),
488
- )
546
+ layer: "top",
547
+ })
489
548
  }
490
549
 
491
550
  const bounds = computeObstacleBounds(obstacles)
@@ -12,6 +12,7 @@ export type SimplifiedPcbTrace = {
12
12
  export type Obstacle = {
13
13
  // TODO include ovals
14
14
  type: "rect" // NOTE: most datasets do not contain ovals
15
+ layers: string[]
15
16
  center: { x: number; y: number }
16
17
  width: number
17
18
  height: number
@@ -20,7 +21,7 @@ export type Obstacle = {
20
21
 
21
22
  export interface SimpleRouteConnection {
22
23
  name: string
23
- pointsToConnect: Array<{ x: number; y: number }>
24
+ pointsToConnect: Array<{ x: number; y: number; layer: string }>
24
25
  }
25
26
 
26
27
  export interface SimpleRouteJson {
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.48",
5
+ "version": "0.0.50",
6
6
  "types": "dist/index.d.ts",
7
7
  "main": "dist/index.js",
8
8
  "files": [
@@ -23,7 +23,7 @@
23
23
  "@types/react": "^18.3.3",
24
24
  "@types/react-reconciler": "^0.28.8",
25
25
  "bun-match-svg": "0.0.2",
26
- "circuit-to-svg": "^0.0.18",
26
+ "circuit-to-svg": "^0.0.22",
27
27
  "debug": "^4.3.6",
28
28
  "howfat": "^0.3.8",
29
29
  "looks-same": "^9.0.1",
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@lume/kiwi": "^0.4.3",
37
- "@tscircuit/infgrid-ijump-astar": "^0.0.9",
37
+ "@tscircuit/infgrid-ijump-astar": "^0.0.10",
38
38
  "@tscircuit/props": "^0.0.62",
39
39
  "@tscircuit/soup": "^0.0.67",
40
40
  "@tscircuit/soup-util": "^0.0.23",