@tscircuit/core 0.0.905 → 0.0.906

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 +277 -121
  2. package/package.json +1 -1
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
  }
@@ -15635,9 +15635,9 @@ var Board = class extends Group6 {
15635
15635
  center
15636
15636
  };
15637
15637
  if (outline) {
15638
- update.outline = outline.map((point) => ({
15639
- x: point.x + (props.outlineOffsetX ?? 0),
15640
- y: point.y + (props.outlineOffsetY ?? 0)
15638
+ update.outline = outline.map((point2) => ({
15639
+ x: point2.x + (props.outlineOffsetX ?? 0),
15640
+ y: point2.y + (props.outlineOffsetY ?? 0)
15641
15641
  }));
15642
15642
  }
15643
15643
  db.pcb_board.update(this.pcb_board_id, update);
@@ -15716,8 +15716,8 @@ var Board = class extends Group6 {
15716
15716
  });
15717
15717
  }
15718
15718
  if (props.outline) {
15719
- const xValues = props.outline.map((point) => point.x);
15720
- const yValues = props.outline.map((point) => point.y);
15719
+ const xValues = props.outline.map((point2) => point2.x);
15720
+ const yValues = props.outline.map((point2) => point2.y);
15721
15721
  const minX = Math.min(...xValues);
15722
15722
  const maxX = Math.max(...xValues);
15723
15723
  const minY = Math.min(...yValues);
@@ -15743,9 +15743,9 @@ var Board = class extends Group6 {
15743
15743
  num_layers: this.allLayers.length,
15744
15744
  width: computedWidth,
15745
15745
  height: computedHeight,
15746
- outline: outline?.map((point) => ({
15747
- x: point.x + (props.outlineOffsetX ?? 0),
15748
- y: point.y + (props.outlineOffsetY ?? 0)
15746
+ outline: outline?.map((point2) => ({
15747
+ x: point2.x + (props.outlineOffsetX ?? 0),
15748
+ y: point2.y + (props.outlineOffsetY ?? 0)
15749
15749
  })),
15750
15750
  material: props.material
15751
15751
  });
@@ -15821,6 +15821,7 @@ import { panelProps } from "@tscircuit/props";
15821
15821
  import { distance as distance9 } from "circuit-json";
15822
15822
 
15823
15823
  // lib/utils/panels/generate-panel-tabs-and-mouse-bites.ts
15824
+ import * as Flatten from "@flatten-js/core";
15824
15825
  var DEFAULT_PANEL_MARGIN = 5;
15825
15826
  var DEFAULT_TAB_LENGTH = 5;
15826
15827
  var DEFAULT_TAB_WIDTH = 2;
@@ -15835,15 +15836,15 @@ function rectanglesOverlap(rect1, rect2) {
15835
15836
  const r2Top = rect2.center.y + rect2.height / 2;
15836
15837
  return !(r1Right <= r2Left || r1Left >= r2Right || r1Top <= r2Bottom || r1Bottom >= r2Top);
15837
15838
  }
15838
- function pointOverlapsRectangle(point, radius, rect) {
15839
+ function pointOverlapsRectangle(point2, radius, rect) {
15839
15840
  const rectLeft = rect.center.x - rect.width / 2;
15840
15841
  const rectRight = rect.center.x + rect.width / 2;
15841
15842
  const rectBottom = rect.center.y - rect.height / 2;
15842
15843
  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;
15844
+ const closestX = Math.max(rectLeft, Math.min(point2.x, rectRight));
15845
+ const closestY = Math.max(rectBottom, Math.min(point2.y, rectTop));
15846
+ const distanceX = point2.x - closestX;
15847
+ const distanceY = point2.y - closestY;
15847
15848
  return distanceX * distanceX + distanceY * distanceY <= radius * radius;
15848
15849
  }
15849
15850
  function generateTabsForEdge({
@@ -16019,44 +16020,199 @@ function generateMouseBitesForEdge({
16019
16020
  }
16020
16021
  return mouseBites;
16021
16022
  }
16023
+ var generatePanelTabsAndMouseBitesForOutlines = (outline, otherBoards, options) => {
16024
+ const {
16025
+ boardGap,
16026
+ tabLength,
16027
+ // along edge
16028
+ tabWidth,
16029
+ // extrusion
16030
+ mouseBites,
16031
+ mouseBiteHoleDiameter,
16032
+ mouseBiteHoleSpacing
16033
+ } = options;
16034
+ const tabCutouts = [];
16035
+ const mouseBiteHoles = [];
16036
+ if (outline.length < 2) {
16037
+ return { tabCutouts, mouseBiteHoles };
16038
+ }
16039
+ const outlinePolygon = new Flatten.Polygon(
16040
+ outline.map((p) => Flatten.point(p.x, p.y))
16041
+ );
16042
+ const otherBoardGeometries = otherBoards.map(
16043
+ (b) => b.outline ? new Flatten.Polygon(b.outline.map((p) => Flatten.point(p.x, p.y))) : b.width && b.height ? new Flatten.Box(
16044
+ b.center.x - b.width / 2,
16045
+ b.center.y - b.height / 2,
16046
+ b.center.x + b.width / 2,
16047
+ b.center.y + b.height / 2
16048
+ ) : null
16049
+ );
16050
+ for (let i = 0; i < outline.length; i++) {
16051
+ const p1 = outline[i];
16052
+ const p2 = outline[(i + 1) % outline.length];
16053
+ const segmentVector = Flatten.vector(p2.x - p1.x, p2.y - p1.y);
16054
+ const segmentLength = segmentVector.length;
16055
+ const physicalTabLength = boardGap;
16056
+ const cutoutLength = tabLength;
16057
+ if (segmentLength < physicalTabLength) continue;
16058
+ const segmentDirVec = segmentVector.normalize();
16059
+ const isHorizontal = Math.abs(segmentVector.y) < 1e-9;
16060
+ const isVertical = Math.abs(segmentVector.x) < 1e-9;
16061
+ const isAxisAligned = isHorizontal || isVertical;
16062
+ let normalVec = segmentDirVec.rotate(Math.PI / 2);
16063
+ const midPoint = Flatten.point(p1.x, p1.y).translate(
16064
+ segmentDirVec.multiply(segmentLength / 2)
16065
+ );
16066
+ const testPointInward = midPoint.translate(normalVec.multiply(0.01));
16067
+ if (outlinePolygon.contains(testPointInward)) {
16068
+ normalVec = normalVec.multiply(-1);
16069
+ }
16070
+ const testPointOutward = midPoint.translate(
16071
+ normalVec.multiply((tabWidth + physicalTabLength) / 2)
16072
+ );
16073
+ let isExterior = true;
16074
+ for (const geom of otherBoardGeometries) {
16075
+ if (geom?.contains(testPointOutward)) {
16076
+ isExterior = false;
16077
+ break;
16078
+ }
16079
+ }
16080
+ if (!isExterior) continue;
16081
+ const numTabs = Math.max(
16082
+ 1,
16083
+ Math.floor(segmentLength / (physicalTabLength + cutoutLength))
16084
+ );
16085
+ const totalContentLength = numTabs * physicalTabLength;
16086
+ const totalGapLength = segmentLength - totalContentLength;
16087
+ const gapSize = totalGapLength / (numTabs + 1);
16088
+ if (gapSize < 0) continue;
16089
+ const tabsOnSegment = [];
16090
+ for (let j = 0; j < numTabs; j++) {
16091
+ const tabStartDist = gapSize * (j + 1) + physicalTabLength * j;
16092
+ tabsOnSegment.push({
16093
+ start: tabStartDist,
16094
+ end: tabStartDist + physicalTabLength
16095
+ });
16096
+ }
16097
+ const extrusion = normalVec.multiply(tabWidth);
16098
+ for (let j = 0; j <= numTabs; j++) {
16099
+ const gapStartDist = j === 0 ? 0 : tabsOnSegment[j - 1].end;
16100
+ const gapEndDist = j === numTabs ? segmentLength : tabsOnSegment[j].start;
16101
+ const gapLength = gapEndDist - gapStartDist;
16102
+ if (gapLength < 1e-6) continue;
16103
+ if (isAxisAligned) {
16104
+ const width = isHorizontal ? gapLength : tabWidth;
16105
+ const height = isHorizontal ? tabWidth : gapLength;
16106
+ const gapCenterAlongSegment = Flatten.point(p1.x, p1.y).translate(
16107
+ segmentDirVec.multiply(gapStartDist + gapLength / 2)
16108
+ );
16109
+ const center = gapCenterAlongSegment.translate(extrusion.multiply(0.5));
16110
+ tabCutouts.push({
16111
+ type: "pcb_cutout",
16112
+ shape: "rect",
16113
+ center,
16114
+ width,
16115
+ height,
16116
+ corner_radius: Math.min(width, height) / 2
16117
+ });
16118
+ } else {
16119
+ const width = gapLength;
16120
+ const height = tabWidth;
16121
+ const gapCenterAlongSegment = Flatten.point(p1.x, p1.y).translate(
16122
+ segmentDirVec.multiply(gapStartDist + gapLength / 2)
16123
+ );
16124
+ const center = gapCenterAlongSegment.translate(extrusion.multiply(0.5));
16125
+ const rotationDeg = segmentDirVec.slope * 180 / Math.PI;
16126
+ tabCutouts.push({
16127
+ type: "pcb_cutout",
16128
+ shape: "rect",
16129
+ center,
16130
+ width,
16131
+ height,
16132
+ rotation: rotationDeg,
16133
+ corner_radius: Math.min(width, height) / 2
16134
+ });
16135
+ }
16136
+ }
16137
+ if (mouseBites) {
16138
+ const holeSpacing = mouseBiteHoleDiameter + mouseBiteHoleSpacing;
16139
+ for (const tab of tabsOnSegment) {
16140
+ const tabLength2 = tab.end - tab.start;
16141
+ if (tabLength2 < holeSpacing) continue;
16142
+ const numBitesInTab = Math.floor(tabLength2 / holeSpacing);
16143
+ if (numBitesInTab <= 0) continue;
16144
+ const biteStartOffset = tab.start + (tabLength2 - (numBitesInTab - 1) * holeSpacing) / 2;
16145
+ for (let k = 0; k < numBitesInTab; k++) {
16146
+ const biteDist = biteStartOffset + k * holeSpacing;
16147
+ const pos = Flatten.point(p1.x, p1.y).translate(
16148
+ segmentDirVec.multiply(biteDist)
16149
+ );
16150
+ mouseBiteHoles.push({
16151
+ x: pos.x,
16152
+ y: pos.y
16153
+ });
16154
+ }
16155
+ }
16156
+ }
16157
+ }
16158
+ return { tabCutouts, mouseBiteHoles };
16159
+ };
16022
16160
  function generatePanelTabsAndMouseBites(boards, options) {
16023
- const allTabCutouts = [];
16161
+ const finalTabCutouts = [];
16024
16162
  const allMouseBites = [];
16025
16163
  for (let boardIndex = 0; boardIndex < boards.length; boardIndex++) {
16026
16164
  const board = boards[boardIndex];
16027
16165
  const otherBoards = boards.filter((_, i) => i !== boardIndex);
16028
- for (const edge of ["top", "bottom", "left", "right"]) {
16029
- const edgeTabs = generateTabsForEdge({
16030
- board,
16031
- edge,
16166
+ if (board.outline && board.outline.length > 0) {
16167
+ const mouseBiteDiameter2 = options.tabWidth * 0.45;
16168
+ const mouseBiteSpacing = mouseBiteDiameter2 * 0.1;
16169
+ const generated = generatePanelTabsAndMouseBitesForOutlines(
16170
+ board.outline,
16032
16171
  otherBoards,
16033
- options
16034
- });
16035
- allTabCutouts.push(...edgeTabs);
16036
- if (options.mouseBites) {
16037
- const edgeMouseBites = generateMouseBitesForEdge({
16172
+ {
16173
+ ...options,
16174
+ mouseBiteHoleDiameter: mouseBiteDiameter2,
16175
+ mouseBiteHoleSpacing: mouseBiteSpacing
16176
+ }
16177
+ );
16178
+ finalTabCutouts.push(...generated.tabCutouts);
16179
+ allMouseBites.push(...generated.mouseBiteHoles);
16180
+ } else {
16181
+ for (const edge of ["top", "bottom", "left", "right"]) {
16182
+ const edgeTabs = generateTabsForEdge({
16038
16183
  board,
16039
16184
  edge,
16040
- edgeTabs,
16041
- allBoards: otherBoards,
16185
+ otherBoards,
16042
16186
  options
16043
16187
  });
16044
- allMouseBites.push(...edgeMouseBites);
16188
+ for (const tab of edgeTabs) {
16189
+ const tabWidthDimension = Math.min(tab.width, tab.height);
16190
+ finalTabCutouts.push({
16191
+ type: "pcb_cutout",
16192
+ shape: "rect",
16193
+ center: tab.center,
16194
+ width: tab.width,
16195
+ height: tab.height,
16196
+ corner_radius: tabWidthDimension / 2
16197
+ });
16198
+ }
16199
+ if (options.mouseBites) {
16200
+ const edgeMouseBites = generateMouseBitesForEdge({
16201
+ board,
16202
+ edge,
16203
+ edgeTabs,
16204
+ allBoards: otherBoards,
16205
+ options
16206
+ });
16207
+ allMouseBites.push(...edgeMouseBites);
16208
+ }
16045
16209
  }
16046
16210
  }
16047
16211
  }
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
- });
16212
+ const tabCutouts = finalTabCutouts.map((tab, index) => ({
16213
+ ...tab,
16214
+ pcb_cutout_id: `panel_tab_${index}`
16215
+ }));
16060
16216
  const mouseBiteDiameter = options.tabWidth * 0.45;
16061
16217
  const mouseBiteHoles = allMouseBites.map((bite, index) => ({
16062
16218
  type: "pcb_hole",
@@ -17211,8 +17367,8 @@ var PcbNotePath = class extends PrimitiveComponent2 {
17211
17367
  const subcircuit = this.getSubcircuit();
17212
17368
  const group = this.getGroup();
17213
17369
  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;
17370
+ const transformedRoute = props.route.map((point2) => {
17371
+ const { x, y, ...rest } = point2;
17216
17372
  const numericX = typeof x === "string" ? parseFloat(x) : x;
17217
17373
  const numericY = typeof y === "string" ? parseFloat(y) : y;
17218
17374
  const transformed = applyToPoint15(transform, { x: numericX, y: numericY });
@@ -17232,10 +17388,10 @@ var PcbNotePath = class extends PrimitiveComponent2 {
17232
17388
  const { _parsedProps: props } = this;
17233
17389
  if (props.route.length === 0) return { width: 0, height: 0 };
17234
17390
  const xs = props.route.map(
17235
- (point) => typeof point.x === "string" ? parseFloat(point.x) : point.x
17391
+ (point2) => typeof point2.x === "string" ? parseFloat(point2.x) : point2.x
17236
17392
  );
17237
17393
  const ys = props.route.map(
17238
- (point) => typeof point.y === "string" ? parseFloat(point.y) : point.y
17394
+ (point2) => typeof point2.y === "string" ? parseFloat(point2.y) : point2.y
17239
17395
  );
17240
17396
  const minX = Math.min(...xs);
17241
17397
  const maxX = Math.max(...xs);
@@ -19261,7 +19417,7 @@ import { identity as identity5 } from "transformation-matrix";
19261
19417
  var package_default = {
19262
19418
  name: "@tscircuit/core",
19263
19419
  type: "module",
19264
- version: "0.0.904",
19420
+ version: "0.0.905",
19265
19421
  types: "dist/index.d.ts",
19266
19422
  main: "dist/index.js",
19267
19423
  module: "dist/index.js",
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.906",
5
5
  "types": "dist/index.d.ts",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",