@tscircuit/core 0.0.1
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/README.md +35 -0
- package/dist/index.cjs +5353 -0
- package/index.ts +1 -0
- package/lib/Project.ts +84 -0
- package/lib/components/base-components/NormalComponent.ts +330 -0
- package/lib/components/base-components/PrimitiveComponent.ts +299 -0
- package/lib/components/base-components/Renderable.ts +113 -0
- package/lib/components/index.ts +14 -0
- package/lib/components/normal-components/Board.ts +38 -0
- package/lib/components/normal-components/Capacitor.ts +23 -0
- package/lib/components/normal-components/Diode.ts +27 -0
- package/lib/components/normal-components/Led.ts +27 -0
- package/lib/components/normal-components/Resistor.ts +19 -0
- package/lib/components/primitive-components/Footprint.ts +4 -0
- package/lib/components/primitive-components/Group.ts +10 -0
- package/lib/components/primitive-components/Net.ts +12 -0
- package/lib/components/primitive-components/Port.ts +187 -0
- package/lib/components/primitive-components/SmtPad.ts +80 -0
- package/lib/components/primitive-components/Trace.ts +219 -0
- package/lib/components/primitive-components/TraceHint.ts +4 -0
- package/lib/fiber/catalogue.ts +22 -0
- package/lib/fiber/create-instance-from-react-element.ts +168 -0
- package/lib/fiber/intrinsic-jsx.ts +43 -0
- package/lib/index.ts +5 -0
- package/lib/register-catalogue.ts +3 -0
- package/lib/utils/autorouting/SimpleRouteJson.ts +37 -0
- package/lib/utils/autorouting/computeObstacleBounds.ts +10 -0
- package/lib/utils/constants.ts +35 -0
- package/lib/utils/createComponentsFromSoup.ts +86 -0
- package/lib/utils/get-relative-direction.ts +14 -0
- package/lib/utils/getPortFromHints.ts +10 -0
- package/lib/utils/projectPointInDirection.ts +18 -0
- package/lib/utils/selector-matching/index.ts +62 -0
- package/package.json +40 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { PCBPlacementError, PCBTraceError } from "@tscircuit/soup"
|
|
2
|
+
import { Component, createElement, type ReactElement } from "react"
|
|
3
|
+
|
|
4
|
+
export const orderedRenderPhases = [
|
|
5
|
+
"ReactSubtreesRender", // probably going to be removed b/c subtrees should render instantly
|
|
6
|
+
"SourceRender",
|
|
7
|
+
"SourceParentAttachment",
|
|
8
|
+
"PortDiscovery", // probably going to be removed b/c port discovery can always be done on prop change
|
|
9
|
+
"PortMatching",
|
|
10
|
+
"SourceTraceRender",
|
|
11
|
+
"SchematicComponentRender",
|
|
12
|
+
"SchematicLayout",
|
|
13
|
+
"SchematicPortRender",
|
|
14
|
+
"SchematicTraceRender",
|
|
15
|
+
"PcbComponentRender",
|
|
16
|
+
"PcbPortRender",
|
|
17
|
+
"PcbPrimitiveRender",
|
|
18
|
+
"PcbParentAttachment",
|
|
19
|
+
"PcbLayout",
|
|
20
|
+
"PcbTraceRender",
|
|
21
|
+
"CadModelRender",
|
|
22
|
+
"PcbAnalysis",
|
|
23
|
+
] as const
|
|
24
|
+
|
|
25
|
+
export type RenderPhase = (typeof orderedRenderPhases)[number]
|
|
26
|
+
|
|
27
|
+
export type RenderPhaseFn<K extends RenderPhase = RenderPhase> =
|
|
28
|
+
| `doInitial${K}`
|
|
29
|
+
| `update${K}`
|
|
30
|
+
| `remove${K}`
|
|
31
|
+
|
|
32
|
+
export type RenderPhaseStates = Record<RenderPhase, { initialized: boolean }>
|
|
33
|
+
|
|
34
|
+
export type RenderPhaseFunctions = {
|
|
35
|
+
[T in RenderPhaseFn]?: () => void
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type IRenderable = RenderPhaseFunctions & {
|
|
39
|
+
renderPhaseStates: RenderPhaseStates
|
|
40
|
+
runRenderPhase(phase: RenderPhase): void
|
|
41
|
+
runRenderPhaseForChildren(phase: RenderPhase): void
|
|
42
|
+
shouldBeRemoved: boolean
|
|
43
|
+
children: IRenderable[]
|
|
44
|
+
runRenderCycle(): void
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let globalRenderCounter = 0
|
|
48
|
+
export abstract class Renderable implements IRenderable {
|
|
49
|
+
renderPhaseStates: RenderPhaseStates
|
|
50
|
+
shouldBeRemoved = false
|
|
51
|
+
children: IRenderable[]
|
|
52
|
+
|
|
53
|
+
/** PCB-only SMTPads, PlatedHoles, Holes, Silkscreen elements etc. */
|
|
54
|
+
isPcbPrimitive = false
|
|
55
|
+
/** Schematic-only, lines, boxes, indicators etc. */
|
|
56
|
+
isSchematicPrimitive = false
|
|
57
|
+
|
|
58
|
+
_renderId: string
|
|
59
|
+
|
|
60
|
+
constructor(props: any) {
|
|
61
|
+
this._renderId = `${globalRenderCounter++}`
|
|
62
|
+
this.children = []
|
|
63
|
+
this.renderPhaseStates = {} as RenderPhaseStates
|
|
64
|
+
for (const phase of orderedRenderPhases) {
|
|
65
|
+
this.renderPhaseStates[phase] = { initialized: false }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
runRenderCycle() {
|
|
70
|
+
for (const renderPhase of orderedRenderPhases) {
|
|
71
|
+
this.runRenderPhaseForChildren(renderPhase)
|
|
72
|
+
this.runRenderPhase(renderPhase)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* This runs all the render methods for a given phase, calling one of:
|
|
78
|
+
* - doInitial*
|
|
79
|
+
* - update*
|
|
80
|
+
* -remove*
|
|
81
|
+
* ...depending on the current state of the component.
|
|
82
|
+
*/
|
|
83
|
+
runRenderPhase(phase: RenderPhase) {
|
|
84
|
+
const isInitialized = this.renderPhaseStates[phase].initialized
|
|
85
|
+
if (!isInitialized && this.shouldBeRemoved) return
|
|
86
|
+
if (this.shouldBeRemoved && isInitialized) {
|
|
87
|
+
;(this as any)?.[`remove${phase}`]?.()
|
|
88
|
+
this.renderPhaseStates[phase].initialized = false
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
if (isInitialized) {
|
|
92
|
+
;(this as any)?.[`update${phase}`]?.()
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
;(this as any)?.[`doInitial${phase}`]?.()
|
|
96
|
+
this.renderPhaseStates[phase].initialized = true
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
runRenderPhaseForChildren(phase: RenderPhase): void {
|
|
100
|
+
for (const child of this.children) {
|
|
101
|
+
child.runRenderPhaseForChildren(phase)
|
|
102
|
+
child.runRenderPhase(phase)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
renderError(message: string | PCBTraceError | PCBPlacementError) {
|
|
107
|
+
// TODO add to render phase error list and try to add position or
|
|
108
|
+
// relationships etc.
|
|
109
|
+
if (typeof message === "string") {
|
|
110
|
+
throw new Error(message)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { NormalComponent } from "./base-components/NormalComponent"
|
|
2
|
+
export { PrimitiveComponent } from "./base-components/PrimitiveComponent"
|
|
3
|
+
export { Renderable, type IRenderable } from "./base-components/Renderable"
|
|
4
|
+
export { Board } from "./normal-components/Board"
|
|
5
|
+
export { Footprint } from "./primitive-components/Footprint"
|
|
6
|
+
export { SmtPad } from "./primitive-components/SmtPad"
|
|
7
|
+
export { Port } from "./primitive-components/Port"
|
|
8
|
+
export { Resistor } from "./normal-components/Resistor"
|
|
9
|
+
export { Led } from "./normal-components/Led"
|
|
10
|
+
export { Capacitor } from "./normal-components/Capacitor"
|
|
11
|
+
export { Net } from "./primitive-components/Net"
|
|
12
|
+
export { Trace } from "./primitive-components/Trace"
|
|
13
|
+
export { TraceHint } from "./primitive-components/TraceHint"
|
|
14
|
+
export { Group } from "./primitive-components/Group"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { boardProps } from "@tscircuit/props"
|
|
2
|
+
import type { z } from "zod"
|
|
3
|
+
import { NormalComponent } from "../base-components/NormalComponent"
|
|
4
|
+
import { identity, type Matrix } from "transformation-matrix"
|
|
5
|
+
|
|
6
|
+
export class Board extends NormalComponent<typeof boardProps> {
|
|
7
|
+
pcb_board_id: string | null = null
|
|
8
|
+
|
|
9
|
+
get config() {
|
|
10
|
+
return {
|
|
11
|
+
zodProps: boardProps,
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
doInitialPcbComponentRender(): void {
|
|
16
|
+
const { db } = this.project!
|
|
17
|
+
const { _parsedProps: props } = this
|
|
18
|
+
|
|
19
|
+
const pcb_board = db.pcb_board.insert({
|
|
20
|
+
center: { x: props.pcbX, y: props.pcbY },
|
|
21
|
+
width: props.width,
|
|
22
|
+
height: props.height,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
this.pcb_board_id = pcb_board.pcb_board_id
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
removePcbComponentRender(): void {
|
|
29
|
+
const { db } = this.project!
|
|
30
|
+
if (!this.pcb_board_id) return
|
|
31
|
+
db.pcb_board.delete(this.pcb_board_id!)
|
|
32
|
+
this.pcb_board_id = null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
computePcbGlobalTransform(): Matrix {
|
|
36
|
+
return identity()
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ledProps } from "@tscircuit/props"
|
|
2
|
+
import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
|
|
3
|
+
import { FTYPE, SYMBOL } from "lib/utils/constants"
|
|
4
|
+
|
|
5
|
+
type PortNames =
|
|
6
|
+
| "1"
|
|
7
|
+
| "2"
|
|
8
|
+
| "pin1"
|
|
9
|
+
| "pin2"
|
|
10
|
+
| "left"
|
|
11
|
+
| "right"
|
|
12
|
+
| "anode"
|
|
13
|
+
| "cathode"
|
|
14
|
+
|
|
15
|
+
export class Capacitor extends PrimitiveComponent<typeof ledProps, PortNames> {
|
|
16
|
+
get config() {
|
|
17
|
+
return {
|
|
18
|
+
// schematicSymbolName: BASE_SYMBOLS.capacitor,
|
|
19
|
+
zodProps: ledProps,
|
|
20
|
+
sourceFtype: FTYPE.simple_capacitor,
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { diodeProps } from "@tscircuit/props"
|
|
2
|
+
import {
|
|
3
|
+
FTYPE,
|
|
4
|
+
SYMBOL,
|
|
5
|
+
type Ftype,
|
|
6
|
+
type TwoPinPorts,
|
|
7
|
+
} from "lib/utils/constants"
|
|
8
|
+
import { Port } from "../primitive-components/Port"
|
|
9
|
+
import { NormalComponent } from "../base-components/NormalComponent"
|
|
10
|
+
|
|
11
|
+
export class Diode extends NormalComponent<typeof diodeProps, TwoPinPorts> {
|
|
12
|
+
pin1 = this.portMap.pin1
|
|
13
|
+
pin2 = this.portMap.pin2
|
|
14
|
+
|
|
15
|
+
get config() {
|
|
16
|
+
return {
|
|
17
|
+
// schematicSymbolName: "diode" as BaseSymbolName,
|
|
18
|
+
zodProps: diodeProps,
|
|
19
|
+
sourceFtype: "simple_diode" as Ftype,
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
initPorts() {
|
|
24
|
+
this.add(new Port({ name: "pin1", aliases: ["1", "pin1"] }))
|
|
25
|
+
this.add(new Port({ name: "pin2", aliases: ["2", "pin2"] }))
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ledProps } from "@tscircuit/props"
|
|
2
|
+
import type {
|
|
3
|
+
BaseSymbolName,
|
|
4
|
+
Ftype,
|
|
5
|
+
PolarizedPassivePorts,
|
|
6
|
+
} from "lib/utils/constants"
|
|
7
|
+
import { NormalComponent } from "../base-components/NormalComponent"
|
|
8
|
+
|
|
9
|
+
export class Led extends NormalComponent<
|
|
10
|
+
typeof ledProps,
|
|
11
|
+
PolarizedPassivePorts
|
|
12
|
+
> {
|
|
13
|
+
get config() {
|
|
14
|
+
return {
|
|
15
|
+
schematicSymbolName: "led" as BaseSymbolName,
|
|
16
|
+
zodProps: ledProps,
|
|
17
|
+
sourceFtype: "simple_diode" as Ftype,
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
pos = this.portMap.pin1
|
|
22
|
+
pin1 = this.portMap.pin1
|
|
23
|
+
anode = this.portMap.pin1
|
|
24
|
+
neg = this.portMap.pin2
|
|
25
|
+
pin2 = this.portMap.pin2
|
|
26
|
+
cathode = this.portMap.pin2
|
|
27
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { resistorProps } from "@tscircuit/props"
|
|
2
|
+
import type { PassivePorts, Ftype, BaseSymbolName } from "lib/utils/constants"
|
|
3
|
+
import { NormalComponent } from "../base-components/NormalComponent"
|
|
4
|
+
|
|
5
|
+
export class Resistor extends NormalComponent<
|
|
6
|
+
typeof resistorProps,
|
|
7
|
+
PassivePorts
|
|
8
|
+
> {
|
|
9
|
+
get config() {
|
|
10
|
+
return {
|
|
11
|
+
schematicSymbolName: "boxresistor" as BaseSymbolName,
|
|
12
|
+
zodProps: resistorProps,
|
|
13
|
+
sourceFtype: "simple_resistor" as Ftype,
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
pin1 = this.portMap.pin1
|
|
18
|
+
pin2 = this.portMap.pin2
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
|
|
2
|
+
import { z } from "zod"
|
|
3
|
+
|
|
4
|
+
export const netProps = z.object({
|
|
5
|
+
name: z.string(),
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
export class Net extends PrimitiveComponent<typeof netProps> {
|
|
9
|
+
getPortSelector() {
|
|
10
|
+
return `net.${this.props.name}`
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import type { PCBSMTPad } from "@tscircuit/soup"
|
|
2
|
+
import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
|
|
3
|
+
import { z } from "zod"
|
|
4
|
+
import { getRelativeDirection } from "lib/utils/get-relative-direction"
|
|
5
|
+
import { symbols, type SchSymbol } from "schematic-symbols"
|
|
6
|
+
import { applyToPoint, compose, translate } from "transformation-matrix"
|
|
7
|
+
|
|
8
|
+
export const portProps = z.object({
|
|
9
|
+
name: z.string().optional(),
|
|
10
|
+
pinNumber: z.number().optional(),
|
|
11
|
+
aliases: z.array(z.string()).optional(),
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
export type PortProps = z.infer<typeof portProps>
|
|
15
|
+
|
|
16
|
+
export class Port extends PrimitiveComponent<typeof portProps> {
|
|
17
|
+
source_port_id: string | null = null
|
|
18
|
+
pcb_port_id: string | null = null
|
|
19
|
+
schematic_port_id: string | null = null
|
|
20
|
+
|
|
21
|
+
schematicSymbolPortDef: SchSymbol["ports"][number] | null = null
|
|
22
|
+
matchedComponents: PrimitiveComponent[]
|
|
23
|
+
facingDirection: "up" | "down" | "left" | "right" | null = null
|
|
24
|
+
|
|
25
|
+
constructor(props: z.input<typeof portProps>) {
|
|
26
|
+
if (!props.name && props.pinNumber) props.name = `pin${props.pinNumber}`
|
|
27
|
+
if (!props.name) {
|
|
28
|
+
throw new Error("Port must have a name or a pinNumber")
|
|
29
|
+
}
|
|
30
|
+
super(props)
|
|
31
|
+
this.matchedComponents = []
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getGlobalPcbPosition(): { x: number; y: number } {
|
|
35
|
+
const matchedPcbElm = this.matchedComponents.find((c) => c.isPcbPrimitive)
|
|
36
|
+
|
|
37
|
+
if (!matchedPcbElm) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`Port ${this} has no matched pcb component, can't get global schematic position`,
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return matchedPcbElm?.getGlobalPcbPosition() ?? { x: 0, y: 0 }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getGlobalSchematicPosition(): { x: number; y: number } {
|
|
47
|
+
if (!this.schematicSymbolPortDef) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Could not find schematic symbol port for port ${this} so couldn't determine port position`,
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const symbol = this.parent?.getSchematicSymbol()
|
|
54
|
+
if (!symbol) throw new Error(`Could not find parent symbol for ${this}`)
|
|
55
|
+
|
|
56
|
+
const transform = compose(
|
|
57
|
+
this.parent!.computeSchematicGlobalTransform(),
|
|
58
|
+
translate(-symbol.center.x, -symbol.center.y),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return applyToPoint(transform, this.schematicSymbolPortDef)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Smtpads and platedholes call this method to register themselves as a match
|
|
66
|
+
* for this port. All the matching is done by primitives other than the Port,
|
|
67
|
+
* but everyone registers themselves as a match with their Port.
|
|
68
|
+
*/
|
|
69
|
+
registerMatch(component: PrimitiveComponent) {
|
|
70
|
+
this.matchedComponents.push(component)
|
|
71
|
+
}
|
|
72
|
+
getNameAndAliases() {
|
|
73
|
+
const { _parsedProps: props } = this
|
|
74
|
+
return Array.from(
|
|
75
|
+
new Set([
|
|
76
|
+
...(props.aliases ?? []),
|
|
77
|
+
props.name,
|
|
78
|
+
...(typeof props.pinNumber === "number"
|
|
79
|
+
? [`pin${props.pinNumber}`, props.pinNumber.toString()]
|
|
80
|
+
: []),
|
|
81
|
+
]),
|
|
82
|
+
) as string[]
|
|
83
|
+
}
|
|
84
|
+
isMatchingPort(port: Port) {
|
|
85
|
+
return this.isMatchingAnyOf(port.getNameAndAliases())
|
|
86
|
+
}
|
|
87
|
+
getPortSelector() {
|
|
88
|
+
return `.${this.parent?.props.name} > port.${this.props.name}`
|
|
89
|
+
// return `#${this.props.id}`
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
doInitialSourceRender(): void {
|
|
93
|
+
const { db } = this.project!
|
|
94
|
+
const { _parsedProps: props } = this
|
|
95
|
+
|
|
96
|
+
const port_hints = this.getNameAndAliases()
|
|
97
|
+
|
|
98
|
+
const source_port = db.source_port.insert({
|
|
99
|
+
name: props.name!,
|
|
100
|
+
pin_number: props.pinNumber,
|
|
101
|
+
port_hints,
|
|
102
|
+
source_component_id: this.parent?.source_component_id!,
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
this.source_port_id = source_port.source_port_id
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
doInitialSourceParentAttachment(): void {
|
|
109
|
+
const { db } = this.project!
|
|
110
|
+
if (!this.parent?.source_component_id) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`${this.getString()} has no parent source component (parent: ${this.parent?.getString()})`,
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
db.source_port.update(this.source_port_id!, {
|
|
117
|
+
source_component_id: this.parent?.source_component_id!,
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
this.source_component_id = this.parent?.source_component_id
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* For PcbPorts, we use the parent attachment phase to determine where to place
|
|
125
|
+
* the pcb_port (prior to this phase, the smtpad/platedhole isn't guaranteed
|
|
126
|
+
* to exist)
|
|
127
|
+
*/
|
|
128
|
+
doInitialPcbPortRender(): void {
|
|
129
|
+
const { db } = this.project!
|
|
130
|
+
const { matchedComponents } = this
|
|
131
|
+
|
|
132
|
+
if (!this.parent?.pcb_component_id) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
`${this.getString()} has no parent pcb component, cannot render pcb_port (parent: ${this.parent?.getString()})`,
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const pcbMatches = matchedComponents.filter((c) => c.isPcbPrimitive)
|
|
139
|
+
|
|
140
|
+
if (pcbMatches.length === 0) return
|
|
141
|
+
|
|
142
|
+
if (pcbMatches.length > 1) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`${this.getString()} has multiple pcb matches, unclear how to place pcb_port: ${pcbMatches.map((c) => c.getString()).join(", ")}`,
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const pcbMatch: any = pcbMatches[0]
|
|
149
|
+
|
|
150
|
+
if ("getGlobalPcbPosition" in pcbMatch) {
|
|
151
|
+
const pcb_port = db.pcb_port.insert({
|
|
152
|
+
pcb_component_id: this.parent?.pcb_component_id!,
|
|
153
|
+
layers: ["top"],
|
|
154
|
+
|
|
155
|
+
...pcbMatch.getGlobalPcbPosition(),
|
|
156
|
+
|
|
157
|
+
source_port_id: this.source_port_id!,
|
|
158
|
+
})
|
|
159
|
+
this.pcb_port_id = pcb_port.pcb_port_id
|
|
160
|
+
} else {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`${pcbMatch.getString()} does not have a getGlobalPcbPosition method (needed for pcb_port placement)`,
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
doInitialSchematicPortRender(): void {
|
|
168
|
+
const { db } = this.project!
|
|
169
|
+
const { _parsedProps: props } = this
|
|
170
|
+
|
|
171
|
+
if (!this.parent) return
|
|
172
|
+
|
|
173
|
+
const center = this.getGlobalSchematicPosition()
|
|
174
|
+
const parentCenter = this.parent?.getGlobalSchematicPosition()
|
|
175
|
+
|
|
176
|
+
this.facingDirection = getRelativeDirection(parentCenter, center)
|
|
177
|
+
|
|
178
|
+
const schematic_port = db.schematic_port.insert({
|
|
179
|
+
schematic_component_id: this.parent?.schematic_component_id!,
|
|
180
|
+
center,
|
|
181
|
+
source_port_id: this.source_port_id!,
|
|
182
|
+
facing_direction: this.facingDirection,
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
this.schematic_port_id = schematic_port.schematic_port_id
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { PrimitiveComponent } from "../base-components/PrimitiveComponent"
|
|
2
|
+
import { smtPadProps } from "@tscircuit/props"
|
|
3
|
+
import type { Port } from "./Port"
|
|
4
|
+
import type { RenderPhaseFn } from "../base-components/Renderable"
|
|
5
|
+
import type { PCBSMTPad } from "@tscircuit/soup"
|
|
6
|
+
|
|
7
|
+
export class SmtPad extends PrimitiveComponent<typeof smtPadProps> {
|
|
8
|
+
pcb_smtpad_id: string | null = null
|
|
9
|
+
|
|
10
|
+
matchedPort: Port | null = null
|
|
11
|
+
|
|
12
|
+
isPcbPrimitive = true
|
|
13
|
+
|
|
14
|
+
get config() {
|
|
15
|
+
return {
|
|
16
|
+
zodProps: smtPadProps,
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
doInitialPortMatching(): void {
|
|
21
|
+
const parentPorts = (this.parent?.children ?? []).filter(
|
|
22
|
+
(c) => c.componentName === "Port",
|
|
23
|
+
) as Port[]
|
|
24
|
+
|
|
25
|
+
if (!this.props.portHints) {
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
for (const port of parentPorts) {
|
|
30
|
+
if (port.isMatchingAnyOf(this.props.portHints)) {
|
|
31
|
+
this.matchedPort = port
|
|
32
|
+
port.registerMatch(this)
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
doInitialPcbPrimitiveRender(): void {
|
|
39
|
+
const { db } = this.project!
|
|
40
|
+
const { _parsedProps: props } = this
|
|
41
|
+
if (!props.portHints) return
|
|
42
|
+
const position = this.getGlobalPcbPosition()
|
|
43
|
+
let pcb_smtpad: PCBSMTPad | null = null
|
|
44
|
+
if (props.shape === "circle") {
|
|
45
|
+
pcb_smtpad = db.pcb_smtpad.insert({
|
|
46
|
+
pcb_component_id: this.parent?.pcb_component_id!,
|
|
47
|
+
pcb_port_id: this.matchedPort?.pcb_port_id!,
|
|
48
|
+
layer: props.layer ?? "top",
|
|
49
|
+
shape: "circle",
|
|
50
|
+
|
|
51
|
+
// @ts-ignore: no idea why this is triggering
|
|
52
|
+
radius: props.radius!,
|
|
53
|
+
|
|
54
|
+
port_hints: props.portHints.map((ph) => ph.toString()),
|
|
55
|
+
|
|
56
|
+
x: position.x,
|
|
57
|
+
y: position.y,
|
|
58
|
+
})
|
|
59
|
+
} else if (props.shape === "rect") {
|
|
60
|
+
pcb_smtpad = db.pcb_smtpad.insert({
|
|
61
|
+
pcb_component_id: this.parent?.pcb_component_id!,
|
|
62
|
+
pcb_port_id: this.matchedPort?.pcb_port_id!,
|
|
63
|
+
layer: props.layer ?? "top",
|
|
64
|
+
shape: "rect",
|
|
65
|
+
|
|
66
|
+
// @ts-ignore: no idea why this is triggering
|
|
67
|
+
width: props.width,
|
|
68
|
+
height: props.height,
|
|
69
|
+
|
|
70
|
+
port_hints: props.portHints.map((ph) => ph.toString()),
|
|
71
|
+
|
|
72
|
+
x: position.x,
|
|
73
|
+
y: position.y,
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
if (pcb_smtpad) {
|
|
77
|
+
this.pcb_smtpad_id = pcb_smtpad.pcb_smtpad_id
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|