@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,239 +0,0 @@
|
|
|
1
|
-
import type { footprintProps } from "@tscircuit/props"
|
|
2
|
-
import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
|
|
3
|
-
import type { Constraint } from "./Constraint"
|
|
4
|
-
import * as kiwi from "@lume/kiwi"
|
|
5
|
-
import Debug from "debug"
|
|
6
|
-
|
|
7
|
-
const debug = Debug("tscircuit:core:footprint")
|
|
8
|
-
|
|
9
|
-
export class Footprint extends PrimitiveComponent<typeof footprintProps> {
|
|
10
|
-
/**
|
|
11
|
-
* A footprint is a constrainedlayout, the db elements are adjusted according
|
|
12
|
-
* to any constraints that are defined.
|
|
13
|
-
*/
|
|
14
|
-
doInitialPcbFootprintLayout() {
|
|
15
|
-
const constraints = this.children.filter(
|
|
16
|
-
(child) => child.componentName === "Constraint",
|
|
17
|
-
) as Constraint[]
|
|
18
|
-
|
|
19
|
-
if (constraints.length === 0) return
|
|
20
|
-
|
|
21
|
-
const involvedComponents = constraints
|
|
22
|
-
.flatMap(
|
|
23
|
-
(constraint) =>
|
|
24
|
-
constraint._getAllReferencedComponents().componentsWithSelectors,
|
|
25
|
-
)
|
|
26
|
-
.map(({ component, selector, componentSelector, edge }) => ({
|
|
27
|
-
component,
|
|
28
|
-
selector,
|
|
29
|
-
componentSelector,
|
|
30
|
-
edge,
|
|
31
|
-
bounds: component._getPcbCircuitJsonBounds(),
|
|
32
|
-
}))
|
|
33
|
-
|
|
34
|
-
if (involvedComponents.some((c) => c.edge)) {
|
|
35
|
-
throw new Error(
|
|
36
|
-
"edge constraints not implemented yet for footprint layout, contributions welcome!",
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function getComponentDetails(selector: string) {
|
|
41
|
-
return involvedComponents.find(({ selector: s }) => s === selector)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const solver = new kiwi.Solver()
|
|
45
|
-
|
|
46
|
-
const kVars: { [varName: string]: kiwi.Variable } = {}
|
|
47
|
-
function getKVar(name: string) {
|
|
48
|
-
if (!(name in kVars)) {
|
|
49
|
-
kVars[name] = new kiwi.Variable(name)
|
|
50
|
-
solver.addEditVariable(kVars[name], kiwi.Strength.weak)
|
|
51
|
-
}
|
|
52
|
-
return kVars[name]
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// 1. Create kiwi variables to represent each component's center
|
|
56
|
-
for (const { selector, bounds } of involvedComponents) {
|
|
57
|
-
const kvx = getKVar(`${selector}_x`)
|
|
58
|
-
const kvy = getKVar(`${selector}_y`)
|
|
59
|
-
solver.suggestValue(kvx, bounds.center.x)
|
|
60
|
-
solver.suggestValue(kvy, bounds.center.y)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// 2. Add kiwi constraints using the parsed constraint properties
|
|
64
|
-
for (const constraint of constraints) {
|
|
65
|
-
const props = constraint._parsedProps
|
|
66
|
-
|
|
67
|
-
if ("xDist" in props) {
|
|
68
|
-
const { xDist, left, right, edgeToEdge, centerToCenter } = props
|
|
69
|
-
const leftVar = getKVar(`${left}_x`)
|
|
70
|
-
const rightVar = getKVar(`${right}_x`)
|
|
71
|
-
const leftBounds = getComponentDetails(left)?.bounds!
|
|
72
|
-
const rightBounds = getComponentDetails(right)?.bounds!
|
|
73
|
-
|
|
74
|
-
if (centerToCenter) {
|
|
75
|
-
// right - left = xdist
|
|
76
|
-
const expr = new kiwi.Expression(rightVar, [-1, leftVar])
|
|
77
|
-
solver.addConstraint(
|
|
78
|
-
new kiwi.Constraint(
|
|
79
|
-
expr,
|
|
80
|
-
kiwi.Operator.Eq,
|
|
81
|
-
props.xDist,
|
|
82
|
-
kiwi.Strength.required,
|
|
83
|
-
),
|
|
84
|
-
)
|
|
85
|
-
} else if (edgeToEdge) {
|
|
86
|
-
// rightEdge - leftEdge = xdist
|
|
87
|
-
// right - rightBounds.width/2 - left - leftBounds.width/2 = xdist
|
|
88
|
-
const expr = new kiwi.Expression(
|
|
89
|
-
rightVar,
|
|
90
|
-
-rightBounds.width / 2,
|
|
91
|
-
[-1, leftVar],
|
|
92
|
-
-leftBounds.width / 2,
|
|
93
|
-
)
|
|
94
|
-
solver.addConstraint(
|
|
95
|
-
new kiwi.Constraint(
|
|
96
|
-
expr,
|
|
97
|
-
kiwi.Operator.Eq,
|
|
98
|
-
props.xDist,
|
|
99
|
-
kiwi.Strength.required,
|
|
100
|
-
),
|
|
101
|
-
)
|
|
102
|
-
}
|
|
103
|
-
} else if ("yDist" in props) {
|
|
104
|
-
const { yDist, top, bottom, edgeToEdge, centerToCenter } = props
|
|
105
|
-
const topVar = getKVar(`${top}_y`)
|
|
106
|
-
const bottomVar = getKVar(`${bottom}_y`)
|
|
107
|
-
const topBounds = getComponentDetails(top)?.bounds!
|
|
108
|
-
const bottomBounds = getComponentDetails(bottom)?.bounds!
|
|
109
|
-
|
|
110
|
-
// Top - Bottom = ydist
|
|
111
|
-
|
|
112
|
-
if (centerToCenter) {
|
|
113
|
-
// top - bottom = ydist
|
|
114
|
-
const expr = new kiwi.Expression(topVar, [-1, bottomVar])
|
|
115
|
-
solver.addConstraint(
|
|
116
|
-
new kiwi.Constraint(
|
|
117
|
-
expr,
|
|
118
|
-
kiwi.Operator.Eq,
|
|
119
|
-
props.yDist,
|
|
120
|
-
kiwi.Strength.required,
|
|
121
|
-
),
|
|
122
|
-
)
|
|
123
|
-
} else if (edgeToEdge) {
|
|
124
|
-
// topElmBottomEdge - bottomElmTopEdge = ydist
|
|
125
|
-
// topElmCenterY - topElmHeight/2 - bottomElmCenterY - bottomElmHeight/2 = ydist
|
|
126
|
-
const expr = new kiwi.Expression(
|
|
127
|
-
topVar,
|
|
128
|
-
topBounds.height / 2,
|
|
129
|
-
[-1, bottomVar],
|
|
130
|
-
-bottomBounds.height / 2,
|
|
131
|
-
)
|
|
132
|
-
solver.addConstraint(
|
|
133
|
-
new kiwi.Constraint(
|
|
134
|
-
expr,
|
|
135
|
-
kiwi.Operator.Eq,
|
|
136
|
-
props.yDist,
|
|
137
|
-
kiwi.Strength.required,
|
|
138
|
-
),
|
|
139
|
-
)
|
|
140
|
-
}
|
|
141
|
-
} else if ("sameY" in props) {
|
|
142
|
-
const { for: selectors } = props
|
|
143
|
-
if (selectors.length < 2) continue
|
|
144
|
-
const vars = selectors.map((selector) => getKVar(`${selector}_y`))
|
|
145
|
-
const expr = new kiwi.Expression(...vars.slice(1))
|
|
146
|
-
|
|
147
|
-
solver.addConstraint(
|
|
148
|
-
new kiwi.Constraint(
|
|
149
|
-
expr,
|
|
150
|
-
kiwi.Operator.Eq,
|
|
151
|
-
vars[0],
|
|
152
|
-
kiwi.Strength.required,
|
|
153
|
-
),
|
|
154
|
-
)
|
|
155
|
-
} else if ("sameX" in props) {
|
|
156
|
-
const { for: selectors } = props
|
|
157
|
-
if (selectors.length < 2) continue
|
|
158
|
-
const vars = selectors.map((selector) => getKVar(`${selector}_x`))
|
|
159
|
-
const expr = new kiwi.Expression(...vars.slice(1))
|
|
160
|
-
solver.addConstraint(
|
|
161
|
-
new kiwi.Constraint(
|
|
162
|
-
expr,
|
|
163
|
-
kiwi.Operator.Eq,
|
|
164
|
-
vars[0],
|
|
165
|
-
kiwi.Strength.required,
|
|
166
|
-
),
|
|
167
|
-
)
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// 3. Solve the system of equations
|
|
172
|
-
solver.updateVariables()
|
|
173
|
-
if (debug.enabled) {
|
|
174
|
-
console.log("Solution to layout constraints:")
|
|
175
|
-
console.table(
|
|
176
|
-
Object.entries(kVars).map(([key, kvar]) => ({
|
|
177
|
-
var: key,
|
|
178
|
-
val: kvar.value(),
|
|
179
|
-
})),
|
|
180
|
-
)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// 3.1 Compute the global offset. There are different ways to do this:
|
|
184
|
-
// - If any component has a fixed position, then that can be used as the
|
|
185
|
-
// origin to determine the offset of all other components
|
|
186
|
-
// - If no component has a fixed position, then we recenter everything
|
|
187
|
-
// using the new bounds of all the involved components
|
|
188
|
-
|
|
189
|
-
// TODO determine if there's a fixed component
|
|
190
|
-
|
|
191
|
-
// Determine the new bounds all the involved components and compute the
|
|
192
|
-
// bounds of this footprint
|
|
193
|
-
const bounds = {
|
|
194
|
-
left: Infinity,
|
|
195
|
-
right: -Infinity,
|
|
196
|
-
top: -Infinity,
|
|
197
|
-
bottom: Infinity,
|
|
198
|
-
}
|
|
199
|
-
for (const {
|
|
200
|
-
selector,
|
|
201
|
-
bounds: { width, height },
|
|
202
|
-
} of involvedComponents) {
|
|
203
|
-
const kvx = getKVar(`${selector}_x`)
|
|
204
|
-
const kvy = getKVar(`${selector}_y`)
|
|
205
|
-
|
|
206
|
-
const newLeft = kvx.value() - width / 2
|
|
207
|
-
const newRight = kvx.value() + width / 2
|
|
208
|
-
const newTop = kvy.value() + height / 2
|
|
209
|
-
const newBottom = kvy.value() - height / 2
|
|
210
|
-
|
|
211
|
-
bounds.left = Math.min(bounds.left, newLeft)
|
|
212
|
-
bounds.right = Math.max(bounds.right, newRight)
|
|
213
|
-
bounds.top = Math.max(bounds.top, newTop)
|
|
214
|
-
bounds.bottom = Math.min(bounds.bottom, newBottom)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Compute the global offset, we can use this to recenter each component
|
|
218
|
-
const globalOffset = {
|
|
219
|
-
x: -(bounds.right + bounds.left) / 2,
|
|
220
|
-
y: -(bounds.top + bounds.bottom) / 2,
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const containerPos =
|
|
224
|
-
this.getPrimitiveContainer()!._getGlobalPcbPositionBeforeLayout()
|
|
225
|
-
|
|
226
|
-
globalOffset.x += containerPos.x
|
|
227
|
-
globalOffset.y += containerPos.y
|
|
228
|
-
|
|
229
|
-
// 4. Update the component positions
|
|
230
|
-
for (const { component, selector } of involvedComponents) {
|
|
231
|
-
const kvx = getKVar(`${selector}_x`)
|
|
232
|
-
const kvy = getKVar(`${selector}_y`)
|
|
233
|
-
component._setPositionFromLayout({
|
|
234
|
-
x: kvx.value() + globalOffset.x,
|
|
235
|
-
y: kvy.value() + globalOffset.y,
|
|
236
|
-
})
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
groupProps,
|
|
3
|
-
type GroupProps,
|
|
4
|
-
type SubcircuitGroupProps,
|
|
5
|
-
} from "@tscircuit/props"
|
|
6
|
-
import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
|
|
7
|
-
import { compose, identity } from "transformation-matrix"
|
|
8
|
-
import { z } from "zod"
|
|
9
|
-
import { NormalComponent } from "../base-components/NormalComponent"
|
|
10
|
-
import { TraceHint } from "./TraceHint"
|
|
11
|
-
|
|
12
|
-
export class Group<
|
|
13
|
-
Props extends z.ZodType<any, any, any> = typeof groupProps,
|
|
14
|
-
> extends NormalComponent<Props> {
|
|
15
|
-
get config() {
|
|
16
|
-
return {
|
|
17
|
-
zodProps: groupProps as unknown as Props,
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
doInitialCreateTraceHintsFromProps(): void {
|
|
22
|
-
const { _parsedProps: props } = this
|
|
23
|
-
const { db } = this.root!
|
|
24
|
-
|
|
25
|
-
const groupProps = props as SubcircuitGroupProps
|
|
26
|
-
|
|
27
|
-
if (!this.isSubcircuit) return
|
|
28
|
-
|
|
29
|
-
const manualTraceHints = groupProps.layout?.manual_trace_hints
|
|
30
|
-
|
|
31
|
-
if (!manualTraceHints) return
|
|
32
|
-
|
|
33
|
-
for (const manualTraceHint of manualTraceHints) {
|
|
34
|
-
this.add(
|
|
35
|
-
new TraceHint({
|
|
36
|
-
for: manualTraceHint.pcb_port_selector,
|
|
37
|
-
offsets: manualTraceHint.offsets,
|
|
38
|
-
}),
|
|
39
|
-
)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
|
|
2
|
-
import { holeProps } from "@tscircuit/props"
|
|
3
|
-
import type { PCBHole } from "@tscircuit/soup"
|
|
4
|
-
|
|
5
|
-
export class Hole extends PrimitiveComponent<typeof holeProps> {
|
|
6
|
-
pcb_hole_id: string | null = null
|
|
7
|
-
isPcbPrimitive = true
|
|
8
|
-
|
|
9
|
-
get config() {
|
|
10
|
-
return {
|
|
11
|
-
zodProps: holeProps,
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
getPcbSize(): { width: number; height: number } {
|
|
16
|
-
const { _parsedProps: props } = this
|
|
17
|
-
return { width: props.diameter, height: props.diameter }
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
doInitialPcbPrimitiveRender(): void {
|
|
21
|
-
const { db } = this.root!
|
|
22
|
-
const { _parsedProps: props } = this
|
|
23
|
-
const position = this._getGlobalPcbPositionBeforeLayout()
|
|
24
|
-
|
|
25
|
-
const inserted_hole = db.pcb_hole.insert({
|
|
26
|
-
hole_shape: "circle",
|
|
27
|
-
// @ts-ignore
|
|
28
|
-
hole_diameter: props.diameter,
|
|
29
|
-
x: position.x,
|
|
30
|
-
y: position.y,
|
|
31
|
-
})
|
|
32
|
-
this.pcb_hole_id = inserted_hole.pcb_hole_id!
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
_getPcbCircuitJsonBounds(): {
|
|
36
|
-
center: { x: number; y: number }
|
|
37
|
-
bounds: { left: number; top: number; right: number; bottom: number }
|
|
38
|
-
width: number
|
|
39
|
-
height: number
|
|
40
|
-
} {
|
|
41
|
-
const { db } = this.root!
|
|
42
|
-
const hole = db.pcb_hole.get(this.pcb_hole_id!)!
|
|
43
|
-
const size = this.getPcbSize()
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
center: { x: hole.x, y: hole.y },
|
|
47
|
-
bounds: {
|
|
48
|
-
left: hole.x - size.width / 2,
|
|
49
|
-
top: hole.y - size.height / 2,
|
|
50
|
-
right: hole.x + size.width / 2,
|
|
51
|
-
bottom: hole.y + size.height / 2,
|
|
52
|
-
},
|
|
53
|
-
width: size.width,
|
|
54
|
-
height: size.height,
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
_setPositionFromLayout(newCenter: { x: number; y: number }) {
|
|
59
|
-
const { db } = this.root!
|
|
60
|
-
db.pcb_hole.update(this.pcb_hole_id!, {
|
|
61
|
-
x: newCenter.x,
|
|
62
|
-
y: newCenter.y,
|
|
63
|
-
})
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
|
|
2
|
-
import { pcbKeepoutProps } from "@tscircuit/props"
|
|
3
|
-
import type { RenderPhaseFn } from "../base-components/Renderable"
|
|
4
|
-
import type { PCBKeepout } from "@tscircuit/soup"
|
|
5
|
-
import { decomposeTSR } from "transformation-matrix"
|
|
6
|
-
|
|
7
|
-
export class Keepout extends PrimitiveComponent<typeof pcbKeepoutProps> {
|
|
8
|
-
pcb_keepout_id: string | null = null
|
|
9
|
-
|
|
10
|
-
isPcbPrimitive = true
|
|
11
|
-
|
|
12
|
-
get config() {
|
|
13
|
-
return {
|
|
14
|
-
zodProps: pcbKeepoutProps,
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
doInitialPcbPrimitiveRender(): void {
|
|
19
|
-
const { db } = this.root!
|
|
20
|
-
const { _parsedProps: props } = this
|
|
21
|
-
const position = this._getGlobalPcbPositionBeforeLayout()
|
|
22
|
-
const decomposedMat = decomposeTSR(
|
|
23
|
-
this._computePcbGlobalTransformBeforeLayout(),
|
|
24
|
-
)
|
|
25
|
-
const isRotated90 =
|
|
26
|
-
Math.abs(decomposedMat.rotation.angle * (180 / Math.PI) - 90) < 0.01
|
|
27
|
-
|
|
28
|
-
let pcb_keepout: PCBKeepout | null = null
|
|
29
|
-
if (props.shape === "circle") {
|
|
30
|
-
pcb_keepout = db.pcb_keepout.insert({
|
|
31
|
-
layers: ["top"],
|
|
32
|
-
shape: "circle",
|
|
33
|
-
// @ts-ignore: no idea why this is triggering
|
|
34
|
-
radius: props.radius,
|
|
35
|
-
center: {
|
|
36
|
-
x: position.x,
|
|
37
|
-
y: position.y,
|
|
38
|
-
},
|
|
39
|
-
})
|
|
40
|
-
} else if (props.shape === "rect") {
|
|
41
|
-
pcb_keepout = db.pcb_keepout.insert({
|
|
42
|
-
layers: ["top"],
|
|
43
|
-
shape: "rect",
|
|
44
|
-
...(isRotated90
|
|
45
|
-
? { width: props.height, height: props.width }
|
|
46
|
-
: { width: props.width, height: props.height }),
|
|
47
|
-
// @ts-ignore: no idea why this is triggering
|
|
48
|
-
center: {
|
|
49
|
-
x: position.x,
|
|
50
|
-
y: position.y,
|
|
51
|
-
},
|
|
52
|
-
})
|
|
53
|
-
}
|
|
54
|
-
if (pcb_keepout) {
|
|
55
|
-
this.pcb_keepout_id = pcb_keepout.pcb_keepout_id
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
|
|
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"
|
|
8
|
-
|
|
9
|
-
export const netProps = z.object({
|
|
10
|
-
name: z.string(),
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
export class Net extends PrimitiveComponent<typeof netProps> {
|
|
14
|
-
source_net_id?: string
|
|
15
|
-
|
|
16
|
-
getPortSelector() {
|
|
17
|
-
return `net.${this.props.name}`
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
doInitialSourceRender(): void {
|
|
21
|
-
const { db } = this.root!
|
|
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.root!
|
|
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._getGlobalPcbPositionBeforeLayout(),
|
|
113
|
-
)
|
|
114
|
-
const Bpositions: Array<{ x: number; y: number }> = B.ports.map((port) =>
|
|
115
|
-
port._getGlobalPcbPositionBeforeLayout(),
|
|
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
|
-
}
|
|
171
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
|
|
2
|
-
import { platedHoleProps } from "@tscircuit/props"
|
|
3
|
-
import type { Port } from "./Port"
|
|
4
|
-
import type { PCBPlatedHoleInput } from "@tscircuit/soup"
|
|
5
|
-
|
|
6
|
-
export class PlatedHole extends PrimitiveComponent<typeof platedHoleProps> {
|
|
7
|
-
pcb_plated_hole_id: string | null = null
|
|
8
|
-
matchedPort: Port | null = null
|
|
9
|
-
isPcbPrimitive = true
|
|
10
|
-
|
|
11
|
-
get config() {
|
|
12
|
-
return {
|
|
13
|
-
zodProps: platedHoleProps,
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
getAvailablePcbLayers(): string[] {
|
|
18
|
-
// TODO use project layerCount
|
|
19
|
-
return ["top", "inner1", "inner2", "bottom"]
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
getPcbSize(): { width: number; height: number } {
|
|
23
|
-
const { _parsedProps: props } = this
|
|
24
|
-
if (props.shape === "circle") {
|
|
25
|
-
return { width: props.outerDiameter, height: props.outerDiameter }
|
|
26
|
-
}
|
|
27
|
-
if (props.shape === "oval") {
|
|
28
|
-
return { width: props.outerWidth, height: props.outerHeight }
|
|
29
|
-
}
|
|
30
|
-
throw new Error(
|
|
31
|
-
`getPcbSize for shape "${(props as any).shape}" not implemented for ${this.componentName}`,
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
_getPcbCircuitJsonBounds(): {
|
|
36
|
-
center: { x: number; y: number }
|
|
37
|
-
bounds: { left: number; top: number; right: number; bottom: number }
|
|
38
|
-
width: number
|
|
39
|
-
height: number
|
|
40
|
-
} {
|
|
41
|
-
const { db } = this.root!
|
|
42
|
-
const platedHole = db.pcb_plated_hole.get(this.pcb_plated_hole_id!)!
|
|
43
|
-
const size = this.getPcbSize()
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
center: { x: platedHole.x, y: platedHole.y },
|
|
47
|
-
bounds: {
|
|
48
|
-
left: platedHole.x - size.width / 2,
|
|
49
|
-
top: platedHole.y - size.height / 2,
|
|
50
|
-
right: platedHole.x + size.width / 2,
|
|
51
|
-
bottom: platedHole.y + size.height / 2,
|
|
52
|
-
},
|
|
53
|
-
width: size.width,
|
|
54
|
-
height: size.height,
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
_setPositionFromLayout(newCenter: { x: number; y: number }) {
|
|
59
|
-
const { db } = this.root!
|
|
60
|
-
db.pcb_plated_hole.update(this.pcb_plated_hole_id!, {
|
|
61
|
-
x: newCenter.x,
|
|
62
|
-
y: newCenter.y,
|
|
63
|
-
})
|
|
64
|
-
this.matchedPort?._setPositionFromLayout(newCenter)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
doInitialPortMatching(): void {
|
|
68
|
-
const parentPorts = this.getPrimitiveContainer()?.selectAll(
|
|
69
|
-
"port",
|
|
70
|
-
) as Port[]
|
|
71
|
-
|
|
72
|
-
if (!this.props.portHints) {
|
|
73
|
-
return
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
for (const port of parentPorts) {
|
|
77
|
-
if (port.isMatchingAnyOf(this.props.portHints)) {
|
|
78
|
-
this.matchedPort = port
|
|
79
|
-
port.registerMatch(this)
|
|
80
|
-
return
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
doInitialPcbPrimitiveRender(): void {
|
|
86
|
-
const { db } = this.root!
|
|
87
|
-
const { _parsedProps: props } = this
|
|
88
|
-
const position = this._getGlobalPcbPositionBeforeLayout()
|
|
89
|
-
const pcb_component_id =
|
|
90
|
-
this.parent?.pcb_component_id ??
|
|
91
|
-
this.getPrimitiveContainer()?.pcb_component_id!
|
|
92
|
-
if (props.shape === "circle") {
|
|
93
|
-
const pcb_plated_hole = db.pcb_plated_hole.insert({
|
|
94
|
-
pcb_component_id,
|
|
95
|
-
pcb_port_id: this.matchedPort?.pcb_port_id!,
|
|
96
|
-
// @ts-ignore - some issue with @tscircuit/soup union type
|
|
97
|
-
outer_diameter: props.outerDiameter,
|
|
98
|
-
hole_diameter: props.holeDiameter,
|
|
99
|
-
shape: "circle" as const,
|
|
100
|
-
port_hints: this.getNameAndAliases(),
|
|
101
|
-
x: position.x,
|
|
102
|
-
y: position.y,
|
|
103
|
-
layers: ["top", "bottom"],
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
this.pcb_plated_hole_id = pcb_plated_hole.pcb_plated_hole_id
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
doInitialPcbPortAttachment(): void {
|
|
111
|
-
const { db } = this.root!
|
|
112
|
-
db.pcb_plated_hole.update(this.pcb_plated_hole_id!, {
|
|
113
|
-
pcb_port_id: this.matchedPort?.pcb_port_id!,
|
|
114
|
-
})
|
|
115
|
-
}
|
|
116
|
-
}
|