@tscircuit/core 0.0.78 → 0.0.80

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/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as zod from 'zod';
2
2
  import { ZodType, z } from 'zod';
3
3
  import { AnySoupElement, PCBTraceError, PCBPlacementError, AnySourceComponent, RouteHintPoint } from '@tscircuit/soup';
4
+ import { LayerRef } from 'circuit-json';
4
5
  import { SoupUtilObjects } from '@tscircuit/soup-util';
5
6
  import { ReactElement } from 'react';
6
7
  import { Matrix } from 'transformation-matrix';
@@ -17,6 +18,13 @@ declare class Circuit {
17
18
  _hasRenderedAtleastOnce: boolean;
18
19
  constructor();
19
20
  add(componentOrElm: PrimitiveComponent | ReactElement): void;
21
+ /**
22
+ * Get the main board for this Circuit.
23
+ */
24
+ _getBoard(): PrimitiveComponent & {
25
+ boardThickness: number;
26
+ allLayers: LayerRef[];
27
+ };
20
28
  _guessRootComponent(): void;
21
29
  render(): void;
22
30
  getSoup(): AnySoupElement[];
@@ -158,6 +166,17 @@ declare abstract class PrimitiveComponent<ZodProps extends ZodType = any> extend
158
166
  width: number;
159
167
  height: number;
160
168
  };
169
+ /**
170
+ * Determine if this pcb primitive should be flipped because the primitive
171
+ * container is flipped
172
+ *
173
+ * TODO use footprint.originalLayer instead of assuming everything is defined
174
+ * relative to the top layer
175
+ */
176
+ _getPcbPrimitiveFlippedHelpers(): {
177
+ isFlipped: boolean;
178
+ maybeFlipLayer: (layer: LayerRef) => LayerRef;
179
+ };
161
180
  /**
162
181
  * Set the position of this component from the layout solver. This method
163
182
  * should operate using CircuitJson associated with this component, like
@@ -707,6 +726,11 @@ declare class Board extends Group<typeof boardProps> {
707
726
  }[] | undefined;
708
727
  }>;
709
728
  };
729
+ get boardThickness(): number;
730
+ /**
731
+ * Get all available layers for the board
732
+ */
733
+ get allLayers(): string[];
710
734
  doInitialPcbComponentRender(): void;
711
735
  removePcbComponentRender(): void;
712
736
  _computePcbGlobalTransformBeforeLayout(): Matrix;
@@ -801,7 +825,6 @@ declare class SmtPad extends PrimitiveComponent<typeof smtPadProps> {
801
825
  height: number;
802
826
  };
803
827
  doInitialPortMatching(): void;
804
- getAvailablePcbLayers(): string[];
805
828
  doInitialPcbPrimitiveRender(): void;
806
829
  doInitialPcbPortAttachment(): void;
