added edge tabs and a lot of bug fixes abizt overlaps
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
368
src/app.js
368
src/app.js
@@ -23,6 +23,262 @@ export function getLastBusbarGeometries() {
|
||||
return lastComputedGeometries;
|
||||
}
|
||||
|
||||
function getEdgeTabCenters(positions, cellRadius, spacing, layoutType) {
|
||||
if (!Array.isArray(positions) || positions.length < 2) {
|
||||
return { top: [], bottom: [] };
|
||||
}
|
||||
|
||||
const rows = new Map();
|
||||
for (const [x, y] of positions) {
|
||||
const key = y.toFixed(4);
|
||||
if (!rows.has(key)) rows.set(key, []);
|
||||
rows.get(key).push([x, y]);
|
||||
}
|
||||
|
||||
const rowKeys = Array.from(rows.keys()).sort((a, b) => Number(a) - Number(b));
|
||||
if (rowKeys.length === 0) return { top: [], bottom: [] };
|
||||
|
||||
const topRow = (rows.get(rowKeys[0]) || []).slice().sort((a, b) => a[0] - b[0]);
|
||||
const bottomRow = (rows.get(rowKeys[rowKeys.length - 1]) || []).slice().sort((a, b) => a[0] - b[0]);
|
||||
const topY = Math.min(...positions.map(([, y]) => y)) - cellRadius - spacing;
|
||||
const bottomY = Math.max(...positions.map(([, y]) => y)) + cellRadius + spacing;
|
||||
const minAllX = Math.min(...positions.map(([x]) => x));
|
||||
|
||||
const topMidpoints = topRow.slice(0, -1).map((cell, index) => ({
|
||||
key: `top_${index}`,
|
||||
x: (cell[0] + topRow[index + 1][0]) / 2,
|
||||
y: topY,
|
||||
}));
|
||||
const bottomMidpoints = bottomRow.slice(0, -1).map((cell, index) => ({
|
||||
key: `bottom_${index}`,
|
||||
x: (cell[0] + bottomRow[index + 1][0]) / 2,
|
||||
y: bottomY,
|
||||
}));
|
||||
|
||||
// Grid: no extra tab on either edge
|
||||
if (layoutType === 'grid') {
|
||||
return { top: topMidpoints, bottom: bottomMidpoints };
|
||||
}
|
||||
|
||||
// Vertical honeycomb: column pitch = min X delta between any two cells
|
||||
if (layoutType === 'vertical') {
|
||||
const allXSorted = [...new Set(positions.map(([x]) => Math.round(x * 1000)))]
|
||||
.sort((a, b) => a - b).map(v => v / 1000);
|
||||
const colPitch = allXSorted.length >= 2 ? allXSorted[1] - allXSorted[0] : 0;
|
||||
// Even-column rows start at minAllX; odd-column rows are offset by colPitch
|
||||
const topIsEven = colPitch === 0 || (topRow[0][0] - minAllX) < colPitch / 2;
|
||||
const bottomIsEven = colPitch === 0 || (bottomRow[0][0] - minAllX) < colPitch / 2;
|
||||
return {
|
||||
top: topIsEven
|
||||
? topMidpoints
|
||||
: [{ key: 'top_extra_left', x: topRow[0][0] - colPitch / 2, y: topY },
|
||||
...topMidpoints,
|
||||
{ key: 'top_extra_right', x: topRow[topRow.length - 1][0] + colPitch / 2, y: topY }],
|
||||
bottom: bottomIsEven
|
||||
? bottomMidpoints
|
||||
: [{ key: 'bottom_extra_left', x: bottomRow[0][0] - colPitch / 2, y: bottomY },
|
||||
...bottomMidpoints,
|
||||
{ key: 'bottom_extra_right', x: bottomRow[bottomRow.length - 1][0] + colPitch / 2, y: bottomY }],
|
||||
};
|
||||
}
|
||||
|
||||
// Horizontal honeycomb: 1 extra tab on the side that has a gap to the wall
|
||||
const topPitch = topRow.length >= 2 ? topRow[topRow.length - 1][0] - topRow[topRow.length - 2][0] : 0;
|
||||
const bottomPitch = bottomRow.length >= 2 ? bottomRow[bottomRow.length - 1][0] - bottomRow[bottomRow.length - 2][0] : 0;
|
||||
const topExtraRight = topRow.length < 2 || (topRow[0][0] - minAllX) < topPitch / 4;
|
||||
const bottomExtraRight = bottomRow.length < 2 || (bottomRow[0][0] - minAllX) < bottomPitch / 4;
|
||||
return {
|
||||
top: topExtraRight
|
||||
? [...topMidpoints, { key: 'top_extra', x: topRow[topRow.length - 1][0] + topPitch / 2, y: topY }]
|
||||
: [{ key: 'top_extra', x: topRow[0][0] - topPitch / 2, y: topY }, ...topMidpoints],
|
||||
bottom: bottomExtraRight
|
||||
? [...bottomMidpoints, { key: 'bottom_extra', x: bottomRow[bottomRow.length - 1][0] + bottomPitch / 2, y: bottomY }]
|
||||
: [{ key: 'bottom_extra', x: bottomRow[0][0] - bottomPitch / 2, y: bottomY }, ...bottomMidpoints],
|
||||
};
|
||||
}
|
||||
|
||||
function buildBusbarAnchorCandidates(geometry, positions) {
|
||||
const candidates = [];
|
||||
const seen = new Set();
|
||||
const addPoint = (x, y) => {
|
||||
const key = `${x.toFixed(4)},${y.toFixed(4)}`;
|
||||
if (seen.has(key)) return;
|
||||
seen.add(key);
|
||||
candidates.push([x, y]);
|
||||
};
|
||||
|
||||
for (const index of geometry?.padIndices || []) {
|
||||
const point = positions[index];
|
||||
if (!point) continue;
|
||||
addPoint(point[0], point[1]);
|
||||
}
|
||||
|
||||
for (const edge of geometry?.edges || []) {
|
||||
const stops = [positions[edge.from], ...(edge.waypoints || []), positions[edge.to]].filter(Boolean);
|
||||
for (let i = 0; i < stops.length - 1; i++) {
|
||||
const a = stops[i];
|
||||
const b = stops[i + 1];
|
||||
addPoint((a[0] + b[0]) / 2, (a[1] + b[1]) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
for (const pad of geometry?.extraPads || []) {
|
||||
if (!Array.isArray(pad?.pos)) continue;
|
||||
addPoint(pad.pos[0], pad.pos[1]);
|
||||
}
|
||||
|
||||
for (const segment of geometry?.extraSegments || []) {
|
||||
if (!Array.isArray(segment?.from) || !Array.isArray(segment?.to)) continue;
|
||||
addPoint(segment.from[0], segment.from[1]);
|
||||
addPoint(segment.to[0], segment.to[1]);
|
||||
addPoint((segment.from[0] + segment.to[0]) / 2, (segment.from[1] + segment.to[1]) / 2);
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
function attachEdgeTabsToNearestBusbars(busbars, geometries, positions, options) {
|
||||
const {
|
||||
enabled = false,
|
||||
cellRadius,
|
||||
spacing,
|
||||
tabWidth,
|
||||
tabOverlapSide,
|
||||
overlapLength = 28,
|
||||
layoutType = 'honeycomb',
|
||||
} = options;
|
||||
|
||||
if (enabled !== true) return;
|
||||
if (tabOverlapSide !== 'top' && tabOverlapSide !== 'bottom') return;
|
||||
|
||||
const tabCenters = getEdgeTabCenters(positions, cellRadius, spacing, layoutType)[tabOverlapSide];
|
||||
if (tabCenters.length === 0) return;
|
||||
|
||||
const inwardDirection = tabOverlapSide === 'top' ? 1 : -1;
|
||||
const maxHorizontalGap = Math.max((tabWidth || 0), cellRadius * 2 + spacing * 2);
|
||||
const maxVerticalGap = overlapLength + cellRadius * 2 + spacing * 2;
|
||||
const betweenBusbarThreshold = Math.max((tabWidth || 0) * 0.6, cellRadius * 0.75);
|
||||
const connectorRadius = Math.max(0.05, ((tabWidth || 0) - 0.5) / 2);
|
||||
|
||||
const selectedByBusbar = [];
|
||||
|
||||
for (let i = 0; i < busbars.length; i++) {
|
||||
const busbar = busbars[i];
|
||||
const geometry = geometries[i];
|
||||
if (!geometry || geometry.blocked) continue;
|
||||
|
||||
const anchors = buildBusbarAnchorCandidates(geometry, positions);
|
||||
if (anchors.length === 0) continue;
|
||||
|
||||
const extremeY = tabOverlapSide === 'top'
|
||||
? Math.min(...anchors.map(([, y]) => y))
|
||||
: Math.max(...anchors.map(([, y]) => y));
|
||||
const edgeBand = Math.max(cellRadius * 1.1, spacing + cellRadius * 0.35);
|
||||
const edgeAnchors = anchors.filter(([, y]) => (
|
||||
tabOverlapSide === 'top' ? y <= extremeY + edgeBand : y >= extremeY - edgeBand
|
||||
));
|
||||
if (edgeAnchors.length === 0) continue;
|
||||
|
||||
let best = null;
|
||||
for (const tab of tabCenters) {
|
||||
for (const anchor of edgeAnchors) {
|
||||
const dx = Math.abs(anchor[0] - tab.x);
|
||||
const dy = Math.abs(tab.y - anchor[1]);
|
||||
if (dx > maxHorizontalGap || dy > maxVerticalGap) continue;
|
||||
const score = dx * 3 + dy;
|
||||
const candidate = {
|
||||
busbarIndex: i,
|
||||
geometry,
|
||||
anchor,
|
||||
score,
|
||||
tabKey: tab.key,
|
||||
tab,
|
||||
deltaX: anchor[0] - tab.x,
|
||||
};
|
||||
if (!best || candidate.score < best.score) {
|
||||
best = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (best) selectedByBusbar.push(best);
|
||||
}
|
||||
|
||||
const conflictsByTab = new Map();
|
||||
for (const candidate of selectedByBusbar) {
|
||||
if (!conflictsByTab.has(candidate.tabKey)) conflictsByTab.set(candidate.tabKey, []);
|
||||
conflictsByTab.get(candidate.tabKey).push(candidate);
|
||||
}
|
||||
|
||||
for (const best of selectedByBusbar) {
|
||||
const sameTabCandidates = conflictsByTab.get(best.tabKey) || [];
|
||||
const leftCandidate = sameTabCandidates.find((candidate) => candidate.deltaX < -betweenBusbarThreshold);
|
||||
const rightCandidate = sameTabCandidates.find((candidate) => candidate.deltaX > betweenBusbarThreshold);
|
||||
if (leftCandidate && rightCandidate) continue;
|
||||
|
||||
const anchorPoint = best.anchor.slice();
|
||||
const edgePoint = [best.tab.x, best.tab.y];
|
||||
const innerPoint = [best.tab.x, best.tab.y + inwardDirection * overlapLength];
|
||||
|
||||
const busbar = busbars[best.busbarIndex];
|
||||
const overlapOutward = Number(busbar.overlapSize) > 0 ? Number(busbar.overlapSize) : 0;
|
||||
const outerPoint = overlapOutward > 0
|
||||
? [best.tab.x, best.tab.y - inwardDirection * overlapOutward]
|
||||
: null;
|
||||
|
||||
best.geometry.extraPads = Array.isArray(best.geometry.extraPads)
|
||||
? best.geometry.extraPads
|
||||
: [];
|
||||
best.geometry.extraPads.push({
|
||||
key: `bms_tab_anchor_${tabOverlapSide}_${best.tabKey}`,
|
||||
pos: anchorPoint,
|
||||
radius: connectorRadius,
|
||||
});
|
||||
best.geometry.extraPads.push({
|
||||
key: `bms_tab_edge_${tabOverlapSide}_${best.tabKey}`,
|
||||
pos: edgePoint,
|
||||
radius: connectorRadius,
|
||||
});
|
||||
best.geometry.extraPads.push({
|
||||
key: `bms_tab_inner_${tabOverlapSide}_${best.tabKey}`,
|
||||
pos: innerPoint,
|
||||
radius: connectorRadius,
|
||||
});
|
||||
if (outerPoint) {
|
||||
best.geometry.extraPads.push({
|
||||
key: `bms_tab_outer_${tabOverlapSide}_${best.tabKey}`,
|
||||
pos: outerPoint,
|
||||
radius: connectorRadius,
|
||||
});
|
||||
}
|
||||
best.geometry.extraSegments = Array.isArray(best.geometry.extraSegments)
|
||||
? best.geometry.extraSegments
|
||||
: [];
|
||||
best.geometry.extraSegments.push({
|
||||
from: anchorPoint,
|
||||
to: innerPoint,
|
||||
fromKey: `bms_tab_anchor_${tabOverlapSide}_${best.tabKey}`,
|
||||
toKey: `bms_tab_inner_${tabOverlapSide}_${best.tabKey}`,
|
||||
radius: connectorRadius,
|
||||
});
|
||||
best.geometry.extraSegments.push({
|
||||
from: edgePoint,
|
||||
to: innerPoint,
|
||||
fromKey: `bms_tab_edge_${tabOverlapSide}_${best.tabKey}`,
|
||||
toKey: `bms_tab_inner_${tabOverlapSide}_${best.tabKey}`,
|
||||
radius: connectorRadius,
|
||||
});
|
||||
if (outerPoint) {
|
||||
best.geometry.extraSegments.push({
|
||||
from: edgePoint,
|
||||
to: outerPoint,
|
||||
fromKey: `bms_tab_edge_${tabOverlapSide}_${best.tabKey}`,
|
||||
toKey: `bms_tab_outer_${tabOverlapSide}_${best.tabKey}`,
|
||||
radius: connectorRadius,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drawBothCanvases(positions, cellSize, padRadius, spacing) {
|
||||
drawPreview(positions, cellSize);
|
||||
|
||||
@@ -202,9 +458,37 @@ export function updatePreview(resetView = false) {
|
||||
const cellRadius = cellSize / 2;
|
||||
const busbarPadRadius = Math.max(cellRadius - ledgeWidth, 1.0);
|
||||
const busbarKeepoutRadius = 4.0;
|
||||
const busbarCellCutoutEnabled = document.getElementById('busbarCellCutoutEnabled')?.checked === true;
|
||||
const packBounds = {
|
||||
left: Math.min(...positions.map(p => p[0])) - cellRadius - spacing,
|
||||
right: Math.max(...positions.map(p => p[0])) + cellRadius + spacing,
|
||||
bottom: Math.min(...positions.map(p => p[1])) - cellRadius - spacing,
|
||||
top: Math.max(...positions.map(p => p[1])) + cellRadius + spacing,
|
||||
};
|
||||
lastComputedGeometries = busbarStore.list.map(bb =>
|
||||
computeBusbarGeometry(bb.cellIndices, positions, cellRadius, busbarPadRadius, spacing, busbarKeepoutRadius)
|
||||
computeBusbarGeometry(
|
||||
bb.cellIndices,
|
||||
positions,
|
||||
cellRadius,
|
||||
busbarPadRadius,
|
||||
spacing,
|
||||
busbarKeepoutRadius,
|
||||
packBounds,
|
||||
bb.overlapEnabled !== false,
|
||||
layoutType,
|
||||
bb.overlapSize,
|
||||
busbarCellCutoutEnabled,
|
||||
)
|
||||
);
|
||||
attachEdgeTabsToNearestBusbars(busbarStore.list, lastComputedGeometries, positions, {
|
||||
enabled: document.getElementById('bmsHolesType')?.value === 'tabs',
|
||||
cellRadius,
|
||||
spacing,
|
||||
tabWidth: parseFloat(document.getElementById('height')?.value) || 10.0,
|
||||
tabOverlapSide: document.getElementById('tabOverlapSide')?.value || 'off',
|
||||
overlapLength: parseFloat(document.getElementById('height')?.value) || 10.0,
|
||||
layoutType,
|
||||
});
|
||||
lastBusbarDrawArgs = { positions, cellSize, padRadius: busbarPadRadius, spacing };
|
||||
drawBothCanvases(positions, cellSize, busbarPadRadius, spacing);
|
||||
|
||||
@@ -448,14 +732,15 @@ export async function generateLayout() {
|
||||
}
|
||||
}
|
||||
|
||||
const tabWidth = parseFloat(document.getElementById('tabWidth').value) || 4.0;
|
||||
const tabDepth = parseFloat(document.getElementById('tabDepth').value) || 1.0;
|
||||
const tabWidth = parseFloat(document.getElementById('height').value) || 10.0;
|
||||
const tabLength = parseFloat(document.getElementById('tabLength')?.value) || 10.0;
|
||||
const tabOverlapSide = document.getElementById('tabOverlapSide')?.value || 'off';
|
||||
|
||||
const config = {
|
||||
cellSize, spacing, height, terminalDiameter, terminalDepth,
|
||||
coverThickness, roundedCorners, bmsHoles, ledgeWidth,
|
||||
filletBms, circleHoleOffset, useTabs, useFullCircles, bmsHoleDiameter,
|
||||
tabWidth, tabDepth,
|
||||
tabWidth, tabLength, tabDepth: 1.0, tabOverlapSide, layoutType,
|
||||
};
|
||||
|
||||
const holderShape = create3DModel(positions, config);
|
||||
@@ -467,9 +752,37 @@ export async function generateLayout() {
|
||||
|
||||
const busbarPadRadius = Math.max(cellRadius - ledgeWidth, 1.0);
|
||||
const busbarKeepoutRadius = terminalDiameter / 2;
|
||||
const busbarCellCutoutEnabled = document.getElementById('busbarCellCutoutEnabled')?.checked === true;
|
||||
const packBounds = {
|
||||
left: Math.min(...positions.map(p => p[0])) - cellRadius - spacing,
|
||||
right: Math.max(...positions.map(p => p[0])) + cellRadius + spacing,
|
||||
bottom: Math.min(...positions.map(p => p[1])) - cellRadius - spacing,
|
||||
top: Math.max(...positions.map(p => p[1])) + cellRadius + spacing,
|
||||
};
|
||||
const busbarGeometries = busbarStore.list.map(bb =>
|
||||
computeBusbarGeometry(bb.cellIndices, positions, cellRadius, busbarPadRadius, spacing, busbarKeepoutRadius)
|
||||
computeBusbarGeometry(
|
||||
bb.cellIndices,
|
||||
positions,
|
||||
cellRadius,
|
||||
busbarPadRadius,
|
||||
spacing,
|
||||
busbarKeepoutRadius,
|
||||
packBounds,
|
||||
bb.overlapEnabled !== false,
|
||||
layoutType,
|
||||
bb.overlapSize,
|
||||
busbarCellCutoutEnabled,
|
||||
)
|
||||
);
|
||||
attachEdgeTabsToNearestBusbars(busbarStore.list, busbarGeometries, positions, {
|
||||
enabled: bmsHolesType === 'tabs',
|
||||
cellRadius,
|
||||
spacing,
|
||||
tabWidth,
|
||||
tabOverlapSide,
|
||||
overlapLength: height,
|
||||
layoutType,
|
||||
});
|
||||
|
||||
for (let i = 0; i < busbarStore.list.length; i++) {
|
||||
const bb = busbarStore.list[i];
|
||||
@@ -492,10 +805,18 @@ export async function generateLayout() {
|
||||
// signatures are congruent, so we only need to print one copy. Uses sorted
|
||||
// pairwise cell distances; pairwise distance sets are the same under
|
||||
// translation, rotation, and mirror.
|
||||
const busbarSignature = (bb) => {
|
||||
const busbarSignature = (bb, geom) => {
|
||||
const idxs = bb.cellIndices;
|
||||
const cellCutoutEnabled = document.getElementById('busbarCellCutoutEnabled')?.checked === true;
|
||||
const tabSegments = (Array.isArray(geom?.extraSegments) ? geom.extraSegments : [])
|
||||
.filter((segment) => String(segment?.fromKey || '').startsWith('bms_tab_') || String(segment?.toKey || '').startsWith('bms_tab_'))
|
||||
.map((segment) => `${segment.from[0].toFixed(3)},${segment.from[1].toFixed(3)}>${segment.to[0].toFixed(3)},${segment.to[1].toFixed(3)}`)
|
||||
.sort()
|
||||
.join(';');
|
||||
if (idxs.length === 0) return null;
|
||||
if (idxs.length === 1) return `single|${bb.thickness.toFixed(2)}`;
|
||||
if (idxs.length === 1) {
|
||||
return `single|${bb.thickness.toFixed(2)}|ov:${bb.overlapEnabled === true ? 1 : 0}|os:${Number(bb.overlapSize ?? 10).toFixed(2)}|cc:${cellCutoutEnabled ? 1 : 0}|tabs:${tabSegments}`;
|
||||
}
|
||||
const pts = idxs.map(i => centeredPositions[i]).filter(Boolean);
|
||||
const dists = [];
|
||||
for (let a = 0; a < pts.length; a++) {
|
||||
@@ -504,7 +825,7 @@ export async function generateLayout() {
|
||||
}
|
||||
}
|
||||
dists.sort((x, y) => x - y);
|
||||
return `${pts.length}|${bb.thickness.toFixed(2)}|${dists.map(d => d.toFixed(3)).join(',')}`;
|
||||
return `${pts.length}|${bb.thickness.toFixed(2)}|ov:${bb.overlapEnabled === true ? 1 : 0}|os:${Number(bb.overlapSize ?? 10).toFixed(2)}|cc:${cellCutoutEnabled ? 1 : 0}|tabs:${tabSegments}|${dists.map(d => d.toFixed(3)).join(',')}`;
|
||||
};
|
||||
|
||||
// Export format for busbars: STEP solid or DXF flat pattern. The cellholder
|
||||
@@ -518,14 +839,14 @@ export async function generateLayout() {
|
||||
for (let i = 0; i < busbarStore.list.length; i++) {
|
||||
const bb = busbarStore.list[i];
|
||||
if (bb.cellIndices.length === 0) continue;
|
||||
const sig = busbarSignature(bb);
|
||||
const sig = busbarSignature(bb, busbarGeometries[i]);
|
||||
if (sig && sigSeen.has(sig)) {
|
||||
sigSeen.get(sig).copies.push(bb.name);
|
||||
continue;
|
||||
}
|
||||
const entry = { bb, geom: busbarGeometries[i], copies: [bb.name], shape: null };
|
||||
if (busbarFormat === 'step') {
|
||||
entry.shape = build3DBusbar(busbarGeometries[i], centeredPositions, busbarPadRadius, height, bb.thickness);
|
||||
entry.shape = build3DBusbar(centeredGeom(busbarGeometries[i], holderCenterX, holderCenterY), centeredPositions, busbarPadRadius, height, bb.thickness);
|
||||
if (!entry.shape) continue;
|
||||
}
|
||||
uniqueBusbars.push(entry);
|
||||
@@ -540,7 +861,7 @@ export async function generateLayout() {
|
||||
const { bb, geom, shape } = uniqueBusbars[i];
|
||||
const base = `busbar_${safeName(bb.name)}`;
|
||||
if (busbarFormat === 'dxf') {
|
||||
const content = buildBusbarDXF(geom, centeredPositions, busbarPadRadius);
|
||||
const content = buildBusbarDXF(centeredGeom(geom, holderCenterX, holderCenterY), centeredPositions, busbarPadRadius);
|
||||
downloadDXF(content, `${base}.dxf`);
|
||||
} else {
|
||||
downloadSTEP(shape, `${base}.step`);
|
||||
@@ -570,6 +891,21 @@ export async function generateLayout() {
|
||||
|
||||
// ── Per-busbar download helpers ────────────────────────────────────────────────
|
||||
|
||||
// Return a copy of geom with extraPads, extraSegments, and cutouts shifted by (-cx, -cy).
|
||||
function centeredGeom(geom, cx, cy) {
|
||||
if (!geom) return geom;
|
||||
const cutouts = Array.isArray(geom.cutouts)
|
||||
? geom.cutouts.map(c => ({ ...c, center: [c.center[0] - cx, c.center[1] - cy] }))
|
||||
: geom.cutouts;
|
||||
const extraPads = Array.isArray(geom.extraPads)
|
||||
? geom.extraPads.map(p => ({ ...p, pos: [p.pos[0] - cx, p.pos[1] - cy] }))
|
||||
: geom.extraPads;
|
||||
const extraSegments = Array.isArray(geom.extraSegments)
|
||||
? geom.extraSegments.map(s => ({ ...s, from: [s.from[0] - cx, s.from[1] - cy], to: [s.to[0] - cx, s.to[1] - cy] }))
|
||||
: geom.extraSegments;
|
||||
return { ...geom, cutouts, extraPads, extraSegments };
|
||||
}
|
||||
|
||||
function getBusbarExportContext() {
|
||||
if (!lastBusbarDrawArgs || lastComputedGeometries.length === 0) return null;
|
||||
const { positions, padRadius } = lastBusbarDrawArgs;
|
||||
@@ -579,7 +915,7 @@ function getBusbarExportContext() {
|
||||
const height = parseFloat(document.getElementById('height').value);
|
||||
const busbarFormat = document.getElementById('busbarFormat')?.value || 'step';
|
||||
const safeName = (name) => (name || '').replace(/[^A-Za-z0-9]+/g, '_').replace(/^_+|_+$/g, '') || 'busbar';
|
||||
return { centeredPositions, padRadius, height, busbarFormat, safeName };
|
||||
return { centeredPositions, centerX: cx, centerY: cy, padRadius, height, busbarFormat, safeName };
|
||||
}
|
||||
|
||||
export async function downloadSingleBusbar(busbarId) {
|
||||
@@ -609,10 +945,10 @@ export async function downloadSingleBusbar(busbarId) {
|
||||
await new Promise(r => setTimeout(r, 20));
|
||||
try {
|
||||
if (ctx.busbarFormat === 'dxf') {
|
||||
const content = buildBusbarDXF(geom, ctx.centeredPositions, ctx.padRadius);
|
||||
const content = buildBusbarDXF(centeredGeom(geom, ctx.centerX, ctx.centerY), ctx.centeredPositions, ctx.padRadius);
|
||||
downloadDXF(content, `${base}.dxf`);
|
||||
} else {
|
||||
const shape = build3DBusbar(geom, ctx.centeredPositions, ctx.padRadius, ctx.height, bb.thickness);
|
||||
const shape = build3DBusbar(centeredGeom(geom, ctx.centerX, ctx.centerY), ctx.centeredPositions, ctx.padRadius, ctx.height, bb.thickness);
|
||||
if (!shape) { showStatus(`Failed to build 3D shape for ${bb.name}.`, 'error'); return; }
|
||||
downloadSTEP(shape, `${base}.step`);
|
||||
}
|
||||
@@ -648,10 +984,10 @@ export async function downloadAllBusbarsZip() {
|
||||
for (const { bb, geom } of eligible) {
|
||||
const base = `busbar_${ctx.safeName(bb.name)}`;
|
||||
if (ctx.busbarFormat === 'dxf') {
|
||||
const content = buildBusbarDXF(geom, ctx.centeredPositions, ctx.padRadius);
|
||||
const content = buildBusbarDXF(centeredGeom(geom, ctx.centerX, ctx.centerY), ctx.centeredPositions, ctx.padRadius);
|
||||
zip.file(`${base}.dxf`, content);
|
||||
} else {
|
||||
const shape = build3DBusbar(geom, ctx.centeredPositions, ctx.padRadius, ctx.height, bb.thickness);
|
||||
const shape = build3DBusbar(centeredGeom(geom, ctx.centerX, ctx.centerY), ctx.centeredPositions, ctx.padRadius, ctx.height, bb.thickness);
|
||||
if (!shape) continue;
|
||||
const bytes = buildSTEPBytes(shape, `_zip_${base}.step`);
|
||||
if (bytes) zip.file(`${base}.step`, bytes);
|
||||
|
||||
Reference in New Issue
Block a user