@tscircuit/core 0.0.905 → 0.0.907

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.
Files changed (2) hide show
  1. package/dist/index.js +280 -123
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -2335,10 +2335,10 @@ var SmtPad = class extends PrimitiveComponent2 {
2335
2335
  pcb_group_id: this.getGroup()?.pcb_group_id ?? void 0
2336
2336
  });
2337
2337
  } else if (props.shape === "polygon") {
2338
- const transformedPoints = props.points.map((point) => {
2338
+ const transformedPoints = props.points.map((point2) => {
2339
2339
  const transformed = applyToPoint2(globalTransform, {
2340
- x: distance.parse(point.x),
2341
- y: distance.parse(point.y)
2340
+ x: distance.parse(point2.x),
2341
+ y: distance.parse(point2.y)
2342
2342
  });
2343
2343
  return {
2344
2344
  x: transformed.x,
@@ -2546,18 +2546,18 @@ var SilkscreenPath = class extends PrimitiveComponent2 {
2546
2546
  if (!currentPath) return;
2547
2547
  let currentCenterX = 0;
2548
2548
  let currentCenterY = 0;
2549
- for (const point of currentPath.route) {
2550
- currentCenterX += point.x;
2551
- currentCenterY += point.y;
2549
+ for (const point2 of currentPath.route) {
2550
+ currentCenterX += point2.x;
2551
+ currentCenterY += point2.y;
2552
2552
  }
2553
2553
  currentCenterX /= currentPath.route.length;
2554
2554
  currentCenterY /= currentPath.route.length;
2555
2555
  const offsetX = newCenter.x - currentCenterX;
2556
2556
  const offsetY = newCenter.y - currentCenterY;
2557
- const newRoute = currentPath.route.map((point) => ({
2558
- ...point,
2559
- x: point.x + offsetX,
2560
- y: point.y + offsetY
2557
+ const newRoute = currentPath.route.map((point2) => ({
2558
+ ...point2,
2559
+ x: point2.x + offsetX,
2560
+ y: point2.y + offsetY
2561
2561
  }));
2562
2562
  db.pcb_silkscreen_path.update(this.pcb_silkscreen_path_id, {
2563
2563
  route: newRoute
@@ -2569,11 +2569,11 @@ var SilkscreenPath = class extends PrimitiveComponent2 {
2569
2569
  return { width: 0, height: 0 };
2570
2570
  }
2571
2571
  let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
2572
- for (const point of props.route) {
2573
- minX = Math.min(minX, point.x);
2574
- maxX = Math.max(maxX, point.x);
2575
- minY = Math.min(minY, point.y);
2576
- maxY = Math.max(maxY, point.y);
2572
+ for (const point2 of props.route) {
2573
+ minX = Math.min(minX, point2.x);
2574
+ maxX = Math.max(maxX, point2.x);
2575
+ minY = Math.min(minY, point2.y);
2576
+ maxY = Math.max(maxY, point2.y);
2577
2577
  }
2578
2578
  return {
2579
2579
  width: maxX - minX,
@@ -2609,14 +2609,14 @@ var PcbTrace = class extends PrimitiveComponent2 {
2609
2609
  const subcircuit = this.getSubcircuit();
2610
2610
  const { maybeFlipLayer } = this._getPcbPrimitiveFlippedHelpers();
2611
2611
  const parentTransform = this._computePcbGlobalTransformBeforeLayout();
2612
- const transformedRoute = props.route.map((point) => {
2613
- const { x, y, ...restOfPoint } = point;
2612
+ const transformedRoute = props.route.map((point2) => {
2613
+ const { x, y, ...restOfPoint } = point2;
2614
2614
  const transformedPoint = applyToPoint4(parentTransform, { x, y });
2615
- if (point.route_type === "wire" && point.layer) {
2615
+ if (point2.route_type === "wire" && point2.layer) {
2616
2616
  return {
2617
2617
  ...transformedPoint,
2618
2618
  ...restOfPoint,
2619
- layer: maybeFlipLayer(point.layer)
2619
+ layer: maybeFlipLayer(point2.layer)
2620
2620
  };
2621
2621
  }
2622
2622
  return { ...transformedPoint, ...restOfPoint };
@@ -2636,16 +2636,16 @@ var PcbTrace = class extends PrimitiveComponent2 {
2636
2636
  return { width: 0, height: 0 };
2637
2637
  }
2638
2638
  let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
2639
- for (const point of props.route) {
2640
- minX = Math.min(minX, point.x);
2641
- maxX = Math.max(maxX, point.x);
2642
- minY = Math.min(minY, point.y);
2643
- maxY = Math.max(maxY, point.y);
2644
- if (point.route_type === "wire") {
2645
- minX = Math.min(minX, point.x - point.width / 2);
2646
- maxX = Math.max(maxX, point.x + point.width / 2);
2647
- minY = Math.min(minY, point.y - point.width / 2);
2648
- maxY = Math.max(maxY, point.y + point.width / 2);
2639
+ for (const point2 of props.route) {
2640
+ minX = Math.min(minX, point2.x);
2641
+ maxX = Math.max(maxX, point2.x);
2642
+ minY = Math.min(minY, point2.y);
2643
+ maxY = Math.max(maxY, point2.y);
2644
+ if (point2.route_type === "wire") {
2645
+ minX = Math.min(minX, point2.x - point2.width / 2);
2646
+ maxX = Math.max(maxX, point2.x + point2.width / 2);
2647
+ minY = Math.min(minY, point2.y - point2.width / 2);
2648
+ maxY = Math.max(maxY, point2.y + point2.width / 2);
2649
2649
  }
2650
2650
  }
2651
2651
  if (minX === Infinity || maxX === -Infinity || minY === Infinity || maxY === -Infinity) {
@@ -2911,9 +2911,9 @@ var PlatedHole = class extends PrimitiveComponent2 {
2911
2911
  });
2912
2912
  this.pcb_plated_hole_id = pcb_plated_hole.pcb_plated_hole_id;
2913
2913
  } else if (props.shape === "hole_with_polygon_pad") {
2914
- const padOutline = (props.padOutline || []).map((point) => {
2915
- const x = typeof point.x === "number" ? point.x : parseFloat(String(point.x));
2916
- const y = typeof point.y === "number" ? point.y : parseFloat(String(point.y));
2914
+ const padOutline = (props.padOutline || []).map((point2) => {
2915
+ const x = typeof point2.x === "number" ? point2.x : parseFloat(String(point2.x));
2916
+ const y = typeof point2.y === "number" ? point2.y : parseFloat(String(point2.y));
2917
2917
  return {
2918
2918
  x,
2919
2919
  y
@@ -3273,11 +3273,11 @@ var Cutout = class extends PrimitiveComponent2 {
3273
3273
  if (props.shape === "polygon") {
3274
3274
  if (props.points.length === 0) return { width: 0, height: 0 };
3275
3275
  let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
3276
- for (const point of props.points) {
3277
- minX = Math.min(minX, point.x);
3278
- maxX = Math.max(maxX, point.x);
3279
- minY = Math.min(minY, point.y);
3280
- maxY = Math.max(maxY, point.y);
3276
+ for (const point2 of props.points) {
3277
+ minX = Math.min(minX, point2.x);
3278
+ maxX = Math.max(maxX, point2.x);
3279
+ minY = Math.min(minY, point2.y);
3280
+ maxY = Math.max(maxY, point2.y);
3281
3281
  }
3282
3282
  return { width: maxX - minX, height: maxY - minY };
3283
3283
  }
@@ -3316,11 +3316,11 @@ var Cutout = class extends PrimitiveComponent2 {
3316
3316
  } else if (cutout.shape === "polygon") {
3317
3317
  if (cutout.points.length === 0) return super._getPcbCircuitJsonBounds();
3318
3318
  let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
3319
- for (const point of cutout.points) {
3320
- minX = Math.min(minX, point.x);
3321
- maxX = Math.max(maxX, point.x);
3322
- minY = Math.min(minY, point.y);
3323
- maxY = Math.max(maxY, point.y);
3319
+ for (const point2 of cutout.points) {
3320
+ minX = Math.min(minX, point2.x);
3321
+ maxX = Math.max(maxX, point2.x);
3322
+ minY = Math.min(minY, point2.y);
3323
+ maxY = Math.max(maxY, point2.y);
3324
3324
  }
3325
3325
  return {
3326
3326
  center: { x: (minX + maxX) / 2, y: (minY + maxY) / 2 },
@@ -5324,13 +5324,13 @@ var getDistance = (a, b) => {
5324
5324
  const bPos = "_getGlobalPcbPositionBeforeLayout" in b ? b._getGlobalPcbPositionBeforeLayout() : b;
5325
5325
  return Math.sqrt((aPos.x - bPos.x) ** 2 + (aPos.y - bPos.y) ** 2);
5326
5326
  };
5327
- function getClosest(point, candidates) {
5327
+ function getClosest(point2, candidates) {
5328
5328
  if (candidates.length === 0)
5329
5329
  throw new Error("No candidates given to getClosest method");
5330
5330
  let closest = candidates[0];
5331
5331
  let closestDist = Infinity;
5332
5332
  for (const candidate of candidates) {
5333
- const dist = getDistance(point, candidate);
5333
+ const dist = getDistance(point2, candidate);
5334
5334
  if (dist < closestDist) {
5335
5335
  closest = candidate;
5336
5336
  closestDist = dist;
@@ -5464,24 +5464,24 @@ var isRouteOutsideBoard = (mergedRoute, { db }) => {
5464
5464
  const pcbBoard = db.pcb_board.list()[0];
5465
5465
  if (pcbBoard.outline) {
5466
5466
  const boardOutline = pcbBoard.outline;
5467
- const isInsidePolygon = (point, polygon) => {
5467
+ const isInsidePolygon = (point2, polygon) => {
5468
5468
  let inside = false;
5469
5469
  for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
5470
5470
  const xi = polygon[i].x, yi = polygon[i].y;
5471
5471
  const xj = polygon[j].x, yj = polygon[j].y;
5472
- const intersect = yi > point.y !== yj > point.y && point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi;
5472
+ const intersect = yi > point2.y !== yj > point2.y && point2.x < (xj - xi) * (point2.y - yi) / (yj - yi) + xi;
5473
5473
  if (intersect) inside = !inside;
5474
5474
  }
5475
5475
  return inside;
5476
5476
  };
5477
- return mergedRoute.some((point) => !isInsidePolygon(point, boardOutline));
5477
+ return mergedRoute.some((point2) => !isInsidePolygon(point2, boardOutline));
5478
5478
  }
5479
5479
  const boardWidth = pcbBoard.width;
5480
5480
  const boardHeight = pcbBoard.height;
5481
5481
  const boardCenterX = pcbBoard.center.x;
5482
5482
  const boardCenterY = pcbBoard.center.y;
5483
- const outsideBoard = mergedRoute.some((point) => {
5484
- return point.x < boardCenterX - boardWidth / 2 || point.y < boardCenterY - boardHeight / 2 || point.x > boardCenterX + boardWidth / 2 || point.y > boardCenterY + boardHeight / 2;
5483
+ const outsideBoard = mergedRoute.some((point2) => {
5484
+ return point2.x < boardCenterX - boardWidth / 2 || point2.y < boardCenterY - boardHeight / 2 || point2.x > boardCenterX + boardWidth / 2 || point2.y > boardCenterY + boardHeight / 2;
5485
5485
  });
5486
5486
  return outsideBoard;
5487
5487
  };
@@ -6063,12 +6063,12 @@ var createSchematicTraceCrossingSegments = ({
6063
6063
 
6064
6064
  // lib/components/primitive-components/Trace/trace-utils/create-schematic-trace-junctions.ts
6065
6065
  var TOLERANCE = 1e-3;
6066
- var isPointWithinEdge = (point, edge) => {
6066
+ var isPointWithinEdge = (point2, edge) => {
6067
6067
  const minX = Math.min(edge.from.x, edge.to.x);
6068
6068
  const maxX = Math.max(edge.from.x, edge.to.x);
6069
6069
  const minY = Math.min(edge.from.y, edge.to.y);
6070
6070
  const maxY = Math.max(edge.from.y, edge.to.y);
6071
- return point.x >= minX && point.x <= maxX && point.y >= minY && point.y <= maxY;
6071
+ return point2.x >= minX && point2.x <= maxX && point2.y >= minY && point2.y <= maxY;
6072
6072
  };
6073
6073
  var getEdgeOrientation = (edge) => {
6074
6074
  const isVertical = Math.abs(edge.from.x - edge.to.x) < TOLERANCE;
@@ -6673,15 +6673,15 @@ import { getFullConnectivityMapFromCircuitJson } from "circuit-json-to-connectiv
6673
6673
  function getTraceLength(route) {
6674
6674
  let totalLength = 0;
6675
6675
  for (let i = 0; i < route.length; i++) {
6676
- const point = route[i];
6677
- if (point.route_type === "wire") {
6676
+ const point2 = route[i];
6677
+ if (point2.route_type === "wire") {
6678
6678
  const nextPoint = route[i + 1];
6679
6679
  if (nextPoint) {
6680
- const dx = nextPoint.x - point.x;
6681
- const dy = nextPoint.y - point.y;
6680
+ const dx = nextPoint.x - point2.x;
6681
+ const dy = nextPoint.y - point2.y;
6682
6682
  totalLength += Math.sqrt(dx * dx + dy * dy);
6683
6683
  }
6684
- } else if (point.route_type === "via") {
6684
+ } else if (point2.route_type === "via") {
6685
6685
  totalLength += 1.6;
6686
6686
  }
6687
6687
  }
@@ -6991,17 +6991,17 @@ function Trace_doInitialPcbTraceRender(trace) {
6991
6991
  });
6992
6992
  trace._portsRoutedOnPcb = ports;
6993
6993
  trace.pcb_trace_id = pcb_trace.pcb_trace_id;
6994
- for (const point of mergedRoute) {
6995
- if (point.route_type === "via") {
6994
+ for (const point2 of mergedRoute) {
6995
+ if (point2.route_type === "via") {
6996
6996
  db.pcb_via.insert({
6997
6997
  pcb_trace_id: pcb_trace.pcb_trace_id,
6998
- x: point.x,
6999
- y: point.y,
6998
+ x: point2.x,
6999
+ y: point2.y,
7000
7000
  hole_diameter: holeDiameter,
7001
7001
  outer_diameter: padDiameter,
7002
- layers: [point.from_layer, point.to_layer],
7003
- from_layer: point.from_layer,
7004
- to_layer: point.to_layer
7002
+ layers: [point2.from_layer, point2.to_layer],
7003
+ from_layer: point2.from_layer,
7004
+ to_layer: point2.to_layer
7005
7005
  });
7006
7006
  }
7007
7007
  }
@@ -10247,7 +10247,7 @@ var getSimpleRouteJsonFromCircuitJson = ({
10247
10247
  // subcircuit
10248
10248
  layerCount: board?.num_layers ?? 2,
10249
10249
  minTraceWidth,
10250
- outline: board?.outline?.map((point) => ({ ...point }))
10250
+ outline: board?.outline?.map((point2) => ({ ...point2 }))
10251
10251
  },
10252
10252
  connMap
10253
10253
  };
@@ -13735,9 +13735,9 @@ var Group6 = class extends NormalComponent3 {
13735
13735
  const { _parsedProps: props } = this;
13736
13736
  const groupProps2 = props;
13737
13737
  const hasOutline = groupProps2.outline && groupProps2.outline.length > 0;
13738
- const numericOutline = hasOutline ? groupProps2.outline.map((point) => ({
13739
- x: distance8.parse(point.x),
13740
- y: distance8.parse(point.y)
13738
+ const numericOutline = hasOutline ? groupProps2.outline.map((point2) => ({
13739
+ x: distance8.parse(point2.x),
13740
+ y: distance8.parse(point2.y)
13741
13741
  })) : void 0;
13742
13742
  const pcb_group = db.pcb_group.insert({
13743
13743
  is_subcircuit: this.isSubcircuit,
@@ -13766,9 +13766,9 @@ var Group6 = class extends NormalComponent3 {
13766
13766
  if (this.pcb_group_id) {
13767
13767
  const hasExplicitPositioning = this._parsedProps.pcbX !== void 0 || this._parsedProps.pcbY !== void 0;
13768
13768
  if (hasOutline) {
13769
- const numericOutline = props.outline.map((point) => ({
13770
- x: distance8.parse(point.x),
13771
- y: distance8.parse(point.y)
13769
+ const numericOutline = props.outline.map((point2) => ({
13770
+ x: distance8.parse(point2.x),
13771
+ y: distance8.parse(point2.y)
13772
13772
  }));
13773
13773
  const outlineBounds = getBoundsFromPoints3(numericOutline);
13774
13774
  if (!outlineBounds) return;
@@ -14183,20 +14183,20 @@ var Group6 = class extends NormalComponent3 {
14183
14183
  continue;
14184
14184
  }
14185
14185
  if (pcb_trace.type === "pcb_trace") {
14186
- for (const point of pcb_trace.route) {
14187
- if (point.route_type === "via") {
14186
+ for (const point2 of pcb_trace.route) {
14187
+ if (point2.route_type === "via") {
14188
14188
  db.pcb_via.insert({
14189
14189
  pcb_trace_id: pcb_trace.pcb_trace_id,
14190
- x: point.x,
14191
- y: point.y,
14190
+ x: point2.x,
14191
+ y: point2.y,
14192
14192
  hole_diameter: holeDiameter,
14193
14193
  outer_diameter: padDiameter,
14194
14194
  layers: [
14195
- point.from_layer,
14196
- point.to_layer
14195
+ point2.from_layer,
14196
+ point2.to_layer
14197
14197
  ],
14198
- from_layer: point.from_layer,
14199
- to_layer: point.to_layer
14198
+ from_layer: point2.from_layer,
14199
+ to_layer: point2.to_layer
14200
14200
  });
14201
14201
  }
14202
14202
  }
@@ -15047,7 +15047,8 @@ var Inductor = class extends NormalComponent3 {
15047
15047
  const source_component = db.source_component.insert({
15048
15048
  name: this.name,
15049
15049
  ftype: FTYPE.simple_inductor,
15050
- inductance: props.inductance,
15050
+ inductance: this.props.inductance,
15051
+ display_inductance: this._getSchematicSymbolDisplayValue(),
15051
15052
  supplier_part_numbers: props.supplierPartNumbers,
15052
15053
  are_pins_interchangeable: true
15053
15054
  });
@@ -15635,9 +15636,9 @@ var Board = class extends Group6 {
15635
15636
  center
15636
15637
  };
15637
15638
  if (outline) {
15638
- update.outline = outline.map((point) => ({
15639
- x: point.x + (props.outlineOffsetX ?? 0),
15640
- y: point.y + (props.outlineOffsetY ?? 0)
15639
+ update.outline = outline.map((point2) => ({
15640
+ x: point2.x + (props.outlineOffsetX ?? 0),
15641
+ y: point2.y + (props.outlineOffsetY ?? 0)
15641
15642
  }));
15642
15643
  }
15643
15644
  db.pcb_board.update(this.pcb_board_id, update);
@@ -15716,8 +15717,8 @@ var Board = class extends Group6 {
15716
15717
  });
15717
15718
  }
15718
15719
  if (props.outline) {
15719
- const xValues = props.outline.map((point) => point.x);
15720
- const yValues = props.outline.map((point) => point.y);
15720
+ const xValues = props.outline.map((point2) => point2.x);
15721
+ const yValues = props.outline.map((point2) => point2.y);
15721
15722
  const minX = Math.min(...xValues);
15722
15723
  const maxX = Math.max(...xValues);
15723
15724
  const minY = Math.min(...yValues);
@@ -15743,9 +15744,9 @@ var Board = class extends Group6 {
15743
15744
  num_layers: this.allLayers.length,
15744
15745
  width: computedWidth,
15745
15746
  height: computedHeight,
15746
- outline: outline?.map((point) => ({
15747
- x: point.x + (props.outlineOffsetX ?? 0),
15748
- y: point.y + (props.outlineOffsetY ?? 0)
15747
+ outline: outline?.map((point2) => ({
15748
+ x: point2.x + (props.outlineOffsetX ?? 0),
15749
+ y: point2.y + (props.outlineOffsetY ?? 0)
15749
15750
  })),
15750
15751
  material: props.material
15751
15752
  });
@@ -15821,6 +15822,7 @@ import { panelProps } from "@tscircuit/props";
15821
15822
  import { distance as distance9 } from "circuit-json";
15822
15823
 
15823
15824
  // lib/utils/panels/generate-panel-tabs-and-mouse-bites.ts
15825
+ import * as Flatten from "@flatten-js/core";
15824
15826
  var DEFAULT_PANEL_MARGIN = 5;
15825
15827
  var DEFAULT_TAB_LENGTH = 5;
15826
15828
  var DEFAULT_TAB_WIDTH = 2;
@@ -15835,15 +15837,15 @@ function rectanglesOverlap(rect1, rect2) {
15835
15837
  const r2Top = rect2.center.y + rect2.height / 2;
15836
15838
  return !(r1Right <= r2Left || r1Left >= r2Right || r1Top <= r2Bottom || r1Bottom >= r2Top);
15837
15839
  }
15838
- function pointOverlapsRectangle(point, radius, rect) {
15840
+ function pointOverlapsRectangle(point2, radius, rect) {
15839
15841
  const rectLeft = rect.center.x - rect.width / 2;
15840
15842
  const rectRight = rect.center.x + rect.width / 2;
15841
15843
  const rectBottom = rect.center.y - rect.height / 2;
15842
15844
  const rectTop = rect.center.y + rect.height / 2;
15843
- const closestX = Math.max(rectLeft, Math.min(point.x, rectRight));
15844
- const closestY = Math.max(rectBottom, Math.min(point.y, rectTop));
15845
- const distanceX = point.x - closestX;
15846
- const distanceY = point.y - closestY;
15845
+ const closestX = Math.max(rectLeft, Math.min(point2.x, rectRight));
15846
+ const closestY = Math.max(rectBottom, Math.min(point2.y, rectTop));
15847
+ const distanceX = point2.x - closestX;
15848
+ const distanceY = point2.y - closestY;
15847
15849
  return distanceX * distanceX + distanceY * distanceY <= radius * radius;
15848
15850
  }
15849
15851
  function generateTabsForEdge({
@@ -16019,44 +16021,199 @@ function generateMouseBitesForEdge({
16019
16021
  }
16020
16022
  return mouseBites;
16021
16023
  }
16024
+ var generatePanelTabsAndMouseBitesForOutlines = (outline, otherBoards, options) => {
16025
+ const {
16026
+ boardGap,
16027
+ tabLength,
16028
+ // along edge
16029
+ tabWidth,
16030
+ // extrusion
16031
+ mouseBites,
16032
+ mouseBiteHoleDiameter,
16033
+ mouseBiteHoleSpacing
16034
+ } = options;
16035
+ const tabCutouts = [];
16036
+ const mouseBiteHoles = [];
16037
+ if (outline.length < 2) {
16038
+ return { tabCutouts, mouseBiteHoles };
16039
+ }
16040
+ const outlinePolygon = new Flatten.Polygon(
16041
+ outline.map((p) => Flatten.point(p.x, p.y))
16042
+ );
16043
+ const otherBoardGeometries = otherBoards.map(
16044
+ (b) => b.outline ? new Flatten.Polygon(b.outline.map((p) => Flatten.point(p.x, p.y))) : b.width && b.height ? new Flatten.Box(
16045
+ b.center.x - b.width / 2,
16046
+ b.center.y - b.height / 2,
16047
+ b.center.x + b.width / 2,
16048
+ b.center.y + b.height / 2
16049
+ ) : null
16050
+ );
16051
+ for (let i = 0; i < outline.length; i++) {
16052
+ const p1 = outline[i];
16053
+ const p2 = outline[(i + 1) % outline.length];
16054
+ const segmentVector = Flatten.vector(p2.x - p1.x, p2.y - p1.y);
16055
+ const segmentLength = segmentVector.length;
16056
+ const physicalTabLength = boardGap;
16057
+ const cutoutLength = tabLength;
16058
+ if (segmentLength < physicalTabLength) continue;
16059
+ const segmentDirVec = segmentVector.normalize();
16060
+ const isHorizontal = Math.abs(segmentVector.y) < 1e-9;
16061
+ const isVertical = Math.abs(segmentVector.x) < 1e-9;
16062
+ const isAxisAligned = isHorizontal || isVertical;
16063
+ let normalVec = segmentDirVec.rotate(Math.PI / 2);
16064
+ const midPoint = Flatten.point(p1.x, p1.y).translate(
16065
+ segmentDirVec.multiply(segmentLength / 2)
16066
+ );
16067
+ const testPointInward = midPoint.translate(normalVec.multiply(0.01));
16068
+ if (outlinePolygon.contains(testPointInward)) {
16069
+ normalVec = normalVec.multiply(-1);
16070
+ }
16071
+ const testPointOutward = midPoint.translate(
16072
+ normalVec.multiply((tabWidth + physicalTabLength) / 2)
16073
+ );
16074
+ let isExterior = true;
16075
+ for (const geom of otherBoardGeometries) {
16076
+ if (geom?.contains(testPointOutward)) {
16077
+ isExterior = false;
16078
+ break;
16079
+ }
16080
+ }
16081
+ if (!isExterior) continue;
16082
+ const numTabs = Math.max(
16083
+ 1,
16084
+ Math.floor(segmentLength / (physicalTabLength + cutoutLength))
16085
+ );
16086
+ const totalContentLength = numTabs * physicalTabLength;
16087
+ const totalGapLength = segmentLength - totalContentLength;
16088
+ const gapSize = totalGapLength / (numTabs + 1);
16089
+ if (gapSize < 0) continue;
16090
+ const tabsOnSegment = [];
16091
+ for (let j = 0; j < numTabs; j++) {
16092
+ const tabStartDist = gapSize * (j + 1) + physicalTabLength * j;
16093
+ tabsOnSegment.push({
16094
+ start: tabStartDist,
16095
+ end: tabStartDist + physicalTabLength
16096
+ });
16097
+ }
16098
+ const extrusion = normalVec.multiply(tabWidth);
16099
+ for (let j = 0; j <= numTabs; j++) {
16100
+ const gapStartDist = j === 0 ? 0 : tabsOnSegment[j - 1].end;
16101
+ const gapEndDist = j === numTabs ? segmentLength : tabsOnSegment[j].start;
16102
+ const gapLength = gapEndDist - gapStartDist;
16103
+ if (gapLength < 1e-6) continue;
16104
+ if (isAxisAligned) {
16105
+ const width = isHorizontal ? gapLength : tabWidth;
16106
+ const height = isHorizontal ? tabWidth : gapLength;
16107
+ const gapCenterAlongSegment = Flatten.point(p1.x, p1.y).translate(
16108
+ segmentDirVec.multiply(gapStartDist + gapLength / 2)
16109
+ );
16110
+ const center = gapCenterAlongSegment.translate(extrusion.multiply(0.5));
16111
+ tabCutouts.push({
16112
+ type: "pcb_cutout",
16113
+ shape: "rect",
16114
+ center,
16115
+ width,
16116
+ height,
16117
+ corner_radius: Math.min(width, height) / 2
16118
+ });
16119
+ } else {
16120
+ const width = gapLength;
16121
+ const height = tabWidth;
16122
+ const gapCenterAlongSegment = Flatten.point(p1.x, p1.y).translate(
16123
+ segmentDirVec.multiply(gapStartDist + gapLength / 2)
16124
+ );
16125
+ const center = gapCenterAlongSegment.translate(extrusion.multiply(0.5));
16126
+ const rotationDeg = segmentDirVec.slope * 180 / Math.PI;
16127
+ tabCutouts.push({
16128
+ type: "pcb_cutout",
16129
+ shape: "rect",
16130
+ center,
16131
+ width,
16132
+ height,
16133
+ rotation: rotationDeg,
16134
+ corner_radius: Math.min(width, height) / 2
16135
+ });
16136
+ }
16137
+ }
16138
+ if (mouseBites) {
16139
+ const holeSpacing = mouseBiteHoleDiameter + mouseBiteHoleSpacing;
16140
+ for (const tab of tabsOnSegment) {
16141
+ const tabLength2 = tab.end - tab.start;
16142
+ if (tabLength2 < holeSpacing) continue;
16143
+ const numBitesInTab = Math.floor(tabLength2 / holeSpacing);
16144
+ if (numBitesInTab <= 0) continue;
16145
+ const biteStartOffset = tab.start + (tabLength2 - (numBitesInTab - 1) * holeSpacing) / 2;
16146
+ for (let k = 0; k < numBitesInTab; k++) {
16147
+ const biteDist = biteStartOffset + k * holeSpacing;
16148
+ const pos = Flatten.point(p1.x, p1.y).translate(
16149
+ segmentDirVec.multiply(biteDist)
16150
+ );
16151
+ mouseBiteHoles.push({
16152
+ x: pos.x,
16153
+ y: pos.y
16154
+ });
16155
+ }
16156
+ }
16157
+ }
16158
+ }
16159
+ return { tabCutouts, mouseBiteHoles };
16160
+ };
16022
16161
  function generatePanelTabsAndMouseBites(boards, options) {
16023
- const allTabCutouts = [];
16162
+ const finalTabCutouts = [];
16024
16163
  const allMouseBites = [];
16025
16164
  for (let boardIndex = 0; boardIndex < boards.length; boardIndex++) {
16026
16165
  const board = boards[boardIndex];
16027
16166
  const otherBoards = boards.filter((_, i) => i !== boardIndex);
16028
- for (const edge of ["top", "bottom", "left", "right"]) {
16029
- const edgeTabs = generateTabsForEdge({
16030
- board,
16031
- edge,
16167
+ if (board.outline && board.outline.length > 0) {
16168
+ const mouseBiteDiameter2 = options.tabWidth * 0.45;
16169
+ const mouseBiteSpacing = mouseBiteDiameter2 * 0.1;
16170
+ const generated = generatePanelTabsAndMouseBitesForOutlines(
16171
+ board.outline,
16032
16172
  otherBoards,
16033
- options
16034
- });
16035
- allTabCutouts.push(...edgeTabs);
16036
- if (options.mouseBites) {
16037
- const edgeMouseBites = generateMouseBitesForEdge({
16173
+ {
16174
+ ...options,
16175
+ mouseBiteHoleDiameter: mouseBiteDiameter2,
16176
+ mouseBiteHoleSpacing: mouseBiteSpacing
16177
+ }
16178
+ );
16179
+ finalTabCutouts.push(...generated.tabCutouts);
16180
+ allMouseBites.push(...generated.mouseBiteHoles);
16181
+ } else {
16182
+ for (const edge of ["top", "bottom", "left", "right"]) {
16183
+ const edgeTabs = generateTabsForEdge({
16038
16184
  board,
16039
16185
  edge,
16040
- edgeTabs,
16041
- allBoards: otherBoards,
16186
+ otherBoards,
16042
16187
  options
16043
16188
  });
16044
- allMouseBites.push(...edgeMouseBites);
16189
+ for (const tab of edgeTabs) {
16190
+ const tabWidthDimension = Math.min(tab.width, tab.height);
16191
+ finalTabCutouts.push({
16192
+ type: "pcb_cutout",
16193
+ shape: "rect",
16194
+ center: tab.center,
16195
+ width: tab.width,
16196
+ height: tab.height,
16197
+ corner_radius: tabWidthDimension / 2
16198
+ });
16199
+ }
16200
+ if (options.mouseBites) {
16201
+ const edgeMouseBites = generateMouseBitesForEdge({
16202
+ board,
16203
+ edge,
16204
+ edgeTabs,
16205
+ allBoards: otherBoards,
16206
+ options
16207
+ });
16208
+ allMouseBites.push(...edgeMouseBites);
16209
+ }
16045
16210
  }
16046
16211
  }
16047
16212
  }
16048
- const tabCutouts = allTabCutouts.map((tab, index) => {
16049
- const tabWidthDimension = Math.min(tab.width, tab.height);
16050
- return {
16051
- type: "pcb_cutout",
16052
- pcb_cutout_id: `panel_tab_${index}`,
16053
- shape: "rect",
16054
- center: tab.center,
16055
- width: tab.width,
16056
- height: tab.height,
16057
- corner_radius: tabWidthDimension / 2
16058
- };
16059
- });
16213
+ const tabCutouts = finalTabCutouts.map((tab, index) => ({
16214
+ ...tab,
16215
+ pcb_cutout_id: `panel_tab_${index}`
16216
+ }));
16060
16217
  const mouseBiteDiameter = options.tabWidth * 0.45;
16061
16218
  const mouseBiteHoles = allMouseBites.map((bite, index) => ({
16062
16219
  type: "pcb_hole",
@@ -17211,8 +17368,8 @@ var PcbNotePath = class extends PrimitiveComponent2 {
17211
17368
  const subcircuit = this.getSubcircuit();
17212
17369
  const group = this.getGroup();
17213
17370
  const pcb_component_id = this.parent?.pcb_component_id ?? this.getPrimitiveContainer()?.pcb_component_id ?? void 0;
17214
- const transformedRoute = props.route.map((point) => {
17215
- const { x, y, ...rest } = point;
17371
+ const transformedRoute = props.route.map((point2) => {
17372
+ const { x, y, ...rest } = point2;
17216
17373
  const numericX = typeof x === "string" ? parseFloat(x) : x;
17217
17374
  const numericY = typeof y === "string" ? parseFloat(y) : y;
17218
17375
  const transformed = applyToPoint15(transform, { x: numericX, y: numericY });
@@ -17232,10 +17389,10 @@ var PcbNotePath = class extends PrimitiveComponent2 {
17232
17389
  const { _parsedProps: props } = this;
17233
17390
  if (props.route.length === 0) return { width: 0, height: 0 };
17234
17391
  const xs = props.route.map(
17235
- (point) => typeof point.x === "string" ? parseFloat(point.x) : point.x
17392
+ (point2) => typeof point2.x === "string" ? parseFloat(point2.x) : point2.x
17236
17393
  );
17237
17394
  const ys = props.route.map(
17238
- (point) => typeof point.y === "string" ? parseFloat(point.y) : point.y
17395
+ (point2) => typeof point2.y === "string" ? parseFloat(point2.y) : point2.y
17239
17396
  );
17240
17397
  const minX = Math.min(...xs);
17241
17398
  const maxX = Math.max(...xs);
@@ -19261,7 +19418,7 @@ import { identity as identity5 } from "transformation-matrix";
19261
19418
  var package_default = {
19262
19419
  name: "@tscircuit/core",
19263
19420
  type: "module",
19264
- version: "0.0.904",
19421
+ version: "0.0.906",
19265
19422
  types: "dist/index.d.ts",
19266
19423
  main: "dist/index.js",
19267
19424
  module: "dist/index.js",
@@ -19324,7 +19481,7 @@ var package_default = {
19324
19481
  "circuit-json-to-connectivity-map": "^0.0.22",
19325
19482
  "circuit-json-to-gltf": "^0.0.31",
19326
19483
  "circuit-json-to-simple-3d": "^0.0.9",
19327
- "circuit-json-to-spice": "^0.0.27",
19484
+ "circuit-json-to-spice": "^0.0.30",
19328
19485
  "circuit-to-svg": "^0.0.284",
19329
19486
  concurrently: "^9.1.2",
19330
19487
  "connectivity-map": "^1.0.0",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/core",
3
3
  "type": "module",
4
- "version": "0.0.905",
4
+ "version": "0.0.907",
5
5
  "types": "dist/index.d.ts",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
@@ -64,7 +64,7 @@
64
64
  "circuit-json-to-connectivity-map": "^0.0.22",
65
65
  "circuit-json-to-gltf": "^0.0.31",
66
66
  "circuit-json-to-simple-3d": "^0.0.9",
67
- "circuit-json-to-spice": "^0.0.27",
67
+ "circuit-json-to-spice": "^0.0.30",
68
68
  "circuit-to-svg": "^0.0.284",
69
69
  "concurrently": "^9.1.2",
70
70
  "connectivity-map": "^1.0.0",