@tscircuit/core 0.0.1429 → 0.0.1431

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
@@ -135849,6 +135849,9 @@ declare class Mosfet extends NormalComponent<typeof mosfetProps> {
135849
135849
  } & {
135850
135850
  channelType: zod.ZodEnum<["n", "p"]>;
135851
135851
  mosfetMode: zod.ZodEnum<["enhancement", "depletion"]>;
135852
+ symbolDrainSide: zod.ZodOptional<zod.ZodEnum<["left", "right", "top", "bottom"]>>;
135853
+ symbolSourceSide: zod.ZodOptional<zod.ZodEnum<["left", "right", "top", "bottom"]>>;
135854
+ symbolGateSide: zod.ZodOptional<zod.ZodEnum<["left", "right", "top", "bottom"]>>;
135852
135855
  connections: zod.ZodOptional<zod.ZodRecord<zod.ZodEnum<["pin1", "drain", "pin2", "source", "pin3", "gate"]>, zod.ZodUnion<[zod.ZodUnion<[zod.ZodString, zod.ZodReadonly<zod.ZodArray<zod.ZodString, "many">>]>, zod.ZodArray<zod.ZodString, "many">]>>>;
135853
135856
  }, "strip", zod.ZodTypeAny, {
135854
135857
  name: string;
@@ -136431,6 +136434,9 @@ declare class Mosfet extends NormalComponent<typeof mosfetProps> {
136431
136434
  schSectionName?: string | undefined;
136432
136435
  schSheetName?: string | undefined;
136433
136436
  connections?: Partial<Record<"pin1" | "pin2" | "source" | "pin3" | "gate" | "drain", string | readonly string[] | string[]>> | undefined;
136437
+ symbolDrainSide?: "left" | "right" | "top" | "bottom" | undefined;
136438
+ symbolSourceSide?: "left" | "right" | "top" | "bottom" | undefined;
136439
+ symbolGateSide?: "left" | "right" | "top" | "bottom" | undefined;
136434
136440
  }, {
136435
136441
  name: string;
136436
136442
  channelType: "n" | "p";
@@ -137014,6 +137020,9 @@ declare class Mosfet extends NormalComponent<typeof mosfetProps> {
137014
137020
  schSectionName?: string | undefined;
137015
137021
  schSheetName?: string | undefined;
137016
137022
  connections?: Partial<Record<"pin1" | "pin2" | "source" | "pin3" | "gate" | "drain", string | readonly string[] | string[]>> | undefined;
137023
+ symbolDrainSide?: "left" | "right" | "top" | "bottom" | undefined;
137024
+ symbolSourceSide?: "left" | "right" | "top" | "bottom" | undefined;
137025
+ symbolGateSide?: "left" | "right" | "top" | "bottom" | undefined;
137017
137026
  }>;
137018
137027
  shouldRenderAsSchematicBox: boolean;
137019
137028
  };
package/dist/index.js CHANGED
@@ -2546,51 +2546,29 @@ var isRouteOutsideBoard = ({
2546
2546
  );
2547
2547
  };
2548
2548
 
2549
- // lib/utils/obstacles/getObstaclesFromRoute.ts
2550
- var isCloseTo = (a, b) => Math.abs(a - b) < 1e-4;
2551
- var getObstaclesFromRoute = (route, source_trace_id, { viaDiameter = 0.5 } = {}) => {
2552
- const obstacles = [];
2553
- for (let i = 0; i < route.length - 1; i++) {
2554
- const [start, end] = [route[i], route[i + 1]];
2555
- const prev = i - 1 >= 0 ? route[i - 1] : null;
2556
- const isHorz = isCloseTo(start.y, end.y);
2557
- const isVert = isCloseTo(start.x, end.x);
2558
- if (!isHorz && !isVert) {
2559
- throw new Error(
2560
- `getObstaclesFromTrace currently only supports horizontal and vertical traces (not diagonals) Conflicting trace: ${source_trace_id}, start: (${start.x}, ${start.y}), end: (${end.x}, ${end.y})`
2561
- );
2562
- }
2563
- const obstacle = {
2564
- type: "rect",
2565
- layers: [start.layer],
2566
- center: {
2567
- x: (start.x + end.x) / 2,
2568
- y: (start.y + end.y) / 2
2569
- },
2570
- width: isHorz ? Math.abs(start.x - end.x) : 0.1,
2571
- // TODO use route width
2572
- height: isVert ? Math.abs(start.y - end.y) : 0.1,
2573
- // TODO use route width
2574
- connectedTo: [source_trace_id]
2575
- };
2576
- obstacles.push(obstacle);
2577
- if (prev && prev.layer === start.layer && start.layer !== end.layer) {
2578
- const via = {
2579
- type: "rect",
2580
- layers: [start.layer, end.layer],
2549
+ // lib/utils/obstacles/fillCircleWithRects.ts
2550
+ function fillCircleWithRects(circle, options = {}) {
2551
+ const { center, radius } = circle;
2552
+ const { rectHeight = 0.1 } = options;
2553
+ const rects = [];
2554
+ const numSlices = Math.ceil(radius * 2 / rectHeight);
2555
+ for (let i = 0; i < numSlices; i++) {
2556
+ const y = center.y - radius + (i + 0.5) * rectHeight;
2557
+ const dy = y - center.y;
2558
+ const halfWidth = Math.sqrt(radius * radius - dy * dy);
2559
+ if (halfWidth > 0) {
2560
+ rects.push({
2581
2561
  center: {
2582
- x: start.x,
2583
- y: start.y
2562
+ x: center.x,
2563
+ y
2584
2564
  },
2585
- connectedTo: [source_trace_id],
2586
- width: viaDiameter,
2587
- height: viaDiameter
2588
- };
2589
- obstacles.push(via);
2565
+ width: halfWidth * 2,
2566
+ height: rectHeight
2567
+ });
2590
2568
  }
2591
2569
  }
2592
- return obstacles;
2593
- };
2570
+ return rects;
2571
+ }
2594
2572
 
2595
2573
  // lib/utils/obstacles/fillPolygonWithRects.ts
2596
2574
  function fillPolygonWithRects(polygon, options = {}) {
@@ -2633,29 +2611,88 @@ function fillPolygonWithRects(polygon, options = {}) {
2633
2611
  return rects;
2634
2612
  }
2635
2613
 
2636
- // lib/utils/obstacles/fillCircleWithRects.ts
2637
- function fillCircleWithRects(circle, options = {}) {
2638
- const { center, radius } = circle;
2639
- const { rectHeight = 0.1 } = options;
2640
- const rects = [];
2641
- const numSlices = Math.ceil(radius * 2 / rectHeight);
2642
- for (let i = 0; i < numSlices; i++) {
2643
- const y = center.y - radius + (i + 0.5) * rectHeight;
2644
- const dy = y - center.y;
2645
- const halfWidth = Math.sqrt(radius * radius - dy * dy);
2646
- if (halfWidth > 0) {
2647
- rects.push({
2614
+ // lib/utils/obstacles/getAxisAlignedRectFromPolygon.ts
2615
+ var AXIS_ALIGNED_RECT_TOLERANCE_MM = 1e-3;
2616
+ var getAxisAlignedRectFromPolygon = (points) => {
2617
+ const isCloseTo2 = (a, b) => Math.abs(a - b) <= AXIS_ALIGNED_RECT_TOLERANCE_MM;
2618
+ const normalizedPoints = points.filter(
2619
+ (point6, index) => index === 0 || !isCloseTo2(point6.x, points[index - 1].x) || !isCloseTo2(point6.y, points[index - 1].y)
2620
+ );
2621
+ if (normalizedPoints.length > 1 && isCloseTo2(normalizedPoints[0].x, normalizedPoints.at(-1).x) && isCloseTo2(normalizedPoints[0].y, normalizedPoints.at(-1).y)) {
2622
+ normalizedPoints.pop();
2623
+ }
2624
+ if (normalizedPoints.length < 4) return null;
2625
+ const minX = Math.min(...normalizedPoints.map((point6) => point6.x));
2626
+ const maxX = Math.max(...normalizedPoints.map((point6) => point6.x));
2627
+ const minY = Math.min(...normalizedPoints.map((point6) => point6.y));
2628
+ const maxY = Math.max(...normalizedPoints.map((point6) => point6.y));
2629
+ const expectedCorners = [
2630
+ { x: minX, y: minY },
2631
+ { x: minX, y: maxY },
2632
+ { x: maxX, y: minY },
2633
+ { x: maxX, y: maxY }
2634
+ ];
2635
+ if (!normalizedPoints.every(
2636
+ (point6) => isCloseTo2(point6.x, minX) || isCloseTo2(point6.x, maxX) || isCloseTo2(point6.y, minY) || isCloseTo2(point6.y, maxY)
2637
+ ) || !expectedCorners.every(
2638
+ (corner) => normalizedPoints.some(
2639
+ (point6) => isCloseTo2(point6.x, corner.x) && isCloseTo2(point6.y, corner.y)
2640
+ )
2641
+ )) {
2642
+ return null;
2643
+ }
2644
+ return {
2645
+ center: { x: (minX + maxX) / 2, y: (minY + maxY) / 2 },
2646
+ width: maxX - minX,
2647
+ height: maxY - minY
2648
+ };
2649
+ };
2650
+
2651
+ // lib/utils/obstacles/getObstaclesFromRoute.ts
2652
+ var isCloseTo = (a, b) => Math.abs(a - b) < 1e-4;
2653
+ var getObstaclesFromRoute = (route, source_trace_id, { viaDiameter = 0.5 } = {}) => {
2654
+ const obstacles = [];
2655
+ for (let i = 0; i < route.length - 1; i++) {
2656
+ const [start, end] = [route[i], route[i + 1]];
2657
+ const prev = i - 1 >= 0 ? route[i - 1] : null;
2658
+ const isHorz = isCloseTo(start.y, end.y);
2659
+ const isVert = isCloseTo(start.x, end.x);
2660
+ if (!isHorz && !isVert) {
2661
+ throw new Error(
2662
+ `getObstaclesFromTrace currently only supports horizontal and vertical traces (not diagonals) Conflicting trace: ${source_trace_id}, start: (${start.x}, ${start.y}), end: (${end.x}, ${end.y})`
2663
+ );
2664
+ }
2665
+ const obstacle = {
2666
+ type: "rect",
2667
+ layers: [start.layer],
2668
+ center: {
2669
+ x: (start.x + end.x) / 2,
2670
+ y: (start.y + end.y) / 2
2671
+ },
2672
+ width: isHorz ? Math.abs(start.x - end.x) : 0.1,
2673
+ // TODO use route width
2674
+ height: isVert ? Math.abs(start.y - end.y) : 0.1,
2675
+ // TODO use route width
2676
+ connectedTo: [source_trace_id]
2677
+ };
2678
+ obstacles.push(obstacle);
2679
+ if (prev && prev.layer === start.layer && start.layer !== end.layer) {
2680
+ const via = {
2681
+ type: "rect",
2682
+ layers: [start.layer, end.layer],
2648
2683
  center: {
2649
- x: center.x,
2650
- y
2684
+ x: start.x,
2685
+ y: start.y
2651
2686
  },
2652
- width: halfWidth * 2,
2653
- height: rectHeight
2654
- });
2687
+ connectedTo: [source_trace_id],
2688
+ width: viaDiameter,
2689
+ height: viaDiameter
2690
+ };
2691
+ obstacles.push(via);
2655
2692
  }
2656
2693
  }
2657
- return rects;
2658
- }
2694
+ return obstacles;
2695
+ };
2659
2696
 
2660
2697
  // lib/utils/obstacles/getObstaclesFromCircuitJson.ts
2661
2698
  var EVERY_LAYER = ["top", "inner1", "inner2", "bottom"];
@@ -2729,7 +2766,7 @@ var getObstaclesFromCircuitJson = (circuitJson, connMap) => {
2729
2766
  center: rect.center,
2730
2767
  width: rect.width,
2731
2768
  height: rect.height,
2732
- ccwRotationDegrees: element.ccw_rotation,
2769
+ ccwRotationDegrees: axisAlignedRect ? void 0 : element.ccw_rotation,
2733
2770
  connectedTo: withNetId([element.pcb_smtpad_id])
2734
2771
  });
2735
2772
  } else if (element.shape === "pill" || element.shape === "rotated_pill") {
@@ -2747,24 +2784,31 @@ var getObstaclesFromCircuitJson = (circuitJson, connMap) => {
2747
2784
  connectedTo: withNetId([element.pcb_smtpad_id])
2748
2785
  });
2749
2786
  } else if (element.shape === "polygon") {
2750
- const xs = element.points.map((point6) => point6.x);
2751
- const ys = element.points.map((point6) => point6.y);
2752
- const minX = Math.min(...xs);
2753
- const maxX = Math.max(...xs);
2754
- const minY = Math.min(...ys);
2755
- const maxY = Math.max(...ys);
2756
- obstacles.push({
2757
- componentId: pcbComponentId,
2758
- type: "rect",
2759
- layers: [element.layer],
2760
- center: {
2761
- x: (minX + maxX) / 2,
2762
- y: (minY + maxY) / 2
2763
- },
2764
- width: maxX - minX,
2765
- height: maxY - minY,
2766
- connectedTo: withNetId([element.pcb_smtpad_id])
2787
+ const axisAlignedRect = getAxisAlignedRectFromPolygon(element.points);
2788
+ if (axisAlignedRect) {
2789
+ obstacles.push({
2790
+ componentId: pcbComponentId,
2791
+ type: "rect",
2792
+ layers: [element.layer],
2793
+ ...axisAlignedRect,
2794
+ connectedTo: withNetId([element.pcb_smtpad_id])
2795
+ });
2796
+ continue;
2797
+ }
2798
+ const approximatingRects = fillPolygonWithRects(element.points, {
2799
+ rectHeight: 0.1
2767
2800
  });
2801
+ for (const rect of approximatingRects) {
2802
+ obstacles.push({
2803
+ componentId: pcbComponentId,
2804
+ type: "rect",
2805
+ layers: [element.layer],
2806
+ center: rect.center,
2807
+ width: rect.width,
2808
+ height: rect.height,
2809
+ connectedTo: withNetId([element.pcb_smtpad_id])
2810
+ });
2811
+ }
2768
2812
  }
2769
2813
  } else if (element.type === "pcb_keepout") {
2770
2814
  if (element.shape === "circle") {
@@ -22352,7 +22396,7 @@ function Group_getRoutingPhasePlans(group) {
22352
22396
  var package_default = {
22353
22397
  name: "@tscircuit/core",
22354
22398
  type: "module",
22355
- version: "0.0.1428",
22399
+ version: "0.0.1430",
22356
22400
  types: "dist/index.d.ts",
22357
22401
  main: "dist/index.js",
22358
22402
  module: "dist/index.js",
@@ -22401,7 +22445,7 @@ var package_default = {
22401
22445
  "@tscircuit/math-utils": "^0.0.36",
22402
22446
  "@tscircuit/miniflex": "^0.0.4",
22403
22447
  "@tscircuit/ngspice-spice-engine": "^0.0.19",
22404
- "@tscircuit/props": "^0.0.571",
22448
+ "@tscircuit/props": "^0.0.574",
22405
22449
  "@tscircuit/schematic-match-adapt": "^0.0.18",
22406
22450
  "@tscircuit/schematic-trace-solver": "^0.0.96",
22407
22451
  "@tscircuit/solver-utils": "^0.0.16",
@@ -22436,7 +22480,7 @@ var package_default = {
22436
22480
  poppygl: "^0.0.25",
22437
22481
  react: "^19.1.0",
22438
22482
  "react-dom": "^19.1.0",
22439
- "schematic-symbols": "^0.0.231",
22483
+ "schematic-symbols": "^0.0.232",
22440
22484
  spicets: "^0.0.2",
22441
22485
  spicey: "^0.0.14",
22442
22486
  "stack-svgs": "^0.0.1",
@@ -31741,14 +31785,72 @@ var Crystal = class extends NormalComponent3 {
31741
31785
 
31742
31786
  // lib/components/normal-components/Mosfet.ts
31743
31787
  import { mosfetProps } from "@tscircuit/props";
31788
+
31789
+ // lib/components/normal-components/get-mosfet-schematic-symbol-name.ts
31790
+ var possibleOrientations = [
31791
+ { gate: "left", drain: "top", source: "bottom" },
31792
+ { gate: "left", drain: "bottom", source: "top" },
31793
+ { gate: "right", drain: "top", source: "bottom" },
31794
+ { gate: "right", drain: "bottom", source: "top" },
31795
+ { gate: "top", drain: "left", source: "right" },
31796
+ { gate: "top", drain: "right", source: "left" },
31797
+ { gate: "bottom", drain: "left", source: "right" },
31798
+ { gate: "bottom", drain: "right", source: "left" }
31799
+ ];
31800
+ var getBaselineOrientation = (rotation4) => {
31801
+ const normalizedRotation = (rotation4 % 360 + 360) % 360;
31802
+ if (normalizedRotation % 90 !== 0) {
31803
+ throw new Error(
31804
+ `Schematic rotation ${rotation4} is not supported for Mosfet`
31805
+ );
31806
+ }
31807
+ return [
31808
+ { gate: "left", drain: "top", source: "bottom" },
31809
+ { gate: "top", drain: "right", source: "left" },
31810
+ { gate: "right", drain: "bottom", source: "top" },
31811
+ { gate: "bottom", drain: "left", source: "right" }
31812
+ ][normalizedRotation / 90];
31813
+ };
31814
+ var getRequestedOrientation = (props) => {
31815
+ const requestedSides = {
31816
+ drain: props.symbolDrainSide,
31817
+ source: props.symbolSourceSide,
31818
+ gate: props.symbolGateSide
31819
+ };
31820
+ const baseline = getBaselineOrientation(Number(props.schRotation ?? 0));
31821
+ const matchingOrientations = possibleOrientations.filter(
31822
+ (orientation) => Object.keys(requestedSides).every(
31823
+ (portName) => requestedSides[portName] === void 0 || requestedSides[portName] === orientation[portName]
31824
+ )
31825
+ );
31826
+ if (matchingOrientations.length === 0) {
31827
+ const requestedDescription = Object.entries(requestedSides).filter(([, side]) => side !== void 0).map(([portName, side]) => `${portName}=${side}`).join(", ");
31828
+ throw new Error(
31829
+ `Invalid MOSFET symbol port side combination (${requestedDescription}). The gate must be perpendicular to the drain and source, and the drain and source must be opposite.`
31830
+ );
31831
+ }
31832
+ return matchingOrientations.sort((a, b) => {
31833
+ const score = (orientation) => Object.keys(baseline).filter(
31834
+ (portName) => orientation[portName] !== baseline[portName]
31835
+ ).length;
31836
+ return score(a) - score(b);
31837
+ })[0];
31838
+ };
31839
+ var getMosfetSchematicSymbolName = (props) => {
31840
+ const mosfetMode = props.mosfetMode === "depletion" ? "d" : "e";
31841
+ const baseSymbolName = `${props.channelType}_channel_${mosfetMode}_mosfet_transistor`;
31842
+ const hasSymbolSideProps = props.symbolDrainSide !== void 0 || props.symbolSourceSide !== void 0 || props.symbolGateSide !== void 0;
31843
+ if (!hasSymbolSideProps) return baseSymbolName;
31844
+ const { gate, drain } = getRequestedOrientation(props);
31845
+ return `${baseSymbolName}_gate_${gate}_drain_${drain}`;
31846
+ };
31847
+
31848
+ // lib/components/normal-components/Mosfet.ts
31744
31849
  var Mosfet = class extends NormalComponent3 {
31745
31850
  get config() {
31746
- const mosfetMode = this.props.mosfetMode === "depletion" ? "d" : "e";
31747
- const channelType = this.props.channelType;
31748
- const baseSymbolName = `${channelType}_channel_${mosfetMode}_mosfet_transistor`;
31749
31851
  return {
31750
31852
  componentName: "Mosfet",
31751
- schematicSymbolName: this.props.symbolName ?? baseSymbolName,
31853
+ schematicSymbolName: this.props.symbolName ?? getMosfetSchematicSymbolName(this.props),
31752
31854
  zodProps: mosfetProps,
31753
31855
  shouldRenderAsSchematicBox: false
31754
31856
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/core",
3
3
  "type": "module",
4
- "version": "0.0.1429",
4
+ "version": "0.0.1431",
5
5
  "types": "dist/index.d.ts",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
@@ -50,7 +50,7 @@
50
50
  "@tscircuit/math-utils": "^0.0.36",
51
51
  "@tscircuit/miniflex": "^0.0.4",
52
52
  "@tscircuit/ngspice-spice-engine": "^0.0.19",
53
- "@tscircuit/props": "^0.0.571",
53
+ "@tscircuit/props": "^0.0.574",
54
54
  "@tscircuit/schematic-match-adapt": "^0.0.18",
55
55
  "@tscircuit/schematic-trace-solver": "^0.0.96",
56
56
  "@tscircuit/solver-utils": "^0.0.16",
@@ -85,7 +85,7 @@
85
85
  "poppygl": "^0.0.25",
86
86
  "react": "^19.1.0",
87
87
  "react-dom": "^19.1.0",
88
- "schematic-symbols": "^0.0.231",
88
+ "schematic-symbols": "^0.0.232",
89
89
  "spicets": "^0.0.2",
90
90
  "spicey": "^0.0.14",
91
91
  "stack-svgs": "^0.0.1",