@tscircuit/core 0.0.73 → 0.0.75
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.js +9 -4
- package/package.json +3 -5
- package/index.ts +0 -1
- package/lib/Project.ts +0 -131
- package/lib/components/base-components/NormalComponent.ts +0 -419
- package/lib/components/base-components/PrimitiveComponent.ts +0 -478
- package/lib/components/base-components/Renderable.ts +0 -126
- package/lib/components/index.ts +0 -25
- package/lib/components/normal-components/Board.ts +0 -49
- package/lib/components/normal-components/Capacitor.ts +0 -70
- package/lib/components/normal-components/Chip.ts +0 -98
- package/lib/components/normal-components/Diode.ts +0 -27
- package/lib/components/normal-components/Jumper.ts +0 -101
- package/lib/components/normal-components/Led.ts +0 -27
- package/lib/components/normal-components/Resistor.ts +0 -59
- package/lib/components/primitive-components/Constraint.ts +0 -91
- package/lib/components/primitive-components/FabricationNotePath.ts +0 -50
- package/lib/components/primitive-components/FabricationNoteText.ts +0 -26
- package/lib/components/primitive-components/Footprint.ts +0 -239
- package/lib/components/primitive-components/Group.ts +0 -42
- package/lib/components/primitive-components/Hole.ts +0 -65
- package/lib/components/primitive-components/Keepout.ts +0 -58
- package/lib/components/primitive-components/Net.ts +0 -171
- package/lib/components/primitive-components/PlatedHole.ts +0 -116
- package/lib/components/primitive-components/Port.ts +0 -236
- package/lib/components/primitive-components/SilkscreenPath.ts +0 -48
- package/lib/components/primitive-components/SilkscreenText.ts +0 -25
- package/lib/components/primitive-components/SmtPad.ts +0 -162
- package/lib/components/primitive-components/Trace.ts +0 -635
- package/lib/components/primitive-components/TraceHint.ts +0 -71
- package/lib/fiber/catalogue.ts +0 -22
- package/lib/fiber/create-instance-from-react-element.ts +0 -179
- package/lib/fiber/intrinsic-jsx.ts +0 -48
- package/lib/index.ts +0 -5
- package/lib/register-catalogue.ts +0 -12
- package/lib/soup/underscorifyPinStyles.ts +0 -25
- package/lib/soup/underscorifyPortArrangement.ts +0 -51
- package/lib/utils/autorouting/SimpleRouteJson.ts +0 -48
- package/lib/utils/autorouting/computeObstacleBounds.ts +0 -10
- package/lib/utils/autorouting/findPossibleTraceLayerCombinations.ts +0 -102
- package/lib/utils/autorouting/mergeRoutes.ts +0 -71
- package/lib/utils/components/createNetsFromProps.ts +0 -15
- package/lib/utils/constants.ts +0 -35
- package/lib/utils/createComponentsFromSoup.ts +0 -79
- package/lib/utils/extend-aliases.ts +0 -12
- package/lib/utils/get-bounds-of-pcb-components.ts +0 -41
- package/lib/utils/get-relative-direction.ts +0 -14
- package/lib/utils/getClosest.ts +0 -35
- package/lib/utils/getPortFromHints.ts +0 -10
- package/lib/utils/pairs.ts +0 -10
- package/lib/utils/projectPointInDirection.ts +0 -18
- package/lib/utils/range.ts +0 -8
- package/lib/utils/schematic/getAllDimensionsForSchematicBox.ts +0 -330
- package/lib/utils/schematic/getSizeOfSidesFromPortArrangement.ts +0 -47
- package/lib/utils/selector-matching/index.ts +0 -2
- package/lib/utils/selector-matching/is-matching-path-selector.ts +0 -32
- package/lib/utils/selector-matching/is-matching-selector.ts +0 -64
- package/lib/utils/try-now.ts +0 -7
|
@@ -1,635 +0,0 @@
|
|
|
1
|
-
import { traceProps } from "@tscircuit/props"
|
|
2
|
-
import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
|
|
3
|
-
import type { Port } from "./Port"
|
|
4
|
-
import {
|
|
5
|
-
MultilayerIjump,
|
|
6
|
-
IJumpAutorouter,
|
|
7
|
-
autoroute,
|
|
8
|
-
getObstaclesFromSoup,
|
|
9
|
-
markObstaclesAsConnected,
|
|
10
|
-
} from "@tscircuit/infgrid-ijump-astar"
|
|
11
|
-
import type {
|
|
12
|
-
AnySoupElement,
|
|
13
|
-
LayerRef,
|
|
14
|
-
PCBTrace,
|
|
15
|
-
RouteHintPoint,
|
|
16
|
-
SchematicTrace,
|
|
17
|
-
SourceTrace,
|
|
18
|
-
} from "@tscircuit/soup"
|
|
19
|
-
import type {
|
|
20
|
-
Obstacle,
|
|
21
|
-
SimpleRouteConnection,
|
|
22
|
-
SimpleRouteJson,
|
|
23
|
-
SimplifiedPcbTrace,
|
|
24
|
-
} from "lib/utils/autorouting/SimpleRouteJson"
|
|
25
|
-
import { computeObstacleBounds } from "lib/utils/autorouting/computeObstacleBounds"
|
|
26
|
-
import { projectPointInDirection } from "lib/utils/projectPointInDirection"
|
|
27
|
-
import type { TraceHint } from "./TraceHint"
|
|
28
|
-
import {
|
|
29
|
-
findPossibleTraceLayerCombinations,
|
|
30
|
-
type CandidateTraceLayerCombination,
|
|
31
|
-
} from "lib/utils/autorouting/findPossibleTraceLayerCombinations"
|
|
32
|
-
import { pairs } from "lib/utils/pairs"
|
|
33
|
-
import { mergeRoutes } from "lib/utils/autorouting/mergeRoutes"
|
|
34
|
-
import type { Net } from "./Net"
|
|
35
|
-
import { getClosest } from "lib/utils/getClosest"
|
|
36
|
-
import { z } from "zod"
|
|
37
|
-
import { createNetsFromProps } from "lib/utils/components/createNetsFromProps"
|
|
38
|
-
import {
|
|
39
|
-
isMatchingPathSelector,
|
|
40
|
-
isMatchingSelector,
|
|
41
|
-
} from "lib/utils/selector-matching"
|
|
42
|
-
import { tryNow } from "lib/utils/try-now"
|
|
43
|
-
import { getFullConnectivityMapFromCircuitJson } from "circuit-json-to-connectivity-map"
|
|
44
|
-
|
|
45
|
-
type PcbRouteObjective =
|
|
46
|
-
| RouteHintPoint
|
|
47
|
-
| {
|
|
48
|
-
layers: string[]
|
|
49
|
-
x: number
|
|
50
|
-
y: number
|
|
51
|
-
via?: boolean
|
|
52
|
-
pcb_port_id?: string
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const portToObjective = (port: Port): PcbRouteObjective => {
|
|
56
|
-
const portPosition = port._getGlobalPcbPositionAfterLayout()
|
|
57
|
-
return {
|
|
58
|
-
...portPosition,
|
|
59
|
-
layers: port.getAvailablePcbLayers(),
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const SHOULD_USE_SINGLE_LAYER_ROUTING = false
|
|
64
|
-
|
|
65
|
-
export class Trace extends PrimitiveComponent<typeof traceProps> {
|
|
66
|
-
source_trace_id: string | null = null
|
|
67
|
-
pcb_trace_id: string | null = null
|
|
68
|
-
schematic_trace_id: string | null = null
|
|
69
|
-
_portsRoutedOnPcb: Port[]
|
|
70
|
-
|
|
71
|
-
constructor(props: z.input<typeof traceProps>) {
|
|
72
|
-
super(props)
|
|
73
|
-
this._portsRoutedOnPcb = []
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
get config() {
|
|
77
|
-
return {
|
|
78
|
-
zodProps: traceProps,
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
_getTracePortOrNetSelectorListFromProps(): string[] {
|
|
83
|
-
if ("from" in this.props && "to" in this.props) {
|
|
84
|
-
return [
|
|
85
|
-
typeof this.props.from === "string"
|
|
86
|
-
? this.props.from
|
|
87
|
-
: this.props.from.getPortSelector(),
|
|
88
|
-
typeof this.props.to === "string"
|
|
89
|
-
? this.props.to
|
|
90
|
-
: this.props.to.getPortSelector(),
|
|
91
|
-
]
|
|
92
|
-
}
|
|
93
|
-
if ("path" in this.props) {
|
|
94
|
-
return this.props.path.map((p) =>
|
|
95
|
-
typeof p === "string" ? p : p.getPortSelector(),
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
|
-
return []
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
getTracePortPathSelectors(): string[] {
|
|
102
|
-
return this._getTracePortOrNetSelectorListFromProps().filter(
|
|
103
|
-
(selector) => !selector.includes("net."),
|
|
104
|
-
)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
getTracePathNetSelectors(): string[] {
|
|
108
|
-
return this._getTracePortOrNetSelectorListFromProps().filter((selector) =>
|
|
109
|
-
selector.includes("net."),
|
|
110
|
-
)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
_findConnectedPorts():
|
|
114
|
-
| {
|
|
115
|
-
allPortsFound: true
|
|
116
|
-
ports: Port[]
|
|
117
|
-
portsWithSelectors: Array<{ selector: string; port: Port }>
|
|
118
|
-
}
|
|
119
|
-
| {
|
|
120
|
-
allPortsFound: false
|
|
121
|
-
ports?: undefined
|
|
122
|
-
portsWithSelectors?: undefined
|
|
123
|
-
} {
|
|
124
|
-
const { db } = this.root!
|
|
125
|
-
const { _parsedProps: props, parent } = this
|
|
126
|
-
|
|
127
|
-
if (!parent) throw new Error("Trace has no parent")
|
|
128
|
-
|
|
129
|
-
const portSelectors = this.getTracePortPathSelectors()
|
|
130
|
-
|
|
131
|
-
const portsWithSelectors = portSelectors.map((selector) => ({
|
|
132
|
-
selector,
|
|
133
|
-
port:
|
|
134
|
-
(this.getSubcircuit().selectOne(selector, { type: "port" }) as Port) ??
|
|
135
|
-
null,
|
|
136
|
-
}))
|
|
137
|
-
|
|
138
|
-
for (const { selector, port } of portsWithSelectors) {
|
|
139
|
-
if (!port) {
|
|
140
|
-
const parentSelector = selector.replace(/\>.*$/, "")
|
|
141
|
-
const targetComponent = this.getSubcircuit().selectOne(parentSelector)
|
|
142
|
-
if (!targetComponent) {
|
|
143
|
-
this.renderError(`Could not find port for selector "${selector}"`)
|
|
144
|
-
} else {
|
|
145
|
-
this.renderError(
|
|
146
|
-
`Could not find port for selector "${selector}"\nsearched component ${targetComponent.getString()}, which has ports: ${targetComponent.children
|
|
147
|
-
.filter((c) => c.componentName === "Port")
|
|
148
|
-
.map(
|
|
149
|
-
(c) => `${c.getString()}(${c.getNameAndAliases().join(",")})`,
|
|
150
|
-
)
|
|
151
|
-
.join(" & ")}`,
|
|
152
|
-
)
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (portsWithSelectors.some((p) => !p.port)) {
|
|
158
|
-
return { allPortsFound: false }
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
allPortsFound: true,
|
|
163
|
-
portsWithSelectors,
|
|
164
|
-
ports: portsWithSelectors.map(({ port }) => port),
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
_findConnectedNets(): {
|
|
169
|
-
nets: Net[]
|
|
170
|
-
netsWithSelectors: Array<{ selector: string; net: Net }>
|
|
171
|
-
} {
|
|
172
|
-
const netsWithSelectors = this.getTracePathNetSelectors().map(
|
|
173
|
-
(selector) => ({
|
|
174
|
-
selector,
|
|
175
|
-
net: this.getSubcircuit().selectOne(selector, { type: "net" }) as Net,
|
|
176
|
-
}),
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
const undefinedNets = netsWithSelectors.filter((n) => !n.net)
|
|
180
|
-
if (undefinedNets.length > 0) {
|
|
181
|
-
this.renderError(
|
|
182
|
-
`Could not find net for selector "${undefinedNets[0].selector}" inside ${this}`,
|
|
183
|
-
)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return { netsWithSelectors, nets: netsWithSelectors.map((n) => n.net) }
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Get all the traces that are connected in any degree to this trace, this is
|
|
191
|
-
* used during autorouting to routes to pass through traces connected to the
|
|
192
|
-
* same net.
|
|
193
|
-
*/
|
|
194
|
-
_getAllTracesConnectedToSameNet(): Trace[] {
|
|
195
|
-
const traces = this.getSubcircuit().selectAll("trace") as Trace[]
|
|
196
|
-
|
|
197
|
-
const myNets = this._findConnectedNets().nets
|
|
198
|
-
const myPorts = this._findConnectedPorts().ports ?? []
|
|
199
|
-
|
|
200
|
-
return traces.filter((t) => {
|
|
201
|
-
if (t === this) return false
|
|
202
|
-
const tNets = t._findConnectedNets().nets
|
|
203
|
-
const tPorts = t._findConnectedPorts().ports ?? []
|
|
204
|
-
return (
|
|
205
|
-
tNets.some((n) => myNets.includes(n)) ||
|
|
206
|
-
tPorts.some((p) => myPorts.includes(p))
|
|
207
|
-
)
|
|
208
|
-
})
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Determine if a trace is explicitly connected to a port (not via a net)
|
|
213
|
-
*/
|
|
214
|
-
_isExplicitlyConnectedToPort(port: Port) {
|
|
215
|
-
const { allPortsFound, portsWithSelectors: portsWithMetadata } =
|
|
216
|
-
this._findConnectedPorts()
|
|
217
|
-
if (!allPortsFound) return false
|
|
218
|
-
const ports = portsWithMetadata.map((p) => p.port)
|
|
219
|
-
return ports.includes(port)
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Determine if a trace is explicitly connected to a net (not via a port)
|
|
224
|
-
*/
|
|
225
|
-
_isExplicitlyConnectedToNet(net: Net) {
|
|
226
|
-
const nets = this._findConnectedNets().nets
|
|
227
|
-
return nets.includes(net)
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
doInitialCreateNetsFromProps(): void {
|
|
231
|
-
createNetsFromProps(this, this.getTracePathNetSelectors())
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
doInitialSourceTraceRender(): void {
|
|
235
|
-
const { db } = this.root!
|
|
236
|
-
const { _parsedProps: props, parent } = this
|
|
237
|
-
|
|
238
|
-
if (!parent) {
|
|
239
|
-
this.renderError("Trace has no parent")
|
|
240
|
-
return
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const { allPortsFound, portsWithSelectors: ports } =
|
|
244
|
-
this._findConnectedPorts()
|
|
245
|
-
if (!allPortsFound) return
|
|
246
|
-
|
|
247
|
-
const nets = this._findConnectedNets().nets
|
|
248
|
-
|
|
249
|
-
const trace = db.source_trace.insert({
|
|
250
|
-
connected_source_port_ids: ports.map((p) => p.port.source_port_id!),
|
|
251
|
-
connected_source_net_ids: nets.map((n) => n.source_net_id!),
|
|
252
|
-
})
|
|
253
|
-
|
|
254
|
-
this.source_trace_id = trace.source_trace_id
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
doInitialPcbTraceRender(): void {
|
|
258
|
-
const { db } = this.root!
|
|
259
|
-
const { _parsedProps: props, parent } = this
|
|
260
|
-
|
|
261
|
-
if (!parent) throw new Error("Trace has no parent")
|
|
262
|
-
|
|
263
|
-
if (this.getSubcircuit()._parsedProps.routingDisabled) {
|
|
264
|
-
return
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const { allPortsFound, ports } = this._findConnectedPorts()
|
|
268
|
-
const portsConnectedOnPcbViaNet: Port[] = []
|
|
269
|
-
|
|
270
|
-
if (!allPortsFound) return
|
|
271
|
-
|
|
272
|
-
const nets = this._findConnectedNets().netsWithSelectors
|
|
273
|
-
|
|
274
|
-
if (ports.length === 0 && nets.length === 2) {
|
|
275
|
-
// Find the two optimal points to connect the two nets
|
|
276
|
-
this.renderError(
|
|
277
|
-
`Trace connects two nets, we haven't implemented a way to route this yet`,
|
|
278
|
-
)
|
|
279
|
-
return
|
|
280
|
-
// biome-ignore lint/style/noUselessElse: <explanation>
|
|
281
|
-
} else if (ports.length === 1 && nets.length === 1) {
|
|
282
|
-
// Add a port from the net that is closest to the port
|
|
283
|
-
const port = ports[0]
|
|
284
|
-
const portsInNet = nets[0].net.getAllConnectedPorts()
|
|
285
|
-
const otherPortsInNet = portsInNet.filter((p) => p !== port)
|
|
286
|
-
if (otherPortsInNet.length === 0) {
|
|
287
|
-
console.log(
|
|
288
|
-
"Nothing to connect this port to, the net is empty. TODO should emit a warning!",
|
|
289
|
-
)
|
|
290
|
-
return
|
|
291
|
-
}
|
|
292
|
-
const closestPortInNet = getClosest(port, otherPortsInNet)
|
|
293
|
-
|
|
294
|
-
portsConnectedOnPcbViaNet.push(closestPortInNet)
|
|
295
|
-
|
|
296
|
-
ports.push(closestPortInNet)
|
|
297
|
-
} else if (ports.length > 1 && nets.length >= 1) {
|
|
298
|
-
this.renderError(
|
|
299
|
-
`Trace has more than one port and one or more nets, we don't currently support this type of complex trace routing`,
|
|
300
|
-
)
|
|
301
|
-
return
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const hints = ports.flatMap((port) =>
|
|
305
|
-
port.matchedComponents.filter((c) => c.componentName === "TraceHint"),
|
|
306
|
-
) as TraceHint[]
|
|
307
|
-
|
|
308
|
-
const pcbRouteHints = (this._parsedProps.pcbRouteHints ?? []).concat(
|
|
309
|
-
hints.flatMap((h) => h.getPcbRouteHints()),
|
|
310
|
-
)
|
|
311
|
-
|
|
312
|
-
if (ports.length > 2) {
|
|
313
|
-
this.renderError(
|
|
314
|
-
`Trace has more than two ports (${ports
|
|
315
|
-
.map((p) => p.getString())
|
|
316
|
-
.join(
|
|
317
|
-
", ",
|
|
318
|
-
)}), routing between more than two ports for a single trace is not implemented`,
|
|
319
|
-
)
|
|
320
|
-
return
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const alreadyRoutedTraces = this.getSubcircuit()
|
|
324
|
-
.selectAll("trace")
|
|
325
|
-
.filter(
|
|
326
|
-
(trace) => trace.renderPhaseStates.PcbTraceRender.initialized,
|
|
327
|
-
) as Trace[]
|
|
328
|
-
|
|
329
|
-
const isAlreadyRouted = alreadyRoutedTraces.some(
|
|
330
|
-
(trace) =>
|
|
331
|
-
trace._portsRoutedOnPcb.length === ports.length &&
|
|
332
|
-
trace._portsRoutedOnPcb.every((portRoutedByOtherTrace) =>
|
|
333
|
-
ports.includes(portRoutedByOtherTrace),
|
|
334
|
-
),
|
|
335
|
-
)
|
|
336
|
-
|
|
337
|
-
if (isAlreadyRouted) {
|
|
338
|
-
return
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
let orderedRouteObjectives: PcbRouteObjective[] = []
|
|
342
|
-
|
|
343
|
-
if (pcbRouteHints.length === 0) {
|
|
344
|
-
orderedRouteObjectives = [
|
|
345
|
-
portToObjective(ports[0]),
|
|
346
|
-
portToObjective(ports[1]),
|
|
347
|
-
]
|
|
348
|
-
} else {
|
|
349
|
-
// When we have hints, we have to order the hints then route between each
|
|
350
|
-
// terminal of the trace and the hints
|
|
351
|
-
// TODO order based on proximity to ports
|
|
352
|
-
orderedRouteObjectives = [
|
|
353
|
-
portToObjective(ports[0]),
|
|
354
|
-
...pcbRouteHints,
|
|
355
|
-
portToObjective(ports[1]),
|
|
356
|
-
]
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// Hints can indicate where there should be a via, but the layer is allowed
|
|
360
|
-
// to be unspecified, therefore we need to find possible layer combinations
|
|
361
|
-
// to go to each hint and still route to the start and end points
|
|
362
|
-
const candidateLayerCombinations = findPossibleTraceLayerCombinations(
|
|
363
|
-
orderedRouteObjectives,
|
|
364
|
-
)
|
|
365
|
-
|
|
366
|
-
if (
|
|
367
|
-
SHOULD_USE_SINGLE_LAYER_ROUTING &&
|
|
368
|
-
candidateLayerCombinations.length === 0
|
|
369
|
-
) {
|
|
370
|
-
this.renderError(
|
|
371
|
-
`Could not find a common layer (using hints) for trace ${this.getString()}`,
|
|
372
|
-
)
|
|
373
|
-
return
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
const connMap = getFullConnectivityMapFromCircuitJson(
|
|
377
|
-
this.root!.db.toArray(),
|
|
378
|
-
)
|
|
379
|
-
|
|
380
|
-
// Cache the PCB obstacles, they'll be needed for each segment between
|
|
381
|
-
// ports/hints
|
|
382
|
-
const [obstacles, errGettingObstacles] = tryNow(() =>
|
|
383
|
-
getObstaclesFromSoup(this.root!.db.toArray()),
|
|
384
|
-
)
|
|
385
|
-
|
|
386
|
-
if (errGettingObstacles) {
|
|
387
|
-
this.renderError({
|
|
388
|
-
type: "pcb_error",
|
|
389
|
-
error_type: "pcb_trace_error",
|
|
390
|
-
message: `Error getting obstacles for autorouting: ${errGettingObstacles.message}`,
|
|
391
|
-
source_trace_id: this.source_trace_id!,
|
|
392
|
-
center: { x: 0, y: 0 },
|
|
393
|
-
pcb_port_ids: ports.map((p) => p.pcb_port_id!),
|
|
394
|
-
pcb_trace_id: this.pcb_trace_id!,
|
|
395
|
-
pcb_component_ids: [],
|
|
396
|
-
})
|
|
397
|
-
return
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
for (const obstacle of obstacles) {
|
|
401
|
-
const connectedTo = obstacle.connectedTo
|
|
402
|
-
if (connectedTo.length > 0) {
|
|
403
|
-
const netId = connMap.getNetConnectedToId(obstacle.connectedTo[0])
|
|
404
|
-
if (netId) {
|
|
405
|
-
obstacle.connectedTo.push(netId)
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
let orderedRoutePoints: PcbRouteObjective[] = []
|
|
411
|
-
if (candidateLayerCombinations.length === 0) {
|
|
412
|
-
orderedRoutePoints = orderedRouteObjectives
|
|
413
|
-
} else {
|
|
414
|
-
// TODO explore all candidate layer combinations if one fails
|
|
415
|
-
const candidateLayerSelections = candidateLayerCombinations[0].layer_path
|
|
416
|
-
|
|
417
|
-
/**
|
|
418
|
-
* Apply the candidate layer selections to the route objectives, now we
|
|
419
|
-
* have a set of points that have definite layers
|
|
420
|
-
*/
|
|
421
|
-
orderedRoutePoints = orderedRouteObjectives.map((t, idx) => {
|
|
422
|
-
if (t.via) {
|
|
423
|
-
return {
|
|
424
|
-
...t,
|
|
425
|
-
via_to_layer: candidateLayerSelections[idx],
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
return { ...t, layers: [candidateLayerSelections[idx]] }
|
|
429
|
-
})
|
|
430
|
-
}
|
|
431
|
-
;(orderedRoutePoints[0] as any).pcb_port_id = ports[0].pcb_port_id
|
|
432
|
-
;(orderedRoutePoints[orderedRoutePoints.length - 1] as any).pcb_port_id =
|
|
433
|
-
ports[1].pcb_port_id
|
|
434
|
-
|
|
435
|
-
const routes: PCBTrace["route"][] = []
|
|
436
|
-
for (const [a, b] of pairs(orderedRoutePoints)) {
|
|
437
|
-
const dominantLayer =
|
|
438
|
-
"via_to_layer" in a ? (a.via_to_layer as LayerRef) : null
|
|
439
|
-
const BOUNDS_MARGIN = 2 //mm
|
|
440
|
-
|
|
441
|
-
const aLayer =
|
|
442
|
-
"layers" in a && a.layers.length === 1
|
|
443
|
-
? a.layers[0]
|
|
444
|
-
: (dominantLayer ?? "top")
|
|
445
|
-
const bLayer =
|
|
446
|
-
"layers" in b && b.layers.length === 1
|
|
447
|
-
? b.layers[0]
|
|
448
|
-
: (dominantLayer ?? "top")
|
|
449
|
-
|
|
450
|
-
const pcbPortA = "pcb_port_id" in a ? a.pcb_port_id : null
|
|
451
|
-
const pcbPortB = "pcb_port_id" in b ? b.pcb_port_id : null
|
|
452
|
-
|
|
453
|
-
const minTraceWidth =
|
|
454
|
-
this.getSubcircuit()._parsedProps.minTraceWidth ?? 0.16
|
|
455
|
-
|
|
456
|
-
const ijump = new MultilayerIjump({
|
|
457
|
-
OBSTACLE_MARGIN: minTraceWidth * 2,
|
|
458
|
-
// isRemovePathLoopsEnabled: true,
|
|
459
|
-
optimizeWithGoalBoxes: Boolean(pcbPortA && pcbPortB),
|
|
460
|
-
connMap,
|
|
461
|
-
input: {
|
|
462
|
-
obstacles,
|
|
463
|
-
minTraceWidth,
|
|
464
|
-
connections: [
|
|
465
|
-
{
|
|
466
|
-
name: this.source_trace_id!,
|
|
467
|
-
pointsToConnect: [
|
|
468
|
-
{ ...a, layer: aLayer, pcb_port_id: pcbPortA! },
|
|
469
|
-
{ ...b, layer: bLayer, pcb_port_id: pcbPortB! },
|
|
470
|
-
],
|
|
471
|
-
},
|
|
472
|
-
],
|
|
473
|
-
layerCount: 2,
|
|
474
|
-
bounds: {
|
|
475
|
-
minX: Math.min(a.x, b.x) - BOUNDS_MARGIN,
|
|
476
|
-
maxX: Math.max(a.x, b.x) + BOUNDS_MARGIN,
|
|
477
|
-
minY: Math.min(a.y, b.y) - BOUNDS_MARGIN,
|
|
478
|
-
maxY: Math.max(a.y, b.y) + BOUNDS_MARGIN,
|
|
479
|
-
},
|
|
480
|
-
},
|
|
481
|
-
})
|
|
482
|
-
let traces: SimplifiedPcbTrace[] | null = null
|
|
483
|
-
try {
|
|
484
|
-
traces = ijump.solveAndMapToTraces()
|
|
485
|
-
} catch (e: any) {
|
|
486
|
-
this.renderError({
|
|
487
|
-
type: "pcb_error",
|
|
488
|
-
error_type: "pcb_trace_error",
|
|
489
|
-
message: `error solving route: ${e.message}`,
|
|
490
|
-
source_trace_id: this.source_trace_id!,
|
|
491
|
-
center: { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 },
|
|
492
|
-
pcb_port_ids: ports.map((p) => p.pcb_port_id!),
|
|
493
|
-
pcb_trace_id: this.pcb_trace_id!,
|
|
494
|
-
pcb_component_ids: ports.map((p) => p.pcb_component_id!),
|
|
495
|
-
})
|
|
496
|
-
}
|
|
497
|
-
if (!traces) return
|
|
498
|
-
if (traces.length === 0) {
|
|
499
|
-
this.renderError({
|
|
500
|
-
type: "pcb_error",
|
|
501
|
-
error_type: "pcb_trace_error",
|
|
502
|
-
message: `Could not find a route for ${this}`,
|
|
503
|
-
source_trace_id: this.source_trace_id!,
|
|
504
|
-
center: { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 },
|
|
505
|
-
pcb_port_ids: ports.map((p) => p.pcb_port_id!),
|
|
506
|
-
pcb_trace_id: this.pcb_trace_id!,
|
|
507
|
-
pcb_component_ids: ports.map((p) => p.pcb_component_id!),
|
|
508
|
-
})
|
|
509
|
-
return
|
|
510
|
-
}
|
|
511
|
-
const [trace] = traces as PCBTrace[]
|
|
512
|
-
|
|
513
|
-
// If the autorouter didn't specify a layer, use the dominant layer
|
|
514
|
-
// Some of the single-layer autorouters don't add the layer property
|
|
515
|
-
if (dominantLayer) {
|
|
516
|
-
trace.route = trace.route.map((p) => {
|
|
517
|
-
if (p.route_type === "wire" && !p.layer) {
|
|
518
|
-
p.layer = dominantLayer
|
|
519
|
-
}
|
|
520
|
-
return p
|
|
521
|
-
})
|
|
522
|
-
}
|
|
523
|
-
routes.push(trace.route)
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
const mergedRoute = mergeRoutes(routes)
|
|
527
|
-
|
|
528
|
-
for (const point of mergedRoute) {
|
|
529
|
-
if (point.route_type === "via") {
|
|
530
|
-
db.pcb_via.insert({
|
|
531
|
-
x: point.x,
|
|
532
|
-
y: point.y,
|
|
533
|
-
hole_diameter: 0.3,
|
|
534
|
-
outer_diameter: 0.6,
|
|
535
|
-
layers: [point.from_layer as LayerRef, point.to_layer as LayerRef],
|
|
536
|
-
from_layer: point.from_layer as LayerRef,
|
|
537
|
-
to_layer: point.to_layer as LayerRef,
|
|
538
|
-
})
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
const pcb_trace = db.pcb_trace.insert({
|
|
543
|
-
route: mergedRoute,
|
|
544
|
-
source_trace_id: this.source_trace_id!,
|
|
545
|
-
})
|
|
546
|
-
this._portsRoutedOnPcb = ports
|
|
547
|
-
this.pcb_trace_id = pcb_trace.pcb_trace_id
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
doInitialSchematicTraceRender(): void {
|
|
551
|
-
const { db } = this.root!
|
|
552
|
-
const { _parsedProps: props, parent } = this
|
|
553
|
-
|
|
554
|
-
if (!parent) throw new Error("Trace has no parent")
|
|
555
|
-
|
|
556
|
-
const { allPortsFound, portsWithSelectors: ports } =
|
|
557
|
-
this._findConnectedPorts()
|
|
558
|
-
|
|
559
|
-
if (!allPortsFound) return
|
|
560
|
-
|
|
561
|
-
const obstacles: Obstacle[] = []
|
|
562
|
-
const connection: SimpleRouteConnection = {
|
|
563
|
-
name: this.source_trace_id!,
|
|
564
|
-
pointsToConnect: [],
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
for (const elm of db.toArray()) {
|
|
568
|
-
if (elm.type === "schematic_component") {
|
|
569
|
-
obstacles.push({
|
|
570
|
-
type: "rect",
|
|
571
|
-
layers: ["top"],
|
|
572
|
-
center: elm.center,
|
|
573
|
-
width: elm.size.width,
|
|
574
|
-
height: elm.size.height,
|
|
575
|
-
connectedTo: [],
|
|
576
|
-
})
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
for (const { port } of ports) {
|
|
581
|
-
connection.pointsToConnect.push({
|
|
582
|
-
...projectPointInDirection(
|
|
583
|
-
port._getGlobalSchematicPositionBeforeLayout(),
|
|
584
|
-
port.facingDirection!,
|
|
585
|
-
0.1501,
|
|
586
|
-
),
|
|
587
|
-
layer: "top",
|
|
588
|
-
})
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
const bounds = computeObstacleBounds(obstacles)
|
|
592
|
-
|
|
593
|
-
const simpleRouteJsonInput: SimpleRouteJson = {
|
|
594
|
-
minTraceWidth: 0.1,
|
|
595
|
-
obstacles,
|
|
596
|
-
connections: [connection],
|
|
597
|
-
bounds,
|
|
598
|
-
layerCount: 1,
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
const autorouter = new IJumpAutorouter({
|
|
602
|
-
input: simpleRouteJsonInput,
|
|
603
|
-
})
|
|
604
|
-
const results = autorouter.solve()
|
|
605
|
-
|
|
606
|
-
if (results.length === 0) return
|
|
607
|
-
|
|
608
|
-
const [result] = results
|
|
609
|
-
|
|
610
|
-
if (!result.solved) return
|
|
611
|
-
|
|
612
|
-
const { route } = result
|
|
613
|
-
|
|
614
|
-
const edges: SchematicTrace["edges"] = []
|
|
615
|
-
|
|
616
|
-
for (let i = 0; i < route.length - 1; i++) {
|
|
617
|
-
const from = route[i]
|
|
618
|
-
const to = route[i + 1]
|
|
619
|
-
|
|
620
|
-
edges.push({
|
|
621
|
-
from,
|
|
622
|
-
to,
|
|
623
|
-
// TODO to_schematic_port_id and from_schematic_port_id
|
|
624
|
-
})
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
const trace = db.schematic_trace.insert({
|
|
628
|
-
source_trace_id: this.source_trace_id!,
|
|
629
|
-
|
|
630
|
-
edges,
|
|
631
|
-
})
|
|
632
|
-
|
|
633
|
-
this.schematic_trace_id = trace.schematic_trace_id
|
|
634
|
-
}
|
|
635
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { PrimitiveComponent } from "lib/components/base-components/PrimitiveComponent"
|
|
2
|
-
import { traceHintProps } from "@tscircuit/props"
|
|
3
|
-
import type { Port } from "./Port"
|
|
4
|
-
import type { RouteHintPoint } from "@tscircuit/soup"
|
|
5
|
-
import { applyToPoint } from "transformation-matrix"
|
|
6
|
-
|
|
7
|
-
export class TraceHint extends PrimitiveComponent<typeof traceHintProps> {
|
|
8
|
-
matchedPort: Port | null = null
|
|
9
|
-
|
|
10
|
-
doInitialPortMatching(): void {
|
|
11
|
-
const { db } = this.root!
|
|
12
|
-
const { _parsedProps: props, parent } = this
|
|
13
|
-
|
|
14
|
-
if (!parent) return
|
|
15
|
-
|
|
16
|
-
if (parent.componentName === "Trace") {
|
|
17
|
-
this.renderError(
|
|
18
|
-
`Port inference inside trace is not yet supported (${this})`,
|
|
19
|
-
)
|
|
20
|
-
return
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (!parent) throw new Error("TraceHint has no parent")
|
|
24
|
-
|
|
25
|
-
if (!props.for) {
|
|
26
|
-
this.renderError(`TraceHint has no for property (${this})`)
|
|
27
|
-
return
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const port = parent.selectOne(props.for, { type: "port" }) as Port
|
|
31
|
-
|
|
32
|
-
if (!port) {
|
|
33
|
-
this.renderError(
|
|
34
|
-
`${this} could not find port for selector "${props.for}"`,
|
|
35
|
-
)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
this.matchedPort = port
|
|
39
|
-
port.registerMatch(this)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
getPcbRouteHints(): Array<RouteHintPoint> {
|
|
43
|
-
const { _parsedProps: props } = this
|
|
44
|
-
|
|
45
|
-
const offsets = props.offset ? [props.offset] : props.offsets
|
|
46
|
-
|
|
47
|
-
if (!offsets) return []
|
|
48
|
-
|
|
49
|
-
const globalTransform = this._computePcbGlobalTransformBeforeLayout()
|
|
50
|
-
|
|
51
|
-
return offsets.map(
|
|
52
|
-
(offset): RouteHintPoint => ({
|
|
53
|
-
...applyToPoint(globalTransform, offset),
|
|
54
|
-
via: offset.via,
|
|
55
|
-
to_layer: (offset as any).to_layer,
|
|
56
|
-
trace_width: (offset as any).trace_width,
|
|
57
|
-
}),
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
doInitialPcbTraceHintRender(): void {
|
|
62
|
-
const { db } = this.root!
|
|
63
|
-
const { _parsedProps: props } = this
|
|
64
|
-
|
|
65
|
-
db.pcb_trace_hint.insert({
|
|
66
|
-
pcb_component_id: this.matchedPort?.pcb_component_id!,
|
|
67
|
-
pcb_port_id: this.matchedPort?.pcb_port_id!,
|
|
68
|
-
route: this.getPcbRouteHints(),
|
|
69
|
-
})
|
|
70
|
-
}
|
|
71
|
-
}
|