Skip to content

Commit

Permalink
Merge pull request #20 from tscircuit/pullup
Browse files Browse the repository at this point in the history
Opaque Groups initial, Rename Project to Circuit, add pullupFor and pullupTo, add decouplingTo and decouplingFor
  • Loading branch information
seveibar authored Sep 2, 2024
2 parents 9a8011f + d09fda6 commit 521f970
Show file tree
Hide file tree
Showing 15 changed files with 165 additions and 37 deletions.
11 changes: 8 additions & 3 deletions lib/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { isValidElement, type ReactElement } from "react"
import { createInstanceFromReactElement } from "./fiber/create-instance-from-react-element"
import { identity, type Matrix } from "transformation-matrix"

export class Project {
export class Circuit {
rootComponent: PrimitiveComponent | null = null
children: PrimitiveComponent[]
db: SoupUtilObjects
Expand Down Expand Up @@ -115,7 +115,12 @@ export class Project {
return this.rootComponent?.selectAll(selector) ?? []
}

selectOne(selector: string): PrimitiveComponent | null {
return this.rootComponent?.selectOne(selector) ?? null
selectOne(
selector: string,
opts: { type?: "component" | "port" },
): PrimitiveComponent | null {
return this.rootComponent?.selectOne(selector, opts) ?? null
}
}

export const Project = Circuit
50 changes: 47 additions & 3 deletions lib/components/base-components/PrimitiveComponent.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { AnySoupElement, AnySourceComponent } from "@tscircuit/soup"
import type { Project } from "../../Project"
import type { Circuit } from "../../Project"
import type { ZodType } from "zod"
import { z } from "zod"
import { symbols, type SchSymbol, type BaseSymbolName } from "schematic-symbols"
Expand Down Expand Up @@ -40,7 +40,7 @@ export abstract class PrimitiveComponent<
}
}

project: Project | null = null
project: Circuit | null = null
props: z.input<ZodProps>
_parsedProps: z.infer<ZodProps>

Expand All @@ -49,6 +49,21 @@ export abstract class PrimitiveComponent<

externallyAddedAliases: string[]

/**
* An opaque group is a self-contained subcircuit. All the selectors inside
* an opaque group are relative to the group. You can have multiple opaque
* groups and their selectors will not interact with each other (even if the
* components share the same names) unless you explicitly break out some ports
*/
get isOpaqueGroup() {
return (
Boolean(this.props.opaque) ||
// Implied opaque group for top-level group
(this.lowercaseComponentName === "group" &&
this?.parent?.props?.name === "$root")
)
}

source_group_id: string | null = null
source_component_id: string | null = null
schematic_component_id: string | null = null
Expand All @@ -70,7 +85,7 @@ export abstract class PrimitiveComponent<
}
}

