@tscircuit/core 0.0.872 → 0.0.873

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
@@ -3240,6 +3240,11 @@ declare class Panel extends Group<typeof panelProps> {
3240
3240
  height: zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>;
3241
3241
  children: zod.ZodOptional<zod.ZodAny>;
3242
3242
  noSolderMask: zod.ZodOptional<zod.ZodBoolean>;
3243
+ panelizationMethod: zod.ZodOptional<zod.ZodEnum<["tab-routing", "none"]>>;
3244
+ boardGap: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>>;
3245
+ tabWidth: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>>;
3246
+ tabLength: zod.ZodOptional<zod.ZodEffects<zod.ZodUnion<[zod.ZodString, zod.ZodNumber]>, number, string | number>>;
3247
+ mouseBites: zod.ZodOptional<zod.ZodBoolean>;
3243
3248
  }, "strip", zod.ZodTypeAny, {
3244
3249
  width: number;
3245
3250
  height: number;
@@ -3484,6 +3489,11 @@ declare class Panel extends Group<typeof panelProps> {
3484
3489
  schPack?: boolean | undefined;
3485
3490
  schMatchAdapt?: boolean | undefined;
3486
3491
  noSolderMask?: boolean | undefined;
3492
+ panelizationMethod?: "none" | "tab-routing" | undefined;
3493
+ boardGap?: number | undefined;
3494
+ tabWidth?: number | undefined;
3495
+ tabLength?: number | undefined;
3496
+ mouseBites?: boolean | undefined;
3487
3497
  }, {
3488
3498
  width: string | number;
3489
3499
  height: string | number;
@@ -3730,6 +3740,11 @@ declare class Panel extends Group<typeof panelProps> {
3730
3740
  schPack?: boolean | undefined;
3731
3741
  schMatchAdapt?: boolean | undefined;
3732
3742
  noSolderMask?: boolean | undefined;
3743
+ panelizationMethod?: "none" | "tab-routing" | undefined;
3744
+ boardGap?: string | number | undefined;
3745
+ tabWidth?: string | number | undefined;
3746
+ tabLength?: string | number | undefined;
3747
+ mouseBites?: boolean | undefined;
3733
3748
  }>;
3734
3749
  };
3735
3750
  get isGroup(): boolean;
package/dist/index.js CHANGED
@@ -14333,14 +14333,9 @@ import { panelProps } from "@tscircuit/props";
14333
14333
  import { distance as distance9 } from "circuit-json";
14334
14334
 
14335
14335
  // lib/utils/panels/generate-panel-tabs-and-mouse-bites.ts
14336
- var TAB_CONFIG = {
14337
- TAB_WIDTH: 4,
14338
- TAB_DEPTH: 0.5,
14339
- TAB_TO_SPACE_RATIO: 5,
14340
- MOUSE_BITE_DIAMETER: 0.2,
14341
- MOUSE_BITE_SPACING: 0.1,
14342
- MOUSE_BITES_PER_GAP: 5
14343
- };
14336
+ var DEFAULT_PANEL_MARGIN = 5;
14337
+ var DEFAULT_TAB_LENGTH = 5;
14338
+ var DEFAULT_TAB_WIDTH = 2;
14344
14339
  function rectanglesOverlap(rect1, rect2) {
14345
14340
  const r1Left = rect1.center.x - rect1.width / 2;
14346
14341
  const r1Right = rect1.center.x + rect1.width / 2;
@@ -14363,7 +14358,12 @@ function pointOverlapsRectangle(point, radius, rect) {
14363
14358
  const distanceY = point.y - closestY;
14364
14359
  return distanceX * distanceX + distanceY * distanceY <= radius * radius;
14365
14360
  }
14366
- function generateTabsForEdge(board, edge, existingTabs, otherBoards) {
14361
+ function generateTabsForEdge({
14362
+ board,
14363
+ edge,
14364
+ otherBoards,
14365
+ options
14366
+ }) {
14367
14367
  const tabs = [];
14368
14368
  if (!board.width || !board.height) return tabs;
14369
14369
  const boardLeft = board.center.x - board.width / 2;
@@ -14382,9 +14382,15 @@ function generateTabsForEdge(board, edge, existingTabs, otherBoards) {
14382
14382
  isHorizontal = false;
14383
14383
  edgeCenter = edge === "right" ? boardRight : boardLeft;
14384
14384
  }
14385
- const totalTabWidth = TAB_CONFIG.TAB_WIDTH;
14386
- const minSpacingForMouseBites = TAB_CONFIG.MOUSE_BITES_PER_GAP * TAB_CONFIG.MOUSE_BITE_DIAMETER + (TAB_CONFIG.MOUSE_BITES_PER_GAP - 1) * TAB_CONFIG.MOUSE_BITE_SPACING;
14387
- const fixedSpacing = minSpacingForMouseBites * 1.1;
14385
+ const totalTabWidth = options.tabLength;
14386
+ let fixedSpacing = options.boardGap;
14387
+ if (options.mouseBites) {
14388
+ const mouseBiteDiameter = options.tabWidth * 0.45;
14389
+ const mouseBiteSpacing = mouseBiteDiameter * 0.1;
14390
+ const mouseBitesPerGap = Math.max(2, Math.ceil(options.tabLength / 2));
14391
+ const minSpacingForMouseBites = mouseBitesPerGap * mouseBiteDiameter + (mouseBitesPerGap - 1) * mouseBiteSpacing;
14392
+ fixedSpacing = minSpacingForMouseBites * 1.1;
14393
+ }
14388
14394
  let numTabs = Math.floor(
14389
14395
  (edgeLength - fixedSpacing) / (totalTabWidth + fixedSpacing)
14390
14396
  );
@@ -14409,13 +14415,13 @@ function generateTabsForEdge(board, edge, existingTabs, otherBoards) {
14409
14415
  axisStart = Math.max(axisStart, boardStart);
14410
14416
  axisEnd = Math.min(axisEnd, boardEnd);
14411
14417
  if (isCornerTab) {
14412
- if (isFirstTab) axisStart -= TAB_CONFIG.TAB_DEPTH;
14413
- if (isLastTab) axisEnd += TAB_CONFIG.TAB_DEPTH;
14418
+ if (isFirstTab) axisStart -= options.tabWidth;
14419
+ if (isLastTab) axisEnd += options.tabWidth;
14414
14420
  }
14415
14421
  if (axisEnd <= axisStart) continue;
14416
14422
  const axisCenterOffset = (axisStart + axisEnd) / 2;
14417
14423
  const axisLength = axisEnd - axisStart;
14418
- const crossAxisOffset = edge === "top" || edge === "right" ? TAB_CONFIG.TAB_DEPTH / 2 : -TAB_CONFIG.TAB_DEPTH / 2;
14424
+ const crossAxisOffset = edge === "top" || edge === "right" ? options.tabWidth / 2 : -options.tabWidth / 2;
14419
14425
  const tabCenter = isHorizontal ? {
14420
14426
  x: board.center.x + axisCenterOffset,
14421
14427
  y: edgeCenter + crossAxisOffset
@@ -14423,8 +14429,8 @@ function generateTabsForEdge(board, edge, existingTabs, otherBoards) {
14423
14429
  x: edgeCenter + crossAxisOffset,
14424
14430
  y: board.center.y + axisCenterOffset
14425
14431
  };
14426
- const tabWidth = isHorizontal ? axisLength : TAB_CONFIG.TAB_DEPTH;
14427
- const tabHeight = isHorizontal ? TAB_CONFIG.TAB_DEPTH : axisLength;
14432
+ const tabWidth = isHorizontal ? axisLength : options.tabWidth;
14433
+ const tabHeight = isHorizontal ? options.tabWidth : axisLength;
14428
14434
  const newTab = {
14429
14435
  center: tabCenter,
14430
14436
  width: tabWidth,
@@ -14449,7 +14455,13 @@ function generateTabsForEdge(board, edge, existingTabs, otherBoards) {
14449
14455
  }
14450
14456
  return tabs;
14451
14457
  }
14452
- function generateMouseBitesForEdge(board, edge, edgeTabs, allTabs, allBoards, existingMouseBites) {
14458
+ function generateMouseBitesForEdge({
14459
+ board,
14460
+ edge,
14461
+ edgeTabs,
14462
+ allBoards,
14463
+ options
14464
+ }) {
14453
14465
  const mouseBites = [];
14454
14466
  if (edgeTabs.length === 0) return mouseBites;
14455
14467
  if (!board.width || !board.height) return mouseBites;
@@ -14458,8 +14470,11 @@ function generateMouseBitesForEdge(board, edge, edgeTabs, allTabs, allBoards, ex
14458
14470
  const boardBottom = board.center.y - board.height / 2;
14459
14471
  const boardTop = board.center.y + board.height / 2;
14460
14472
  const isHorizontal = edge === "top" || edge === "bottom";
14473
+ const mouseBiteDiameter = options.tabWidth * 0.45;
14474
+ const mouseBiteSpacing = mouseBiteDiameter * 0.1;
14475
+ const mouseBitesPerGap = Math.max(2, Math.ceil(options.tabLength / 2));
14461
14476
  let mouseBitePosition;
14462
- const radius = TAB_CONFIG.MOUSE_BITE_DIAMETER / 2;
14477
+ const radius = mouseBiteDiameter / 2;
14463
14478
  if (edge === "top") {
14464
14479
  mouseBitePosition = boardTop;
14465
14480
  } else if (edge === "bottom") {
@@ -14489,14 +14504,14 @@ function generateMouseBitesForEdge(board, edge, edgeTabs, allTabs, allBoards, ex
14489
14504
  gapEnd = tab2.center.y - tab2.height / 2;
14490
14505
  }
14491
14506
  const gapLength = gapEnd - gapStart;
14492
- const totalMouseBiteWidth = TAB_CONFIG.MOUSE_BITES_PER_GAP * TAB_CONFIG.MOUSE_BITE_DIAMETER;
14493
- const totalSpacing = (TAB_CONFIG.MOUSE_BITES_PER_GAP - 1) * TAB_CONFIG.MOUSE_BITE_SPACING;
14507
+ const totalMouseBiteWidth = mouseBitesPerGap * mouseBiteDiameter;
14508
+ const totalSpacing = (mouseBitesPerGap - 1) * mouseBiteSpacing;
14494
14509
  if (gapLength < totalMouseBiteWidth + totalSpacing) continue;
14495
14510
  const gapCenter = (gapStart + gapEnd) / 2;
14496
- for (let j = 0; j < TAB_CONFIG.MOUSE_BITES_PER_GAP; j++) {
14497
- const posOffset = (j - (TAB_CONFIG.MOUSE_BITES_PER_GAP - 1) / 2) * (TAB_CONFIG.MOUSE_BITE_DIAMETER + TAB_CONFIG.MOUSE_BITE_SPACING);
14511
+ for (let j = 0; j < mouseBitesPerGap; j++) {
14512
+ const posOffset = (j - (mouseBitesPerGap - 1) / 2) * (mouseBiteDiameter + mouseBiteSpacing);
14498
14513
  const newMouseBite = isHorizontal ? { x: gapCenter + posOffset, y: mouseBitePosition } : { x: mouseBitePosition, y: gapCenter + posOffset };
14499
- const radius2 = TAB_CONFIG.MOUSE_BITE_DIAMETER / 2;
14514
+ const radius2 = mouseBiteDiameter / 2;
14500
14515
  let overlapsBoard = false;
14501
14516
  for (const otherBoard of allBoards) {
14502
14517
  if (!otherBoard.width || !otherBoard.height) continue;
@@ -14516,45 +14531,50 @@ function generateMouseBitesForEdge(board, edge, edgeTabs, allTabs, allBoards, ex
14516
14531
  }
14517
14532
  return mouseBites;
14518
14533
  }
14519
- function generatePanelTabsAndMouseBites(boards) {
14534
+ function generatePanelTabsAndMouseBites(boards, options) {
14520
14535
  const allTabCutouts = [];
14521
14536
  const allMouseBites = [];
14522
14537
  for (let boardIndex = 0; boardIndex < boards.length; boardIndex++) {
14523
14538
  const board = boards[boardIndex];
14524
14539
  const otherBoards = boards.filter((_, i) => i !== boardIndex);
14525
14540
  for (const edge of ["top", "bottom", "left", "right"]) {
14526
- const edgeTabs = generateTabsForEdge(
14527
- board,
14528
- edge,
14529
- allTabCutouts,
14530
- otherBoards
14531
- );
14532
- allTabCutouts.push(...edgeTabs);
14533
- const edgeMouseBites = generateMouseBitesForEdge(
14541
+ const edgeTabs = generateTabsForEdge({
14534
14542
  board,
14535
14543
  edge,
14536
- edgeTabs,
14537
- allTabCutouts,
14538
14544
  otherBoards,
14539
- allMouseBites
14540
- );
14541
- allMouseBites.push(...edgeMouseBites);
14545
+ options
14546
+ });
14547
+ allTabCutouts.push(...edgeTabs);
14548
+ if (options.mouseBites) {
14549
+ const edgeMouseBites = generateMouseBitesForEdge({
14550
+ board,
14551
+ edge,
14552
+ edgeTabs,
14553
+ allBoards: otherBoards,
14554
+ options
14555
+ });
14556
+ allMouseBites.push(...edgeMouseBites);
14557
+ }
14542
14558
  }
14543
14559
  }
14544
- const tabCutouts = allTabCutouts.map((tab, index) => ({
14545
- type: "pcb_cutout",
14546
- pcb_cutout_id: `panel_tab_${index}`,
14547
- shape: "rect",
14548
- center: tab.center,
14549
- width: tab.width,
14550
- height: tab.height,
14551
- corner_radius: 0.25
14552
- }));
14560
+ const tabCutouts = allTabCutouts.map((tab, index) => {
14561
+ const tabWidthDimension = Math.min(tab.width, tab.height);
14562
+ return {
14563
+ type: "pcb_cutout",
14564
+ pcb_cutout_id: `panel_tab_${index}`,
14565
+ shape: "rect",
14566
+ center: tab.center,
14567
+ width: tab.width,
14568
+ height: tab.height,
14569
+ corner_radius: tabWidthDimension / 2
14570
+ };
14571
+ });
14572
+ const mouseBiteDiameter = options.tabWidth * 0.45;
14553
14573
  const mouseBiteHoles = allMouseBites.map((bite, index) => ({
14554
14574
  type: "pcb_hole",
14555
14575
  pcb_hole_id: `panel_mouse_bite_${index}`,
14556
14576
  hole_shape: "circle",
14557
- hole_diameter: TAB_CONFIG.MOUSE_BITE_DIAMETER,
14577
+ hole_diameter: mouseBiteDiameter,
14558
14578
  x: bite.x,
14559
14579
  y: bite.y
14560
14580
  }));
@@ -14600,6 +14620,8 @@ var Panel = class extends Group6 {
14600
14620
  (b) => b.props.pcbX === void 0 && b.props.pcbY === void 0
14601
14621
  );
14602
14622
  if (unpositionedBoards.length > 0 && !hasAnyPositionedBoards) {
14623
+ const tabWidth = this._parsedProps.tabWidth ?? DEFAULT_TAB_WIDTH;
14624
+ const boardGap = this._parsedProps.boardGap ?? tabWidth;
14603
14625
  const gridCols = Math.ceil(Math.sqrt(unpositionedBoards.length));
14604
14626
  const gridRows = Math.ceil(unpositionedBoards.length / gridCols);
14605
14627
  const colWidths = Array(gridCols).fill(0);
@@ -14613,17 +14635,17 @@ var Panel = class extends Group6 {
14613
14635
  colWidths[col] = Math.max(colWidths[col], pcbBoard.width);
14614
14636
  rowHeights[row] = Math.max(rowHeights[row], pcbBoard.height);
14615
14637
  });
14616
- const totalGridWidth = colWidths.reduce((a, b) => a + b, 0) + (gridCols > 1 ? (gridCols - 1) * TAB_CONFIG.TAB_DEPTH : 0);
14617
- const totalGridHeight = rowHeights.reduce((a, b) => a + b, 0) + (gridRows > 1 ? (gridRows - 1) * TAB_CONFIG.TAB_DEPTH : 0);
14638
+ const totalGridWidth = colWidths.reduce((a, b) => a + b, 0) + (gridCols > 1 ? (gridCols - 1) * boardGap : 0);
14639
+ const totalGridHeight = rowHeights.reduce((a, b) => a + b, 0) + (gridRows > 1 ? (gridRows - 1) * boardGap : 0);
14618
14640
  const startX = -totalGridWidth / 2;
14619
14641
  const startY = -totalGridHeight / 2;
14620
14642
  const rowYOffsets = [startY];
14621
14643
  for (let i = 0; i < gridRows - 1; i++) {
14622
- rowYOffsets.push(rowYOffsets[i] + rowHeights[i] + TAB_CONFIG.TAB_DEPTH);
14644
+ rowYOffsets.push(rowYOffsets[i] + rowHeights[i] + boardGap);
14623
14645
  }
14624
14646
  const colXOffsets = [startX];
14625
14647
  for (let i = 0; i < gridCols - 1; i++) {
14626
- colXOffsets.push(colXOffsets[i] + colWidths[i] + TAB_CONFIG.TAB_DEPTH);
14648
+ colXOffsets.push(colXOffsets[i] + colWidths[i] + boardGap);
14627
14649
  }
14628
14650
  unpositionedBoards.forEach((board, i) => {
14629
14651
  const col = i % gridCols;
@@ -14657,9 +14679,8 @@ var Panel = class extends Group6 {
14657
14679
  if (isFinite(minX)) {
14658
14680
  const boundsWidth = maxX - minX;
14659
14681
  const boundsHeight = maxY - minY;
14660
- const margin = TAB_CONFIG.TAB_DEPTH * 3;
14661
- const newPanelWidth = boundsWidth + 2 * margin;
14662
- const newPanelHeight = boundsHeight + 2 * margin;
14682
+ const newPanelWidth = boundsWidth + 2 * DEFAULT_PANEL_MARGIN;
14683
+ const newPanelHeight = boundsHeight + 2 * DEFAULT_PANEL_MARGIN;
14663
14684
  db.pcb_panel.update(this.pcb_panel_id, {
14664
14685
  width: newPanelWidth,
14665
14686
  height: newPanelHeight
@@ -14667,15 +14688,29 @@ var Panel = class extends Group6 {
14667
14688
  }
14668
14689
  }
14669
14690
  if (this._tabsAndMouseBitesGenerated) return;
14670
- const childBoardIds = childBoardInstances.map((c) => c.pcb_board_id).filter((id) => !!id);
14671
- const boardsInPanel = db.pcb_board.list().filter((b) => childBoardIds.includes(b.pcb_board_id));
14672
- if (boardsInPanel.length === 0) return;
14673
- const { tabCutouts, mouseBiteHoles } = generatePanelTabsAndMouseBites(boardsInPanel);
14674
- for (const tabCutout of tabCutouts) {
14675
- db.pcb_cutout.insert(tabCutout);
14676
- }
14677
- for (const mouseBiteHole of mouseBiteHoles) {
14678
- db.pcb_hole.insert(mouseBiteHole);
14691
+ const props = this._parsedProps;
14692
+ const panelizationMethod = props.panelizationMethod ?? "tab-routing";
14693
+ if (panelizationMethod !== "none") {
14694
+ const childBoardIds = childBoardInstances.map((c) => c.pcb_board_id).filter((id) => !!id);
14695
+ const boardsInPanel = db.pcb_board.list().filter((b) => childBoardIds.includes(b.pcb_board_id));
14696
+ if (boardsInPanel.length === 0) return;
14697
+ const tabWidth = props.tabWidth ?? DEFAULT_TAB_WIDTH;
14698
+ const boardGap = props.boardGap ?? tabWidth;
14699
+ const { tabCutouts, mouseBiteHoles } = generatePanelTabsAndMouseBites(
14700
+ boardsInPanel,
14701
+ {
14702
+ boardGap,
14703
+ tabWidth,
14704
+ tabLength: props.tabLength ?? DEFAULT_TAB_LENGTH,
14705
+ mouseBites: props.mouseBites ?? true
14706
+ }
14707
+ );
14708
+ for (const tabCutout of tabCutouts) {
14709
+ db.pcb_cutout.insert(tabCutout);
14710
+ }
14711
+ for (const mouseBiteHole of mouseBiteHoles) {
14712
+ db.pcb_hole.insert(mouseBiteHole);
14713
+ }
14679
14714
  }
14680
14715
  this._tabsAndMouseBitesGenerated = true;
14681
14716
  }
@@ -18493,7 +18528,7 @@ import { identity as identity6 } from "transformation-matrix";
18493
18528
  var package_default = {
18494
18529
  name: "@tscircuit/core",
18495
18530
  type: "module",
18496
- version: "0.0.871",
18531
+ version: "0.0.872",
18497
18532
  types: "dist/index.d.ts",
18498
18533
  main: "dist/index.js",
18499
18534
  module: "dist/index.js",
@@ -18537,7 +18572,7 @@ var package_default = {
18537
18572
  "@tscircuit/math-utils": "^0.0.29",
18538
18573
  "@tscircuit/miniflex": "^0.0.4",
18539
18574
  "@tscircuit/ngspice-spice-engine": "^0.0.3",
18540
- "@tscircuit/props": "^0.0.408",
18575
+ "@tscircuit/props": "^0.0.409",
18541
18576
  "@tscircuit/schematic-autolayout": "^0.0.6",
18542
18577
  "@tscircuit/schematic-match-adapt": "^0.0.16",
18543
18578
  "@tscircuit/schematic-trace-solver": "^v0.0.45",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/core",
3
3
  "type": "module",
4
- "version": "0.0.872",
4
+ "version": "0.0.873",
5
5
  "types": "dist/index.d.ts",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
@@ -45,7 +45,7 @@
45
45
  "@tscircuit/math-utils": "^0.0.29",
46
46
  "@tscircuit/miniflex": "^0.0.4",
47
47
  "@tscircuit/ngspice-spice-engine": "^0.0.3",
48
- "@tscircuit/props": "^0.0.408",
48
+ "@tscircuit/props": "^0.0.409",
49
49
  "@tscircuit/schematic-autolayout": "^0.0.6",
50
50
  "@tscircuit/schematic-match-adapt": "^0.0.16",
51
51
  "@tscircuit/schematic-trace-solver": "^v0.0.45",