@tscircuit/core 0.0.907 → 0.0.909
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.js +149 -505
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -11836,9 +11836,7 @@ import {
|
|
|
11836
11836
|
convertPackOutputToPackInput,
|
|
11837
11837
|
getGraphicsFromPackOutput
|
|
11838
11838
|
} from "calculate-packing";
|
|
11839
|
-
import {
|
|
11840
|
-
length as length4
|
|
11841
|
-
} from "circuit-json";
|
|
11839
|
+
import { length as length4 } from "circuit-json";
|
|
11842
11840
|
import Debug8 from "debug";
|
|
11843
11841
|
|
|
11844
11842
|
// lib/components/primitive-components/Group/Group_doInitialPcbLayoutPack/applyComponentConstraintClusters.ts
|
|
@@ -12211,83 +12209,6 @@ var applyPackOutput = (group, packOutput, clusterMap) => {
|
|
|
12211
12209
|
}
|
|
12212
12210
|
};
|
|
12213
12211
|
|
|
12214
|
-
// lib/utils/packing/getObstacleDimensionsFromElement.ts
|
|
12215
|
-
function getObstacleDimensionsFromSmtPad(pad) {
|
|
12216
|
-
switch (pad.shape) {
|
|
12217
|
-
case "rect":
|
|
12218
|
-
case "rotated_rect":
|
|
12219
|
-
case "pill":
|
|
12220
|
-
case "rotated_pill":
|
|
12221
|
-
return {
|
|
12222
|
-
width: pad.width,
|
|
12223
|
-
height: pad.height
|
|
12224
|
-
};
|
|
12225
|
-
case "circle":
|
|
12226
|
-
return {
|
|
12227
|
-
width: pad.radius * 2,
|
|
12228
|
-
height: pad.radius * 2
|
|
12229
|
-
};
|
|
12230
|
-
case "polygon":
|
|
12231
|
-
if (!pad.points || pad.points.length === 0) {
|
|
12232
|
-
return null;
|
|
12233
|
-
}
|
|
12234
|
-
const xs = pad.points.map((p) => p.x);
|
|
12235
|
-
const ys = pad.points.map((p) => p.y);
|
|
12236
|
-
const minX = Math.min(...xs);
|
|
12237
|
-
const maxX = Math.max(...xs);
|
|
12238
|
-
const minY = Math.min(...ys);
|
|
12239
|
-
const maxY = Math.max(...ys);
|
|
12240
|
-
return {
|
|
12241
|
-
width: maxX - minX,
|
|
12242
|
-
height: maxY - minY
|
|
12243
|
-
};
|
|
12244
|
-
default:
|
|
12245
|
-
return null;
|
|
12246
|
-
}
|
|
12247
|
-
}
|
|
12248
|
-
function getObstacleDimensionsFromPlatedHole(hole) {
|
|
12249
|
-
switch (hole.shape) {
|
|
12250
|
-
case "circular_hole_with_rect_pad":
|
|
12251
|
-
case "pill_hole_with_rect_pad":
|
|
12252
|
-
case "rotated_pill_hole_with_rect_pad":
|
|
12253
|
-
return {
|
|
12254
|
-
width: hole.rect_pad_width,
|
|
12255
|
-
height: hole.rect_pad_height
|
|
12256
|
-
};
|
|
12257
|
-
case "circle":
|
|
12258
|
-
return {
|
|
12259
|
-
width: hole.outer_diameter,
|
|
12260
|
-
height: hole.outer_diameter
|
|
12261
|
-
};
|
|
12262
|
-
case "oval":
|
|
12263
|
-
return {
|
|
12264
|
-
width: hole.outer_width,
|
|
12265
|
-
height: hole.outer_height
|
|
12266
|
-
};
|
|
12267
|
-
case "pill":
|
|
12268
|
-
return {
|
|
12269
|
-
width: hole.outer_width,
|
|
12270
|
-
height: hole.outer_height
|
|
12271
|
-
};
|
|
12272
|
-
case "hole_with_polygon_pad":
|
|
12273
|
-
if (!("pad_outline" in hole) || !hole.pad_outline || hole.pad_outline.length === 0) {
|
|
12274
|
-
return null;
|
|
12275
|
-
}
|
|
12276
|
-
const xs = hole.pad_outline.map((p) => p.x);
|
|
12277
|
-
const ys = hole.pad_outline.map((p) => p.y);
|
|
12278
|
-
const minX = Math.min(...xs);
|
|
12279
|
-
const maxX = Math.max(...xs);
|
|
12280
|
-
const minY = Math.min(...ys);
|
|
12281
|
-
const maxY = Math.max(...ys);
|
|
12282
|
-
return {
|
|
12283
|
-
width: maxX - minX,
|
|
12284
|
-
height: maxY - minY
|
|
12285
|
-
};
|
|
12286
|
-
default:
|
|
12287
|
-
return null;
|
|
12288
|
-
}
|
|
12289
|
-
}
|
|
12290
|
-
|
|
12291
12212
|
// lib/components/primitive-components/Group/Group_doInitialPcbLayoutPack/Group_doInitialPcbLayoutPack.ts
|
|
12292
12213
|
var DEFAULT_MIN_GAP = "1mm";
|
|
12293
12214
|
var debug6 = Debug8("Group_doInitialPcbLayoutPack");
|
|
@@ -12304,7 +12225,7 @@ var Group_doInitialPcbLayoutPack = (group) => {
|
|
|
12304
12225
|
const gap = pcbPackGap ?? pcbGap ?? gapProp;
|
|
12305
12226
|
const gapMm = length4.parse(gap ?? DEFAULT_MIN_GAP);
|
|
12306
12227
|
const chipMarginsMap = {};
|
|
12307
|
-
const
|
|
12228
|
+
const staticPcbComponentIds = /* @__PURE__ */ new Set();
|
|
12308
12229
|
const collectMargins = (comp) => {
|
|
12309
12230
|
if (comp?.pcb_component_id && comp?._parsedProps) {
|
|
12310
12231
|
const props2 = comp._parsedProps;
|
|
@@ -12326,7 +12247,7 @@ var Group_doInitialPcbLayoutPack = (group) => {
|
|
|
12326
12247
|
const childIsGroupOrNormalComponent = child;
|
|
12327
12248
|
if (childIsGroupOrNormalComponent._isNormalComponent && childIsGroupOrNormalComponent.isRelativelyPositioned?.()) {
|
|
12328
12249
|
if (childIsGroupOrNormalComponent.pcb_component_id) {
|
|
12329
|
-
|
|
12250
|
+
staticPcbComponentIds.add(
|
|
12330
12251
|
childIsGroupOrNormalComponent.pcb_component_id
|
|
12331
12252
|
);
|
|
12332
12253
|
}
|
|
@@ -12337,67 +12258,27 @@ var Group_doInitialPcbLayoutPack = (group) => {
|
|
|
12337
12258
|
}
|
|
12338
12259
|
}
|
|
12339
12260
|
}
|
|
12340
|
-
const
|
|
12341
|
-
if (
|
|
12342
|
-
|
|
12343
|
-
|
|
12344
|
-
|
|
12345
|
-
|
|
12346
|
-
|
|
12347
|
-
|
|
12348
|
-
|
|
12349
|
-
|
|
12350
|
-
|
|
12351
|
-
|
|
12352
|
-
(
|
|
12353
|
-
|
|
12354
|
-
|
|
12355
|
-
|
|
12356
|
-
|
|
12357
|
-
const smtpads = db.toArray().filter(
|
|
12358
|
-
(el) => el.type === "pcb_smtpad" && el.pcb_component_id === pcb_component_id
|
|
12359
|
-
);
|
|
12360
|
-
for (const pad of smtpads) {
|
|
12361
|
-
const dimensions = getObstacleDimensionsFromSmtPad(pad);
|
|
12362
|
-
if (!dimensions || dimensions.width === 0 || dimensions.height === 0) {
|
|
12363
|
-
continue;
|
|
12364
|
-
}
|
|
12365
|
-
let centerX;
|
|
12366
|
-
let centerY;
|
|
12367
|
-
if (pad.shape === "polygon") {
|
|
12368
|
-
const xs = pad.points.map((p) => p.x);
|
|
12369
|
-
const ys = pad.points.map((p) => p.y);
|
|
12370
|
-
centerX = componentX + (Math.min(...xs) + Math.max(...xs)) / 2;
|
|
12371
|
-
centerY = componentY + (Math.min(...ys) + Math.max(...ys)) / 2;
|
|
12372
|
-
} else {
|
|
12373
|
-
centerX = componentX + pad.x;
|
|
12374
|
-
centerY = componentY + pad.y;
|
|
12375
|
-
}
|
|
12376
|
-
obstaclesFromRelativelyPositionedComponents.push({
|
|
12377
|
-
obstacleId: pad.pcb_smtpad_id,
|
|
12378
|
-
absoluteCenter: { x: centerX, y: centerY },
|
|
12379
|
-
width: dimensions.width,
|
|
12380
|
-
height: dimensions.height
|
|
12381
|
-
});
|
|
12382
|
-
}
|
|
12383
|
-
const platedHoles = db.toArray().filter(
|
|
12384
|
-
(el) => el.type === "pcb_plated_hole" && el.pcb_component_id === pcb_component_id
|
|
12385
|
-
);
|
|
12386
|
-
for (const hole of platedHoles) {
|
|
12387
|
-
const dimensions = getObstacleDimensionsFromPlatedHole(hole);
|
|
12388
|
-
if (!dimensions || dimensions.width === 0 || dimensions.height === 0) {
|
|
12389
|
-
continue;
|
|
12261
|
+
const isDescendantGroup2 = (db2, groupId, ancestorId) => {
|
|
12262
|
+
if (groupId === ancestorId) return true;
|
|
12263
|
+
const group2 = db2.source_group.get(groupId);
|
|
12264
|
+
if (!group2 || !group2.parent_source_group_id) return false;
|
|
12265
|
+
return isDescendantGroup2(db2, group2.parent_source_group_id, ancestorId);
|
|
12266
|
+
};
|
|
12267
|
+
if (excludedPcbGroupIds.size > 0) {
|
|
12268
|
+
for (const element of db.toArray()) {
|
|
12269
|
+
if (element.type !== "pcb_component") continue;
|
|
12270
|
+
const sourceComponent = db.source_component.get(
|
|
12271
|
+
element.source_component_id
|
|
12272
|
+
);
|
|
12273
|
+
if (!sourceComponent?.source_group_id) continue;
|
|
12274
|
+
for (const groupId of excludedPcbGroupIds) {
|
|
12275
|
+
if (isDescendantGroup2(db, sourceComponent.source_group_id, groupId)) {
|
|
12276
|
+
staticPcbComponentIds.add(element.pcb_component_id);
|
|
12277
|
+
}
|
|
12390
12278
|
}
|
|
12391
|
-
const centerX = componentX + hole.x;
|
|
12392
|
-
const centerY = componentY + hole.y;
|
|
12393
|
-
obstaclesFromRelativelyPositionedComponents.push({
|
|
12394
|
-
obstacleId: hole.pcb_plated_hole_id,
|
|
12395
|
-
absoluteCenter: { x: centerX, y: centerY },
|
|
12396
|
-
width: dimensions.width,
|
|
12397
|
-
height: dimensions.height
|
|
12398
|
-
});
|
|
12399
12279
|
}
|
|
12400
12280
|
}
|
|
12281
|
+
const filteredCircuitJson = db.toArray();
|
|
12401
12282
|
let bounds;
|
|
12402
12283
|
if (props.width !== void 0 && props.height !== void 0) {
|
|
12403
12284
|
const widthMm = length4.parse(props.width);
|
|
@@ -12413,15 +12294,15 @@ var Group_doInitialPcbLayoutPack = (group) => {
|
|
|
12413
12294
|
...convertPackOutputToPackInput(
|
|
12414
12295
|
convertCircuitJsonToPackOutput(filteredCircuitJson, {
|
|
12415
12296
|
source_group_id: group.source_group_id,
|
|
12416
|
-
shouldAddInnerObstacles: true,
|
|
12417
|
-
chipMarginsMap
|
|
12297
|
+
// shouldAddInnerObstacles: true,
|
|
12298
|
+
chipMarginsMap,
|
|
12299
|
+
staticPcbComponentIds: Array.from(staticPcbComponentIds)
|
|
12418
12300
|
})
|
|
12419
12301
|
),
|
|
12420
12302
|
// @ts-expect-error we're missing some pack order strategies
|
|
12421
12303
|
orderStrategy: packOrderStrategy ?? "largest_to_smallest",
|
|
12422
12304
|
placementStrategy: packPlacementStrategy ?? "minimum_sum_squared_distance_to_network",
|
|
12423
12305
|
minGap: gapMm,
|
|
12424
|
-
obstacles: obstaclesFromRelativelyPositionedComponents,
|
|
12425
12306
|
bounds
|
|
12426
12307
|
};
|
|
12427
12308
|
const clusterMap = applyComponentConstraintClusters(group, packInput);
|
|
@@ -15826,208 +15707,10 @@ import * as Flatten from "@flatten-js/core";
|
|
|
15826
15707
|
var DEFAULT_PANEL_MARGIN = 5;
|
|
15827
15708
|
var DEFAULT_TAB_LENGTH = 5;
|
|
15828
15709
|
var DEFAULT_TAB_WIDTH = 2;
|
|
15829
|
-
|
|
15830
|
-
const r1Left = rect1.center.x - rect1.width / 2;
|
|
15831
|
-
const r1Right = rect1.center.x + rect1.width / 2;
|
|
15832
|
-
const r1Bottom = rect1.center.y - rect1.height / 2;
|
|
15833
|
-
const r1Top = rect1.center.y + rect1.height / 2;
|
|
15834
|
-
const r2Left = rect2.center.x - rect2.width / 2;
|
|
15835
|
-
const r2Right = rect2.center.x + rect2.width / 2;
|
|
15836
|
-
const r2Bottom = rect2.center.y - rect2.height / 2;
|
|
15837
|
-
const r2Top = rect2.center.y + rect2.height / 2;
|
|
15838
|
-
return !(r1Right <= r2Left || r1Left >= r2Right || r1Top <= r2Bottom || r1Bottom >= r2Top);
|
|
15839
|
-
}
|
|
15840
|
-
function pointOverlapsRectangle(point2, radius, rect) {
|
|
15841
|
-
const rectLeft = rect.center.x - rect.width / 2;
|
|
15842
|
-
const rectRight = rect.center.x + rect.width / 2;
|
|
15843
|
-
const rectBottom = rect.center.y - rect.height / 2;
|
|
15844
|
-
const rectTop = rect.center.y + rect.height / 2;
|
|
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;
|
|
15849
|
-
return distanceX * distanceX + distanceY * distanceY <= radius * radius;
|
|
15850
|
-
}
|
|
15851
|
-
function generateTabsForEdge({
|
|
15852
|
-
board,
|
|
15853
|
-
edge,
|
|
15854
|
-
otherBoards,
|
|
15855
|
-
options
|
|
15856
|
-
}) {
|
|
15857
|
-
const tabs = [];
|
|
15858
|
-
if (!board.width || !board.height) return tabs;
|
|
15859
|
-
const boardLeft = board.center.x - board.width / 2;
|
|
15860
|
-
const boardRight = board.center.x + board.width / 2;
|
|
15861
|
-
const boardBottom = board.center.y - board.height / 2;
|
|
15862
|
-
const boardTop = board.center.y + board.height / 2;
|
|
15863
|
-
let edgeLength;
|
|
15864
|
-
let isHorizontal;
|
|
15865
|
-
let edgeCenter;
|
|
15866
|
-
if (edge === "top" || edge === "bottom") {
|
|
15867
|
-
edgeLength = board.width;
|
|
15868
|
-
isHorizontal = true;
|
|
15869
|
-
edgeCenter = edge === "top" ? boardTop : boardBottom;
|
|
15870
|
-
} else {
|
|
15871
|
-
edgeLength = board.height;
|
|
15872
|
-
isHorizontal = false;
|
|
15873
|
-
edgeCenter = edge === "right" ? boardRight : boardLeft;
|
|
15874
|
-
}
|
|
15875
|
-
const totalTabWidth = options.tabLength;
|
|
15876
|
-
let fixedSpacing = options.boardGap;
|
|
15877
|
-
if (options.mouseBites) {
|
|
15878
|
-
const mouseBiteDiameter = options.tabWidth * 0.45;
|
|
15879
|
-
const mouseBiteSpacing = mouseBiteDiameter * 0.1;
|
|
15880
|
-
const mouseBitesPerGap = Math.max(2, Math.ceil(options.tabLength / 2));
|
|
15881
|
-
const minSpacingForMouseBites = mouseBitesPerGap * mouseBiteDiameter + (mouseBitesPerGap - 1) * mouseBiteSpacing;
|
|
15882
|
-
fixedSpacing = minSpacingForMouseBites * 1.1;
|
|
15883
|
-
}
|
|
15884
|
-
let numTabs = Math.floor(
|
|
15885
|
-
(edgeLength - fixedSpacing) / (totalTabWidth + fixedSpacing)
|
|
15886
|
-
);
|
|
15887
|
-
if (numTabs < 1 && edgeLength >= totalTabWidth) {
|
|
15888
|
-
numTabs = 1;
|
|
15889
|
-
}
|
|
15890
|
-
if (numTabs === 0) return tabs;
|
|
15891
|
-
const actualSpacing = fixedSpacing;
|
|
15892
|
-
const boardStart = -edgeLength / 2;
|
|
15893
|
-
const boardEnd = edgeLength / 2;
|
|
15894
|
-
for (let i = 0; i < numTabs; i++) {
|
|
15895
|
-
const offsetAlongEdge = boardStart + actualSpacing + i * (totalTabWidth + actualSpacing) + totalTabWidth / 2;
|
|
15896
|
-
const isFirstTab = i === 0;
|
|
15897
|
-
const isLastTab = i === numTabs - 1;
|
|
15898
|
-
const isCornerTab = isFirstTab || isLastTab;
|
|
15899
|
-
let axisStart = offsetAlongEdge - totalTabWidth / 2;
|
|
15900
|
-
let axisEnd = offsetAlongEdge + totalTabWidth / 2;
|
|
15901
|
-
if (isCornerTab) {
|
|
15902
|
-
if (isFirstTab) axisStart = boardStart;
|
|
15903
|
-
if (isLastTab) axisEnd = boardEnd;
|
|
15904
|
-
}
|
|
15905
|
-
axisStart = Math.max(axisStart, boardStart);
|
|
15906
|
-
axisEnd = Math.min(axisEnd, boardEnd);
|
|
15907
|
-
if (isCornerTab) {
|
|
15908
|
-
if (isFirstTab) axisStart -= options.tabWidth;
|
|
15909
|
-
if (isLastTab) axisEnd += options.tabWidth;
|
|
15910
|
-
}
|
|
15911
|
-
if (axisEnd <= axisStart) continue;
|
|
15912
|
-
const axisCenterOffset = (axisStart + axisEnd) / 2;
|
|
15913
|
-
const axisLength = axisEnd - axisStart;
|
|
15914
|
-
const crossAxisOffset = edge === "top" || edge === "right" ? options.tabWidth / 2 : -options.tabWidth / 2;
|
|
15915
|
-
const tabCenter = isHorizontal ? {
|
|
15916
|
-
x: board.center.x + axisCenterOffset,
|
|
15917
|
-
y: edgeCenter + crossAxisOffset
|
|
15918
|
-
} : {
|
|
15919
|
-
x: edgeCenter + crossAxisOffset,
|
|
15920
|
-
y: board.center.y + axisCenterOffset
|
|
15921
|
-
};
|
|
15922
|
-
const tabWidth = isHorizontal ? axisLength : options.tabWidth;
|
|
15923
|
-
const tabHeight = isHorizontal ? options.tabWidth : axisLength;
|
|
15924
|
-
const newTab = {
|
|
15925
|
-
center: tabCenter,
|
|
15926
|
-
width: tabWidth,
|
|
15927
|
-
height: tabHeight,
|
|
15928
|
-
boardId: `${board.center.x}_${board.center.y}`
|
|
15929
|
-
};
|
|
15930
|
-
let overlapsBoard = false;
|
|
15931
|
-
for (const otherBoard of otherBoards) {
|
|
15932
|
-
if (!otherBoard.width || !otherBoard.height) continue;
|
|
15933
|
-
const boardRect = {
|
|
15934
|
-
center: otherBoard.center,
|
|
15935
|
-
width: otherBoard.width,
|
|
15936
|
-
height: otherBoard.height
|
|
15937
|
-
};
|
|
15938
|
-
if (rectanglesOverlap(newTab, boardRect)) {
|
|
15939
|
-
overlapsBoard = true;
|
|
15940
|
-
break;
|
|
15941
|
-
}
|
|
15942
|
-
}
|
|
15943
|
-
if (overlapsBoard && !isCornerTab) continue;
|
|
15944
|
-
tabs.push(newTab);
|
|
15945
|
-
}
|
|
15946
|
-
return tabs;
|
|
15947
|
-
}
|
|
15948
|
-
function generateMouseBitesForEdge({
|
|
15949
|
-
board,
|
|
15950
|
-
edge,
|
|
15951
|
-
edgeTabs,
|
|
15952
|
-
allBoards,
|
|
15953
|
-
options
|
|
15954
|
-
}) {
|
|
15955
|
-
const mouseBites = [];
|
|
15956
|
-
if (edgeTabs.length === 0) return mouseBites;
|
|
15957
|
-
if (!board.width || !board.height) return mouseBites;
|
|
15958
|
-
const boardLeft = board.center.x - board.width / 2;
|
|
15959
|
-
const boardRight = board.center.x + board.width / 2;
|
|
15960
|
-
const boardBottom = board.center.y - board.height / 2;
|
|
15961
|
-
const boardTop = board.center.y + board.height / 2;
|
|
15962
|
-
const isHorizontal = edge === "top" || edge === "bottom";
|
|
15963
|
-
const mouseBiteDiameter = options.tabWidth * 0.45;
|
|
15964
|
-
const mouseBiteSpacing = mouseBiteDiameter * 0.1;
|
|
15965
|
-
const mouseBitesPerGap = Math.max(2, Math.ceil(options.tabLength / 2));
|
|
15966
|
-
let mouseBitePosition;
|
|
15967
|
-
const radius = mouseBiteDiameter / 2;
|
|
15968
|
-
if (edge === "top") {
|
|
15969
|
-
mouseBitePosition = boardTop;
|
|
15970
|
-
} else if (edge === "bottom") {
|
|
15971
|
-
mouseBitePosition = boardBottom;
|
|
15972
|
-
} else if (edge === "right") {
|
|
15973
|
-
mouseBitePosition = boardRight;
|
|
15974
|
-
} else {
|
|
15975
|
-
mouseBitePosition = boardLeft;
|
|
15976
|
-
}
|
|
15977
|
-
const sortedTabs = [...edgeTabs].sort((a, b) => {
|
|
15978
|
-
if (isHorizontal) {
|
|
15979
|
-
return a.center.x - b.center.x;
|
|
15980
|
-
} else {
|
|
15981
|
-
return a.center.y - b.center.y;
|
|
15982
|
-
}
|
|
15983
|
-
});
|
|
15984
|
-
for (let i = 0; i < sortedTabs.length - 1; i++) {
|
|
15985
|
-
const tab1 = sortedTabs[i];
|
|
15986
|
-
const tab2 = sortedTabs[i + 1];
|
|
15987
|
-
let gapStart;
|
|
15988
|
-
let gapEnd;
|
|
15989
|
-
if (isHorizontal) {
|
|
15990
|
-
gapStart = tab1.center.x + tab1.width / 2;
|
|
15991
|
-
gapEnd = tab2.center.x - tab2.width / 2;
|
|
15992
|
-
} else {
|
|
15993
|
-
gapStart = tab1.center.y + tab1.height / 2;
|
|
15994
|
-
gapEnd = tab2.center.y - tab2.height / 2;
|
|
15995
|
-
}
|
|
15996
|
-
const gapLength = gapEnd - gapStart;
|
|
15997
|
-
const totalMouseBiteWidth = mouseBitesPerGap * mouseBiteDiameter;
|
|
15998
|
-
const totalSpacing = (mouseBitesPerGap - 1) * mouseBiteSpacing;
|
|
15999
|
-
if (gapLength < totalMouseBiteWidth + totalSpacing) continue;
|
|
16000
|
-
const gapCenter = (gapStart + gapEnd) / 2;
|
|
16001
|
-
for (let j = 0; j < mouseBitesPerGap; j++) {
|
|
16002
|
-
const posOffset = (j - (mouseBitesPerGap - 1) / 2) * (mouseBiteDiameter + mouseBiteSpacing);
|
|
16003
|
-
const newMouseBite = isHorizontal ? { x: gapCenter + posOffset, y: mouseBitePosition } : { x: mouseBitePosition, y: gapCenter + posOffset };
|
|
16004
|
-
const radius2 = mouseBiteDiameter / 2;
|
|
16005
|
-
let overlapsBoard = false;
|
|
16006
|
-
for (const otherBoard of allBoards) {
|
|
16007
|
-
if (!otherBoard.width || !otherBoard.height) continue;
|
|
16008
|
-
const boardRect = {
|
|
16009
|
-
center: otherBoard.center,
|
|
16010
|
-
width: otherBoard.width,
|
|
16011
|
-
height: otherBoard.height
|
|
16012
|
-
};
|
|
16013
|
-
if (pointOverlapsRectangle(newMouseBite, radius2, boardRect)) {
|
|
16014
|
-
overlapsBoard = true;
|
|
16015
|
-
break;
|
|
16016
|
-
}
|
|
16017
|
-
}
|
|
16018
|
-
if (overlapsBoard) continue;
|
|
16019
|
-
mouseBites.push(newMouseBite);
|
|
16020
|
-
}
|
|
16021
|
-
}
|
|
16022
|
-
return mouseBites;
|
|
16023
|
-
}
|
|
16024
|
-
var generatePanelTabsAndMouseBitesForOutlines = (outline, otherBoards, options) => {
|
|
15710
|
+
var generateCutoutsAndMousebitesForOutline = (outline, options) => {
|
|
16025
15711
|
const {
|
|
16026
|
-
|
|
16027
|
-
|
|
16028
|
-
// along edge
|
|
16029
|
-
tabWidth,
|
|
16030
|
-
// extrusion
|
|
15712
|
+
gapLength,
|
|
15713
|
+
cutoutWidth,
|
|
16031
15714
|
mouseBites,
|
|
16032
15715
|
mouseBiteHoleDiameter,
|
|
16033
15716
|
mouseBiteHoleSpacing
|
|
@@ -16040,181 +15723,142 @@ var generatePanelTabsAndMouseBitesForOutlines = (outline, otherBoards, options)
|
|
|
16040
15723
|
const outlinePolygon = new Flatten.Polygon(
|
|
16041
15724
|
outline.map((p) => Flatten.point(p.x, p.y))
|
|
16042
15725
|
);
|
|
16043
|
-
|
|
16044
|
-
|
|
16045
|
-
|
|
16046
|
-
|
|
16047
|
-
|
|
16048
|
-
|
|
16049
|
-
|
|
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)
|
|
15726
|
+
let is_ccw;
|
|
15727
|
+
if (outline.length > 2) {
|
|
15728
|
+
const p0 = Flatten.point(outline[0].x, outline[0].y);
|
|
15729
|
+
const p1 = Flatten.point(outline[1].x, outline[1].y);
|
|
15730
|
+
const segmentDir = Flatten.vector(p0, p1).normalize();
|
|
15731
|
+
const normalToLeft = segmentDir.rotate(Math.PI / 2);
|
|
15732
|
+
const midPoint = p0.translate(
|
|
15733
|
+
segmentDir.multiply(Flatten.segment(p0, p1).length / 2)
|
|
16066
15734
|
);
|
|
16067
|
-
const
|
|
16068
|
-
|
|
15735
|
+
const testPoint = midPoint.translate(normalToLeft.multiply(0.01));
|
|
15736
|
+
is_ccw = outlinePolygon.contains(testPoint);
|
|
15737
|
+
} else {
|
|
15738
|
+
is_ccw = outlinePolygon.area() > 0;
|
|
15739
|
+
}
|
|
15740
|
+
for (let i = 0; i < outline.length; i++) {
|
|
15741
|
+
const p1_ = outline[i];
|
|
15742
|
+
const p2_ = outline[(i + 1) % outline.length];
|
|
15743
|
+
if (!p1_ || !p2_) continue;
|
|
15744
|
+
const p1 = Flatten.point(p1_.x, p1_.y);
|
|
15745
|
+
const p2 = Flatten.point(p2_.x, p2_.y);
|
|
15746
|
+
const segment2 = Flatten.segment(p1, p2);
|
|
15747
|
+
const segmentLength = segment2.length;
|
|
15748
|
+
if (segmentLength < 1e-6) continue;
|
|
15749
|
+
const segmentVec = Flatten.vector(p1, p2);
|
|
15750
|
+
const segmentDir = segmentVec.normalize();
|
|
15751
|
+
let normalVec = segmentDir.rotate(Math.PI / 2);
|
|
15752
|
+
const midPoint = segment2.middle();
|
|
15753
|
+
const testPoint = midPoint.translate(normalVec.multiply(0.01));
|
|
15754
|
+
if (outlinePolygon.contains(testPoint)) {
|
|
16069
15755
|
normalVec = normalVec.multiply(-1);
|
|
16070
15756
|
}
|
|
16071
|
-
const
|
|
16072
|
-
|
|
16073
|
-
|
|
16074
|
-
|
|
16075
|
-
|
|
16076
|
-
|
|
16077
|
-
|
|
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
|
-
}
|
|
15757
|
+
const numBitesInGap = 2;
|
|
15758
|
+
const totalBitesLength = numBitesInGap * mouseBiteHoleDiameter + (numBitesInGap - 1) * mouseBiteHoleSpacing;
|
|
15759
|
+
let effectiveGapLength;
|
|
15760
|
+
if (mouseBites) {
|
|
15761
|
+
effectiveGapLength = totalBitesLength;
|
|
15762
|
+
} else {
|
|
15763
|
+
effectiveGapLength = gapLength;
|
|
16137
15764
|
}
|
|
15765
|
+
effectiveGapLength = Math.min(effectiveGapLength, segmentLength * 0.9);
|
|
15766
|
+
const gapStartDist = (segmentLength - effectiveGapLength) / 2;
|
|
15767
|
+
const gapEndDist = gapStartDist + effectiveGapLength;
|
|
16138
15768
|
if (mouseBites) {
|
|
16139
|
-
const
|
|
16140
|
-
|
|
16141
|
-
const
|
|
16142
|
-
|
|
16143
|
-
|
|
16144
|
-
|
|
16145
|
-
|
|
16146
|
-
|
|
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
|
-
});
|
|
15769
|
+
const holeAndSpacing = mouseBiteHoleDiameter + mouseBiteHoleSpacing;
|
|
15770
|
+
if (effectiveGapLength >= totalBitesLength && holeAndSpacing > 0) {
|
|
15771
|
+
const firstBiteCenterOffsetInGap = (effectiveGapLength - totalBitesLength) / 2 + mouseBiteHoleDiameter / 2;
|
|
15772
|
+
const firstBiteDistFromP1 = gapStartDist + firstBiteCenterOffsetInGap;
|
|
15773
|
+
for (let k = 0; k < numBitesInGap; k++) {
|
|
15774
|
+
const biteDist = firstBiteDistFromP1 + k * holeAndSpacing;
|
|
15775
|
+
const pos = p1.translate(segmentDir.multiply(biteDist));
|
|
15776
|
+
mouseBiteHoles.push({ x: pos.x, y: pos.y });
|
|
16155
15777
|
}
|
|
16156
15778
|
}
|
|
16157
15779
|
}
|
|
15780
|
+
const p_prev_ = outline[(i - 1 + outline.length) % outline.length];
|
|
15781
|
+
const p_next_ = outline[(i + 2) % outline.length];
|
|
15782
|
+
let start_ext = 0;
|
|
15783
|
+
let end_ext = 0;
|
|
15784
|
+
if (p_prev_ && p_next_) {
|
|
15785
|
+
const vec_in_p1 = Flatten.vector(Flatten.point(p_prev_.x, p_prev_.y), p1);
|
|
15786
|
+
const p1_cross = vec_in_p1.cross(segmentVec);
|
|
15787
|
+
const is_p1_convex = is_ccw ? p1_cross > 1e-9 : p1_cross < -1e-9;
|
|
15788
|
+
const vec_out_p2 = Flatten.vector(p2, Flatten.point(p_next_.x, p_next_.y));
|
|
15789
|
+
const p2_cross = segmentVec.cross(vec_out_p2);
|
|
15790
|
+
const is_p2_convex = is_ccw ? p2_cross > 1e-9 : p2_cross < -1e-9;
|
|
15791
|
+
start_ext = is_p1_convex ? cutoutWidth : 0;
|
|
15792
|
+
end_ext = is_p2_convex ? cutoutWidth : 0;
|
|
15793
|
+
}
|
|
15794
|
+
const cutoutParts = [
|
|
15795
|
+
{ start: 0 - start_ext, end: gapStartDist },
|
|
15796
|
+
{ start: gapEndDist, end: segmentLength + end_ext }
|
|
15797
|
+
];
|
|
15798
|
+
const extrusion = normalVec.multiply(cutoutWidth);
|
|
15799
|
+
for (const part of cutoutParts) {
|
|
15800
|
+
const partLength = part.end - part.start;
|
|
15801
|
+
if (partLength < 1e-6) continue;
|
|
15802
|
+
const partCenterAlongSegment = p1.translate(
|
|
15803
|
+
segmentDir.multiply(part.start + partLength / 2)
|
|
15804
|
+
);
|
|
15805
|
+
const center = partCenterAlongSegment.translate(extrusion.multiply(0.5));
|
|
15806
|
+
const width = partLength;
|
|
15807
|
+
const height = cutoutWidth;
|
|
15808
|
+
const rotationDeg = segmentDir.slope * 180 / Math.PI;
|
|
15809
|
+
tabCutouts.push({
|
|
15810
|
+
type: "pcb_cutout",
|
|
15811
|
+
shape: "rect",
|
|
15812
|
+
center: { x: center.x, y: center.y },
|
|
15813
|
+
width,
|
|
15814
|
+
height,
|
|
15815
|
+
rotation: rotationDeg,
|
|
15816
|
+
corner_radius: cutoutWidth / 2
|
|
15817
|
+
});
|
|
15818
|
+
}
|
|
16158
15819
|
}
|
|
16159
15820
|
return { tabCutouts, mouseBiteHoles };
|
|
16160
15821
|
};
|
|
16161
15822
|
function generatePanelTabsAndMouseBites(boards, options) {
|
|
16162
15823
|
const finalTabCutouts = [];
|
|
16163
15824
|
const allMouseBites = [];
|
|
16164
|
-
|
|
16165
|
-
|
|
16166
|
-
|
|
15825
|
+
const { tabWidth, tabLength, mouseBites: useMouseBites } = options;
|
|
15826
|
+
const processedBoards = boards.map((board) => {
|
|
15827
|
+
if ((!board.outline || board.outline.length === 0) && board.width && board.height) {
|
|
15828
|
+
const w2 = board.width / 2;
|
|
15829
|
+
const h2 = board.height / 2;
|
|
15830
|
+
return {
|
|
15831
|
+
...board,
|
|
15832
|
+
outline: [
|
|
15833
|
+
{ x: board.center.x - w2, y: board.center.y - h2 },
|
|
15834
|
+
{ x: board.center.x + w2, y: board.center.y - h2 },
|
|
15835
|
+
{ x: board.center.x + w2, y: board.center.y + h2 },
|
|
15836
|
+
{ x: board.center.x - w2, y: board.center.y + h2 }
|
|
15837
|
+
]
|
|
15838
|
+
};
|
|
15839
|
+
}
|
|
15840
|
+
return board;
|
|
15841
|
+
});
|
|
15842
|
+
for (const board of processedBoards) {
|
|
16167
15843
|
if (board.outline && board.outline.length > 0) {
|
|
16168
|
-
const mouseBiteDiameter2 =
|
|
15844
|
+
const mouseBiteDiameter2 = tabWidth * 0.45;
|
|
16169
15845
|
const mouseBiteSpacing = mouseBiteDiameter2 * 0.1;
|
|
16170
|
-
const generated =
|
|
16171
|
-
|
|
16172
|
-
|
|
16173
|
-
|
|
16174
|
-
|
|
16175
|
-
|
|
16176
|
-
|
|
16177
|
-
}
|
|
16178
|
-
);
|
|
15846
|
+
const generated = generateCutoutsAndMousebitesForOutline(board.outline, {
|
|
15847
|
+
gapLength: tabLength,
|
|
15848
|
+
cutoutWidth: tabWidth,
|
|
15849
|
+
mouseBites: useMouseBites,
|
|
15850
|
+
mouseBiteHoleDiameter: mouseBiteDiameter2,
|
|
15851
|
+
mouseBiteHoleSpacing: mouseBiteSpacing
|
|
15852
|
+
});
|
|
16179
15853
|
finalTabCutouts.push(...generated.tabCutouts);
|
|
16180
15854
|
allMouseBites.push(...generated.mouseBiteHoles);
|
|
16181
|
-
} else {
|
|
16182
|
-
for (const edge of ["top", "bottom", "left", "right"]) {
|
|
16183
|
-
const edgeTabs = generateTabsForEdge({
|
|
16184
|
-
board,
|
|
16185
|
-
edge,
|
|
16186
|
-
otherBoards,
|
|
16187
|
-
options
|
|
16188
|
-
});
|
|
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
|
-
}
|
|
16210
|
-
}
|
|
16211
15855
|
}
|
|
16212
15856
|
}
|
|
16213
15857
|
const tabCutouts = finalTabCutouts.map((tab, index) => ({
|
|
16214
15858
|
...tab,
|
|
16215
15859
|
pcb_cutout_id: `panel_tab_${index}`
|
|
16216
15860
|
}));
|
|
16217
|
-
const mouseBiteDiameter =
|
|
15861
|
+
const mouseBiteDiameter = tabWidth * 0.45;
|
|
16218
15862
|
const mouseBiteHoles = allMouseBites.map((bite, index) => ({
|
|
16219
15863
|
type: "pcb_hole",
|
|
16220
15864
|
pcb_hole_id: `panel_mouse_bite_${index}`,
|
|
@@ -16562,10 +16206,10 @@ var Jumper = class extends NormalComponent3 {
|
|
|
16562
16206
|
};
|
|
16563
16207
|
for (const trace of traces) {
|
|
16564
16208
|
if (!trace.route) continue;
|
|
16565
|
-
for (const
|
|
16566
|
-
if (
|
|
16567
|
-
|
|
16568
|
-
|
|
16209
|
+
for (const segment2 of trace.route) {
|
|
16210
|
+
if (segment2.route_type !== "wire") continue;
|
|
16211
|
+
segment2.start_pcb_port_id = updatePortId(segment2.start_pcb_port_id);
|
|
16212
|
+
segment2.end_pcb_port_id = updatePortId(segment2.end_pcb_port_id);
|
|
16569
16213
|
}
|
|
16570
16214
|
}
|
|
16571
16215
|
}
|
|
@@ -16752,10 +16396,10 @@ var SolderJumper = class extends NormalComponent3 {
|
|
|
16752
16396
|
};
|
|
16753
16397
|
for (const trace of traces) {
|
|
16754
16398
|
if (!trace.route) continue;
|
|
16755
|
-
for (const
|
|
16756
|
-
if (
|
|
16757
|
-
|
|
16758
|
-
|
|
16399
|
+
for (const segment2 of trace.route) {
|
|
16400
|
+
if (segment2.route_type !== "wire") continue;
|
|
16401
|
+
segment2.start_pcb_port_id = updatePortId(segment2.start_pcb_port_id);
|
|
16402
|
+
segment2.end_pcb_port_id = updatePortId(segment2.end_pcb_port_id);
|
|
16759
16403
|
}
|
|
16760
16404
|
}
|
|
16761
16405
|
}
|
|
@@ -19418,7 +19062,7 @@ import { identity as identity5 } from "transformation-matrix";
|
|
|
19418
19062
|
var package_default = {
|
|
19419
19063
|
name: "@tscircuit/core",
|
|
19420
19064
|
type: "module",
|
|
19421
|
-
version: "0.0.
|
|
19065
|
+
version: "0.0.908",
|
|
19422
19066
|
types: "dist/index.d.ts",
|
|
19423
19067
|
main: "dist/index.js",
|
|
19424
19068
|
module: "dist/index.js",
|
|
@@ -19523,7 +19167,7 @@ var package_default = {
|
|
|
19523
19167
|
dependencies: {
|
|
19524
19168
|
"@flatten-js/core": "^1.6.2",
|
|
19525
19169
|
"@lume/kiwi": "^0.4.3",
|
|
19526
|
-
"calculate-packing": "0.0.
|
|
19170
|
+
"calculate-packing": "0.0.66",
|
|
19527
19171
|
"css-select": "5.1.0",
|
|
19528
19172
|
"format-si-unit": "^0.0.3",
|
|
19529
19173
|
nanoid: "^5.0.7",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tscircuit/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.909",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.js",
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
"dependencies": {
|
|
107
107
|
"@flatten-js/core": "^1.6.2",
|
|
108
108
|
"@lume/kiwi": "^0.4.3",
|
|
109
|
-
"calculate-packing": "0.0.
|
|
109
|
+
"calculate-packing": "0.0.66",
|
|
110
110
|
"css-select": "5.1.0",
|
|
111
111
|
"format-si-unit": "^0.0.3",
|
|
112
112
|
"nanoid": "^5.0.7",
|