diff --git a/lib/components/base-components/NormalComponent.ts b/lib/components/base-components/NormalComponent.ts index 83834953..72f6f0e2 100644 --- a/lib/components/base-components/NormalComponent.ts +++ b/lib/components/base-components/NormalComponent.ts @@ -4,6 +4,7 @@ import type { CadModelObj, CadModelProp, CadModelStl, + SchematicPortArrangement, } from "@tscircuit/props" import { point3, rotation } from "circuit-json" import Debug from "debug" @@ -25,6 +26,12 @@ import { ZodType, z } from "zod" import { Footprint } from "../primitive-components/Footprint" import { Port } from "../primitive-components/Port" import { PrimitiveComponent } from "./PrimitiveComponent" +import { + getAllDimensionsForSchematicBox, + type SchematicBoxDimensions, +} from "lib/utils/schematic/getAllDimensionsForSchematicBox" +import { underscorifyPortArrangement } from "lib/soup/underscorifyPortArrangement" +import { underscorifyPinStyles } from "lib/soup/underscorifyPinStyles" const debug = Debug("tscircuit:core") @@ -253,14 +260,29 @@ export class NormalComponent< /** * Render the schematic component for this NormalComponent using the - * config.schematicSymbolName if it exists. + * config.schematicSymbolName if it exists, or create a generic box if + * no symbol is defined. * * You can override this method to do more complicated things. */ doInitialSchematicComponentRender() { - const { db } = this.root! const { schematicSymbolName } = this.config - if (!schematicSymbolName) return + if (schematicSymbolName) { + return this._doInitialSchematicComponentRenderWithSymbol() + } + + const dimensions = this._getSchematicBoxDimensions() + if (dimensions) { + return this._doInitialSchematicComponentRenderWithSchematicBoxDimensions() + } + + // No schematic symbol or dimensions defined, this could be a board, group + // or other NormalComponent that doesn't have a schematic representation + } + + _doInitialSchematicComponentRenderWithSymbol() { + const { db } = this.root! + // TODO switch between horizontal and vertical based on schRotation const base_symbol_name = this.config.schematicSymbolName const symbol_name_horz = `${base_symbol_name}_horz` @@ -281,18 +303,49 @@ export class NormalComponent< const symbol: SchSymbol | undefined = symbols[symbol_name] - if (!symbol) { - throw new Error(`Could not find schematic-symbol "${symbol_name}"`) + if (symbol) { + const schematic_component = db.schematic_component.insert({ + center: { x: this.props.schX ?? 0, y: this.props.schY ?? 0 }, + rotation: this.props.schRotation ?? 0, + size: symbol.size, + source_component_id: this.source_component_id!, + + symbol_name, + }) + this.schematic_component_id = schematic_component.schematic_component_id + } + } + + _doInitialSchematicComponentRenderWithSchematicBoxDimensions() { + const { db } = this.root! + const { _parsedProps: props } = this + const dimensions = this._getSchematicBoxDimensions()! + + const primaryPortLabels: Record = {} + for (const [port, label] of Object.entries(props.pinLabels ?? {})) { + primaryPortLabels[port] = Array.isArray(label) ? label[0] : label } + console.log("inserting schematic component") const schematic_component = db.schematic_component.insert({ - center: { x: this.props.schX ?? 0, y: this.props.schY ?? 0 }, - rotation: this.props.schRotation ?? 0, - size: symbol.size, - source_component_id: this.source_component_id!, + center: { x: props.schX ?? 0, y: props.schY ?? 0 }, + rotation: props.schRotation ?? 0, + size: dimensions.getSize(), + + port_arrangement: underscorifyPortArrangement( + props.schPortArrangement as any, + ), + + pin_spacing: props.schPinSpacing ?? 0.2, + + // @ts-ignore soup needs to support distance for pin_styles + pin_styles: underscorifyPinStyles(props.schPinStyle), - symbol_name, + port_labels: primaryPortLabels, + + source_component_id: this.source_component_id!, }) + this.schematic_component_id = schematic_component.schematic_component_id } @@ -545,6 +598,51 @@ export class NormalComponent< } } + _getPinCount(): number { + const { _parsedProps: props } = this + const pinCountFromSchArrangement = + (props.schPortArrangement?.leftSize ?? 0) + + (props.schPortArrangement?.rightSize ?? 0) + + (props.schPortArrangement?.topSize ?? 0) + + (props.schPortArrangement?.bottomSize ?? 0) + const pinCount = + pinCountFromSchArrangement || this.getPortsFromFootprint().length + return pinCount + } + + /** + * Override the schematic port arrangement if you want to customize where pins + * appear on a schematic box, e.g. for a pin header + */ + _getSchematicPortArrangement(): SchematicPortArrangement | null { + return this._parsedProps.schPortArrangement + } + + _getSchematicBoxDimensions(): SchematicBoxDimensions | null { + // Only valid if we don't have a schematic symbol + if (this.getSchematicSymbol()) return null + if (!this.config.shouldRenderAsSchematicBox) return null + + const { _parsedProps: props } = this + + const pinCount = this._getPinCount() + + const pinSpacing = props.schPinSpacing ?? 0.2 + + const dimensions = getAllDimensionsForSchematicBox({ + schWidth: props.schWidth, + schHeight: props.schHeight, + schPinSpacing: pinSpacing, + schPinStyle: props.schPinStyle, + + pinCount, + + schPortArrangement: this._getSchematicPortArrangement(), + }) + + return dimensions + } + doInitialCadModelRender(): void { const { db } = this.root! const { boardThickness = 0 } = this.root?._getBoard() ?? {} diff --git a/lib/components/base-components/PrimitiveComponent.ts b/lib/components/base-components/PrimitiveComponent.ts index dee30fdd..adf9edd6 100644 --- a/lib/components/base-components/PrimitiveComponent.ts +++ b/lib/components/base-components/PrimitiveComponent.ts @@ -17,12 +17,14 @@ import { z } from "zod" import type { Circuit } from "../../Circuit" import type { ISubcircuit } from "../primitive-components/Group/ISubcircuit" import { Renderable } from "./Renderable" +import type { SchematicBoxDimensions } from "lib/utils/schematic/getAllDimensionsForSchematicBox" export interface BaseComponentConfig { componentName: string schematicSymbolName?: BaseSymbolName | null zodProps: ZodType sourceFtype?: AnySourceComponent["ftype"] | null + shouldRenderAsSchematicBox?: boolean } /** @@ -509,6 +511,22 @@ export abstract class PrimitiveComponent< return descendants } + /** + * Return the number of pins in this component, this is important for + * NormalComponents + */ + _getPinCount(): number { + return 0 + } + + /** + * If this component represents a SchematicBox (like a Chip), return the + * dimensions of the box, which allows computing the position of ports etc. + */ + _getSchematicBoxDimensions(): SchematicBoxDimensions | null { + return null + } + // TODO we shouldn't need to override this, errors can be rendered and handled // by the Renderable class, however, the Renderable class currently doesn't // have access to the database or cleanup diff --git a/lib/components/normal-components/Chip.ts b/lib/components/normal-components/Chip.ts index bb83c6f3..1e9889c5 100644 --- a/lib/components/normal-components/Chip.ts +++ b/lib/components/normal-components/Chip.ts @@ -11,12 +11,13 @@ export class Chip extends NormalComponent< typeof chipProps, PinLabels > { - schematicDimensions: SchematicBoxDimensions | null = null + schematicBoxDimensions: SchematicBoxDimensions | null = null get config() { return { componentName: "Chip", zodProps: chipProps, + shouldRenderAsSchematicBox: true, } } @@ -34,60 +35,38 @@ export class Chip extends NormalComponent< this.source_component_id = source_component.source_component_id! } - doInitialSchematicComponentRender() { - const { db } = this.root! - const { _parsedProps: props } = this - - const pinCountFromSchArrangement = - (props.schPortArrangement?.leftSize ?? 0) + - (props.schPortArrangement?.rightSize ?? 0) + - (props.schPortArrangement?.topSize ?? 0) + - (props.schPortArrangement?.bottomSize ?? 0) - const pinCount = pinCountFromSchArrangement || this.getPortsFromFootprint().length - - const pinSpacing = props.schPinSpacing ?? 0.2 - - const dimensions = getAllDimensionsForSchematicBox({ - schWidth: props.schWidth, - schHeight: props.schHeight, - schPinSpacing: pinSpacing, - schPinStyle: props.schPinStyle, - - pinCount, + // doInitialSchematicComponentRender() { + // const { db } = this.root! + // const { _parsedProps: props } = this + // const dimensions = this._getSchematicBoxDimensions()! + // this.schematicBoxDimensions = dimensions - // @ts-ignore there's a subtley in the definition difference with - // leftSide/rightSide/topSide/bottomSide in how the direction is defined - // that doesn't really matter - schPortArrangement: props.schPortArrangement, - }) - this.schematicDimensions = dimensions - - const primaryPortLabels: Record = {} - for (const [port, label] of Object.entries(props.pinLabels ?? {})) { - primaryPortLabels[port] = Array.isArray(label) ? label[0] : label - } + // const primaryPortLabels: Record = {} + // for (const [port, label] of Object.entries(props.pinLabels ?? {})) { + // primaryPortLabels[port] = Array.isArray(label) ? label[0] : label + // } - const schematic_component = db.schematic_component.insert({ - center: { x: props.schX ?? 0, y: props.schY ?? 0 }, - rotation: props.schRotation ?? 0, - size: dimensions.getSize(), + // const schematic_component = db.schematic_component.insert({ + // center: { x: props.schX ?? 0, y: props.schY ?? 0 }, + // rotation: props.schRotation ?? 0, + // size: dimensions.getSize(), - port_arrangement: underscorifyPortArrangement( - props.schPortArrangement as any, - ), + // port_arrangement: underscorifyPortArrangement( + // props.schPortArrangement as any, + // ), - pin_spacing: pinSpacing, + // pin_spacing: props.schPinSpacing ?? 0.2, - // @ts-ignore soup needs to support distance for pin_styles - pin_styles: underscorifyPinStyles(props.schPinStyle), + // // @ts-ignore soup needs to support distance for pin_styles + // pin_styles: underscorifyPinStyles(props.schPinStyle), - port_labels: primaryPortLabels, + // port_labels: primaryPortLabels, - source_component_id: this.source_component_id!, - }) + // source_component_id: this.source_component_id!, + // }) - this.schematic_component_id = schematic_component.schematic_component_id - } + // this.schematic_component_id = schematic_component.schematic_component_id + // } doInitialPcbComponentRender() { const { db } = this.root! diff --git a/lib/components/normal-components/PinHeader.ts b/lib/components/normal-components/PinHeader.ts index 94be2a20..c74fa95c 100644 --- a/lib/components/normal-components/PinHeader.ts +++ b/lib/components/normal-components/PinHeader.ts @@ -1,4 +1,4 @@ -import { pinHeaderProps } from "@tscircuit/props" +import { pinHeaderProps, type SchematicPortArrangement } from "@tscircuit/props" import { NormalComponent } from "../base-components/NormalComponent" import { Port } from "../primitive-components/Port" import type { BaseSymbolName } from "lib/utils/constants" @@ -8,7 +8,7 @@ export class PinHeader extends NormalComponent { return { componentName: "PinHeader", zodProps: pinHeaderProps, - schematicSymbolName: "pinrow_horz" as BaseSymbolName, + shouldRenderAsSchematicBox: true, } } @@ -41,6 +41,13 @@ export class PinHeader extends NormalComponent { } } + _getSchematicPortArrangement(): SchematicPortArrangement | null { + return { + leftSize: 0, + rightSize: this._parsedProps.pinCount ?? 1, + } + } + doInitialSourceRender() { const { db } = this.root! const { _parsedProps: props } = this @@ -56,22 +63,4 @@ export class PinHeader extends NormalComponent { this.source_component_id = source_component.source_component_id } - - doInitialSchematicComponentRender() { - const { db } = this.root! - const { _parsedProps: props } = this - - const schematic_component = db.schematic_component.insert({ - center: { x: props.schX ?? 0, y: props.schY ?? 0 }, - rotation: props.schRotation ?? 0, - size: { width: 2, height: props.pinCount ?? 1 }, - source_component_id: this.source_component_id!, - port_arrangement: { - left_size: 0, - right_size: props.pinCount ?? 1, - }, - }) - - this.schematic_component_id = schematic_component.schematic_component_id - } } diff --git a/lib/components/primitive-components/Port.ts b/lib/components/primitive-components/Port.ts index b72a926e..e96aaeda 100644 --- a/lib/components/primitive-components/Port.ts +++ b/lib/components/primitive-components/Port.ts @@ -79,26 +79,28 @@ export class Port extends PrimitiveComponent { } _getGlobalSchematicPositionBeforeLayout(): { x: number; y: number } { - if (!this.schematicSymbolPortDef) { - return applyToPoint(this.parent!.computeSchematicGlobalTransform(), { - x: 0, - y: 0, - }) - } - const symbol = this.parent?.getSchematicSymbol() - if (!symbol) { - console.warn(`Could not find parent symbol for ${this}`) - return { x: 0, y: 0 } + if (symbol && this.schematicSymbolPortDef) { + const transform = compose( + this.parent!.computeSchematicGlobalTransform(), + translate(-symbol.center.x, -symbol.center.y), + ) + + return applyToPoint(transform, this.schematicSymbolPortDef!) } - const offsetY = -0.045 - const transform = compose( - this.parent!.computeSchematicGlobalTransform(), - translate(-symbol.center.x, -symbol.center.y + offsetY), - ) + const parentBoxDim = this?.parent?._getSchematicBoxDimensions() + if (parentBoxDim && this.props.pinNumber !== undefined) { + const localPortPosition = parentBoxDim.getPortPositionByPinNumber( + this.props.pinNumber!, + ) + return applyToPoint( + this.parent!.computeSchematicGlobalTransform(), + localPortPosition, + ) + } - return applyToPoint(transform, this.schematicSymbolPortDef) + return { x: 0, y: 0 } } _getGlobalSchematicPositionAfterLayout(): { x: number; y: number } { diff --git a/tests/components/normal-components/__snapshots__/header-schematic.snap.svg b/tests/components/normal-components/__snapshots__/header-schematic.snap.svg index 185ca881..6bd580a9 100644 --- a/tests/components/normal-components/__snapshots__/header-schematic.snap.svg +++ b/tests/components/normal-components/__snapshots__/header-schematic.snap.svg @@ -4,9 +4,9 @@ .component { fill: none; stroke: rgb(132, 0, 0); } .chip { fill: rgb(255, 255, 194); stroke: rgb(132, 0, 0); } .component-pin { fill: none; stroke: rgb(132, 0, 0); } - .trace { stroke: rgb(0, 150, 0); stroke-width: 1.7142857142857142; fill: none; } - .text { font-family: Arial, sans-serif; font-size: 17.142857142857142px; fill: rgb(0, 150, 0); } - .pin-number { font-size: 12.857142857142856px; fill: rgb(169, 0, 0); } + .trace { stroke: rgb(0, 150, 0); stroke-width: 2.857142857142857; fill: none; } + .text { font-family: Arial, sans-serif; font-size: 28.571428571428573px; fill: rgb(0, 150, 0); } + .pin-number { font-size: 21.428571428571427px; fill: rgb(169, 0, 0); } .port-label { fill: rgb(0, 100, 100); } - .component-name { font-size: 21.428571428571427px; fill: rgb(0, 100, 100); } - 1234 \ No newline at end of file + .component-name { font-size: 35.714285714285715px; fill: rgb(0, 100, 100); } + 1234 \ No newline at end of file