Skip to content

Commit

Permalink
Merge pull request #36 from tscircuit/samex
Browse files Browse the repository at this point in the history
implement sameX/sameY, refactor constraint reference code in prepartion for edge selectors
  • Loading branch information
seveibar authored Sep 6, 2024
2 parents c752725 + 3f4fda1 commit 96d9e8a
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 26 deletions.
Binary file modified bun.lockb
Binary file not shown.
54 changes: 45 additions & 9 deletions lib/components/primitive-components/Constraint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ import { constraintProps } from "@tscircuit/props"
import { z } from "zod"
import { PrimitiveComponent } from "../base-components/PrimitiveComponent"

const edgeSpecifiers = [
"leftedge",
"rightedge",
"topedge",
"bottomedge",
"center",
] as const

export type EdgeSpecifier = (typeof edgeSpecifiers)[number]

export class Constraint extends PrimitiveComponent<typeof constraintProps> {
get config() {
return {
Expand All @@ -13,34 +23,60 @@ export class Constraint extends PrimitiveComponent<typeof constraintProps> {
super(props)
if ("xdist" in props || "ydist" in props) {
if (!("edgeToEdge" in props) && !("centerToCenter" in props)) {
// TODO don't throw an error if the selectors specify an edge
throw new Error(
"edgeToEdge, centerToCenter (or from*/to* props) must be set for xdist or ydist for <constraint />",
`edgeToEdge, centerToCenter must be set for xDist or yDist for ${this}`,
)
}
}
if ("for" in props && props.for.length < 2) {
throw new Error(`"for" must have at least two selectors for ${this}`)
}
}

_getAllReferencedComponents(): {
componentsWithSelectors: Array<{
component: PrimitiveComponent<any>
selector: string
componentSelector: string
edge: EdgeSpecifier | undefined
}>
} {
const componentsWithSelectors: Array<{
component: PrimitiveComponent<any>
selector: string
componentSelector: string
edge: EdgeSpecifier | undefined
}> = []

const subcircuit = this.getSubcircuit()

function addComponentFromSelector(selector: string) {
// TODO this selector has to be modified in case it contains a leftedge,
// center/topedge/rightedge indicator
const component = subcircuit.selectOne(selector)
if (component) {
const maybeEdge = selector.split(" ").pop() as EdgeSpecifier
const edge = edgeSpecifiers.includes(maybeEdge) ? maybeEdge : undefined

componentsWithSelectors.push({
selector,
component,
componentSelector: edge ? selector.replace(` ${edge}`, "") : selector,
edge,
})
}
}

for (const key of ["left", "right", "top", "bottom"]) {
if (key in this._parsedProps) {
const selector = (this._parsedProps as any)[key] as string
const component = this.getSubcircuit().selectOne(selector)
if (component) {
componentsWithSelectors.push({
selector,
component,
})
}
addComponentFromSelector((this._parsedProps as any)[key] as string)
}
}

if ("for" in this._parsedProps) {
for (const selector of this._parsedProps.for) {
addComponentFromSelector(selector)
}
}

Expand Down
53 changes: 44 additions & 9 deletions lib/components/primitive-components/Footprint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,20 @@ export class Footprint extends PrimitiveComponent<typeof footprintProps> {
(constraint) =>
constraint._getAllReferencedComponents().componentsWithSelectors,
)
.map(({ component, selector }) => ({
.map(({ component, selector, componentSelector, edge }) => ({
component,
selector,
componentSelector,
edge,
bounds: component._getCircuitJsonBounds(),
}))

if (involvedComponents.some((c) => c.edge)) {
throw new Error(
"edge constraints not implemented yet for footprint layout, contributions welcome!",
)
}

function getComponentDetails(selector: string) {
return involvedComponents.find(({ selector: s }) => s === selector)
}
Expand Down Expand Up @@ -56,8 +64,8 @@ export class Footprint extends PrimitiveComponent<typeof footprintProps> {
for (const constraint of constraints) {
const props = constraint._parsedProps

if ("xdist" in props) {
const { xdist, left, right, edgeToEdge, centerToCenter } = props
if ("xDist" in props) {
const { xDist, left, right, edgeToEdge, centerToCenter } = props
const leftVar = getKVar(`${left}_x`)
const rightVar = getKVar(`${right}_x`)
const leftBounds = getComponentDetails(left)?.bounds!
Expand All @@ -70,7 +78,7 @@ export class Footprint extends PrimitiveComponent<typeof footprintProps> {
new kiwi.Constraint(
expr,
kiwi.Operator.Eq,
props.xdist,
props.xDist,
kiwi.Strength.required,
),
)
Expand All @@ -87,13 +95,13 @@ export class Footprint extends PrimitiveComponent<typeof footprintProps> {
new kiwi.Constraint(
expr,
kiwi.Operator.Eq,
props.xdist,
props.xDist,
kiwi.Strength.required,
),
)
}
} else if ("ydist" in props) {
const { ydist, top, bottom, edgeToEdge, centerToCenter } = props
} else if ("yDist" in props) {
const { yDist, top, bottom, edgeToEdge, centerToCenter } = props
const topVar = getKVar(`${top}_y`)
const bottomVar = getKVar(`${bottom}_y`)
const topBounds = getComponentDetails(top)?.bounds!
Expand All @@ -108,7 +116,7 @@ export class Footprint extends PrimitiveComponent<typeof footprintProps> {
new kiwi.Constraint(
expr,
kiwi.Operator.Eq,
props.ydist,
props.yDist,
kiwi.Strength.required,
),
)
Expand All @@ -125,11 +133,38 @@ export class Footprint extends PrimitiveComponent<typeof footprintProps> {
new kiwi.Constraint(
expr,
kiwi.Operator.Eq,
props.ydist,
props.yDist,
kiwi.Strength.required,
),
)
}
} else if ("sameY" in props) {
const { for: selectors } = props
if (selectors.length < 2) continue
const vars = selectors.map((selector) => getKVar(`${selector}_y`))
const expr = new kiwi.Expression(...vars.slice(1))

solver.addConstraint(
new kiwi.Constraint(
expr,
kiwi.Operator.Eq,
vars[0],
kiwi.Strength.required,
),
)
} else if ("sameX" in props) {
const { for: selectors } = props
if (selectors.length < 2) continue
const vars = selectors.map((selector) => getKVar(`${selector}_x`))
const expr = new kiwi.Expression(...vars.slice(1))
solver.addConstraint(
new kiwi.Constraint(
expr,
kiwi.Operator.Eq,
vars[0],
kiwi.Strength.required,
),
)
}
}

Expand Down
1 change: 0 additions & 1 deletion lib/components/primitive-components/PlatedHole.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ export class PlatedHole extends PrimitiveComponent<typeof platedHoleProps> {
doInitialPcbPrimitiveRender(): void {
const { db } = this.root!
const { _parsedProps: props } = this
if (!props.portHints) return
const position = this._getGlobalPcbPositionBeforeLayout()
if (props.shape === "circle") {
const pcb_plated_hole = db.pcb_plated_hole.insert({
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"dependencies": {
"@lume/kiwi": "^0.4.3",
"@tscircuit/infgrid-ijump-astar": "^0.0.6",
"@tscircuit/props": "^0.0.61",
"@tscircuit/props": "^0.0.62",
"@tscircuit/soup": "^0.0.66",
"@tscircuit/soup-util": "0.0.18",
"footprinter": "^0.0.44",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 11 additions & 5 deletions tests/components/primitive-components/footprint-layout.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,22 @@ test("footprint layout", () => {
height="1mm"
portHints={["pin2"]}
/>
<hole name="H1" diameter="1mm" />
<constraint pcb edgeToEdge xdist="4mm" left=".pin1" right=".pin2" />
<platedhole
name="H1"
holeDiameter="0.75mm"
outerDiameter="1mm"
shape="circle"
/>
<constraint pcb edgeToEdge xDist="4mm" left=".pin1" right=".pin2" />
<constraint
pcb
centerToCenter
xdist="2.5mm"
xDist="2.5mm"
left=".pin1"
right=".H1"
/>
<constraint centerToCenter ydist="2.5mm" top=".pin1" bottom=".H1" />
<constraint centerToCenter yDist="2.5mm" top=".pin1" bottom=".H1" />
<constraint sameY for={[".pin1", ".pin2"]} />
</footprint>
}
/>
Expand All @@ -56,7 +62,7 @@ test("footprint layout", () => {
expect(pcbPorts[1].x).toBeOneOf([-2.5, 2.5])

// Check hole position
const hole = circuit.db.pcb_hole.list()[0]
const hole = circuit.db.pcb_plated_hole.list()[0]

expect(hole.x).toBeCloseTo(0, 1)
expect(hole.y).toBeCloseTo(-1.25, 1)
Expand Down

0 comments on commit 96d9e8a

Please sign in to comment.