@tscircuit/core 0.0.22 → 0.0.24
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 +273 -277
- package/dist/index.js +351 -67
- package/lib/Project.ts +1 -1
- package/lib/components/base-components/NormalComponent.ts +15 -0
- package/lib/components/base-components/PrimitiveComponent.ts +14 -11
- package/lib/components/base-components/Renderable.ts +2 -0
- package/lib/components/normal-components/Board.ts +3 -2
- package/lib/components/normal-components/Capacitor.ts +9 -2
- package/lib/components/normal-components/Resistor.ts +8 -2
- package/lib/components/primitive-components/Group.ts +6 -2
- package/lib/components/primitive-components/Net.ts +159 -0
- package/lib/components/primitive-components/Port.ts +17 -1
- package/lib/components/primitive-components/Trace.ts +153 -18
- package/lib/fiber/intrinsic-jsx.ts +1 -0
- package/lib/utils/getClosest.ts +29 -0
- package/package.json +2 -2
|
@@ -50,14 +50,14 @@ export abstract class PrimitiveComponent<
|
|
|
50
50
|
externallyAddedAliases: string[]
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
* An
|
|
54
|
-
*
|
|
55
|
-
*
|
|
53
|
+
* An subcircuit is self-contained. All the selectors inside
|
|
54
|
+
* a subcircuit are relative to the subcircuit group. You can have multiple
|
|
55
|
+
* subcircuits and their selectors will not interact with each other (even if the
|
|
56
56
|
* components share the same names) unless you explicitly break out some ports
|
|
57
57
|
*/
|
|
58
|
-
get
|
|
58
|
+
get isSubcircuit() {
|
|
59
59
|
return (
|
|
60
|
-
Boolean(this.props.
|
|
60
|
+
Boolean(this.props.subcircuit) ||
|
|
61
61
|
// Implied opaque group for top-level group
|
|
62
62
|
(this.lowercaseComponentName === "group" &&
|
|
63
63
|
this?.parent?.props?.name === "$root")
|
|
@@ -212,15 +212,15 @@ export abstract class PrimitiveComponent<
|
|
|
212
212
|
component.shouldBeRemoved = true
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
|
|
215
|
+
getSubcircuitSelector(): string {
|
|
216
216
|
const name = this._parsedProps.name
|
|
217
217
|
const endPart = name
|
|
218
218
|
? `${this.lowercaseComponentName}.${name}`
|
|
219
219
|
: this.lowercaseComponentName
|
|
220
220
|
|
|
221
221
|
if (!this.parent) return endPart
|
|
222
|
-
if (this.parent.
|
|
223
|
-
return `${this.parent.
|
|
222
|
+
if (this.parent.isSubcircuit) return endPart
|
|
223
|
+
return `${this.parent.getSubcircuitSelector()} > ${endPart}`
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
getFullPathSelector(): string {
|
|
@@ -266,9 +266,9 @@ export abstract class PrimitiveComponent<
|
|
|
266
266
|
return false
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
|
|
270
|
-
if (this.
|
|
271
|
-
const group = this.parent?.
|
|
269
|
+
getSubcircuit(): PrimitiveComponent {
|
|
270
|
+
if (this.isSubcircuit) return this
|
|
271
|
+
const group = this.parent?.getSubcircuit()
|
|
272
272
|
if (!group)
|
|
273
273
|
throw new Error("Component is not inside an opaque group (no board?)")
|
|
274
274
|
return group
|
|
@@ -349,6 +349,9 @@ export abstract class PrimitiveComponent<
|
|
|
349
349
|
if (parent?.props?.name && props?.name) {
|
|
350
350
|
return `<${cname}#${this._renderId}(.${parent?.props.name}>.${props?.name}) />`
|
|
351
351
|
}
|
|
352
|
+
if (props?.from && props?.to) {
|
|
353
|
+
return `<${cname}#${this._renderId}(from:${props.from} to:${props?.to}) />`
|
|
354
|
+
}
|
|
352
355
|
if (props?.name) {
|
|
353
356
|
return `<${cname}#${this._renderId} name=".${props?.name}" />`
|
|
354
357
|
}
|
|
@@ -3,6 +3,7 @@ import { Component, createElement, type ReactElement } from "react"
|
|
|
3
3
|
|
|
4
4
|
export const orderedRenderPhases = [
|
|
5
5
|
"ReactSubtreesRender", // probably going to be removed b/c subtrees should render instantly
|
|
6
|
+
"CreateNetsFromProps",
|
|
6
7
|
"CreateTracesFromProps",
|
|
7
8
|
"SourceRender",
|
|
8
9
|
"SourceParentAttachment",
|
|
@@ -19,6 +20,7 @@ export const orderedRenderPhases = [
|
|
|
19
20
|
"PcbParentAttachment",
|
|
20
21
|
"PcbLayout",
|
|
21
22
|
"PcbTraceRender",
|
|
23
|
+
"PcbRouteNetIslands",
|
|
22
24
|
"CadModelRender",
|
|
23
25
|
"PcbAnalysis",
|
|
24
26
|
] as const
|
|
@@ -2,11 +2,12 @@ import { boardProps } from "@tscircuit/props"
|
|
|
2
2
|
import type { z } from "zod"
|
|
3
3
|
import { NormalComponent } from "../base-components/NormalComponent"
|
|
4
4
|
import { identity, type Matrix } from "transformation-matrix"
|
|
5
|
+
import { Group } from "../primitive-components/Group"
|
|
5
6
|
|
|
6
|
-
export class Board extends
|
|
7
|
+
export class Board extends Group<typeof boardProps> {
|
|
7
8
|
pcb_board_id: string | null = null
|
|
8
9
|
|
|
9
|
-
get
|
|
10
|
+
get isSubcircuit() {
|
|
10
11
|
return true
|
|
11
12
|
}
|
|
12
13
|
|
|
@@ -29,17 +29,24 @@ export class Capacitor extends NormalComponent<
|
|
|
29
29
|
pin1 = this.portMap.pin1
|
|
30
30
|
pin2 = this.portMap.pin2
|
|
31
31
|
|
|
32
|
+
doInitialCreateNetsFromProps() {
|
|
33
|
+
this._createNetsFromProps([
|
|
34
|
+
this.props.decouplingFor,
|
|
35
|
+
this.props.decouplingTo,
|
|
36
|
+
])
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
doInitialCreateTracesFromProps() {
|
|
33
40
|
if (this.props.decouplingFor && this.props.decouplingTo) {
|
|
34
41
|
this.add(
|
|
35
42
|
new Trace({
|
|
36
|
-
from: `${this.
|
|
43
|
+
from: `${this.getSubcircuitSelector()} > port.1`,
|
|
37
44
|
to: this.props.decouplingFor,
|
|
38
45
|
}),
|
|
39
46
|
)
|
|
40
47
|
this.add(
|
|
41
48
|
new Trace({
|
|
42
|
-
from: `${this.
|
|
49
|
+
from: `${this.getSubcircuitSelector()} > port.2`,
|
|
43
50
|
to: this.props.decouplingTo,
|
|
44
51
|
}),
|
|
45
52
|
)
|
|
@@ -4,6 +4,7 @@ import { NormalComponent } from "../base-components/NormalComponent"
|
|
|
4
4
|
import type { SourceSimpleResistorInput } from "@tscircuit/soup"
|
|
5
5
|
import { z } from "zod"
|
|
6
6
|
import { Trace } from "../primitive-components/Trace"
|
|
7
|
+
import { Net } from "../primitive-components/Net"
|
|
7
8
|
|
|
8
9
|
export class Resistor extends NormalComponent<
|
|
9
10
|
typeof resistorProps,
|
|
@@ -20,22 +21,27 @@ export class Resistor extends NormalComponent<
|
|
|
20
21
|
pin1 = this.portMap.pin1
|
|
21
22
|
pin2 = this.portMap.pin2
|
|
22
23
|
|
|
24
|
+
doInitialCreateNetsFromProps() {
|
|
25
|
+
this._createNetsFromProps([this.props.pullupFor, this.props.pullupTo])
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
doInitialCreateTracesFromProps() {
|
|
24
29
|
if (this.props.pullupFor && this.props.pullupTo) {
|
|
25
30
|
this.add(
|
|
26
31
|
new Trace({
|
|
27
|
-
from: `${this.
|
|
32
|
+
from: `${this.getSubcircuitSelector()} > port.1`,
|
|
28
33
|
to: this.props.pullupFor,
|
|
29
34
|
}),
|
|
30
35
|
)
|
|
31
36
|
this.add(
|
|
32
37
|
new Trace({
|
|
33
|
-
from: `${this.
|
|
38
|
+
from: `${this.getSubcircuitSelector()} > port.2`,
|
|
34
39
|
to: this.props.pullupTo,
|
|
35
40
|
}),
|
|
36
41
|
)
|
|
37
42
|
}
|
|
38
43
|
}
|
|
44
|
+
|
|
39
45
|
doInitialSourceRender() {
|
|
40
46
|
const { db } = this.project!
|
|
41
47
|
const { _parsedProps: props } = this
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { groupProps } from "@tscircuit/props"
|
|
2
2
|
import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
|
|
3
3
|
import { compose, identity } from "transformation-matrix"
|
|
4
|
+
import { z } from "zod"
|
|
5
|
+
import { NormalComponent } from "../base-components/NormalComponent"
|
|
4
6
|
|
|
5
|
-
export class Group
|
|
7
|
+
export class Group<
|
|
8
|
+
Props extends z.ZodType<any, any, any> = typeof groupProps,
|
|
9
|
+
> extends NormalComponent<Props> {
|
|
6
10
|
get config() {
|
|
7
11
|
return {
|
|
8
|
-
zodProps: groupProps,
|
|
12
|
+
zodProps: groupProps as unknown as Props,
|
|
9
13
|
}
|
|
10
14
|
}
|
|
11
15
|
}
|
|
@@ -1,12 +1,171 @@
|
|
|
1
1
|
import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
|
|
2
2
|
import { z } from "zod"
|
|
3
|
+
import type { Port } from "./Port"
|
|
4
|
+
import type { Trace } from "./Trace"
|
|
5
|
+
import { pairs } from "lib/utils/pairs"
|
|
6
|
+
import type { AnySoupElement, SourceTrace } from "@tscircuit/soup"
|
|
7
|
+
import { autoroute } from "@tscircuit/infgrid-ijump-astar"
|
|
3
8
|
|
|
4
9
|
export const netProps = z.object({
|
|
5
10
|
name: z.string(),
|
|
6
11
|
})
|
|
7
12
|
|
|
8
13
|
export class Net extends PrimitiveComponent<typeof netProps> {
|
|
14
|
+
source_net_id?: string
|
|
15
|
+
|
|
9
16
|
getPortSelector() {
|
|
10
17
|
return `net.${this.props.name}`
|
|
11
18
|
}
|
|
19
|
+
|
|
20
|
+
doInitialSourceComponentRender(): void {
|
|
21
|
+
const { db } = this.project!
|
|
22
|
+
const { _parsedProps: props } = this
|
|
23
|
+
|
|
24
|
+
const net = db.source_net.insert({
|
|
25
|
+
name: props.name,
|
|
26
|
+
member_source_group_ids: [],
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
this.source_net_id = net.source_net_id
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get all ports connected to this net.
|
|
34
|
+
*
|
|
35
|
+
* TODO currently we're not checking for indirect connections (traces that are
|
|
36
|
+
* connected to other traces that are in turn connected to the net)
|
|
37
|
+
*/
|
|
38
|
+
getAllConnectedPorts(): Port[] {
|
|
39
|
+
const allPorts = this.getSubcircuit().selectAll("port") as Port[]
|
|
40
|
+
const connectedPorts: Port[] = []
|
|
41
|
+
|
|
42
|
+
for (const port of allPorts) {
|
|
43
|
+
const traces = port._getDirectlyConnectedTraces()
|
|
44
|
+
|
|
45
|
+
for (const trace of traces) {
|
|
46
|
+
if (trace._isExplicitlyConnectedToNet(this)) {
|
|
47
|
+
connectedPorts.push(port)
|
|
48
|
+
break
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return connectedPorts
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get all traces that are directly connected to this net, i.e. they list
|
|
58
|
+
* this net in their path, from, or to props
|
|
59
|
+
*/
|
|
60
|
+
_getAllDirectlyConnectedTraces(): Trace[] {
|
|
61
|
+
const allTraces = this.getSubcircuit().selectAll("trace") as Trace[]
|
|
62
|
+
const connectedTraces: Trace[] = []
|
|
63
|
+
|
|
64
|
+
for (const trace of allTraces) {
|
|
65
|
+
if (trace._isExplicitlyConnectedToNet(this)) {
|
|
66
|
+
connectedTraces.push(trace)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return connectedTraces
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Add PCB Traces to connect net islands together. A net island is a set of
|
|
75
|
+
* ports that are connected to each other. If a there are multiple net islands
|
|
76
|
+
* that means that the net is not fully connected and we need to add traces
|
|
77
|
+
* such that the nets are fully connected
|
|
78
|
+
*/
|
|
79
|
+
doInitialPcbRouteNetIslands(): void {
|
|
80
|
+
const { db } = this.project!
|
|
81
|
+
const { _parsedProps: props } = this
|
|
82
|
+
|
|
83
|
+
const traces = this._getAllDirectlyConnectedTraces().filter(
|
|
84
|
+
(trace) => (trace._portsRoutedOnPcb?.length ?? 0) > 0,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
const islands: Array<{ ports: Port[]; traces: Trace[] }> = []
|
|
88
|
+
|
|
89
|
+
for (const trace of traces) {
|
|
90
|
+
const tracePorts = trace._portsRoutedOnPcb
|
|
91
|
+
const traceIsland = islands.find((island) =>
|
|
92
|
+
tracePorts.some((port) => island.ports.includes(port)),
|
|
93
|
+
)
|
|
94
|
+
if (!traceIsland) {
|
|
95
|
+
islands.push({ ports: [...tracePorts], traces: [trace] })
|
|
96
|
+
continue
|
|
97
|
+
}
|
|
98
|
+
traceIsland.traces.push(trace)
|
|
99
|
+
traceIsland.ports.push(...tracePorts)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (islands.length === 0) {
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Connect islands together by looking at each pair of islands and adding
|
|
107
|
+
// a trace between them
|
|
108
|
+
const islandPairs = pairs(islands)
|
|
109
|
+
for (const [A, B] of islandPairs) {
|
|
110
|
+
// Find two closest ports on the island
|
|
111
|
+
const Apositions: Array<{ x: number; y: number }> = A.ports.map((port) =>
|
|
112
|
+
port.getGlobalPcbPosition(),
|
|
113
|
+
)
|
|
114
|
+
const Bpositions: Array<{ x: number; y: number }> = B.ports.map((port) =>
|
|
115
|
+
port.getGlobalPcbPosition(),
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
let closestDist = Infinity
|
|
119
|
+
let closestPair: [number, number] = [-1, -1]
|
|
120
|
+
for (let i = 0; i < Apositions.length; i++) {
|
|
121
|
+
const Apos = Apositions[i]
|
|
122
|
+
for (let j = 0; j < Bpositions.length; j++) {
|
|
123
|
+
const Bpos = Bpositions[j]
|
|
124
|
+
const dist = Math.sqrt(
|
|
125
|
+
(Apos.x - Bpos.x) ** 2 + (Apos.y - Bpos.y) ** 2,
|
|
126
|
+
)
|
|
127
|
+
if (dist < closestDist) {
|
|
128
|
+
closestDist = dist
|
|
129
|
+
closestPair = [i, j]
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const Aport = A.ports[closestPair[0]]
|
|
135
|
+
const Bport = B.ports[closestPair[1]]
|
|
136
|
+
|
|
137
|
+
const pcbElements: AnySoupElement[] = db
|
|
138
|
+
.toArray()
|
|
139
|
+
.filter(
|
|
140
|
+
(elm) =>
|
|
141
|
+
elm.type === "pcb_smtpad" ||
|
|
142
|
+
elm.type === "pcb_trace" ||
|
|
143
|
+
elm.type === "pcb_plated_hole" ||
|
|
144
|
+
elm.type === "pcb_hole" ||
|
|
145
|
+
elm.type === "source_port" ||
|
|
146
|
+
elm.type === "pcb_port",
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
const { solution } = autoroute(
|
|
150
|
+
pcbElements.concat([
|
|
151
|
+
{
|
|
152
|
+
type: "source_trace",
|
|
153
|
+
source_trace_id: "__net_trace_tmp",
|
|
154
|
+
connected_source_port_ids: [
|
|
155
|
+
Aport.source_port_id!,
|
|
156
|
+
Bport.source_port_id!,
|
|
157
|
+
],
|
|
158
|
+
} as SourceTrace,
|
|
159
|
+
]),
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
const trace = solution[0]
|
|
163
|
+
if (!trace) {
|
|
164
|
+
this.renderError("Failed to route net islands")
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
db.pcb_trace.insert(trace as any)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
12
171
|
}
|
|
@@ -4,6 +4,7 @@ import { z } from "zod"
|
|
|
4
4
|
import { getRelativeDirection } from "lib/utils/get-relative-direction"
|
|
5
5
|
import { symbols, type SchSymbol } from "schematic-symbols"
|
|
6
6
|
import { applyToPoint, compose, translate } from "transformation-matrix"
|
|
7
|
+
import type { Trace } from "./Trace"
|
|
7
8
|
|
|
8
9
|
export const portProps = z.object({
|
|
9
10
|
name: z.string().optional(),
|
|
@@ -90,8 +91,8 @@ export class Port extends PrimitiveComponent<typeof portProps> {
|
|
|
90
91
|
return this.isMatchingAnyOf(port.getNameAndAliases())
|
|
91
92
|
}
|
|
92
93
|
getPortSelector() {
|
|
94
|
+
// TODO this.parent.getSubcircuitSelector() >
|
|
93
95
|
return `.${this.parent?.props.name} > port.${this.props.name}`
|
|
94
|
-
// return `#${this.props.id}`
|
|
95
96
|
}
|
|
96
97
|
getAvailablePcbLayers(): string[] {
|
|
97
98
|
return Array.from(
|
|
@@ -99,6 +100,21 @@ export class Port extends PrimitiveComponent<typeof portProps> {
|
|
|
99
100
|
)
|
|
100
101
|
}
|
|
101
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Return traces that are explicitly connected to this port (not via a net)
|
|
105
|
+
*/
|
|
106
|
+
_getDirectlyConnectedTraces(): Trace[] {
|
|
107
|
+
const allSubcircuitTraces = this.getSubcircuit().selectAll(
|
|
108
|
+
"trace",
|
|
109
|
+
) as Trace[]
|
|
110
|
+
|
|
111
|
+
const connectedTraces = allSubcircuitTraces.filter((trace) =>
|
|
112
|
+
trace._isExplicitlyConnectedToPort(this),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return connectedTraces
|
|
116
|
+
}
|
|
117
|
+
|
|
102
118
|
doInitialSourceRender(): void {
|
|
103
119
|
const { db } = this.project!
|
|
104
120
|
const { _parsedProps: props } = this
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
PCBTrace,
|
|
13
13
|
RouteHintPoint,
|
|
14
14
|
SchematicTrace,
|
|
15
|
+
SourceTrace,
|
|
15
16
|
} from "@tscircuit/soup"
|
|
16
17
|
import type {
|
|
17
18
|
Obstacle,
|
|
@@ -24,6 +25,9 @@ import type { TraceHint } from "./TraceHint"
|
|
|
24
25
|
import { findPossibleTraceLayerCombinations } from "lib/utils/autorouting/findPossibleTraceLayerCombinations"
|
|
25
26
|
import { pairs } from "lib/utils/pairs"
|
|
26
27
|
import { mergeRoutes } from "lib/utils/autorouting/mergeRoutes"
|
|
28
|
+
import type { Net } from "./Net"
|
|
29
|
+
import { getClosest } from "lib/utils/getClosest"
|
|
30
|
+
import { z } from "zod"
|
|
27
31
|
|
|
28
32
|
type PcbRouteObjective =
|
|
29
33
|
| RouteHintPoint
|
|
@@ -41,6 +45,12 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
41
45
|
source_trace_id: string | null = null
|
|
42
46
|
pcb_trace_id: string | null = null
|
|
43
47
|
schematic_trace_id: string | null = null
|
|
48
|
+
_portsRoutedOnPcb: Port[]
|
|
49
|
+
|
|
50
|
+
constructor(props: z.input<typeof traceProps>) {
|
|
51
|
+
super(props)
|
|
52
|
+
this._portsRoutedOnPcb = []
|
|
53
|
+
}
|
|
44
54
|
|
|
45
55
|
get config() {
|
|
46
56
|
return {
|
|
@@ -48,7 +58,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
48
58
|
}
|
|
49
59
|
}
|
|
50
60
|
|
|
51
|
-
|
|
61
|
+
_getTracePortOrNetSelectorListFromProps(): string[] {
|
|
52
62
|
if ("from" in this.props && "to" in this.props) {
|
|
53
63
|
return [
|
|
54
64
|
typeof this.props.from === "string"
|
|
@@ -67,9 +77,29 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
67
77
|
return []
|
|
68
78
|
}
|
|
69
79
|
|
|
80
|
+
getTracePortPathSelectors(): string[] {
|
|
81
|
+
return this._getTracePortOrNetSelectorListFromProps().filter(
|
|
82
|
+
(selector) => !selector.includes("net."),
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
getTracePathNetSelectors(): string[] {
|
|
87
|
+
return this._getTracePortOrNetSelectorListFromProps().filter((selector) =>
|
|
88
|
+
selector.includes("net."),
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
70
92
|
_findConnectedPorts():
|
|
71
|
-
| {
|
|
72
|
-
|
|
93
|
+
| {
|
|
94
|
+
allPortsFound: true
|
|
95
|
+
ports: Port[]
|
|
96
|
+
portsWithSelectors: Array<{ selector: string; port: Port }>
|
|
97
|
+
}
|
|
98
|
+
| {
|
|
99
|
+
allPortsFound: false
|
|
100
|
+
ports?: undefined
|
|
101
|
+
portsWithSelectors?: undefined
|
|
102
|
+
} {
|
|
73
103
|
const { db } = this.project!
|
|
74
104
|
const { _parsedProps: props, parent } = this
|
|
75
105
|
|
|
@@ -77,17 +107,17 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
77
107
|
|
|
78
108
|
const portSelectors = this.getTracePortPathSelectors()
|
|
79
109
|
|
|
80
|
-
const
|
|
110
|
+
const portsWithSelectors = portSelectors.map((selector) => ({
|
|
81
111
|
selector,
|
|
82
112
|
port:
|
|
83
|
-
(this.
|
|
113
|
+
(this.getSubcircuit().selectOne(selector, { type: "port" }) as Port) ??
|
|
84
114
|
null,
|
|
85
115
|
}))
|
|
86
116
|
|
|
87
|
-
for (const { selector, port } of
|
|
117
|
+
for (const { selector, port } of portsWithSelectors) {
|
|
88
118
|
if (!port) {
|
|
89
119
|
const parentSelector = selector.replace(/\>.*$/, "")
|
|
90
|
-
const targetComponent = this.
|
|
120
|
+
const targetComponent = this.getSubcircuit().selectOne(parentSelector)
|
|
91
121
|
if (!targetComponent) {
|
|
92
122
|
this.renderError(`Could not find port for selector "${selector}"`)
|
|
93
123
|
} else {
|
|
@@ -103,11 +133,50 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
103
133
|
}
|
|
104
134
|
}
|
|
105
135
|
|
|
106
|
-
if (
|
|
136
|
+
if (portsWithSelectors.some((p) => !p.port)) {
|
|
107
137
|
return { allPortsFound: false }
|
|
108
138
|
}
|
|
109
139
|
|
|
110
|
-
return {
|
|
140
|
+
return {
|
|
141
|
+
allPortsFound: true,
|
|
142
|
+
portsWithSelectors,
|
|
143
|
+
ports: portsWithSelectors.map(({ port }) => port),
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
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
|
+
}))
|
|
152
|
+
|
|
153
|
+
const undefinedNets = nets.filter((n) => !n.net)
|
|
154
|
+
if (undefinedNets.length > 0) {
|
|
155
|
+
this.renderError(
|
|
156
|
+
`Could not find net for selector "${undefinedNets[0].selector}" inside ${this}`,
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return nets
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Determine if a trace is explicitly connected to a port (not via a net)
|
|
165
|
+
*/
|
|
166
|
+
_isExplicitlyConnectedToPort(port: Port) {
|
|
167
|
+
const { allPortsFound, portsWithSelectors: portsWithMetadata } =
|
|
168
|
+
this._findConnectedPorts()
|
|
169
|
+
if (!allPortsFound) return false
|
|
170
|
+
const ports = portsWithMetadata.map((p) => p.port)
|
|
171
|
+
return ports.includes(port)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Determine if a trace is explicitly connected to a net (not via a port)
|
|
176
|
+
*/
|
|
177
|
+
_isExplicitlyConnectedToNet(net: Net) {
|
|
178
|
+
const nets = this._findConnectedNets().map((n) => n.net)
|
|
179
|
+
return nets.includes(net)
|
|
111
180
|
}
|
|
112
181
|
|
|
113
182
|
doInitialSourceTraceRender(): void {
|
|
@@ -119,12 +188,15 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
119
188
|
return
|
|
120
189
|
}
|
|
121
190
|
|
|
122
|
-
const { allPortsFound, ports } =
|
|
191
|
+
const { allPortsFound, portsWithSelectors: ports } =
|
|
192
|
+
this._findConnectedPorts()
|
|
123
193
|
if (!allPortsFound) return
|
|
124
194
|
|
|
195
|
+
const nets = this._findConnectedNets()
|
|
196
|
+
|
|
125
197
|
const trace = db.source_trace.insert({
|
|
126
198
|
connected_source_port_ids: ports.map((p) => p.port.source_port_id!),
|
|
127
|
-
connected_source_net_ids:
|
|
199
|
+
connected_source_net_ids: nets.map((n) => n.net.source_net_id!),
|
|
128
200
|
})
|
|
129
201
|
|
|
130
202
|
this.source_trace_id = trace.source_trace_id
|
|
@@ -137,9 +209,42 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
137
209
|
if (!parent) throw new Error("Trace has no parent")
|
|
138
210
|
|
|
139
211
|
const { allPortsFound, ports } = this._findConnectedPorts()
|
|
212
|
+
const portsConnectedOnPcbViaNet: Port[] = []
|
|
140
213
|
|
|
141
214
|
if (!allPortsFound) return
|
|
142
215
|
|
|
216
|
+
const nets = this._findConnectedNets()
|
|
217
|
+
|
|
218
|
+
if (ports.length === 0 && nets.length === 2) {
|
|
219
|
+
// Find the two optimal points to connect the two nets
|
|
220
|
+
this.renderError(
|
|
221
|
+
`Trace connects two nets, we haven't implemented a way to route this yet`,
|
|
222
|
+
)
|
|
223
|
+
return
|
|
224
|
+
// biome-ignore lint/style/noUselessElse: <explanation>
|
|
225
|
+
} else if (ports.length === 1 && nets.length === 1) {
|
|
226
|
+
// Add a port from the net that is closest to the port
|
|
227
|
+
const port = ports[0]
|
|
228
|
+
const portsInNet = nets[0].net.getAllConnectedPorts()
|
|
229
|
+
const otherPortsInNet = portsInNet.filter((p) => p !== port)
|
|
230
|
+
if (otherPortsInNet.length === 0) {
|
|
231
|
+
console.log(
|
|
232
|
+
"Nothing to connect this port to, the net is empty. TODO should emit a warning!",
|
|
233
|
+
)
|
|
234
|
+
return
|
|
235
|
+
}
|
|
236
|
+
const closestPortInNet = getClosest(port, otherPortsInNet)
|
|
237
|
+
|
|
238
|
+
portsConnectedOnPcbViaNet.push(closestPortInNet)
|
|
239
|
+
|
|
240
|
+
ports.push(closestPortInNet)
|
|
241
|
+
} else if (ports.length > 1 && nets.length >= 1) {
|
|
242
|
+
this.renderError(
|
|
243
|
+
`Trace has more than one port and one or more nets, we don't currently support this type of complex trace routing`,
|
|
244
|
+
)
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
|
|
143
248
|
const pcbElements: AnySoupElement[] = db
|
|
144
249
|
.toArray()
|
|
145
250
|
.filter(
|
|
@@ -154,7 +259,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
154
259
|
|
|
155
260
|
const source_trace = db.source_trace.get(this.source_trace_id!)!
|
|
156
261
|
|
|
157
|
-
const hints = ports.flatMap((
|
|
262
|
+
const hints = ports.flatMap((port) =>
|
|
158
263
|
port.matchedComponents.filter((c) => c.componentName === "TraceHint"),
|
|
159
264
|
) as TraceHint[]
|
|
160
265
|
|
|
@@ -165,7 +270,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
165
270
|
if (ports.length > 2) {
|
|
166
271
|
this.renderError(
|
|
167
272
|
`Trace has more than two ports (${ports
|
|
168
|
-
.map((p) => p.
|
|
273
|
+
.map((p) => p.getString())
|
|
169
274
|
.join(
|
|
170
275
|
", ",
|
|
171
276
|
)}), routing between more than two ports for a single trace is not implemented`,
|
|
@@ -173,21 +278,50 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
173
278
|
return
|
|
174
279
|
}
|
|
175
280
|
|
|
281
|
+
const alreadyRoutedTraces = this.getSubcircuit()
|
|
282
|
+
.selectAll("trace")
|
|
283
|
+
.filter(
|
|
284
|
+
(trace) => trace.renderPhaseStates.PcbTraceRender.initialized,
|
|
285
|
+
) as Trace[]
|
|
286
|
+
|
|
287
|
+
const isAlreadyRouted = alreadyRoutedTraces.some(
|
|
288
|
+
(trace) =>
|
|
289
|
+
trace._portsRoutedOnPcb.length === ports.length &&
|
|
290
|
+
trace._portsRoutedOnPcb.every((portRoutedByOtherTrace) =>
|
|
291
|
+
ports.includes(portRoutedByOtherTrace),
|
|
292
|
+
),
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
if (isAlreadyRouted) {
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
|
|
176
299
|
if (pcbRouteHints.length === 0) {
|
|
177
|
-
const { solution } = autoroute(
|
|
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
|
+
)
|
|
178
311
|
// TODO for some reason, the solution gets duplicated inside ijump-astar
|
|
179
312
|
const inputPcbTrace = solution[0]
|
|
180
313
|
|
|
181
314
|
if (!inputPcbTrace) {
|
|
182
315
|
// TODO render error indicating we could not find a route
|
|
183
316
|
console.log(
|
|
184
|
-
`Failed to find route
|
|
317
|
+
`Failed to find route from ${ports[0]} to ${ports[1]} (TODO render error!)`,
|
|
185
318
|
)
|
|
186
319
|
return
|
|
187
320
|
}
|
|
188
321
|
const pcb_trace = db.pcb_trace.insert(inputPcbTrace as any)
|
|
189
322
|
|
|
190
323
|
this.pcb_trace_id = pcb_trace.pcb_trace_id
|
|
324
|
+
this._portsRoutedOnPcb = ports
|
|
191
325
|
return
|
|
192
326
|
}
|
|
193
327
|
|
|
@@ -195,9 +329,9 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
195
329
|
// terminal of the trace and the hints
|
|
196
330
|
// TODO order based on proximity to ports
|
|
197
331
|
const orderedRouteObjectives: PcbRouteObjective[] = [
|
|
198
|
-
portToObjective(ports[0]
|
|
332
|
+
portToObjective(ports[0]),
|
|
199
333
|
...pcbRouteHints,
|
|
200
|
-
portToObjective(ports[1]
|
|
334
|
+
portToObjective(ports[1]),
|
|
201
335
|
]
|
|
202
336
|
|
|
203
337
|
// Hints can indicate where there should be a via, but the layer is allowed
|
|
@@ -285,7 +419,8 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
|
285
419
|
|
|
286
420
|
if (!parent) throw new Error("Trace has no parent")
|
|
287
421
|
|
|
288
|
-
const { allPortsFound, ports } =
|
|
422
|
+
const { allPortsFound, portsWithSelectors: ports } =
|
|
423
|
+
this._findConnectedPorts()
|
|
289
424
|
|
|
290
425
|
if (!allPortsFound) return
|
|
291
426
|
|