setProject(project: Project) {
setProject(project: Circuit) {
this.project = project
for (const c of this.children) {
c.setProject(project)
Expand Down Expand Up @@ -197,6 +212,27 @@ export abstract class PrimitiveComponent<
component.shouldBeRemoved = true
}

getOpaqueGroupSelector(): string {
const name = this._parsedProps.name
const endPart = name
? `${this.lowercaseComponentName}.${name}`
: this.lowercaseComponentName

if (!this.parent) return endPart
if (this.parent.isOpaqueGroup) return endPart
return `${this.parent.getOpaqueGroupSelector()} > ${endPart}`
}

getFullPathSelector(): string {
const name = this._parsedProps.name
const endPart = name
? `${this.lowercaseComponentName}.${name}`
: this.lowercaseComponentName
const parentSelector = this.parent?.getFullPathSelector?.()
if (!parentSelector) return endPart
return `${parentSelector} > ${endPart}`
}

getNameAndAliases(): string[] {
return [
this._parsedProps.name,
Expand Down Expand Up @@ -230,6 +266,14 @@ export abstract class PrimitiveComponent<
return false
}

getOpaqueGroup(): PrimitiveComponent {
if (this.isOpaqueGroup) return this
const group = this.parent?.getOpaqueGroup()
if (!group)
throw new Error("Component is not inside an opaque group (no board?)")
return group
}

selectAll(selector: string): PrimitiveComponent[] {
const parts = selector.trim().split(/\s+/)
let results: PrimitiveComponent[] = [this]
Expand Down
1 change: 1 addition & 0 deletions lib/components/base-components/Renderable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Component, createElement, type ReactElement } from "react"

export const orderedRenderPhases = [
"ReactSubtreesRender", // probably going to be removed b/c subtrees should render instantly
"CreateTracesFromProps",
"SourceRender",
"SourceParentAttachment",
"PortDiscovery", // probably going to be removed b/c port discovery can always be done on prop change
Expand Down
4 changes: 4 additions & 0 deletions lib/components/normal-components/Board.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import { identity, type Matrix } from "transformation-matrix"
export class Board extends NormalComponent<typeof boardProps> {
pcb_board_id: string | null = null

get isOpaqueGroup() {
return true
}

get config() {
return {
zodProps: boardProps,
Expand Down
45 changes: 42 additions & 3 deletions lib/components/normal-components/Capacitor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ledProps } from "@tscircuit/props"
import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
import { capacitorProps, ledProps } from "@tscircuit/props"
import { FTYPE, SYMBOL } from "lib/utils/constants"
import { NormalComponent } from "../base-components/NormalComponent"
import type { capacitance, SourceSimpleCapacitorInput } from "@tscircuit/soup"
import { Trace } from "../primitive-components/Trace"

type PortNames =
| "1"
Expand All @@ -13,12 +14,50 @@ type PortNames =
| "anode"
| "cathode"

export class Capacitor extends NormalComponent<typeof ledProps, PortNames> {
export class Capacitor extends NormalComponent<
typeof capacitorProps,
PortNames
> {
get config() {
return {
// schematicSymbolName: BASE_SYMBOLS.capacitor,
zodProps: ledProps,
sourceFtype: FTYPE.simple_capacitor,
}
}

pin1 = this.portMap.pin1
pin2 = this.portMap.pin2

doInitialCreateTracesFromProps() {
if (this.props.decouplingFor && this.props.decouplingTo) {
this.add(
new Trace({
from: `${this.getOpaqueGroupSelector()} > port.1`,
to: this.props.decouplingFor,
}),
)
this.add(
new Trace({
from: `${this.getOpaqueGroupSelector()} > port.2`,
to: this.props.decouplingTo,
}),
)
}
}

doInitialSourceRender() {
const { db } = this.project!
const { _parsedProps: props } = this
const source_component = db.source_component.insert({
ftype: "simple_capacitor",
name: props.name,
// @ts-ignore
manufacturer_part_number: props.manufacturerPartNumber ?? props.mfn,
supplier_part_numbers: props.supplierPartNumbers,

capacitance: props.capacitance,
} as SourceSimpleCapacitorInput)
this.source_component_id = source_component.source_component_id
}
}
18 changes: 18 additions & 0 deletions lib/components/normal-components/Resistor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { resistorProps } from "@tscircuit/props"
import type { PassivePorts, Ftype, BaseSymbolName } from "lib/utils/constants"
import { NormalComponent } from "../base-components/NormalComponent"
import type { SourceSimpleResistorInput } from "@tscircuit/soup"
import { z } from "zod"
import { Trace } from "../primitive-components/Trace"

export class Resistor extends NormalComponent<
typeof resistorProps,
Expand All @@ -18,6 +20,22 @@ export class Resistor extends NormalComponent<
pin1 = this.portMap.pin1
pin2 = this.portMap.pin2

doInitialCreateTracesFromProps() {
if (this.props.pullupFor && this.props.pullupTo) {
this.add(
new Trace({
from: `${this.getOpaqueGroupSelector()} > port.1`,
to: this.props.pullupFor,
}),
)
this.add(
new Trace({
from: `${this.getOpaqueGroupSelector()} > port.2`,
to: this.props.pullupTo,
}),
)
}
}
doInitialSourceRender() {
const { db } = this.project!
const { _parsedProps: props } = this
Expand Down
22 changes: 17 additions & 5 deletions lib/components/primitive-components/Trace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,25 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {

const ports = portSelectors.map((selector) => ({
selector,
port: parent.selectOne(selector, { type: "port" }) as Port,
port:
(this.getOpaqueGroup().selectOne(selector, { type: "port" }) as Port) ??
null,
}))

for (const { selector, port } of ports) {
if (!port) {
const parentSelector = selector.replace(/\>.*$/, "")
const targetComponent = parent.selectOne(parentSelector)
const targetComponent = this.getOpaqueGroup().selectOne(parentSelector)
if (!targetComponent) {
this.renderError(`Could not find port for selector "${selector}"`)
} else {
this.renderError(
`Could not find port for selector "${selector}"\nsearched component ${targetComponent.getString()}, which has ports:${targetComponent.children
`Could not find port for selector "${selector}"\nsearched component ${targetComponent.getString()}, which has ports: ${targetComponent.children
.filter((c) => c.componentName === "Port")
.map((c) => ` ${c.getString()}`)
.join("\n")}`,
.map(
(c) => `${c.getString()}(${c.getNameAndAliases().join(",")})`,
)
.join(" & ")}`,
)
}
}
Expand Down Expand Up @@ -173,6 +177,14 @@ export class Trace extends PrimitiveComponent<typeof traceProps> {
const { solution } = autoroute(pcbElements.concat([source_trace]))
// TODO for some reason, the solution gets duplicated inside ijump-astar
const inputPcbTrace = solution[0]

if (!inputPcbTrace) {
// TODO render error indicating we could not find a route
console.log(
`Failed to find route ffrom ${ports[0].port} to ${ports[1].port} render error!`,
)
return
}
const pcb_trace = db.pcb_trace.insert(inputPcbTrace as any)

this.pcb_trace_id = pcb_trace.pcb_trace_id
Expand Down
2 changes: 1 addition & 1 deletion tests/__snapshots__/example1.snap.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions tests/components/base-components/select-methods.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { it, expect } from "bun:test"
import { Project } from "lib/Project"
import { Circuit } from "lib/Project"
import "lib/register-catalogue"

it("should correctly use selectAll and selectOne methods", () => {
const project = new Project()
const project = new Circuit()

project.add(
<board width="10mm" height="10mm">
Expand Down
2 changes: 1 addition & 1 deletion tests/components/normal-components/chip-aliases.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { it, expect } from "bun:test"
import { Project } from "lib/Project"
import { Circuit } from "lib/Project"
import { Chip } from "lib/components/normal-components/Chip"
import "lib/register-catalogue"
import { getTestFixture } from "tests/fixtures/get-test-fixture"
Expand Down
2 changes: 1 addition & 1 deletion tests/components/normal-components/chip.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { it, expect } from "bun:test"
import { Project } from "lib/Project"
import { Circuit } from "lib/Project"
import { Chip } from "lib/components/normal-components/Chip"
import "lib/register-catalogue"
import { getTestFixture } from "tests/fixtures/get-test-fixture"
Expand Down
26 changes: 15 additions & 11 deletions tests/examples/example1.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { test, expect } from "bun:test"
import { getTestFixture } from "tests/fixtures/get-test-fixture"

test("example1", async () => {
const { project, logSoup } = getTestFixture()
project.add(
<board width="10mm" height="10mm">
const { circuit, logSoup } = getTestFixture()
circuit.add(
<board width="12mm" height="10mm">
<chip
name="U1"
footprint="soic8"
Expand All @@ -17,20 +17,22 @@ test("example1", async () => {
footprint="0402"
resistance="10k"
pullupFor=".U1 port.2"
pullupTo="net.5v"
pcbX={4}
// pullupTo="net.5v"
pullupTo=".J1 pin.1"
pcbX={-4}
pcbY={0}
pcbRotation={90}
pcbRotation={-90}
/>
<capacitor
name="C1"
capacitance="10uF"
footprint="0603"
decouplingFor=".U1 port.PWR"
decouplingTo="net.GND"
pcbX={-5}
// decouplingTo="net.GND"
decouplingTo=".J1 pin.4"
pcbX={4}
pcbY={0}
pcbRotation={90}
pcbRotation={-90}
/>
<jumper pcbX={0} pcbY={-4} name="J1" footprint="pinrow4" />

Expand All @@ -41,10 +43,12 @@ test("example1", async () => {
</board>,
)

project.render()
circuit.render()

await logSoup("example1")

await expect(
project.getSvg({
circuit.getSvg({
view: "pcb",
layer: "top",
}),
Expand Down
6 changes: 3 additions & 3 deletions tests/fixtures/extend-expect-circuit-snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as fs from "node:fs"
import * as path from "node:path"
import looksSame from "looks-same"
import type { AnySoupElement } from "@tscircuit/soup"
import { Project } from "lib/Project"
import { Circuit } from "lib/Project"

async function saveSnapshotOfSoup({
soup,
Expand Down Expand Up @@ -77,7 +77,7 @@ expect.extend({
): Promise<MatcherResult> {
return saveSnapshotOfSoup({
soup:
received instanceof Project
received instanceof Circuit
? received.getSoup()
: (received as AnySoupElement[]),
testPath: args[0],
Expand All @@ -95,7 +95,7 @@ expect.extend({
): Promise<MatcherResult> {
return saveSnapshotOfSoup({
soup:
received instanceof Project
received instanceof Circuit
? received.getSoup()
: (received as AnySoupElement[]),
testPath: args[0],
Expand Down
Loading

0 comments on commit 521f970

Please sign in to comment.