807
830
  _getPcbCircuitJsonBounds(): {
@@ -4075,7 +4098,12 @@ declare class Hole extends PrimitiveComponent<typeof holeProps> {
4075
4098
  }
4076
4099
 
4077
4100
  declare class SilkscreenText extends PrimitiveComponent<typeof silkscreenTextProps> {
4101
+ isPcbPrimitive: boolean;
4078
4102
  doInitialPcbPrimitiveRender(): void;
4103
+ getPcbSize(): {
4104
+ width: number;
4105
+ height: number;
4106
+ };
4079
4107
  }
4080
4108
 
4081
4109
  declare class Keepout extends PrimitiveComponent<typeof pcbKeepoutProps> {
package/dist/index.js CHANGED
@@ -135,6 +135,7 @@ var Renderable = class {
135
135
  import {
136
136
  applyToPoint,
137
137
  compose,
138
+ flipY,
138
139
  identity,
139
140
  rotate,
140
141
  translate
@@ -270,6 +271,25 @@ var PrimitiveComponent = class extends Renderable {
270
271
  )
271
272
  );
272
273
  }
274
+ if (this.isPcbPrimitive) {
275
+ const primitiveContainer = this.getPrimitiveContainer();
276
+ if (primitiveContainer) {
277
+ const isFlipped = primitiveContainer._parsedProps.layer === "bottom";
278
+ const containerCenter = primitiveContainer._getGlobalPcbPositionBeforeLayout();
279
+ if (isFlipped) {
280
+ const flipOperation = compose(
281
+ translate(containerCenter.x, containerCenter.y),
282
+ flipY(),
283
+ translate(-containerCenter.x, -containerCenter.y)
284
+ );
285
+ return compose(
286
+ this.parent?._computePcbGlobalTransformBeforeLayout() ?? identity(),
287
+ flipY(),
288
+ this.computePcbPropsTransform()
289
+ );
290
+ }
291
+ }
292
+ }
273
293
  return compose(
274
294
  this.parent?._computePcbGlobalTransformBeforeLayout() ?? identity(),
275
295
  this.computePcbPropsTransform()
@@ -291,6 +311,24 @@ var PrimitiveComponent = class extends Renderable {
291
311
  height: 0
292
312
  };
293
313
  }
314
+ /**
315
+ * Determine if this pcb primitive should be flipped because the primitive
316
+ * container is flipped
317
+ *
318
+ * TODO use footprint.originalLayer instead of assuming everything is defined
319
+ * relative to the top layer
320
+ */
321
+ _getPcbPrimitiveFlippedHelpers() {
322
+ const container = this.getPrimitiveContainer();
323
+ const isFlipped = !container ? false : container._parsedProps.layer === "bottom";
324
+ const maybeFlipLayer = (layer) => {
325
+ if (isFlipped) {
326
+ return layer === "top" ? "bottom" : "top";
327
+ }
328
+ return layer;
329
+ };
330
+ return { isFlipped, maybeFlipLayer };
331
+ }
294
332
  /**
295
333
  * Set the position of this component from the layout solver. This method
296
334
  * should operate using CircuitJson associated with this component, like
@@ -474,9 +512,16 @@ var PrimitiveComponent = class extends Renderable {
474
512
  }
475
513
  getAvailablePcbLayers() {
476
514
  if (this.isPcbPrimitive) {
477
- if (this.props.layer) return [this.props.layer];
515
+ const { maybeFlipLayer } = this._getPcbPrimitiveFlippedHelpers();
516
+ if ("layer" in this._parsedProps) {
517
+ const layer = maybeFlipLayer(this._parsedProps.layer ?? "top");
518
+ return [layer];
519
+ }
520
+ if ("layers" in this._parsedProps) {
521
+ return this._parsedProps.layers;
522
+ }
478
523
  if (this.componentName === "PlatedHole") {
479
- return ["top", "bottom"];
524
+ return this.root?._getBoard()?.allLayers ?? ["top", "bottom"];
480
525
  }
481
526
  return [];
482
527
  }
@@ -1097,7 +1142,9 @@ function getPortFromHints(hints) {
1097
1142
 
1098
1143
  // lib/components/primitive-components/SmtPad.ts
1099
1144
  import { smtPadProps } from "@tscircuit/props";
1100
- import { decomposeTSR } from "transformation-matrix";
1145
+ import {
1146
+ decomposeTSR
1147
+ } from "transformation-matrix";
1101
1148
  var SmtPad = class extends PrimitiveComponent {
1102
1149
  pcb_smtpad_id = null;
1103
1150
  matchedPort = null;
@@ -1134,18 +1181,18 @@ var SmtPad = class extends PrimitiveComponent {
1134
1181
  }
1135
1182
  }
1136
1183
  }
1137
- getAvailablePcbLayers() {
1138
- return this.props.layer ? [this.props.layer] : [];
1139
- }
1140
1184
  doInitialPcbPrimitiveRender() {
1141
1185
  const { db } = this.root;
1142
1186
  const { _parsedProps: props } = this;
1143
1187
  if (!props.portHints) return;
1188
+ const container = this.getPrimitiveContainer();
1144
1189
  const position = this._getGlobalPcbPositionBeforeLayout();
1190
+ const containerCenter = container?._getGlobalPcbPositionBeforeLayout();
1145
1191
  const decomposedMat = decomposeTSR(
1146
1192
  this._computePcbGlobalTransformBeforeLayout()
1147
1193
  );
1148
1194
  const isRotated90 = Math.abs(decomposedMat.rotation.angle * (180 / Math.PI) - 90) < 0.01;
1195
+ const { maybeFlipLayer } = this._getPcbPrimitiveFlippedHelpers();
1149
1196
  let pcb_smtpad = null;
1150
1197
  const pcb_component_id = this.parent?.pcb_component_id ?? this.getPrimitiveContainer()?.pcb_component_id;
1151
1198
  if (props.shape === "circle") {
@@ -1153,7 +1200,7 @@ var SmtPad = class extends PrimitiveComponent {
1153
1200
  pcb_component_id,
1154
1201
  pcb_port_id: this.matchedPort?.pcb_port_id,
1155
1202
  // port likely isn't matched
1156
- layer: props.layer ?? "top",
1203
+ layer: maybeFlipLayer(props.layer ?? "top"),
1157
1204
  shape: "circle",
1158
1205
  // @ts-ignore: no idea why this is triggering
1159
1206
  radius: props.radius,
@@ -1166,7 +1213,7 @@ var SmtPad = class extends PrimitiveComponent {
1166
1213
  pcb_component_id,
1167
1214
  pcb_port_id: this.matchedPort?.pcb_port_id,
1168
1215
  // port likely isn't matched
1169
- layer: props.layer ?? "top",
1216
+ layer: maybeFlipLayer(props.layer ?? "top"),
1170
1217
  shape: "rect",
1171
1218
  ...isRotated90 ? { width: props.height, height: props.width } : { width: props.width, height: props.height },
1172
1219
  port_hints: props.portHints.map((ph) => ph.toString()),
@@ -1229,7 +1276,7 @@ var SmtPad = class extends PrimitiveComponent {
1229
1276
 
1230
1277
  // lib/components/primitive-components/SilkscreenPath.ts
1231
1278
  import { silkscreenPathProps } from "@tscircuit/props";
1232
- import { applyToPoint as applyToPoint3 } from "transformation-matrix";
1279
+ import { applyToPoint as applyToPoint4 } from "transformation-matrix";
1233
1280
  var SilkscreenPath = class extends PrimitiveComponent {
1234
1281
  pcb_silkscreen_path_id = null;
1235
1282
  get config() {
@@ -1251,7 +1298,7 @@ var SilkscreenPath = class extends PrimitiveComponent {
1251
1298
  pcb_component_id: this.parent?.pcb_component_id,
1252
1299
  layer,
1253
1300
  route: props.route.map((p) => {
1254
- const transformedPosition = applyToPoint3(transform, {
1301
+ const transformedPosition = applyToPoint4(transform, {
1255
1302
  x: p.x,
1256
1303
  y: p.y
1257
1304
  });
@@ -1736,13 +1783,6 @@ var NormalComponent = class extends PrimitiveComponent {
1736
1783
  if (!footprint) return;
1737
1784
  if (typeof footprint === "string") {
1738
1785
  const fpSoup = fp.string(footprint).soup();
1739
- if (this.props.layer) {
1740
- for (const elm of fpSoup) {
1741
- if ("layer" in elm) {
1742
- elm.layer = this.props.layer;
1743
- }
1744
- }
1745
- }
1746
1786
  const fpComponents = createComponentsFromSoup(fpSoup);
1747
1787
  this.addAll(fpComponents);
1748
1788
  }
@@ -1984,6 +2024,7 @@ var NormalComponent = class extends PrimitiveComponent {
1984
2024
  }
1985
2025
  doInitialCadModelRender() {
1986
2026
  const { db } = this.root;
2027
+ const { boardThickness = 0 } = this.root?._getBoard() ?? {};
1987
2028
  const { _parsedProps: props } = this;
1988
2029
  if (props.cadModel) {
1989
2030
  const bounds = this._getPcbCircuitJsonBounds();
@@ -1993,14 +2034,17 @@ var NormalComponent = class extends PrimitiveComponent {
1993
2034
  }
1994
2035
  const cad_model = db.cad_component.insert({
1995
2036
  // TODO z maybe depends on layer
1996
- position: { x: bounds.center.x, y: bounds.center.y, z: 0 },
2037
+ position: {
2038
+ x: bounds.center.x,
2039
+ y: bounds.center.y,
2040
+ z: this.props.layer === "bottom" ? -boardThickness / 2 : boardThickness / 2
2041
+ },
1997
2042
  pcb_component_id: this.pcb_component_id,
1998
2043
  source_component_id: this.source_component_id,
1999
2044
  model_stl_url: "stlUrl" in cadModel ? cadModel.stlUrl : void 0,
2000
2045
  model_obj_url: "objUrl" in cadModel ? cadModel.objUrl : void 0,
2001
- model_jscad: "jscad" in cadModel ? cadModel.jscad : void 0
2002
- // TODO
2003
- // footprinter_string:
2046
+ model_jscad: "jscad" in cadModel ? cadModel.jscad : void 0,
2047
+ footprinter_string: typeof this.props.footprint === "string" && !cadModel ? this.props.footprint : void 0
2004
2048
  });
2005
2049
  }
2006
2050
  }
@@ -2019,7 +2063,7 @@ import "zod";
2019
2063
 
2020
2064
  // lib/components/primitive-components/TraceHint.ts
2021
2065
  import "@tscircuit/props";
2022
- import { applyToPoint as applyToPoint4 } from "transformation-matrix";
2066
+ import { applyToPoint as applyToPoint5 } from "transformation-matrix";
2023
2067
  var TraceHint = class extends PrimitiveComponent {
2024
2068
  matchedPort = null;
2025
2069
  doInitialPortMatching() {
@@ -2053,7 +2097,7 @@ var TraceHint = class extends PrimitiveComponent {
2053
2097
  const globalTransform = this._computePcbGlobalTransformBeforeLayout();
2054
2098
  return offsets.map(
2055
2099
  (offset) => ({
2056
- ...applyToPoint4(globalTransform, offset),
2100
+ ...applyToPoint5(globalTransform, offset),
2057
2101
  via: offset.via,
2058
2102
  to_layer: offset.to_layer,
2059
2103
  trace_width: offset.trace_width
@@ -2107,6 +2151,16 @@ var Board = class extends Group {
2107
2151
  zodProps: boardProps
2108
2152
  };
2109
2153
  }
2154
+ get boardThickness() {
2155
+ const { _parsedProps: props } = this;
2156
+ return 1.4;
2157
+ }
2158
+ /**
2159
+ * Get all available layers for the board
2160
+ */
2161
+ get allLayers() {
2162
+ return ["top", "bottom", "inner1", "inner2"];
2163
+ }
2110
2164
  doInitialPcbComponentRender() {
2111
2165
  const { db } = this.root;
2112
2166
  const { _parsedProps: props } = this;
@@ -3437,23 +3491,34 @@ var Hole = class extends PrimitiveComponent {
3437
3491
  // lib/components/primitive-components/SilkscreenText.ts
3438
3492
  import "@tscircuit/props";
3439
3493
  var SilkscreenText = class extends PrimitiveComponent {
3494
+ isPcbPrimitive = true;
3440
3495
  doInitialPcbPrimitiveRender() {
3441
3496
  const { db } = this.root;
3442
3497
  const { _parsedProps: props } = this;
3443
3498
  const container = this.getPrimitiveContainer();
3499
+ const position = this._getGlobalPcbPositionBeforeLayout();
3500
+ const { maybeFlipLayer } = this._getPcbPrimitiveFlippedHelpers();
3444
3501
  db.pcb_silkscreen_text.insert({
3445
3502
  anchor_alignment: props.anchorAlignment,
3446
3503
  anchor_position: {
3447
- x: props.pcbX ?? 0,
3448
- y: props.pcbY ?? 0
3504
+ x: position.x,
3505
+ y: position.y
3449
3506
  },
3450
3507
  font: props.font ?? "tscircuit2024",
3451
3508
  font_size: props.fontSize ?? 1,
3452
- layer: "top",
3509
+ layer: maybeFlipLayer(props.layer ?? "top"),
3453
3510
  text: props.text ?? "",
3454
3511
  pcb_component_id: container.pcb_component_id
3455
3512
  });
3456
3513
  }
3514
+ getPcbSize() {
3515
+ const { _parsedProps: props } = this;
3516
+ const fontSize = props.fontSize ?? 1;
3517
+ const text = props.text ?? "";
3518
+ const textWidth = text.length * fontSize;
3519
+ const textHeight = fontSize;
3520
+ return { width: textWidth * fontSize, height: textHeight * fontSize };
3521
+ }
3457
3522
  };
3458
3523
 
3459
3524
  // lib/components/primitive-components/FabricationNoteText.ts
@@ -3481,7 +3546,7 @@ var FabricationNoteText = class extends PrimitiveComponent {
3481
3546
 
3482
3547
  // lib/components/primitive-components/FabricationNotePath.ts
3483
3548
  import { fabricationNotePathProps } from "@tscircuit/props";
3484
- import { applyToPoint as applyToPoint5 } from "transformation-matrix";
3549
+ import { applyToPoint as applyToPoint6 } from "transformation-matrix";
3485
3550
  var FabricationNotePath = class extends PrimitiveComponent {
3486
3551
  fabrication_note_path_id = null;
3487
3552
  get config() {
@@ -3504,7 +3569,7 @@ var FabricationNotePath = class extends PrimitiveComponent {
3504
3569
  layer,
3505
3570
  color: props.color,
3506
3571
  route: props.route.map((p) => {
3507
- const transformedPosition = applyToPoint5(transform, {
3572
+ const transformedPosition = applyToPoint6(transform, {
3508
3573
  x: p.x,
3509
3574
  y: p.y
3510
3575
  });
@@ -3520,7 +3585,7 @@ var FabricationNotePath = class extends PrimitiveComponent {
3520
3585
  }
3521
3586
  };
3522
3587
 
3523
- // lib/Project.ts
3588
+ // lib/Circuit.ts
3524
3589
  import { su } from "@tscircuit/soup-util";
3525
3590
  import { isValidElement as isValidElement2 } from "react";
3526
3591
  import { identity as identity5 } from "transformation-matrix";
@@ -3545,6 +3610,14 @@ var Circuit = class {
3545
3610
  }
3546
3611
  this.children.push(component);
3547
3612
  }
3613
+ /**
3614
+ * Get the main board for this Circuit.
3615
+ */
3616
+ _getBoard() {
3617
+ return this.children.find(
3618
+ (c) => c.componentName === "Board"
3619
+ );
3620
+ }
3548
3621
  _guessRootComponent() {
3549
3622
  if (this.firstChild) return;
3550
3623
  if (this.children.length === 1) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/core",
3
3
  "type": "module",
4
- "version": "0.0.78",
4
+ "version": "0.0.80",
5
5
  "types": "dist/index.d.ts",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",