@tscircuit/core 0.0.30 → 0.0.32

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.
@@ -63,6 +63,18 @@ export abstract class PrimitiveComponent<
63
63
  )
64
64
  }
65
65
 
66
+ /**
67
+ * A primitive container is a component that contains one or more ports and
68
+ * primitive components that are designed to interact.
69
+ *
70
+ * For example a resistor contains ports and smtpads that interact, so the
71
+ * resistor is a primitive container. Inside a primitive container, the ports
72
+ * and pads are likely to reference each other and look for eachother during
73
+ * the port matching phase.
74
+ *
75
+ */
76
+ isPrimitiveContainer = false
77
+
66
78
  source_group_id: string | null = null
67
79
  source_component_id: string | null = null
68
80
  schematic_component_id: string | null = null
@@ -120,9 +132,9 @@ export abstract class PrimitiveComponent<
120
132
  * components, including this component's translation and rotation.
121
133
  *
122
134
  * This is used to compute this component's position as well as all children
123
- * components positions
135
+ * components positions before layout is applied
124
136
  */
125
- computePcbGlobalTransform(): Matrix {
137
+ _computePcbGlobalTransformBeforeLayout(): Matrix {
126
138
  const { _parsedProps: props } = this
127
139
  const manualPlacement =
128
140
  this.getSubcircuit()._getManualPlacementForComponent(this)
@@ -134,7 +146,7 @@ export abstract class PrimitiveComponent<
134
146
  this.props.pcbY === undefined
135
147
  ) {
136
148
  return compose(
137
- this.parent?.computePcbGlobalTransform() ?? identity(),
149
+ this.parent?._computePcbGlobalTransformBeforeLayout() ?? identity(),
138
150
  compose(
139
151
  translate(manualPlacement.x, manualPlacement.y),
140
152
  rotate(((props.pcbRotation ?? 0) * Math.PI) / 180),
@@ -143,11 +155,46 @@ export abstract class PrimitiveComponent<
143
155
  }
144
156
 
145
157
  return compose(
146
- this.parent?.computePcbGlobalTransform() ?? identity(),
158
+ this.parent?._computePcbGlobalTransformBeforeLayout() ?? identity(),
147
159
  this.computePcbPropsTransform(),
148
160
  )
149
161
  }
150
162
 
163
+ getPrimitiveContainer(): PrimitiveComponent | null {
164
+ if (this.isPrimitiveContainer) return this
165
+ return this.parent?.getPrimitiveContainer?.() ?? null
166
+ }
167
+
168
+ /**
169
+ * Compute the bounds of this component the circuit json elements associated
170
+ * with it.
171
+ */
172
+ _getCircuitJsonBounds(): {
173
+ center: { x: number; y: number }
174
+ bounds: { left: number; top: number; right: number; bottom: number }
175
+ width: number
176
+ height: number
177
+ } {
178
+ return {
179
+ center: { x: 0, y: 0 },
180
+ bounds: { left: 0, top: 0, right: 0, bottom: 0 },
181
+ width: 0,
182
+ height: 0,
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Set the position of this component from the layout solver. This method
188
+ * should operate using CircuitJson associated with this component, like
189
+ * _getCircuitJsonBounds it can be called multiple times as different
190
+ * parents apply layout to their children.
191
+ */
192
+ _setPositionFromLayout(newCenter: { x: number; y: number }) {
193
+ throw new Error(
194
+ `_setPositionFromLayout not implemented for ${this.componentName}`,
195
+ )
196
+ }
197
+
151
198
  /**
152
199
  * Computes a transformation matrix from the props of this component for
153
200
  * schematic components
@@ -200,7 +247,7 @@ export abstract class PrimitiveComponent<
200
247
  for (const position of placementConfig.positions) {
201
248
  if (isMatchingSelector(component, position.selector)) {
202
249
  const center = applyToPoint(
203
- this.computePcbGlobalTransform(),
250
+ this._computePcbGlobalTransformBeforeLayout(),
204
251
  position.center,
205
252
  )
206
253
  return center
@@ -210,11 +257,14 @@ export abstract class PrimitiveComponent<
210
257
  return null
211
258
  }
212
259
 
213
- getGlobalPcbPosition(): { x: number; y: number } {
214
- return applyToPoint(this.computePcbGlobalTransform(), { x: 0, y: 0 })
260
+ _getGlobalPcbPositionBeforeLayout(): { x: number; y: number } {
261
+ return applyToPoint(this._computePcbGlobalTransformBeforeLayout(), {
262
+ x: 0,
263
+ y: 0,
264
+ })
215
265
  }
216
266
 
217
- getGlobalSchematicPosition(): { x: number; y: number } {
267
+ _getGlobalSchematicPositionBeforeLayout(): { x: number; y: number } {
218
268
  return applyToPoint(this.computeSchematicGlobalTransform(), { x: 0, y: 0 })
219
269
  }
220
270
 
@@ -2,13 +2,13 @@ import type { PCBPlacementError, PCBTraceError } from "@tscircuit/soup"
2
2
  import { Component, createElement, type ReactElement } from "react"
3
3
 
4
4
  export const orderedRenderPhases = [
5
- "ReactSubtreesRender", // probably going to be removed b/c subtrees should render instantly
5
+ "ReactSubtreesRender",
6
6
  "InitializePortsFromChildren",
7
7
  "CreateNetsFromProps",
8
8
  "CreateTracesFromProps",
9
9
  "SourceRender",
10
10
  "SourceParentAttachment",
11
- "PortDiscovery", // probably going to be removed b/c port discovery can always be done on prop change
11
+ "PortDiscovery",
12
12
  "PortMatching",
13
13
  "SourceTraceRender",
14
14
  "SchematicComponentRender",
@@ -16,8 +16,9 @@ export const orderedRenderPhases = [
16
16
  "SchematicPortRender",
17
17
  "SchematicTraceRender",
18
18
  "PcbComponentRender",
19
- "PcbPortRender",
20
19
  "PcbPrimitiveRender",
20
+ "PcbFootprintLayout",
21
+ "PcbPortRender",
21
22
  "PcbParentAttachment",
22
23
  "PcbLayout",
23
24
  "PcbTraceRender",
@@ -16,3 +16,5 @@ export { Chip } from "./normal-components/Chip"
16
16
  export { Jumper } from "./normal-components/Jumper"
17
17
  export { SilkscreenPath } from "./primitive-components/SilkscreenPath"
18
18
  export { PlatedHole } from "./primitive-components/PlatedHole"
19
+ export { Constraint } from "./primitive-components/Constraint"
20
+ export { Hole } from "./primitive-components/Hole"
@@ -42,7 +42,7 @@ export class Board extends Group<typeof boardProps> {
42
42
  this.pcb_board_id = null
43
43
  }
44
44
 
45
- computePcbGlobalTransform(): Matrix {
45
+ _computePcbGlobalTransformBeforeLayout(): Matrix {
46
46
  return identity()
47
47
  }
48
48
  }
@@ -0,0 +1,49 @@
1
+ import { constraintProps } from "@tscircuit/props"
2
+ import { z } from "zod"
3
+ import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
4
+
5
+ export class Constraint extends PrimitiveComponent<typeof constraintProps> {
6
+ get config() {
7
+ return {
8
+ zodProps: constraintProps,
9
+ }
10
+ }
11
+
12
+ constructor(props: z.input<typeof constraintProps>) {
13
+ super(props)
14
+ if ("xdist" in props || "ydist" in props) {
15
+ if (!("edgeToEdge" in props) && !("centerToCenter" in props)) {
16
+ throw new Error(
17
+ "edgeToEdge, centerToCenter (or from*/to* props) must be set for xdist or ydist for <constraint />",
18
+ )
19
+ }
20
+ }
21
+ }
22
+
23
+ _getAllReferencedComponents(): {
24
+ componentsWithSelectors: Array<{
25
+ component: PrimitiveComponent<any>
26
+ selector: string
27
+ }>
28
+ } {
29
+ const componentsWithSelectors: Array<{
30
+ component: PrimitiveComponent<any>
31
+ selector: string
32
+ }> = []
33
+
34
+ for (const key of ["left", "right", "top", "bottom"]) {
35
+ if (key in this._parsedProps) {
36
+ const selector = (this._parsedProps as any)[key] as string
37
+ const component = this.getSubcircuit().selectOne(selector)
38
+ if (component) {
39
+ componentsWithSelectors.push({
40
+ selector,
41
+ component,
42
+ })
43
+ }
44
+ }
45
+ }
46
+
47
+ return { componentsWithSelectors }
48
+ }
49
+ }
@@ -1,4 +1,161 @@
1
1
  import type { footprintProps } from "@tscircuit/props"
