Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 80 additions & 5 deletions lib/components/base-components/NormalComponent/NormalComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ import type {
CadModelStep,
CadModelStl,
CadModelWrl,
FootprintInsertionDirection,
SchematicPortArrangement,
SupplierPartNumbers,
} from "@tscircuit/props"
import {
type AnyCircuitElement,
type LayerRef,
type PcbComponent,
distance,
pcb_component_invalid_layer_error,
pcb_manual_edit_conflict_warning,
Expand All @@ -39,6 +42,10 @@ import {
getPinNumberFromLabels,
getPortFromHints,
} from "lib/utils/getPortFromHints"
import {
isFootprintFlipped,
transformFootprintInsertionDirection,
} from "lib/utils/pcb/transform-footprint-insertion-direction"
import {
type SchematicBoxDimensions,
getAllDimensionsForSchematicBox,
Expand Down Expand Up @@ -842,10 +849,7 @@ export class NormalComponent<
}

// Calculate accumulated rotation from parent transforms
const globalTransform = this._computePcbGlobalTransformBeforeLayout()
const decomposedTransform = decomposeTSR(globalTransform)
const accumulatedRotation =
(decomposedTransform.rotation.angle * 180) / Math.PI
const globalTransformRotation = this.getGlobalTransformRotation()

const pcb_component = db.pcb_component.insert({
center: this._getGlobalPcbPositionBeforeLayout(),
Expand All @@ -856,7 +860,11 @@ export class NormalComponent<
componentLayer === "top" || componentLayer === "bottom"
? componentLayer
: "top",
rotation: props.pcbRotation ?? accumulatedRotation,
rotation: props.pcbRotation ?? globalTransformRotation,
insertion_direction: this._getPcbComponentInsertionDirection(
componentLayer,
globalTransformRotation,
),
source_component_id: this.source_component_id!,
subcircuit_id: subcircuit.subcircuit_id ?? undefined,
do_not_place: props.doNotPlace ?? false,
Expand Down Expand Up @@ -1062,6 +1070,73 @@ export class NormalComponent<
this.initPorts()
}

protected getGlobalTransformRotation(): number {
const globalTransform = this._computePcbGlobalTransformBeforeLayout()
const decomposedTransform = decomposeTSR(globalTransform)
return (decomposedTransform.rotation.angle * 180) / Math.PI
}

private _getFootprintMetadataForPcbComponent():
| {
insertionDirection?: FootprintInsertionDirection
originalLayer?: LayerRef
}
| undefined {
const footprintChild = this.children.find(
(c) => c.componentName === "Footprint",
)

if (footprintChild) {
return {
insertionDirection: footprintChild._parsedProps.insertionDirection,
originalLayer: footprintChild._parsedProps.originalLayer,
}
}

const footprint = this.props.footprint

if (isValidElement(footprint)) {
const footprintProps = footprint.props as {
insertionDirection?: FootprintInsertionDirection
originalLayer?: LayerRef
}
return {
insertionDirection: footprintProps.insertionDirection,
originalLayer: footprintProps.originalLayer,
}
}

if (footprint?.componentName === "Footprint") {
return {
insertionDirection:
footprint._parsedProps?.insertionDirection ??
footprint.props?.insertionDirection,
originalLayer:
footprint._parsedProps?.originalLayer ??
footprint.props?.originalLayer,
}
}

return undefined
}

protected _getPcbComponentInsertionDirection(
componentLayer: LayerRef,
rotationDegrees: number = this.getGlobalTransformRotation(),
): PcbComponent["insertion_direction"] | undefined {
const footprintMetadata = this._getFootprintMetadataForPcbComponent()
if (!footprintMetadata?.insertionDirection) return undefined

return transformFootprintInsertionDirection({
insertionDirection: footprintMetadata.insertionDirection,
rotationDegrees,
isFlipped: isFootprintFlipped({
componentLayer,
originalLayer: footprintMetadata.originalLayer,
}),
})
}

doInitialReactSubtreesRender(): void {
// Add React-based footprint subtree if provided
const fpElm = this.props.footprint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
extractCalcIdentifiers,
} from "lib/utils/evaluateCalcString"
import { getSubcircuitPcbCalcVariables } from "lib/utils/getSubcircuitPcbCalcVariables"
import { isFootprintFlipped } from "lib/utils/pcb/transform-footprint-insertion-direction"
import { getResolvedPcbSx } from "lib/utils/pcbSx/get-resolved-pcb-sx"
import type {
SchematicBoxComponentDimensions,
Expand Down Expand Up @@ -511,17 +512,14 @@ export abstract class PrimitiveComponent<
// If this is a primitive, and the parent primitive container is flipped,
// we flip it's position
if (this.isPcbPrimitive) {
const primitiveContainer = this.getPrimitiveContainer()
if (primitiveContainer) {
const isFlipped = primitiveContainer._parsedProps.layer === "bottom"
const { isFlipped } = this._getPcbPrimitiveFlippedHelpers()

if (isFlipped) {
return compose(
this.parent?._computePcbGlobalTransformBeforeLayout() ?? identity(),
flipY(),
this.computePcbPropsTransform(),
)
}
if (isFlipped) {
return compose(
this.parent?._computePcbGlobalTransformBeforeLayout() ?? identity(),
flipY(),
this.computePcbPropsTransform(),
)
}
}

Expand All @@ -531,6 +529,17 @@ export abstract class PrimitiveComponent<
)
}

private _getEnclosingFootprint(): PrimitiveComponent | null {
let current: PrimitiveComponent | null = this.parent
while (current) {
if (current.componentName === "Footprint") {
return current
}
current = current.parent
}
return null
}

getPrimitiveContainer(): PrimitiveComponent | null {
if (this.isPrimitiveContainer) return this
return this.parent?.getPrimitiveContainer?.() ?? null
Expand Down Expand Up @@ -643,9 +652,14 @@ export abstract class PrimitiveComponent<
maybeFlipLayer: (layer: LayerRef) => LayerRef
} {
const container = this.getPrimitiveContainer()
const footprint =
this.componentName === "Footprint" ? this : this._getEnclosingFootprint()
const isFlipped = !container
? false
: container._parsedProps.layer === "bottom"
: isFootprintFlipped({
componentLayer: container._parsedProps.layer,
originalLayer: footprint?._parsedProps.originalLayer,
})
const maybeFlipLayer = (layer: LayerRef) => {
if (isFlipped) {
return layer === "top" ? "bottom" : "top"
Expand Down
2 changes: 1 addition & 1 deletion lib/components/normal-components/Board.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type { SubcircuitI } from "../primitive-components/Group/Subcircuit/Subci
import { Subcircuit_doInitialRenderIsolatedSubcircuits } from "../primitive-components/Group/Subcircuit/Subcircuit_doInitialRenderIsolatedSubcircuits"
import { Subcircuit_getSubcircuitPropHash } from "../primitive-components/Group/Subcircuit_getSubcircuitPropHash"
import type { BoardI } from "./BoardI"
import { jlcMinTolerances } from "@tscircuit/jlcpcb-manufacturing-specs"
import { jlcMinTolerances } from "../../utils/pcb/jlc-manufacturing-tolerances"

const MIN_EFFECTIVE_BORDER_RADIUS_MM = 0.01

Expand Down
8 changes: 7 additions & 1 deletion lib/components/normal-components/Chip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ export class Chip<PinLabels extends string = never> extends NormalComponent<
// Still create the component but with 'top' as fallback to avoid cascading errors
}

const globalTransformRotation = this.getGlobalTransformRotation()

const pcb_component = db.pcb_component.insert({
center: { x: pcbX, y: pcbY },
width: 2, // Default width, adjust as needed
Expand All @@ -139,7 +141,11 @@ export class Chip<PinLabels extends string = never> extends NormalComponent<
componentLayer === "top" || componentLayer === "bottom"
? componentLayer
: "top",
rotation: props.pcbRotation ?? 0,
rotation: props.pcbRotation ?? globalTransformRotation,
insertion_direction: this._getPcbComponentInsertionDirection(
componentLayer,
globalTransformRotation,
),
source_component_id: this.source_component_id!,
subcircuit_id: this.getSubcircuit().subcircuit_id ?? undefined,
do_not_place: props.doNotPlace ?? false,
Expand Down
7 changes: 6 additions & 1 deletion lib/components/normal-components/Jumper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,18 @@ export class Jumper<PinLabels extends string = never> extends NormalComponent<
const { db } = this.root!
const { _parsedProps: props } = this
const { pcbX, pcbY } = this.getResolvedPcbPositionProp()
const globalTransformRotation = this.getGlobalTransformRotation()

const pcb_component = db.pcb_component.insert({
center: { x: pcbX, y: pcbY },
width: 2, // Default width, adjust as needed
height: 3, // Default height, adjust as needed
layer: props.layer ?? "top",
rotation: props.pcbRotation ?? 0,
rotation: props.pcbRotation ?? globalTransformRotation,
insertion_direction: this._getPcbComponentInsertionDirection(
props.layer ?? "top",
globalTransformRotation,
),
source_component_id: this.source_component_id!,
subcircuit_id: this.getSubcircuit().subcircuit_id ?? undefined,
do_not_place: props.doNotPlace ?? false,
Expand Down
7 changes: 6 additions & 1 deletion lib/components/normal-components/SolderJumper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,18 @@ export class SolderJumper<
const { db } = this.root!
const { _parsedProps: props } = this
const { pcbX, pcbY } = this.getResolvedPcbPositionProp()
const globalTransformRotation = this.getGlobalTransformRotation()

const pcb_component = db.pcb_component.insert({
center: { x: pcbX, y: pcbY },
width: 2, // Default width, adjust as needed
height: 3, // Default height, adjust as needed
layer: props.layer ?? "top",
rotation: props.pcbRotation ?? 0,
rotation: props.pcbRotation ?? globalTransformRotation,
insertion_direction: this._getPcbComponentInsertionDirection(
props.layer ?? "top",
globalTransformRotation,
),
source_component_id: this.source_component_id!,
subcircuit_id: this.getSubcircuit().subcircuit_id ?? undefined,
do_not_place: props.doNotPlace ?? false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { getViaDiameterDefaults } from "../../../utils/pcbStyle/getViaDiameterDe
import type { ManualPcbPathPoint } from "lib/utils/pcbTraceRouteToPcbPath"
import { TraceConnectionError } from "lib/errors"
import { getPcbSelectorErrorForTracePort } from "./getPcbSelectorErrorForTracePort"
import { jlcMinTolerances } from "@tscircuit/jlcpcb-manufacturing-specs"
import { jlcMinTolerances } from "lib/utils/pcb/jlc-manufacturing-tolerances"

export function Trace_doInitialPcbManualTraceRender(trace: Trace) {
if (trace.root?.pcbDisabled) return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { getObstaclesFromCircuitJson } from "lib/utils/obstacles/getObstaclesFro
import { getViaDiameterDefaults } from "lib/utils/pcbStyle/getViaDiameterDefaults"
import { TraceConnectionError } from "lib/errors"
import { getPcbSelectorErrorForTracePort } from "./getPcbSelectorErrorForTracePort"
import { jlcMinTolerances } from "@tscircuit/jlcpcb-manufacturing-specs"
import { jlcMinTolerances } from "lib/utils/pcb/jlc-manufacturing-tolerances"

type PcbRouteObjective =
| RouteHintPoint
Expand Down
12 changes: 12 additions & 0 deletions lib/utils/pcb/jlc-manufacturing-tolerances.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { PcbBoard } from "circuit-json"

export type JlcToleranceMap = Partial<Record<keyof PcbBoard, number>>

export const jlcMinTolerances: JlcToleranceMap = {
min_trace_width: 0.1,
min_via_to_via_clearance: 0.1,
min_trace_to_pad_clearance: 0.1,
min_pad_to_pad_clearance: 0.1,
min_via_hole_diameter: 0.2,
min_via_pad_diameter: 0.3,
}
54 changes: 54 additions & 0 deletions lib/utils/pcb/transform-footprint-insertion-direction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { normalizeDegrees } from "@tscircuit/math-utils"
import type { FootprintInsertionDirection } from "@tscircuit/props"
import type { LayerRef, PcbComponent } from "circuit-json"

// type PcbInsertionDirection = NonNullable<PcbComponent["insertion_direction"]>

const directionToVector: Record<
Exclude<FootprintInsertionDirection, "from_above">,
{ x: number; y: number }
> = {
from_left: { x: -1, y: 0 },
from_right: { x: 1, y: 0 },
from_front: { x: 0, y: 1 },
from_back: { x: 0, y: -1 },
}

export const isFootprintFlipped = (params: {
componentLayer?: LayerRef
originalLayer?: LayerRef
}): boolean => {
const { componentLayer, originalLayer } = params
return (componentLayer === "bottom") !== (originalLayer === "bottom")
}

export const transformFootprintInsertionDirection = (params: {
insertionDirection?: FootprintInsertionDirection
rotationDegrees?: number
isFlipped?: boolean
}): FootprintInsertionDirection | undefined => {
const { insertionDirection, rotationDegrees = 0, isFlipped = false } = params

if (!insertionDirection) return undefined
if (insertionDirection === "from_above") return insertionDirection

const baseVector = directionToVector[insertionDirection]
const angleRadians = (normalizeDegrees(rotationDegrees) * Math.PI) / 180
const rotatedVector = {
x:
baseVector.x * Math.cos(angleRadians) -
baseVector.y * Math.sin(angleRadians),
y:
baseVector.x * Math.sin(angleRadians) +
baseVector.y * Math.cos(angleRadians),
}
const finalVector = isFlipped
? { x: rotatedVector.x, y: -rotatedVector.y }
: rotatedVector

if (Math.abs(finalVector.x) >= Math.abs(finalVector.y)) {
return finalVector.x >= 0 ? "from_right" : "from_left"
}

return finalVector.y >= 0 ? "from_front" : "from_back"
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
"@resvg/resvg-js": "^2.6.2",
"@tscircuit/alphabet": "0.0.25",
"@tscircuit/capacity-autorouter": "^0.0.434",
"@tscircuit/circuit-json-util": "^0.0.94",
"@tscircuit/checks": "0.0.120",
"@tscircuit/circuit-json-util": "^0.0.93",
"@tscircuit/common": "^0.0.20",
"@tscircuit/copper-pour-solver": "^0.0.23",
"@tscircuit/footprinter": "^0.0.351",
Expand All @@ -47,7 +47,7 @@
"@tscircuit/math-utils": "^0.0.36",
"@tscircuit/miniflex": "^0.0.4",
"@tscircuit/ngspice-spice-engine": "^0.0.8",
"@tscircuit/props": "^0.0.510",
"@tscircuit/props": "^0.0.511",
"@tscircuit/schematic-match-adapt": "^0.0.16",
"@tscircuit/schematic-trace-solver": "^v0.0.45",
"@tscircuit/solver-utils": "^0.0.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ test("do not place part should be skipped", async () => {
},
"do_not_place": true,
"height": 4.41,
"insertion_direction": undefined,
"is_allowed_to_be_off_board": false,
"layer": "top",
"metadata": undefined,
Expand Down
Loading
Loading