2
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"
3
6
 
4
- export class Footprint extends PrimitiveComponent<typeof footprintProps> {}
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 }) => ({
27
+ component,
28
+ selector,
29
+ bounds: component._getCircuitJsonBounds(),
30
+ }))
31
+
32
+ function getComponentDetails(selector: string) {
33
+ return involvedComponents.find(({ selector: s }) => s === selector)
34
+ }
35
+
36
+ const solver = new kiwi.Solver()
37
+
38
+ const kVars: { [varName: string]: kiwi.Variable } = {}
39
+ function getKVar(name: string) {
40
+ if (!(name in kVars)) {
41
+ kVars[name] = new kiwi.Variable(name)
42
+ }
43
+ return kVars[name]
44
+ }
45
+
46
+ // 1. Create kiwi variables to represent each component's center
47
+ for (const { selector, bounds } of involvedComponents) {
48
+ const kvx = getKVar(`${selector}_x`)
49
+ const kvy = getKVar(`${selector}_y`)
50
+ solver.addEditVariable(kvx, kiwi.Strength.weak)
51
+ solver.addEditVariable(kvy, kiwi.Strength.weak)
52
+ solver.suggestValue(kvx, bounds.center.x)
53
+ solver.suggestValue(kvy, bounds.center.y)
54
+ }
55
+
56
+ // 2. Add kiwi constraints using the parsed constraint properties
57
+ for (const constraint of constraints) {
58
+ const props = constraint._parsedProps
59
+
60
+ if ("xdist" in props) {
61
+ const { xdist, left, right, edgeToEdge, centerToCenter } = props
62
+ const leftVar = getKVar(`${left}_x`)
63
+ const rightVar = getKVar(`${right}_x`)
64
+ const leftBounds = getComponentDetails(left)?.bounds!
65
+ const rightBounds = getComponentDetails(right)?.bounds!
66
+
67
+ if (centerToCenter) {
68
+ // right - left = xdist
69
+ const expr = new kiwi.Expression(rightVar, [-1, leftVar])
70
+ solver.addConstraint(
71
+ new kiwi.Constraint(
72
+ expr,
73
+ kiwi.Operator.Eq,
74
+ props.xdist,
75
+ kiwi.Strength.required,
76
+ ),
77
+ )
78
+ } else if (edgeToEdge) {
79
+ // rightEdge - leftEdge = xdist
80
+ // right + rightBounds.width/2 - left - leftBounds.width/2 = xdist
81
+ const expr = new kiwi.Expression(
82
+ rightVar,
83
+ rightBounds.width / 2,
84
+ [-1, leftVar],
85
+ -leftBounds.width / 2,
86
+ )
87
+ solver.addConstraint(
88
+ new kiwi.Constraint(
89
+ expr,
90
+ kiwi.Operator.Eq,
91
+ props.xdist,
92
+ kiwi.Strength.required,
93
+ ),
94
+ )
95
+ }
96
+ }
97
+
98
+ // 3. Solve the system of equations
99
+ solver.updateVariables()
100
+ if (debug.enabled) {
101
+ console.log("Solution to layout constraints:")
102
+ console.table(
103
+ Object.entries(kVars).map(([key, kvar]) => ({
104
+ var: key,
105
+ val: kvar.value(),
106
+ })),
107
+ )
108
+ }
109
+
110
+ // 3.1 Compute the global offset. There are different ways to do this:
111
+ // - If any component has a fixed position, then that can be used as the
112
+ // origin to determine the offset of all other components
113
+ // - If no component has a fixed position, then we recenter everything
114
+ // using the new bounds of all the involved components
115
+
116
+ // TODO determine if there's a fixed component
117
+
118
+ // Determine the new bounds all the involved components and compute the
119
+ // bounds of this footprint
120
+ const bounds = {
121
+ left: Infinity,
122
+ right: -Infinity,
123
+ top: -Infinity,
124
+ bottom: Infinity,
125
+ }
126
+ for (const {
127
+ selector,
128
+ bounds: { width, height },
129
+ } of involvedComponents) {
130
+ const kvx = getKVar(`${selector}_x`)
131
+ const kvy = getKVar(`${selector}_y`)
132
+
133
+ const newLeft = kvx.value() - width / 2
134
+ const newRight = kvx.value() + width / 2
135
+ const newTop = kvy.value() + height / 2
136
+ const newBottom = kvy.value() - height / 2
137
+
138
+ bounds.left = Math.min(bounds.left, newLeft)
139
+ bounds.right = Math.max(bounds.right, newRight)
140
+ bounds.top = Math.max(bounds.top, newTop)
141
+ bounds.bottom = Math.min(bounds.bottom, newBottom)
142
+ }
143
+
144
+ // Compute the global offset, we can use this to recenter each component
145
+ const globalOffset = {
146
+ x: -(bounds.right + bounds.left) / 2,
147
+ y: -(bounds.top + bounds.bottom) / 2,
148
+ }
149
+
150
+ // 4. Update the component positions
151
+ for (const { component, selector } of involvedComponents) {
152
+ const kvx = getKVar(`${selector}_x`)
153
+ const kvy = getKVar(`${selector}_y`)
154
+ component._setPositionFromLayout({
155
+ x: kvx.value() + globalOffset.x,
156
+ y: kvy.value() + globalOffset.y,
157
+ })
158
+ }
159
+ }
160
+ }
161
+ }
@@ -0,0 +1,36 @@
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.holeDiameter, height: props.holeDiameter }
18
+ }
19
+
20
+ doInitialPcbPrimitiveRender(): void {
21
+ const { db } = this.root!
22
+ const { _parsedProps: props } = this
23
+ const position = this._getGlobalPcbPositionBeforeLayout()
24
+
25
+ const pcb_hole: PCBHole = {
26
+ type: "pcb_hole",
27
+ hole_shape: "round",
28
+ hole_diameter: props.holeDiameter,
29
+ x: position.x,
30
+ y: position.y,
31
+ }
32
+
33
+ const inserted_hole = db.pcb_hole.insert(pcb_hole)
34
+ // this.pcb_hole_id = inserted_hole.pcb_hole_id!
35
+ }
36
+ }
@@ -109,10 +109,10 @@ export class Net extends PrimitiveComponent<typeof netProps> {
109
109
  for (const [A, B] of islandPairs) {
110
110
  // Find two closest ports on the island
111
111
  const Apositions: Array<{ x: number; y: number }> = A.ports.map((port) =>
112
- port.getGlobalPcbPosition(),
112
+ port._getGlobalPcbPositionBeforeLayout(),
113
113
  )
114
114
  const Bpositions: Array<{ x: number; y: number }> = B.ports.map((port) =>
115
- port.getGlobalPcbPosition(),
115
+ port._getGlobalPcbPositionBeforeLayout(),
116
116
  )
117
117
 
118
118
  let closestDist = Infinity
@@ -49,7 +49,7 @@ export class PlatedHole extends PrimitiveComponent<typeof platedHoleProps> {
49
49
  const { db } = this.root!
50
50
  const { _parsedProps: props } = this
51
51
  if (!props.portHints) return
52
- const position = this.getGlobalPcbPosition()
52
+ const position = this._getGlobalPcbPositionBeforeLayout()
53
53
  if (props.shape === "circle") {
54
54
  const plated_hole_input: PCBPlatedHoleInput = {
55
55
  pcb_component_id: this.parent?.pcb_component_id!,
@@ -32,7 +32,7 @@ export class Port extends PrimitiveComponent<typeof portProps> {
32
32
  this.matchedComponents = []
33
33
  }
34
34
 
35
- getGlobalPcbPosition(): { x: number; y: number } {
35
+ _getGlobalPcbPositionBeforeLayout(): { x: number; y: number } {
36
36
  const matchedPcbElm = this.matchedComponents.find((c) => c.isPcbPrimitive)
37
37
 
38
38
  if (!matchedPcbElm) {
@@ -41,10 +41,10 @@ export class Port extends PrimitiveComponent<typeof portProps> {
41
41
  )
42
42
  }
43
43
 
44
- return matchedPcbElm?.getGlobalPcbPosition() ?? { x: 0, y: 0 }
44
+ return matchedPcbElm?._getGlobalPcbPositionBeforeLayout() ?? { x: 0, y: 0 }
45
45
  }
46
46
 
47
- getGlobalSchematicPosition(): { x: number; y: number } {
47
+ _getGlobalSchematicPositionBeforeLayout(): { x: number; y: number } {
48
48
  if (!this.schematicSymbolPortDef) {
49
49
  return applyToPoint(this.parent!.computeSchematicGlobalTransform(), {
50
50
  x: 0,
@@ -146,11 +146,6 @@ export class Port extends PrimitiveComponent<typeof portProps> {
146
146
  this.source_component_id = this.parent?.source_component_id
147
147
  }
148
148
 
149
- /**
150
- * For PcbPorts, we use the parent attachment phase to determine where to place
151
- * the pcb_port (prior to this phase, the smtpad/platedhole isn't guaranteed
152
- * to exist)
153
- */
154
149
  doInitialPcbPortRender(): void {
155
150
  const { db } = this.root!
156
151
  const { matchedComponents } = this
@@ -173,19 +168,19 @@ export class Port extends PrimitiveComponent<typeof portProps> {
173
168
 
174
169
  const pcbMatch: any = pcbMatches[0]
175
170
 
176
- if ("getGlobalPcbPosition" in pcbMatch) {
171
+ if ("_getCircuitJsonBounds" in pcbMatch) {
177
172
  const pcb_port = db.pcb_port.insert({
178
173
  pcb_component_id: this.parent?.pcb_component_id!,
179
174
  layers: ["top"],
180
175
 
181
- ...pcbMatch.getGlobalPcbPosition(),
176
+ ...pcbMatch._getCircuitJsonBounds().center,
182
177
 
183
178
  source_port_id: this.source_port_id!,
184
179
  })
185
180
  this.pcb_port_id = pcb_port.pcb_port_id
186
181
  } else {
187
182
  throw new Error(
188
- `${pcbMatch.getString()} does not have a getGlobalPcbPosition method (needed for pcb_port placement)`,
183
+ `${pcbMatch.getString()} does not have a _getGlobalPcbPositionBeforeLayout method (needed for pcb_port placement)`,
189
184
  )
190
185
  }
191
186
  }
@@ -196,8 +191,8 @@ export class Port extends PrimitiveComponent<typeof portProps> {
196
191
 
197
192
  if (!this.parent) return
198
193
 
199
- const center = this.getGlobalSchematicPosition()
200
- const parentCenter = this.parent?.getGlobalSchematicPosition()
194
+ const center = this._getGlobalSchematicPositionBeforeLayout()
195
+ const parentCenter = this.parent?._getGlobalSchematicPositionBeforeLayout()
201
196
 
202
197
  this.facingDirection = getRelativeDirection(parentCenter, center)
203
198
 
@@ -24,7 +24,7 @@ export class SilkscreenPath extends PrimitiveComponent<
24
24
  )
25
25
  }
26
26
 
27
- const transform = this.computePcbGlobalTransform()
27
+ const transform = this._computePcbGlobalTransformBeforeLayout()
28
28
 
29
29
  const pcb_silkscreen_path = db.pcb_silkscreen_path.insert({
30
30
  pcb_component_id: this.parent?.pcb_component_id!,
@@ -32,8 +32,8 @@ export class SmtPad extends PrimitiveComponent<typeof smtPadProps> {
32
32
  }
33
33
 
34
34
  doInitialPortMatching(): void {
35
- const parentPorts = (this.parent?.children ?? []).filter(
36
- (c) => c.componentName === "Port",
35
+ const parentPorts = this.getPrimitiveContainer()?.selectAll(
36
+ "port",
37
37
  ) as Port[]
38
38
 
39
39
  if (!this.props.portHints) {
@@ -53,8 +53,10 @@ export class SmtPad extends PrimitiveComponent<typeof smtPadProps> {
53
53
  const { db } = this.root!
54
54
  const { _parsedProps: props } = this
55
55
  if (!props.portHints) return
56
- const position = this.getGlobalPcbPosition()
57
- const decomposedMat = decomposeTSR(this.computePcbGlobalTransform())
56
+ const position = this._getGlobalPcbPositionBeforeLayout()
57
+ const decomposedMat = decomposeTSR(
58
+ this._computePcbGlobalTransformBeforeLayout(),
59
+ )
58
60
  const isRotated90 =
59
61
  Math.abs(decomposedMat.rotation.angle * (180 / Math.PI) - 90) < 0.01
60
62
  let pcb_smtpad: PCBSMTPad | null = null
@@ -94,4 +96,52 @@ export class SmtPad extends PrimitiveComponent<typeof smtPadProps> {
94
96
  this.pcb_smtpad_id = pcb_smtpad.pcb_smtpad_id
95
97
  }
96
98
  }
99
+
100
+ _getCircuitJsonBounds(): {
101
+ center: { x: number; y: number }
102
+ bounds: { left: number; top: number; right: number; bottom: number }
103
+ width: number
104
+ height: number
105
+ } {
106
+ const { db } = this.root!
107
+ const smtpad = db.pcb_smtpad.get(this.pcb_smtpad_id!)!
108
+
109
+ if (smtpad.shape === "rect") {
110
+ return {
111
+ center: { x: smtpad.x, y: smtpad.y },
112
+ bounds: {
113
+ left: smtpad.x - smtpad.width / 2,
114
+ top: smtpad.y - smtpad.height / 2,
115
+ right: smtpad.x + smtpad.width / 2,
116
+ bottom: smtpad.y + smtpad.height / 2,
117
+ },
118
+ width: smtpad.width,
119
+ height: smtpad.height,
120
+ }
121
+ }
122
+ if (smtpad.shape === "circle") {
123
+ return {
124
+ center: { x: smtpad.x, y: smtpad.y },
125
+ bounds: {
126
+ left: smtpad.x - smtpad.radius,
127
+ top: smtpad.y - smtpad.radius,
128
+ right: smtpad.x + smtpad.radius,
129
+ bottom: smtpad.y + smtpad.radius,
130
+ },
131
+ width: smtpad.radius * 2,
132
+ height: smtpad.radius * 2,
133
+ }
134
+ }
135
+ throw new Error(
136
+ `circuitJson bounds calculation not implemented for shape "${(smtpad as any).shape}"`,
137
+ )
138
+ }
139
+
140
+ _setPositionFromLayout(newCenter: { x: number; y: number }) {
141
+ const { db } = this.root!
142
+ db.pcb_smtpad.update(this.pcb_smtpad_id!, {
143
+ x: newCenter.x,
144
+ y: newCenter.y,
145
+ })
146
+ }
97
147
  }
@@ -38,7 +38,7 @@ type PcbRouteObjective =
38
38
  | { layers: string[]; x: number; y: number; via?: boolean }
39
39
 
40
40
  const portToObjective = (port: Port): PcbRouteObjective => {
41
- const portPosition = port.getGlobalPcbPosition()
41
+ const portPosition = port._getGlobalPcbPositionBeforeLayout()
42
42
  return {
43
43
  ...portPosition,
44
44
  layers: port.getAvailablePcbLayers(),
@@ -476,7 +476,7 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
476
476
  for (const { port } of ports) {
477
477
  connection.pointsToConnect.push(
478
478
  projectPointInDirection(
479
- port.getGlobalSchematicPosition(),
479
+ port._getGlobalSchematicPositionBeforeLayout(),
480
480
  port.facingDirection!,
481
481
  0.1501,
482
482
  ),
@@ -46,7 +46,7 @@ export class TraceHint extends PrimitiveComponent<typeof traceHintProps> {
46
46
 
47
47
  if (!offsets) return []
48
48
 
49
- const globalTransform = this.computePcbGlobalTransform()
49
+ const globalTransform = this._computePcbGlobalTransformBeforeLayout()
50
50
 
51
51
  return offsets.map(
52
52
  (offset): RouteHintPoint => ({
@@ -39,6 +39,8 @@ declare global {
39
39
  pcbtrace: Props.PcbTraceProps
40
40
  fabricationnotetext: Props.FabricationNoteTextProps
41
41
  fabricationnotepath: Props.FabricationNotePathProps
42
+ constraint: Props.ConstraintProps
43
+ constrainedlayout: Props.ConstrainedLayoutProps
42
44
  jscad: any
43
45
  }
44
46
  }
@@ -1,10 +1,16 @@
1
1
  export type PointLike =
2
- | { getGlobalPcbPosition: () => { x: number; y: number } }
2
+ | { _getGlobalPcbPositionBeforeLayout: () => { x: number; y: number } }
3
3
  | { x: number; y: number }
4
4
 
5
5
  const getDistance = (a: PointLike, b: PointLike) => {
6
- const aPos = "getGlobalPcbPosition" in a ? a.getGlobalPcbPosition() : a
7
- const bPos = "getGlobalPcbPosition" in b ? b.getGlobalPcbPosition() : b
6
+ const aPos =
7
+ "_getGlobalPcbPositionBeforeLayout" in a
8
+ ? a._getGlobalPcbPositionBeforeLayout()
9
+ : a
10
+ const bPos =
11
+ "_getGlobalPcbPositionBeforeLayout" in b
12
+ ? b._getGlobalPcbPositionBeforeLayout()
13
+ : b
8
14
  return Math.sqrt((aPos.x - bPos.x) ** 2 + (aPos.y - bPos.y) ** 2)
9
15
  }
10
16
 
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.30",
5
+ "version": "0.0.32",
6
6
  "types": "dist/index.d.ts",
7
7
  "main": "dist/index.js",
8
8
  "files": [
@@ -19,10 +19,12 @@
19
19
  "@tscircuit/layout": "^0.0.28",
20
20
  "@tscircuit/log-soup": "^1.0.2",
21
21
  "@types/bun": "latest",
22
+ "@types/debug": "^4.1.12",
22
23
  "@types/react": "^18.3.3",
23
24
  "@types/react-reconciler": "^0.28.8",
24
25
  "bun-match-svg": "0.0.2",
25
26
  "circuit-to-svg": "^0.0.18",
27
+ "debug": "^4.3.6",
26
28
  "howfat": "^0.3.8",
27
29
  "looks-same": "^9.0.1",
28
30
  "tsup": "^8.2.4"
@@ -31,8 +33,9 @@
31
33
  "typescript": "^5.0.0"
32
34
  },
33
35
  "dependencies": {
36
+ "@lume/kiwi": "^0.4.3",
34
37
  "@tscircuit/infgrid-ijump-astar": "^0.0.6",
35
- "@tscircuit/props": "^0.0.52",
38
+ "@tscircuit/props": "^0.0.58",
36
39
  "@tscircuit/soup": "^0.0.58",
37
40
  "@tscircuit/soup-util": "0.0.18",
38
41
  "footprinter": "^0.0.44",