@tscircuit/3d-viewer 0.0.282 → 0.0.284
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 +1 -2
- package/dist/index.js +187 -1005
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -9914,7 +9914,7 @@ var require_vectorText = __commonJS({
|
|
|
9914
9914
|
}
|
|
9915
9915
|
return line3;
|
|
9916
9916
|
};
|
|
9917
|
-
var
|
|
9917
|
+
var vectorText2 = (options, text) => {
|
|
9918
9918
|
const {
|
|
9919
9919
|
xOffset,
|
|
9920
9920
|
yOffset,
|
|
@@ -9971,7 +9971,7 @@ var require_vectorText = __commonJS({
|
|
|
9971
9971
|
}
|
|
9972
9972
|
return output;
|
|
9973
9973
|
};
|
|
9974
|
-
module.exports =
|
|
9974
|
+
module.exports = vectorText2;
|
|
9975
9975
|
}
|
|
9976
9976
|
});
|
|
9977
9977
|
|
|
@@ -13980,7 +13980,7 @@ var require_browser = __commonJS({
|
|
|
13980
13980
|
});
|
|
13981
13981
|
|
|
13982
13982
|
// src/CadViewer.tsx
|
|
13983
|
-
import { useState as
|
|
13983
|
+
import { useState as useState10, useRef as useRef8, useEffect as useEffect7 } from "react";
|
|
13984
13984
|
|
|
13985
13985
|
// src/CadViewerJscad.tsx
|
|
13986
13986
|
import { su as su4 } from "@tscircuit/soup-util";
|
|
@@ -17282,7 +17282,7 @@ import {
|
|
|
17282
17282
|
// package.json
|
|
17283
17283
|
var package_default = {
|
|
17284
17284
|
name: "@tscircuit/3d-viewer",
|
|
17285
|
-
version: "0.0.
|
|
17285
|
+
version: "0.0.283",
|
|
17286
17286
|
main: "./dist/index.js",
|
|
17287
17287
|
module: "./dist/index.js",
|
|
17288
17288
|
type: "module",
|
|
@@ -17314,7 +17314,7 @@ var package_default = {
|
|
|
17314
17314
|
"@react-three/drei": "^9.121.4",
|
|
17315
17315
|
"@react-three/fiber": "^8.17.14",
|
|
17316
17316
|
"@tscircuit/core": "^0.0.454",
|
|
17317
|
-
"@tscircuit/props": "^0.0.
|
|
17317
|
+
"@tscircuit/props": "^0.0.241",
|
|
17318
17318
|
"@tscircuit/soup-util": "^0.0.41",
|
|
17319
17319
|
"jscad-electronics": "^0.0.29",
|
|
17320
17320
|
"jscad-fiber": "^0.0.79",
|
|
@@ -17677,10 +17677,6 @@ var colors = {
|
|
|
17677
17677
|
fr1Copper: [0.8, 0.4, 0.2],
|
|
17678
17678
|
fr1CopperSolderWithMask: [0.9, 0.6, 0.2]
|
|
17679
17679
|
};
|
|
17680
|
-
var MANIFOLD_Z_OFFSET = 1e-3;
|
|
17681
|
-
var SMOOTH_CIRCLE_SEGMENTS = 32;
|
|
17682
|
-
var DEFAULT_SMT_PAD_THICKNESS = 0.035;
|
|
17683
|
-
var TRACE_TEXTURE_RESOLUTION = 50;
|
|
17684
17680
|
var boardMaterialColors = {
|
|
17685
17681
|
fr1: colors.fr1Copper,
|
|
17686
17682
|
fr4: colors.fr4Green
|
|
@@ -19056,14 +19052,13 @@ var CadViewerJscad = forwardRef2(
|
|
|
19056
19052
|
);
|
|
19057
19053
|
|
|
19058
19054
|
// src/CadViewerManifold.tsx
|
|
19059
|
-
import { su as
|
|
19060
|
-
import
|
|
19061
|
-
import { useEffect as useEffect6, useMemo as useMemo8, useState as useState9 } from "react";
|
|
19055
|
+
import { su as su6 } from "@tscircuit/soup-util";
|
|
19056
|
+
import { useMemo as useMemo8 } from "react";
|
|
19062
19057
|
|
|
19063
19058
|
// src/hooks/useManifoldBoardBuilder.ts
|
|
19064
19059
|
import { useState as useState8, useEffect as useEffect5, useMemo as useMemo7, useRef as useRef6 } from "react";
|
|
19065
|
-
import { su as
|
|
19066
|
-
import * as
|
|
19060
|
+
import { su as su5 } from "@tscircuit/soup-util";
|
|
19061
|
+
import * as THREE5 from "three";
|
|
19067
19062
|
|
|
19068
19063
|
// src/utils/manifold-mesh-to-three-geometry.ts
|
|
19069
19064
|
import * as THREE4 from "three";
|
|
@@ -19086,914 +19081,150 @@ function manifoldMeshToThreeGeometry(manifoldMesh) {
|
|
|
19086
19081
|
return geometry;
|
|
19087
19082
|
}
|
|
19088
19083
|
|
|
19089
|
-
// src/utils/trace-texture.ts
|
|
19090
|
-
import * as THREE5 from "three";
|
|
19091
|
-
import { su as su5 } from "@tscircuit/soup-util";
|
|
19092
|
-
function isWireRoutePoint(point) {
|
|
19093
|
-
return point && point.route_type === "wire" && typeof point.layer === "string" && typeof point.width === "number";
|
|
19094
|
-
}
|
|
19095
|
-
function createTraceTextureForLayer({
|
|
19096
|
-
layer,
|
|
19097
|
-
circuitJson,
|
|
19098
|
-
boardData,
|
|
19099
|
-
traceColor,
|
|
19100
|
-
traceTextureResolution
|
|
19101
|
-
}) {
|
|
19102
|
-
const pcbTraces = su5(circuitJson).pcb_trace.list();
|
|
19103
|
-
const allPcbVias = su5(circuitJson).pcb_via.list();
|
|
19104
|
-
const allPcbPlatedHoles = su5(
|
|
19105
|
-
circuitJson
|
|
19106
|
-
).pcb_plated_hole.list();
|
|
19107
|
-
const tracesOnLayer = pcbTraces.filter(
|
|
19108
|
-
(t) => t.route.some((p) => isWireRoutePoint(p) && p.layer === layer)
|
|
19109
|
-
);
|
|
19110
|
-
if (tracesOnLayer.length === 0) return null;
|
|
19111
|
-
const canvas = document.createElement("canvas");
|
|
19112
|
-
const canvasWidth = Math.floor(boardData.width * traceTextureResolution);
|
|
19113
|
-
const canvasHeight = Math.floor(boardData.height * traceTextureResolution);
|
|
19114
|
-
canvas.width = canvasWidth;
|
|
19115
|
-
canvas.height = canvasHeight;
|
|
19116
|
-
const ctx = canvas.getContext("2d");
|
|
19117
|
-
if (!ctx) return null;
|
|
19118
|
-
if (layer === "bottom") {
|
|
19119
|
-
ctx.translate(0, canvasHeight);
|
|
19120
|
-
ctx.scale(1, -1);
|
|
19121
|
-
}
|
|
19122
|
-
tracesOnLayer.forEach((trace) => {
|
|
19123
|
-
let firstPoint = true;
|
|
19124
|
-
ctx.beginPath();
|
|
19125
|
-
ctx.strokeStyle = traceColor;
|
|
19126
|
-
ctx.lineCap = "round";
|
|
19127
|
-
ctx.lineJoin = "round";
|
|
19128
|
-
let currentLineWidth = 0;
|
|
19129
|
-
for (const point of trace.route) {
|
|
19130
|
-
if (!isWireRoutePoint(point) || point.layer !== layer) {
|
|
19131
|
-
if (!firstPoint) ctx.stroke();
|
|
19132
|
-
firstPoint = true;
|
|
19133
|
-
continue;
|
|
19134
|
-
}
|
|
19135
|
-
const pcbX = point.x;
|
|
19136
|
-
const pcbY = point.y;
|
|
19137
|
-
currentLineWidth = point.width * traceTextureResolution;
|
|
19138
|
-
ctx.lineWidth = currentLineWidth;
|
|
19139
|
-
const canvasX = (pcbX - boardData.center.x + boardData.width / 2) * traceTextureResolution;
|
|
19140
|
-
const canvasY = (-(pcbY - boardData.center.y) + boardData.height / 2) * traceTextureResolution;
|
|
19141
|
-
if (firstPoint) {
|
|
19142
|
-
ctx.moveTo(canvasX, canvasY);
|
|
19143
|
-
firstPoint = false;
|
|
19144
|
-
} else {
|
|
19145
|
-
ctx.lineTo(canvasX, canvasY);
|
|
19146
|
-
}
|
|
19147
|
-
}
|
|
19148
|
-
if (!firstPoint) {
|
|
19149
|
-
ctx.stroke();
|
|
19150
|
-
}
|
|
19151
|
-
});
|
|
19152
|
-
ctx.globalCompositeOperation = "destination-out";
|
|
19153
|
-
ctx.fillStyle = "black";
|
|
19154
|
-
allPcbVias.forEach((via) => {
|
|
19155
|
-
const canvasX = (via.x - boardData.center.x + boardData.width / 2) * traceTextureResolution;
|
|
19156
|
-
const canvasY = (-(via.y - boardData.center.y) + boardData.height / 2) * traceTextureResolution;
|
|
19157
|
-
const canvasRadius = via.outer_diameter / 2 * traceTextureResolution;
|
|
19158
|
-
ctx.beginPath();
|
|
19159
|
-
ctx.arc(canvasX, canvasY, canvasRadius, 0, 2 * Math.PI, false);
|
|
19160
|
-
ctx.fill();
|
|
19161
|
-
});
|
|
19162
|
-
allPcbPlatedHoles.forEach((ph) => {
|
|
19163
|
-
if (ph.layers.includes(layer) && ph.shape === "circle") {
|
|
19164
|
-
const canvasX = (ph.x - boardData.center.x + boardData.width / 2) * traceTextureResolution;
|
|
19165
|
-
const canvasY = (-(ph.y - boardData.center.y) + boardData.height / 2) * traceTextureResolution;
|
|
19166
|
-
const canvasRadius = ph.outer_diameter / 2 * traceTextureResolution;
|
|
19167
|
-
ctx.beginPath();
|
|
19168
|
-
ctx.arc(canvasX, canvasY, canvasRadius, 0, 2 * Math.PI, false);
|
|
19169
|
-
ctx.fill();
|
|
19170
|
-
}
|
|
19171
|
-
});
|
|
19172
|
-
ctx.globalCompositeOperation = "source-over";
|
|
19173
|
-
const texture = new THREE5.CanvasTexture(canvas);
|
|
19174
|
-
texture.generateMipmaps = true;
|
|
19175
|
-
texture.minFilter = THREE5.LinearMipmapLinearFilter;
|
|
19176
|
-
texture.magFilter = THREE5.LinearFilter;
|
|
19177
|
-
texture.anisotropy = 16;
|
|
19178
|
-
texture.needsUpdate = true;
|
|
19179
|
-
return texture;
|
|
19180
|
-
}
|
|
19181
|
-
|
|
19182
|
-
// src/utils/silkscreen-texture.ts
|
|
19183
|
-
var import_text2 = __toESM(require_text(), 1);
|
|
19184
|
-
import * as THREE6 from "three";
|
|
19185
|
-
import { su as su6 } from "@tscircuit/soup-util";
|
|
19186
|
-
function createSilkscreenTextureForLayer({
|
|
19187
|
-
layer,
|
|
19188
|
-
circuitJson,
|
|
19189
|
-
boardData,
|
|
19190
|
-
silkscreenColor = "rgb(255,255,255)",
|
|
19191
|
-
traceTextureResolution
|
|
19192
|
-
}) {
|
|
19193
|
-
const pcbSilkscreenTexts = su6(circuitJson).pcb_silkscreen_text.list();
|
|
19194
|
-
const pcbSilkscreenPaths = su6(circuitJson).pcb_silkscreen_path.list();
|
|
19195
|
-
const textsOnLayer = pcbSilkscreenTexts.filter((t) => t.layer === layer);
|
|
19196
|
-
const pathsOnLayer = pcbSilkscreenPaths.filter((p) => p.layer === layer);
|
|
19197
|
-
if (textsOnLayer.length === 0 && pathsOnLayer.length === 0) return null;
|
|
19198
|
-
const canvas = document.createElement("canvas");
|
|
19199
|
-
const canvasWidth = Math.floor(boardData.width * traceTextureResolution);
|
|
19200
|
-
const canvasHeight = Math.floor(boardData.height * traceTextureResolution);
|
|
19201
|
-
canvas.width = canvasWidth;
|
|
19202
|
-
canvas.height = canvasHeight;
|
|
19203
|
-
const ctx = canvas.getContext("2d");
|
|
19204
|
-
if (!ctx) return null;
|
|
19205
|
-
if (layer === "bottom") {
|
|
19206
|
-
ctx.translate(0, canvasHeight);
|
|
19207
|
-
ctx.scale(1, -1);
|
|
19208
|
-
}
|
|
19209
|
-
ctx.strokeStyle = silkscreenColor;
|
|
19210
|
-
ctx.fillStyle = silkscreenColor;
|
|
19211
|
-
pathsOnLayer.forEach((path) => {
|
|
19212
|
-
if (path.route.length < 2) return;
|
|
19213
|
-
ctx.beginPath();
|
|
19214
|
-
ctx.lineWidth = (path.stroke_width || 0.1) * traceTextureResolution;
|
|
19215
|
-
ctx.lineCap = "round";
|
|
19216
|
-
ctx.lineJoin = "round";
|
|
19217
|
-
path.route.forEach((point, index) => {
|
|
19218
|
-
const canvasX = (point.x - boardData.center.x + boardData.width / 2) * traceTextureResolution;
|
|
19219
|
-
const canvasY = (-(point.y - boardData.center.y) + boardData.height / 2) * traceTextureResolution;
|
|
19220
|
-
if (index === 0) ctx.moveTo(canvasX, canvasY);
|
|
19221
|
-
else ctx.lineTo(canvasX, canvasY);
|
|
19222
|
-
});
|
|
19223
|
-
ctx.stroke();
|
|
19224
|
-
});
|
|
19225
|
-
textsOnLayer.forEach((textS) => {
|
|
19226
|
-
const fontSize = textS.font_size || 0.25;
|
|
19227
|
-
const textStrokeWidth = Math.min(Math.max(0.01, fontSize * 0.1), fontSize * 0.05) * traceTextureResolution;
|
|
19228
|
-
ctx.lineWidth = textStrokeWidth;
|
|
19229
|
-
ctx.lineCap = "butt";
|
|
19230
|
-
ctx.lineJoin = "miter";
|
|
19231
|
-
const rawTextOutlines = (0, import_text2.vectorText)({
|
|
19232
|
-
height: fontSize * 0.45,
|
|
19233
|
-
input: textS.text
|
|
19234
|
-
});
|
|
19235
|
-
const processedTextOutlines = [];
|
|
19236
|
-
rawTextOutlines.forEach((outline) => {
|
|
19237
|
-
if (outline.length === 29) {
|
|
19238
|
-
processedTextOutlines.push(
|
|
19239
|
-
outline.slice(0, 15)
|
|
19240
|
-
);
|
|
19241
|
-
processedTextOutlines.push(
|
|
19242
|
-
outline.slice(14, 29)
|
|
19243
|
-
);
|
|
19244
|
-
} else if (outline.length === 17) {
|
|
19245
|
-
processedTextOutlines.push(
|
|
19246
|
-
outline.slice(0, 10)
|
|
19247
|
-
);
|
|
19248
|
-
processedTextOutlines.push(
|
|
19249
|
-
outline.slice(9, 17)
|
|
19250
|
-
);
|
|
19251
|
-
} else {
|
|
19252
|
-
processedTextOutlines.push(outline);
|
|
19253
|
-
}
|
|
19254
|
-
});
|
|
19255
|
-
const points = processedTextOutlines.flat();
|
|
19256
|
-
const textBounds = {
|
|
19257
|
-
minX: points.length > 0 ? Math.min(...points.map((p) => p[0])) : 0,
|
|
19258
|
-
maxX: points.length > 0 ? Math.max(...points.map((p) => p[0])) : 0,
|
|
19259
|
-
minY: points.length > 0 ? Math.min(...points.map((p) => p[1])) : 0,
|
|
19260
|
-
maxY: points.length > 0 ? Math.max(...points.map((p) => p[1])) : 0
|
|
19261
|
-
};
|
|
19262
|
-
const textCenterX = (textBounds.minX + textBounds.maxX) / 2;
|
|
19263
|
-
const textCenterY = (textBounds.minY + textBounds.maxY) / 2;
|
|
19264
|
-
let xOff = -textCenterX;
|
|
19265
|
-
let yOff = -textCenterY;
|
|
19266
|
-
const alignment = textS.anchor_alignment || "center";
|
|
19267
|
-
if (alignment.includes("left")) {
|
|
19268
|
-
xOff = -textBounds.minX;
|
|
19269
|
-
} else if (alignment.includes("right")) {
|
|
19270
|
-
xOff = -textBounds.maxX;
|
|
19271
|
-
}
|
|
19272
|
-
if (alignment.includes("top")) {
|
|
19273
|
-
yOff = -textBounds.maxY;
|
|
19274
|
-
} else if (alignment.includes("bottom")) {
|
|
19275
|
-
yOff = -textBounds.minY;
|
|
19276
|
-
}
|
|
19277
|
-
const transformMatrices = [];
|
|
19278
|
-
let rotationDeg = textS.ccw_rotation ?? 0;
|
|
19279
|
-
if (textS.layer === "bottom") {
|
|
19280
|
-
transformMatrices.push(
|
|
19281
|
-
translate2(textCenterX, textCenterY),
|
|
19282
|
-
{ a: -1, b: 0, c: 0, d: 1, e: 0, f: 0 },
|
|
19283
|
-
translate2(-textCenterX, -textCenterY)
|
|
19284
|
-
);
|
|
19285
|
-
rotationDeg = -rotationDeg;
|
|
19286
|
-
}
|
|
19287
|
-
if (rotationDeg) {
|
|
19288
|
-
const rad = rotationDeg * Math.PI / 180;
|
|
19289
|
-
transformMatrices.push(
|
|
19290
|
-
translate2(textCenterX, textCenterY),
|
|
19291
|
-
rotate(rad),
|
|
19292
|
-
translate2(-textCenterX, -textCenterY)
|
|
19293
|
-
);
|
|
19294
|
-
}
|
|
19295
|
-
const finalTransformMatrix = transformMatrices.length > 0 ? compose(...transformMatrices) : void 0;
|
|
19296
|
-
processedTextOutlines.forEach((segment) => {
|
|
19297
|
-
ctx.beginPath();
|
|
19298
|
-
segment.forEach((p, index) => {
|
|
19299
|
-
let transformedP = { x: p[0], y: p[1] };
|
|
19300
|
-
if (finalTransformMatrix) {
|
|
19301
|
-
transformedP = applyToPoint(finalTransformMatrix, transformedP);
|
|
19302
|
-
}
|
|
19303
|
-
const pcbX = transformedP.x + xOff + textS.anchor_position.x;
|
|
19304
|
-
const pcbY = transformedP.y + yOff + textS.anchor_position.y;
|
|
19305
|
-
const canvasX = (pcbX - boardData.center.x + boardData.width / 2) * traceTextureResolution;
|
|
19306
|
-
const canvasY = (-(pcbY - boardData.center.y) + boardData.height / 2) * traceTextureResolution;
|
|
19307
|
-
if (index === 0) ctx.moveTo(canvasX, canvasY);
|
|
19308
|
-
else ctx.lineTo(canvasX, canvasY);
|
|
19309
|
-
});
|
|
19310
|
-
ctx.stroke();
|
|
19311
|
-
});
|
|
19312
|
-
});
|
|
19313
|
-
const texture = new THREE6.CanvasTexture(canvas);
|
|
19314
|
-
texture.generateMipmaps = true;
|
|
19315
|
-
texture.minFilter = THREE6.LinearMipmapLinearFilter;
|
|
19316
|
-
texture.magFilter = THREE6.LinearFilter;
|
|
19317
|
-
texture.anisotropy = 16;
|
|
19318
|
-
texture.needsUpdate = true;
|
|
19319
|
-
return texture;
|
|
19320
|
-
}
|
|
19321
|
-
|
|
19322
|
-
// src/utils/manifold/process-non-plated-holes.ts
|
|
19323
|
-
import { su as su7 } from "@tscircuit/soup-util";
|
|
19324
|
-
|
|
19325
|
-
// src/utils/hole-geoms.ts
|
|
19326
|
-
function createCircleHoleDrill({
|
|
19327
|
-
Manifold,
|
|
19328
|
-
x,
|
|
19329
|
-
y,
|
|
19330
|
-
diameter,
|
|
19331
|
-
thickness,
|
|
19332
|
-
segments = 32
|
|
19333
|
-
}) {
|
|
19334
|
-
const drill = Manifold.cylinder(
|
|
19335
|
-
thickness * 1.2,
|
|
19336
|
-
diameter / 2,
|
|
19337
|
-
diameter / 2,
|
|
19338
|
-
segments,
|
|
19339
|
-
true
|
|
19340
|
-
);
|
|
19341
|
-
return drill.translate([x, y, 0]);
|
|
19342
|
-
}
|
|
19343
|
-
function createPlatedHoleDrill({
|
|
19344
|
-
Manifold,
|
|
19345
|
-
x,
|
|
19346
|
-
y,
|
|
19347
|
-
outerDiameter,
|
|
19348
|
-
thickness,
|
|
19349
|
-
zOffset = 1e-3,
|
|
19350
|
-
segments = 32
|
|
19351
|
-
}) {
|
|
19352
|
-
const boardHoleRadius = outerDiameter / 2 + zOffset;
|
|
19353
|
-
const drill = Manifold.cylinder(
|
|
19354
|
-
thickness * 1.2,
|
|
19355
|
-
boardHoleRadius,
|
|
19356
|
-
boardHoleRadius,
|
|
19357
|
-
segments,
|
|
19358
|
-
true
|
|
19359
|
-
);
|
|
19360
|
-
return drill.translate([x, y, 0]);
|
|
19361
|
-
}
|
|
19362
|
-
|
|
19363
|
-
// src/utils/manifold/process-non-plated-holes.ts
|
|
19364
|
-
function isCircleHole(hole) {
|
|
19365
|
-
return (hole.shape === "circle" || hole.hole_shape === "circle") && typeof hole.hole_diameter === "number";
|
|
19366
|
-
}
|
|
19367
|
-
function processNonPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
|
|
19368
|
-
const nonPlatedHoleBoardDrills = [];
|
|
19369
|
-
const pcbHoles = su7(circuitJson).pcb_hole.list();
|
|
19370
|
-
pcbHoles.forEach((hole) => {
|
|
19371
|
-
if (isCircleHole(hole)) {
|
|
19372
|
-
const translatedDrill = createCircleHoleDrill({
|
|
19373
|
-
Manifold,
|
|
19374
|
-
x: hole.x,
|
|
19375
|
-
y: hole.y,
|
|
19376
|
-
diameter: hole.hole_diameter,
|
|
19377
|
-
thickness: pcbThickness,
|
|
19378
|
-
segments: SMOOTH_CIRCLE_SEGMENTS
|
|
19379
|
-
});
|
|
19380
|
-
manifoldInstancesForCleanup.push(translatedDrill);
|
|
19381
|
-
nonPlatedHoleBoardDrills.push(translatedDrill);
|
|
19382
|
-
}
|
|
19383
|
-
});
|
|
19384
|
-
return { nonPlatedHoleBoardDrills };
|
|
19385
|
-
}
|
|
19386
|
-
|
|
19387
|
-
// src/utils/manifold/process-plated-holes.ts
|
|
19388
|
-
import { su as su8 } from "@tscircuit/soup-util";
|
|
19389
|
-
import * as THREE7 from "three";
|
|
19390
|
-
var COPPER_COLOR = new THREE7.Color(...colors.copper);
|
|
19391
|
-
function processPlatedHolesForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
|
|
19392
|
-
const platedHoleBoardDrills = [];
|
|
19393
|
-
const pcbPlatedHoles = su8(circuitJson).pcb_plated_hole.list();
|
|
19394
|
-
const platedHoleCopperGeoms = [];
|
|
19395
|
-
pcbPlatedHoles.forEach((ph, index) => {
|
|
19396
|
-
if (ph.shape === "circle") {
|
|
19397
|
-
const translatedDrill = createPlatedHoleDrill({
|
|
19398
|
-
Manifold,
|
|
19399
|
-
x: ph.x,
|
|
19400
|
-
y: ph.y,
|
|
19401
|
-
outerDiameter: ph.outer_diameter,
|
|
19402
|
-
// Drill for the board
|
|
19403
|
-
thickness: pcbThickness,
|
|
19404
|
-
zOffset: MANIFOLD_Z_OFFSET,
|
|
19405
|
-
segments: SMOOTH_CIRCLE_SEGMENTS
|
|
19406
|
-
});
|
|
19407
|
-
manifoldInstancesForCleanup.push(translatedDrill);
|
|
19408
|
-
platedHoleBoardDrills.push(translatedDrill);
|
|
19409
|
-
const copperPartThickness = pcbThickness + 2 * MANIFOLD_Z_OFFSET;
|
|
19410
|
-
let platedPart = Manifold.cylinder(
|
|
19411
|
-
copperPartThickness,
|
|
19412
|
-
ph.outer_diameter / 2,
|
|
19413
|
-
ph.outer_diameter / 2,
|
|
19414
|
-
SMOOTH_CIRCLE_SEGMENTS,
|
|
19415
|
-
true
|
|
19416
|
-
);
|
|
19417
|
-
manifoldInstancesForCleanup.push(platedPart);
|
|
19418
|
-
const drillForCopper = Manifold.cylinder(
|
|
19419
|
-
copperPartThickness * 1.05,
|
|
19420
|
-
// ensure it cuts through
|
|
19421
|
-
ph.hole_diameter / 2,
|
|
19422
|
-
ph.hole_diameter / 2,
|
|
19423
|
-
SMOOTH_CIRCLE_SEGMENTS,
|
|
19424
|
-
true
|
|
19425
|
-
);
|
|
19426
|
-
manifoldInstancesForCleanup.push(drillForCopper);
|
|
19427
|
-
const finalPlatedPartOp = platedPart.subtract(drillForCopper);
|
|
19428
|
-
manifoldInstancesForCleanup.push(finalPlatedPartOp);
|
|
19429
|
-
const translatedPlatedPart = finalPlatedPartOp.translate([ph.x, ph.y, 0]);
|
|
19430
|
-
manifoldInstancesForCleanup.push(translatedPlatedPart);
|
|
19431
|
-
const threeGeom = manifoldMeshToThreeGeometry(
|
|
19432
|
-
translatedPlatedPart.getMesh()
|
|
19433
|
-
);
|
|
19434
|
-
platedHoleCopperGeoms.push({
|
|
19435
|
-
key: `ph-${ph.pcb_plated_hole_id || index}`,
|
|
19436
|
-
geometry: threeGeom,
|
|
19437
|
-
color: COPPER_COLOR
|
|
19438
|
-
});
|
|
19439
|
-
} else if (ph.shape === "pill") {
|
|
19440
|
-
const holeWidthRaw = ph.hole_width;
|
|
19441
|
-
const holeHeightRaw = ph.hole_height;
|
|
19442
|
-
const shouldRotate = holeHeightRaw > holeWidthRaw;
|
|
19443
|
-
const holeW = shouldRotate ? holeHeightRaw : holeWidthRaw;
|
|
19444
|
-
const holeH = shouldRotate ? holeWidthRaw : holeHeightRaw;
|
|
19445
|
-
const defaultPadExtension = 0.4;
|
|
19446
|
-
const outerW = shouldRotate ? ph.outer_height ?? holeH + defaultPadExtension / 2 : ph.outer_width ?? holeW + defaultPadExtension / 2;
|
|
19447
|
-
const outerH = shouldRotate ? ph.outer_width ?? holeW + defaultPadExtension / 2 : ph.outer_height ?? holeH + defaultPadExtension / 2;
|
|
19448
|
-
const createPill = (width, height, depth) => {
|
|
19449
|
-
const radius = height / 2;
|
|
19450
|
-
const rectLength = width - height;
|
|
19451
|
-
let pillOp;
|
|
19452
|
-
if (rectLength < 1e-9) {
|
|
19453
|
-
pillOp = Manifold.cylinder(
|
|
19454
|
-
depth,
|
|
19455
|
-
radius,
|
|
19456
|
-
radius,
|
|
19457
|
-
SMOOTH_CIRCLE_SEGMENTS,
|
|
19458
|
-
true
|
|
19459
|
-
);
|
|
19460
|
-
} else {
|
|
19461
|
-
const rect = Manifold.cube(
|
|
19462
|
-
[Math.max(0, rectLength), height, depth],
|
|
19463
|
-
true
|
|
19464
|
-
);
|
|
19465
|
-
const cap1 = Manifold.cylinder(
|
|
19466
|
-
depth,
|
|
19467
|
-
radius,
|
|
19468
|
-
radius,
|
|
19469
|
-
SMOOTH_CIRCLE_SEGMENTS,
|
|
19470
|
-
true
|
|
19471
|
-
).translate([-rectLength / 2, 0, 0]);
|
|
19472
|
-
const cap2 = Manifold.cylinder(
|
|
19473
|
-
depth,
|
|
19474
|
-
radius,
|
|
19475
|
-
radius,
|
|
19476
|
-
SMOOTH_CIRCLE_SEGMENTS,
|
|
19477
|
-
true
|
|
19478
|
-
).translate([rectLength / 2, 0, 0]);
|
|
19479
|
-
pillOp = Manifold.union([rect, cap1, cap2]);
|
|
19480
|
-
manifoldInstancesForCleanup.push(rect, cap1, cap2);
|
|
19481
|
-
}
|
|
19482
|
-
manifoldInstancesForCleanup.push(pillOp);
|
|
19483
|
-
return pillOp;
|
|
19484
|
-
};
|
|
19485
|
-
const drillW = holeW + 2 * MANIFOLD_Z_OFFSET;
|
|
19486
|
-
const drillH = holeH + 2 * MANIFOLD_Z_OFFSET;
|
|
19487
|
-
const drillDepth = pcbThickness * 1.2;
|
|
19488
|
-
let boardPillDrillOp = createPill(drillW, drillH, drillDepth);
|
|
19489
|
-
if (shouldRotate) {
|
|
19490
|
-
const rotatedOp = boardPillDrillOp.rotate([0, 0, 90]);
|
|
19491
|
-
manifoldInstancesForCleanup.push(rotatedOp);
|
|
19492
|
-
boardPillDrillOp = rotatedOp;
|
|
19493
|
-
}
|
|
19494
|
-
const translatedBoardPillDrill = boardPillDrillOp.translate([
|
|
19495
|
-
ph.x,
|
|
19496
|
-
ph.y,
|
|
19497
|
-
0
|
|
19498
|
-
]);
|
|
19499
|
-
manifoldInstancesForCleanup.push(translatedBoardPillDrill);
|
|
19500
|
-
platedHoleBoardDrills.push(translatedBoardPillDrill);
|
|
19501
|
-
const copperPartThickness = pcbThickness + 2 * MANIFOLD_Z_OFFSET;
|
|
19502
|
-
const outerCopperOpUnrotated = createPill(
|
|
19503
|
-
outerW,
|
|
19504
|
-
outerH,
|
|
19505
|
-
copperPartThickness
|
|
19506
|
-
);
|
|
19507
|
-
const innerDrillOpUnrotated = createPill(
|
|
19508
|
-
holeW,
|
|
19509
|
-
holeH,
|
|
19510
|
-
copperPartThickness * 1.05
|
|
19511
|
-
// Make drill slightly thicker to ensure cut
|
|
19512
|
-
);
|
|
19513
|
-
let finalPlatedPartOp = outerCopperOpUnrotated.subtract(
|
|
19514
|
-
innerDrillOpUnrotated
|
|
19515
|
-
);
|
|
19516
|
-
manifoldInstancesForCleanup.push(finalPlatedPartOp);
|
|
19517
|
-
if (shouldRotate) {
|
|
19518
|
-
const rotatedOp = finalPlatedPartOp.rotate([0, 0, 90]);
|
|
19519
|
-
manifoldInstancesForCleanup.push(rotatedOp);
|
|
19520
|
-
finalPlatedPartOp = rotatedOp;
|
|
19521
|
-
}
|
|
19522
|
-
const translatedPlatedPart = finalPlatedPartOp.translate([ph.x, ph.y, 0]);
|
|
19523
|
-
manifoldInstancesForCleanup.push(translatedPlatedPart);
|
|
19524
|
-
const threeGeom = manifoldMeshToThreeGeometry(
|
|
19525
|
-
translatedPlatedPart.getMesh()
|
|
19526
|
-
);
|
|
19527
|
-
platedHoleCopperGeoms.push({
|
|
19528
|
-
key: `ph-${ph.pcb_plated_hole_id || index}`,
|
|
19529
|
-
geometry: threeGeom,
|
|
19530
|
-
color: COPPER_COLOR
|
|
19531
|
-
});
|
|
19532
|
-
}
|
|
19533
|
-
});
|
|
19534
|
-
return { platedHoleBoardDrills, platedHoleCopperGeoms };
|
|
19535
|
-
}
|
|
19536
|
-
|
|
19537
|
-
// src/utils/manifold/process-vias.ts
|
|
19538
|
-
import { su as su9 } from "@tscircuit/soup-util";
|
|
19539
|
-
import * as THREE8 from "three";
|
|
19540
|
-
|
|
19541
|
-
// src/utils/via-geoms.ts
|
|
19542
|
-
function createViaCopper({
|
|
19543
|
-
Manifold,
|
|
19544
|
-
x,
|
|
19545
|
-
y,
|
|
19546
|
-
outerDiameter,
|
|
19547
|
-
holeDiameter,
|
|
19548
|
-
thickness,
|
|
19549
|
-
zOffset = 1e-3,
|
|
19550
|
-
segments = 32
|
|
19551
|
-
}) {
|
|
19552
|
-
const copperPartThickness = thickness + 2 * zOffset;
|
|
19553
|
-
let viaCopper = Manifold.cylinder(
|
|
19554
|
-
copperPartThickness,
|
|
19555
|
-
outerDiameter / 2,
|
|
19556
|
-
-1,
|
|
19557
|
-
segments,
|
|
19558
|
-
true
|
|
19559
|
-
);
|
|
19560
|
-
const drill = Manifold.cylinder(
|
|
19561
|
-
copperPartThickness * 1.05,
|
|
19562
|
-
holeDiameter / 2,
|
|
19563
|
-
-1,
|
|
19564
|
-
segments,
|
|
19565
|
-
true
|
|
19566
|
-
);
|
|
19567
|
-
const finalViaCopperOp = viaCopper.subtract(drill);
|
|
19568
|
-
return finalViaCopperOp.translate([x, y, 0]);
|
|
19569
|
-
}
|
|
19570
|
-
|
|
19571
|
-
// src/utils/manifold/process-vias.ts
|
|
19572
|
-
var COPPER_COLOR2 = new THREE8.Color(...colors.copper);
|
|
19573
|
-
function processViasForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
|
|
19574
|
-
const viaBoardDrills = [];
|
|
19575
|
-
const pcbVias = su9(circuitJson).pcb_via.list();
|
|
19576
|
-
const viaCopperGeoms = [];
|
|
19577
|
-
pcbVias.forEach((via, index) => {
|
|
19578
|
-
if (typeof via.outer_diameter === "number") {
|
|
19579
|
-
const translatedDrill = createPlatedHoleDrill({
|
|
19580
|
-
Manifold,
|
|
19581
|
-
x: via.x,
|
|
19582
|
-
y: via.y,
|
|
19583
|
-
outerDiameter: via.outer_diameter,
|
|
19584
|
-
thickness: pcbThickness,
|
|
19585
|
-
zOffset: MANIFOLD_Z_OFFSET,
|
|
19586
|
-
segments: SMOOTH_CIRCLE_SEGMENTS
|
|
19587
|
-
});
|
|
19588
|
-
manifoldInstancesForCleanup.push(translatedDrill);
|
|
19589
|
-
viaBoardDrills.push(translatedDrill);
|
|
19590
|
-
}
|
|
19591
|
-
if (typeof via.outer_diameter === "number" && typeof via.hole_diameter === "number") {
|
|
19592
|
-
const translatedViaCopper = createViaCopper({
|
|
19593
|
-
Manifold,
|
|
19594
|
-
x: via.x,
|
|
19595
|
-
y: via.y,
|
|
19596
|
-
outerDiameter: via.outer_diameter,
|
|
19597
|
-
holeDiameter: via.hole_diameter,
|
|
19598
|
-
thickness: pcbThickness,
|
|
19599
|
-
zOffset: MANIFOLD_Z_OFFSET,
|
|
19600
|
-
segments: SMOOTH_CIRCLE_SEGMENTS
|
|
19601
|
-
});
|
|
19602
|
-
manifoldInstancesForCleanup.push(translatedViaCopper);
|
|
19603
|
-
const threeGeom = manifoldMeshToThreeGeometry(
|
|
19604
|
-
translatedViaCopper.getMesh()
|
|
19605
|
-
);
|
|
19606
|
-
viaCopperGeoms.push({
|
|
19607
|
-
key: `via-${via.pcb_via_id || index}`,
|
|
19608
|
-
geometry: threeGeom,
|
|
19609
|
-
color: COPPER_COLOR2
|
|
19610
|
-
});
|
|
19611
|
-
}
|
|
19612
|
-
});
|
|
19613
|
-
return { viaBoardDrills, viaCopperGeoms };
|
|
19614
|
-
}
|
|
19615
|
-
|
|
19616
|
-
// src/utils/manifold/process-smt-pads.ts
|
|
19617
|
-
import { su as su10 } from "@tscircuit/soup-util";
|
|
19618
|
-
import * as THREE9 from "three";
|
|
19619
|
-
|
|
19620
|
-
// src/utils/pad-geoms.ts
|
|
19621
|
-
function createPadManifoldOp({
|
|
19622
|
-
Manifold,
|
|
19623
|
-
pad,
|
|
19624
|
-
padBaseThickness
|
|
19625
|
-
}) {
|
|
19626
|
-
if (pad.shape === "rect") {
|
|
19627
|
-
return Manifold.cube([pad.width, pad.height, padBaseThickness], true);
|
|
19628
|
-
} else if (pad.shape === "circle" && pad.radius) {
|
|
19629
|
-
return Manifold.cylinder(padBaseThickness, pad.radius, -1, 32, true);
|
|
19630
|
-
}
|
|
19631
|
-
return null;
|
|
19632
|
-
}
|
|
19633
|
-
|
|
19634
|
-
// src/utils/manifold/process-smt-pads.ts
|
|
19635
|
-
var COPPER_COLOR3 = new THREE9.Color(...colors.copper);
|
|
19636
|
-
function processSmtPadsForManifold(Manifold, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
|
|
19637
|
-
const smtPadGeoms = [];
|
|
19638
|
-
const smtPads = su10(circuitJson).pcb_smtpad.list();
|
|
19639
|
-
smtPads.forEach((pad, index) => {
|
|
19640
|
-
const padBaseThickness = DEFAULT_SMT_PAD_THICKNESS;
|
|
19641
|
-
const zPos = pad.layer === "bottom" ? -pcbThickness / 2 - padBaseThickness / 2 - MANIFOLD_Z_OFFSET : pcbThickness / 2 + padBaseThickness / 2 + MANIFOLD_Z_OFFSET;
|
|
19642
|
-
let padManifoldOp = createPadManifoldOp({
|
|
19643
|
-
Manifold,
|
|
19644
|
-
pad,
|
|
19645
|
-
padBaseThickness
|
|
19646
|
-
});
|
|
19647
|
-
if (padManifoldOp) {
|
|
19648
|
-
manifoldInstancesForCleanup.push(padManifoldOp);
|
|
19649
|
-
const translatedPad = padManifoldOp.translate([pad.x, pad.y, zPos]);
|
|
19650
|
-
manifoldInstancesForCleanup.push(translatedPad);
|
|
19651
|
-
const threeGeom = manifoldMeshToThreeGeometry(translatedPad.getMesh());
|
|
19652
|
-
smtPadGeoms.push({
|
|
19653
|
-
key: `pad-${pad.pcb_smtpad_id || index}`,
|
|
19654
|
-
geometry: threeGeom,
|
|
19655
|
-
color: COPPER_COLOR3
|
|
19656
|
-
});
|
|
19657
|
-
}
|
|
19658
|
-
});
|
|
19659
|
-
return { smtPadGeoms };
|
|
19660
|
-
}
|
|
19661
|
-
|
|
19662
|
-
// src/utils/manifold/create-manifold-board.ts
|
|
19663
|
-
var arePointsClockwise2 = (points) => {
|
|
19664
|
-
let area = 0;
|
|
19665
|
-
for (let i = 0; i < points.length; i++) {
|
|
19666
|
-
const j = (i + 1) % points.length;
|
|
19667
|
-
if (points[i] && points[j]) {
|
|
19668
|
-
area += points[i][0] * points[j][1];
|
|
19669
|
-
area -= points[j][0] * points[i][1];
|
|
19670
|
-
}
|
|
19671
|
-
}
|
|
19672
|
-
const signedArea = area / 2;
|
|
19673
|
-
return signedArea <= 0;
|
|
19674
|
-
};
|
|
19675
|
-
function createManifoldBoard(Manifold, CrossSection, boardData, pcbThickness, manifoldInstancesForCleanup) {
|
|
19676
|
-
let boardOp;
|
|
19677
|
-
if (boardData.outline && boardData.outline.length >= 3) {
|
|
19678
|
-
let outlineVec2 = boardData.outline.map((p) => [
|
|
19679
|
-
p.x,
|
|
19680
|
-
p.y
|
|
19681
|
-
]);
|
|
19682
|
-
if (arePointsClockwise2(outlineVec2)) {
|
|
19683
|
-
outlineVec2 = outlineVec2.reverse();
|
|
19684
|
-
}
|
|
19685
|
-
const crossSection = CrossSection.ofPolygons([outlineVec2]);
|
|
19686
|
-
manifoldInstancesForCleanup.push(crossSection);
|
|
19687
|
-
boardOp = Manifold.extrude(
|
|
19688
|
-
crossSection,
|
|
19689
|
-
pcbThickness,
|
|
19690
|
-
void 0,
|
|
19691
|
-
// nDivisions
|
|
19692
|
-
void 0,
|
|
19693
|
-
// twistDegrees
|
|
19694
|
-
void 0,
|
|
19695
|
-
// scaleTop
|
|
19696
|
-
true
|
|
19697
|
-
// center (for Z-axis)
|
|
19698
|
-
);
|
|
19699
|
-
} else {
|
|
19700
|
-
if (boardData.outline && boardData.outline.length > 0) {
|
|
19701
|
-
console.warn(
|
|
19702
|
-
"Board outline has fewer than 3 points, falling back to rectangular board."
|
|
19703
|
-
);
|
|
19704
|
-
}
|
|
19705
|
-
boardOp = Manifold.cube(
|
|
19706
|
-
[boardData.width, boardData.height, pcbThickness],
|
|
19707
|
-
true
|
|
19708
|
-
// center (for all axes)
|
|
19709
|
-
);
|
|
19710
|
-
}
|
|
19711
|
-
manifoldInstancesForCleanup.push(boardOp);
|
|
19712
|
-
boardOp = boardOp.translate([boardData.center.x, boardData.center.y, 0]);
|
|
19713
|
-
manifoldInstancesForCleanup.push(boardOp);
|
|
19714
|
-
return boardOp;
|
|
19715
|
-
}
|
|
19716
|
-
|
|
19717
|
-
// src/utils/manifold/process-cutouts.ts
|
|
19718
|
-
import { su as su11 } from "@tscircuit/soup-util";
|
|
19719
|
-
var arePointsClockwise3 = (points) => {
|
|
19720
|
-
let area = 0;
|
|
19721
|
-
for (let i = 0; i < points.length; i++) {
|
|
19722
|
-
const j = (i + 1) % points.length;
|
|
19723
|
-
if (points[i] && points[j]) {
|
|
19724
|
-
area += points[i][0] * points[j][1];
|
|
19725
|
-
area -= points[j][0] * points[i][1];
|
|
19726
|
-
}
|
|
19727
|
-
}
|
|
19728
|
-
const signedArea = area / 2;
|
|
19729
|
-
return signedArea <= 0;
|
|
19730
|
-
};
|
|
19731
|
-
function processCutoutsForManifold(Manifold, CrossSection, circuitJson, pcbThickness, manifoldInstancesForCleanup) {
|
|
19732
|
-
const cutoutOps = [];
|
|
19733
|
-
const pcbCutouts = su11(circuitJson).pcb_cutout.list();
|
|
19734
|
-
for (const cutout of pcbCutouts) {
|
|
19735
|
-
let cutoutOp;
|
|
19736
|
-
const cutoutHeight = pcbThickness * 1.5;
|
|
19737
|
-
switch (cutout.shape) {
|
|
19738
|
-
case "rect":
|
|
19739
|
-
cutoutOp = Manifold.cube(
|
|
19740
|
-
[cutout.width, cutout.height, cutoutHeight],
|
|
19741
|
-
true
|
|
19742
|
-
// centered
|
|
19743
|
-
);
|
|
19744
|
-
manifoldInstancesForCleanup.push(cutoutOp);
|
|
19745
|
-
if (cutout.rotation) {
|
|
19746
|
-
const rotationRadians = cutout.rotation * Math.PI / 180;
|
|
19747
|
-
const rotatedOp = cutoutOp.rotate([0, 0, cutout.rotation]);
|
|
19748
|
-
manifoldInstancesForCleanup.push(rotatedOp);
|
|
19749
|
-
cutoutOp = rotatedOp;
|
|
19750
|
-
}
|
|
19751
|
-
cutoutOp = cutoutOp.translate([
|
|
19752
|
-
cutout.center.x,
|
|
19753
|
-
cutout.center.y,
|
|
19754
|
-
0
|
|
19755
|
-
// Centered vertically by Manifold.cube, so Z is 0 for board plane
|
|
19756
|
-
]);
|
|
19757
|
-
manifoldInstancesForCleanup.push(cutoutOp);
|
|
19758
|
-
break;
|
|
19759
|
-
case "circle":
|
|
19760
|
-
cutoutOp = Manifold.cylinder(
|
|
19761
|
-
cutoutHeight,
|
|
19762
|
-
cutout.radius,
|
|
19763
|
-
-1,
|
|
19764
|
-
// default for radiusHigh
|
|
19765
|
-
SMOOTH_CIRCLE_SEGMENTS,
|
|
19766
|
-
true
|
|
19767
|
-
// centered
|
|
19768
|
-
);
|
|
19769
|
-
manifoldInstancesForCleanup.push(cutoutOp);
|
|
19770
|
-
cutoutOp = cutoutOp.translate([cutout.center.x, cutout.center.y, 0]);
|
|
19771
|
-
manifoldInstancesForCleanup.push(cutoutOp);
|
|
19772
|
-
break;
|
|
19773
|
-
case "polygon":
|
|
19774
|
-
if (cutout.points.length < 3) {
|
|
19775
|
-
console.warn(
|
|
19776
|
-
`PCB Cutout [${cutout.pcb_cutout_id}] polygon has fewer than 3 points, skipping.`
|
|
19777
|
-
);
|
|
19778
|
-
continue;
|
|
19779
|
-
}
|
|
19780
|
-
let pointsVec2 = cutout.points.map((p) => [
|
|
19781
|
-
p.x,
|
|
19782
|
-
p.y
|
|
19783
|
-
]);
|
|
19784
|
-
if (arePointsClockwise3(pointsVec2)) {
|
|
19785
|
-
pointsVec2 = pointsVec2.reverse();
|
|
19786
|
-
}
|
|
19787
|
-
const crossSection = CrossSection.ofPolygons([pointsVec2]);
|
|
19788
|
-
manifoldInstancesForCleanup.push(crossSection);
|
|
19789
|
-
cutoutOp = Manifold.extrude(
|
|
19790
|
-
crossSection,
|
|
19791
|
-
cutoutHeight,
|
|
19792
|
-
0,
|
|
19793
|
-
// nDivisions
|
|
19794
|
-
0,
|
|
19795
|
-
// twistDegrees
|
|
19796
|
-
[1, 1],
|
|
19797
|
-
// scaleTop
|
|
19798
|
-
true
|
|
19799
|
-
// center extrusion
|
|
19800
|
-
);
|
|
19801
|
-
manifoldInstancesForCleanup.push(cutoutOp);
|
|
19802
|
-
break;
|
|
19803
|
-
default:
|
|
19804
|
-
console.warn(
|
|
19805
|
-
`Unsupported cutout shape: ${cutout.shape} for cutout ${cutout.pcb_cutout_id}`
|
|
19806
|
-
);
|
|
19807
|
-
continue;
|
|
19808
|
-
}
|
|
19809
|
-
if (cutoutOp) {
|
|
19810
|
-
cutoutOps.push(cutoutOp);
|
|
19811
|
-
}
|
|
19812
|
-
}
|
|
19813
|
-
return { cutoutOps };
|
|
19814
|
-
}
|
|
19815
|
-
|
|
19816
19084
|
// src/hooks/useManifoldBoardBuilder.ts
|
|
19817
|
-
var
|
|
19085
|
+
var MANIFOLD_CDN_BASE_URL = "https://cdn.jsdelivr.net/npm/manifold-3d@3.1.1";
|
|
19086
|
+
var useManifoldBoardBuilder = (circuitJson) => {
|
|
19818
19087
|
const [geoms, setGeoms] = useState8(null);
|
|
19819
19088
|
const [textures, setTextures] = useState8(null);
|
|
19820
19089
|
const [pcbThickness, setPcbThickness] = useState8(null);
|
|
19821
19090
|
const [error, setError] = useState8(null);
|
|
19822
19091
|
const [isLoading, setIsLoading] = useState8(true);
|
|
19823
|
-
const
|
|
19824
|
-
const
|
|
19825
|
-
if (!circuitJson) return null;
|
|
19826
|
-
const boards = su12(circuitJson).pcb_board.list();
|
|
19827
|
-
if (boards.length === 0) {
|
|
19828
|
-
return null;
|
|
19829
|
-
}
|
|
19830
|
-
return boards[0];
|
|
19831
|
-
}, [circuitJson]);
|
|
19092
|
+
const [boardData, setBoardData] = useState8(null);
|
|
19093
|
+
const workerRef = useRef6(null);
|
|
19832
19094
|
useEffect5(() => {
|
|
19833
|
-
if (!
|
|
19095
|
+
if (!circuitJson) {
|
|
19834
19096
|
setGeoms(null);
|
|
19835
19097
|
setTextures(null);
|
|
19836
19098
|
setPcbThickness(null);
|
|
19837
|
-
|
|
19838
|
-
|
|
19839
|
-
|
|
19099
|
+
setError(null);
|
|
19100
|
+
setIsLoading(false);
|
|
19101
|
+
setBoardData(null);
|
|
19102
|
+
return;
|
|
19103
|
+
}
|
|
19104
|
+
const boards = su5(circuitJson).pcb_board.list();
|
|
19105
|
+
if (boards.length === 0) {
|
|
19106
|
+
setError("No pcb_board found in circuitJson.");
|
|
19840
19107
|
setIsLoading(false);
|
|
19841
19108
|
return;
|
|
19842
19109
|
}
|
|
19843
19110
|
setIsLoading(true);
|
|
19844
19111
|
setError(null);
|
|
19845
|
-
|
|
19846
|
-
|
|
19847
|
-
|
|
19848
|
-
|
|
19849
|
-
|
|
19850
|
-
const
|
|
19851
|
-
|
|
19852
|
-
|
|
19853
|
-
|
|
19854
|
-
|
|
19855
|
-
|
|
19856
|
-
|
|
19857
|
-
|
|
19858
|
-
|
|
19859
|
-
|
|
19860
|
-
|
|
19861
|
-
)
|
|
19862
|
-
|
|
19863
|
-
|
|
19864
|
-
|
|
19865
|
-
|
|
19866
|
-
|
|
19867
|
-
|
|
19868
|
-
|
|
19869
|
-
|
|
19870
|
-
|
|
19871
|
-
|
|
19872
|
-
|
|
19873
|
-
|
|
19874
|
-
|
|
19875
|
-
|
|
19876
|
-
|
|
19877
|
-
|
|
19878
|
-
|
|
19879
|
-
|
|
19880
|
-
|
|
19881
|
-
|
|
19882
|
-
|
|
19883
|
-
|
|
19884
|
-
|
|
19885
|
-
|
|
19886
|
-
|
|
19887
|
-
|
|
19888
|
-
|
|
19889
|
-
|
|
19890
|
-
|
|
19891
|
-
|
|
19892
|
-
|
|
19893
|
-
|
|
19894
|
-
|
|
19895
|
-
|
|
19896
|
-
|
|
19897
|
-
|
|
19898
|
-
|
|
19899
|
-
|
|
19900
|
-
|
|
19901
|
-
|
|
19902
|
-
|
|
19903
|
-
|
|
19904
|
-
|
|
19905
|
-
|
|
19906
|
-
|
|
19907
|
-
|
|
19908
|
-
|
|
19909
|
-
|
|
19910
|
-
|
|
19911
|
-
|
|
19912
|
-
|
|
19913
|
-
|
|
19914
|
-
|
|
19915
|
-
|
|
19916
|
-
|
|
19917
|
-
|
|
19918
|
-
|
|
19919
|
-
|
|
19112
|
+
setGeoms(null);
|
|
19113
|
+
setTextures(null);
|
|
19114
|
+
if (workerRef.current) {
|
|
19115
|
+
workerRef.current.terminate();
|
|
19116
|
+
}
|
|
19117
|
+
const worker = new Worker(
|
|
19118
|
+
new URL("../workers/manifold-builder.worker.ts", import.meta.url),
|
|
19119
|
+
{ type: "module" }
|
|
19120
|
+
);
|
|
19121
|
+
workerRef.current = worker;
|
|
19122
|
+
worker.postMessage({
|
|
19123
|
+
circuitJson,
|
|
19124
|
+
manifoldPath: `${MANIFOLD_CDN_BASE_URL}/manifold.wasm`
|
|
19125
|
+
});
|
|
19126
|
+
worker.onmessage = (event) => {
|
|
19127
|
+
const { type, payload, error: workerError, message } = event.data;
|
|
19128
|
+
switch (type) {
|
|
19129
|
+
case "pcb_thickness":
|
|
19130
|
+
setPcbThickness(payload);
|
|
19131
|
+
break;
|
|
19132
|
+
case "board_data":
|
|
19133
|
+
setBoardData(payload);
|
|
19134
|
+
break;
|
|
19135
|
+
case "geom_update":
|
|
19136
|
+
setGeoms((prevGeoms) => {
|
|
19137
|
+
const newGeoms = { ...prevGeoms };
|
|
19138
|
+
if (payload.board) {
|
|
19139
|
+
newGeoms.board = {
|
|
19140
|
+
geometry: manifoldMeshToThreeGeometry(payload.board.mesh),
|
|
19141
|
+
color: new THREE5.Color(...payload.board.color)
|
|
19142
|
+
};
|
|
19143
|
+
}
|
|
19144
|
+
const processGeomArray = (key) => {
|
|
19145
|
+
if (payload[key]) {
|
|
19146
|
+
const geomArray = payload[key].map((item) => ({
|
|
19147
|
+
key: item.key,
|
|
19148
|
+
geometry: manifoldMeshToThreeGeometry(item.mesh),
|
|
19149
|
+
color: new THREE5.Color(...item.color)
|
|
19150
|
+
}));
|
|
19151
|
+
newGeoms[key] = [...newGeoms[key] ?? [], ...geomArray];
|
|
19152
|
+
}
|
|
19153
|
+
};
|
|
19154
|
+
processGeomArray("platedHoles");
|
|
19155
|
+
processGeomArray("smtPads");
|
|
19156
|
+
processGeomArray("vias");
|
|
19157
|
+
return newGeoms;
|
|
19158
|
+
});
|
|
19159
|
+
break;
|
|
19160
|
+
case "texture_update":
|
|
19161
|
+
setTextures((prevTextures) => {
|
|
19162
|
+
const newTextures = { ...prevTextures };
|
|
19163
|
+
for (const key in payload) {
|
|
19164
|
+
const imageBitmap = payload[key];
|
|
19165
|
+
const texture = new THREE5.CanvasTexture(imageBitmap);
|
|
19166
|
+
texture.generateMipmaps = true;
|
|
19167
|
+
texture.minFilter = THREE5.LinearMipmapLinearFilter;
|
|
19168
|
+
texture.magFilter = THREE5.LinearFilter;
|
|
19169
|
+
texture.anisotropy = 16;
|
|
19170
|
+
texture.needsUpdate = true;
|
|
19171
|
+
newTextures[key] = texture;
|
|
19172
|
+
}
|
|
19173
|
+
return newTextures;
|
|
19174
|
+
});
|
|
19175
|
+
break;
|
|
19176
|
+
case "done":
|
|
19177
|
+
setIsLoading(false);
|
|
19178
|
+
break;
|
|
19179
|
+
case "error":
|
|
19180
|
+
setError(workerError);
|
|
19181
|
+
setIsLoading(false);
|
|
19182
|
+
break;
|
|
19183
|
+
case "log":
|
|
19184
|
+
console.time(message);
|
|
19185
|
+
break;
|
|
19186
|
+
case "log_end":
|
|
19187
|
+
console.timeEnd(message);
|
|
19188
|
+
break;
|
|
19920
19189
|
}
|
|
19921
|
-
|
|
19922
|
-
|
|
19923
|
-
|
|
19924
|
-
currentPcbThickness,
|
|
19925
|
-
manifoldInstancesForCleanup.current
|
|
19926
|
-
);
|
|
19927
|
-
currentGeoms.smtPads = smtPadGeoms;
|
|
19928
|
-
setGeoms(currentGeoms);
|
|
19929
|
-
const traceColorArr = tracesMaterialColors[boardData.material] ?? colors.fr4GreenSolderWithMask;
|
|
19930
|
-
const traceColor = `rgb(${Math.round(traceColorArr[0] * 255)}, ${Math.round(traceColorArr[1] * 255)}, ${Math.round(traceColorArr[2] * 255)})`;
|
|
19931
|
-
currentTextures.topTrace = createTraceTextureForLayer({
|
|
19932
|
-
layer: "top",
|
|
19933
|
-
circuitJson,
|
|
19934
|
-
boardData,
|
|
19935
|
-
traceColor,
|
|
19936
|
-
traceTextureResolution: TRACE_TEXTURE_RESOLUTION
|
|
19937
|
-
});
|
|
19938
|
-
currentTextures.bottomTrace = createTraceTextureForLayer({
|
|
19939
|
-
layer: "bottom",
|
|
19940
|
-
circuitJson,
|
|
19941
|
-
boardData,
|
|
19942
|
-
traceColor,
|
|
19943
|
-
traceTextureResolution: TRACE_TEXTURE_RESOLUTION
|
|
19944
|
-
});
|
|
19945
|
-
const silkscreenColor = "rgb(255,255,255)";
|
|
19946
|
-
currentTextures.topSilkscreen = createSilkscreenTextureForLayer({
|
|
19947
|
-
layer: "top",
|
|
19948
|
-
circuitJson,
|
|
19949
|
-
boardData,
|
|
19950
|
-
silkscreenColor,
|
|
19951
|
-
traceTextureResolution: TRACE_TEXTURE_RESOLUTION
|
|
19952
|
-
});
|
|
19953
|
-
currentTextures.bottomSilkscreen = createSilkscreenTextureForLayer({
|
|
19954
|
-
layer: "bottom",
|
|
19955
|
-
circuitJson,
|
|
19956
|
-
boardData,
|
|
19957
|
-
silkscreenColor,
|
|
19958
|
-
traceTextureResolution: TRACE_TEXTURE_RESOLUTION
|
|
19959
|
-
});
|
|
19960
|
-
setTextures(currentTextures);
|
|
19961
|
-
} catch (e) {
|
|
19962
|
-
console.error("Error processing geometry with Manifold in hook:", e);
|
|
19963
|
-
setError(
|
|
19964
|
-
e.message || "An unknown error occurred while processing geometry in hook."
|
|
19965
|
-
);
|
|
19966
|
-
setGeoms(null);
|
|
19967
|
-
setTextures(null);
|
|
19968
|
-
} finally {
|
|
19190
|
+
};
|
|
19191
|
+
worker.onerror = (err) => {
|
|
19192
|
+
setError(`Worker error: ${err.message}`);
|
|
19969
19193
|
setIsLoading(false);
|
|
19970
|
-
}
|
|
19194
|
+
};
|
|
19971
19195
|
return () => {
|
|
19972
|
-
|
|
19973
|
-
|
|
19196
|
+
if (workerRef.current) {
|
|
19197
|
+
workerRef.current.terminate();
|
|
19198
|
+
workerRef.current = null;
|
|
19199
|
+
}
|
|
19974
19200
|
};
|
|
19975
|
-
}, [
|
|
19201
|
+
}, [circuitJson]);
|
|
19202
|
+
const derivedBoardData = useMemo7(() => {
|
|
19203
|
+
if (!circuitJson) return null;
|
|
19204
|
+
const boards = su5(circuitJson).pcb_board.list();
|
|
19205
|
+
return boards[0] ?? null;
|
|
19206
|
+
}, [circuitJson]);
|
|
19976
19207
|
return {
|
|
19977
19208
|
geoms,
|
|
19978
19209
|
textures,
|
|
19979
19210
|
pcbThickness,
|
|
19980
19211
|
error,
|
|
19981
19212
|
isLoading,
|
|
19982
|
-
boardData
|
|
19213
|
+
boardData: boardData ?? derivedBoardData
|
|
19983
19214
|
};
|
|
19984
19215
|
};
|
|
19985
19216
|
|
|
19986
19217
|
// src/utils/manifold/create-three-geometry-meshes.ts
|
|
19987
|
-
import * as
|
|
19218
|
+
import * as THREE6 from "three";
|
|
19988
19219
|
function createGeometryMeshes(geoms) {
|
|
19989
19220
|
const meshes = [];
|
|
19990
19221
|
if (!geoms) return meshes;
|
|
19991
19222
|
if (geoms.board && geoms.board.geometry) {
|
|
19992
|
-
const mesh = new
|
|
19223
|
+
const mesh = new THREE6.Mesh(
|
|
19993
19224
|
geoms.board.geometry,
|
|
19994
|
-
new
|
|
19225
|
+
new THREE6.MeshStandardMaterial({
|
|
19995
19226
|
color: geoms.board.color,
|
|
19996
|
-
side:
|
|
19227
|
+
side: THREE6.DoubleSide,
|
|
19997
19228
|
flatShading: true
|
|
19998
19229
|
})
|
|
19999
19230
|
);
|
|
@@ -20003,11 +19234,11 @@ function createGeometryMeshes(geoms) {
|
|
|
20003
19234
|
const createMeshesFromArray = (geomArray) => {
|
|
20004
19235
|
if (geomArray) {
|
|
20005
19236
|
geomArray.forEach((comp) => {
|
|
20006
|
-
const mesh = new
|
|
19237
|
+
const mesh = new THREE6.Mesh(
|
|
20007
19238
|
comp.geometry,
|
|
20008
|
-
new
|
|
19239
|
+
new THREE6.MeshStandardMaterial({
|
|
20009
19240
|
color: comp.color,
|
|
20010
|
-
side:
|
|
19241
|
+
side: THREE6.DoubleSide,
|
|
20011
19242
|
flatShading: true
|
|
20012
19243
|
// Consistent with board
|
|
20013
19244
|
})
|
|
@@ -20024,21 +19255,21 @@ function createGeometryMeshes(geoms) {
|
|
|
20024
19255
|
}
|
|
20025
19256
|
|
|
20026
19257
|
// src/utils/manifold/create-three-texture-meshes.ts
|
|
20027
|
-
import * as
|
|
19258
|
+
import * as THREE7 from "three";
|
|
20028
19259
|
function createTextureMeshes(textures, boardData, pcbThickness) {
|
|
20029
19260
|
const meshes = [];
|
|
20030
19261
|
if (!textures || !boardData || pcbThickness === null) return meshes;
|
|
20031
19262
|
const createTexturePlane = (texture, yOffset, isBottomLayer, keySuffix) => {
|
|
20032
19263
|
if (!texture) return null;
|
|
20033
|
-
const planeGeom = new
|
|
20034
|
-
const material = new
|
|
19264
|
+
const planeGeom = new THREE7.PlaneGeometry(boardData.width, boardData.height);
|
|
19265
|
+
const material = new THREE7.MeshBasicMaterial({
|
|
20035
19266
|
map: texture,
|
|
20036
19267
|
transparent: true,
|
|
20037
|
-
side:
|
|
19268
|
+
side: THREE7.DoubleSide,
|
|
20038
19269
|
depthWrite: false
|
|
20039
19270
|
// Important for layers to avoid z-fighting issues with board itself
|
|
20040
19271
|
});
|
|
20041
|
-
const mesh = new
|
|
19272
|
+
const mesh = new THREE7.Mesh(planeGeom, material);
|
|
20042
19273
|
mesh.position.set(boardData.center.x, boardData.center.y, yOffset);
|
|
20043
19274
|
if (isBottomLayer) {
|
|
20044
19275
|
mesh.rotation.set(Math.PI, 0, 0);
|
|
@@ -20081,40 +19312,11 @@ function createTextureMeshes(textures, boardData, pcbThickness) {
|
|
|
20081
19312
|
|
|
20082
19313
|
// src/CadViewerManifold.tsx
|
|
20083
19314
|
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
20084
|
-
var MANIFOLD_CDN_BASE_URL = "https://cdn.jsdelivr.net/npm/manifold-3d@3.1.1";
|
|
20085
19315
|
var CadViewerManifold = ({
|
|
20086
19316
|
circuitJson,
|
|
20087
19317
|
autoRotateDisabled,
|
|
20088
19318
|
clickToInteractEnabled
|
|
20089
19319
|
}) => {
|
|
20090
|
-
const [manifoldJSModule, setManifoldJSModule] = useState9(null);
|
|
20091
|
-
const [manifoldLoadingError, setManifoldLoadingError] = useState9(null);
|
|
20092
|
-
useEffect6(() => {
|
|
20093
|
-
const loadManifoldWasmFromCDN = async () => {
|
|
20094
|
-
try {
|
|
20095
|
-
const wasmUrl = `${MANIFOLD_CDN_BASE_URL}/manifold.wasm`;
|
|
20096
|
-
const manifoldConfig = {
|
|
20097
|
-
locateFile: (path, scriptDirectory) => {
|
|
20098
|
-
if (path === "manifold.wasm") {
|
|
20099
|
-
return wasmUrl;
|
|
20100
|
-
}
|
|
20101
|
-
return scriptDirectory + path;
|
|
20102
|
-
}
|
|
20103
|
-
};
|
|
20104
|
-
const loadedModule = await ManifoldModule(
|
|
20105
|
-
manifoldConfig
|
|
20106
|
-
);
|
|
20107
|
-
loadedModule.setup();
|
|
20108
|
-
setManifoldJSModule(loadedModule);
|
|
20109
|
-
} catch (error) {
|
|
20110
|
-
console.error("Failed to load Manifold from CDN:", error);
|
|
20111
|
-
setManifoldLoadingError(
|
|
20112
|
-
`Failed to load Manifold module: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
20113
|
-
);
|
|
20114
|
-
}
|
|
20115
|
-
};
|
|
20116
|
-
loadManifoldWasmFromCDN();
|
|
20117
|
-
}, []);
|
|
20118
19320
|
const {
|
|
20119
19321
|
geoms,
|
|
20120
19322
|
textures,
|
|
@@ -20122,14 +19324,14 @@ var CadViewerManifold = ({
|
|
|
20122
19324
|
error: builderError,
|
|
20123
19325
|
isLoading: builderIsLoading,
|
|
20124
19326
|
boardData
|
|
20125
|
-
} = useManifoldBoardBuilder(
|
|
19327
|
+
} = useManifoldBoardBuilder(circuitJson);
|
|
20126
19328
|
const geometryMeshes = useMemo8(() => createGeometryMeshes(geoms), [geoms]);
|
|
20127
19329
|
const textureMeshes = useMemo8(
|
|
20128
19330
|
() => createTextureMeshes(textures, boardData, pcbThickness),
|
|
20129
19331
|
[textures, boardData, pcbThickness]
|
|
20130
19332
|
);
|
|
20131
19333
|
const cadComponents = useMemo8(
|
|
20132
|
-
() => circuitJson ?
|
|
19334
|
+
() => circuitJson ? su6(circuitJson).cad_component.list() : [],
|
|
20133
19335
|
[circuitJson]
|
|
20134
19336
|
);
|
|
20135
19337
|
const boardDimensions = useMemo8(() => {
|
|
@@ -20145,26 +19347,6 @@ var CadViewerManifold = ({
|
|
|
20145
19347
|
const largestDim = Math.max(safeWidth, safeHeight, 5);
|
|
20146
19348
|
return [largestDim * 0.75, largestDim * 0.75, largestDim * 0.75];
|
|
20147
19349
|
}, [boardData]);
|
|
20148
|
-
if (manifoldLoadingError) {
|
|
20149
|
-
return /* @__PURE__ */ jsxs8(
|
|
20150
|
-
"div",
|
|
20151
|
-
{
|
|
20152
|
-
style: {
|
|
20153
|
-
color: "red",
|
|
20154
|
-
padding: "1em",
|
|
20155
|
-
border: "1px solid red",
|
|
20156
|
-
margin: "1em"
|
|
20157
|
-
},
|
|
20158
|
-
children: [
|
|
20159
|
-
"Error: ",
|
|
20160
|
-
manifoldLoadingError
|
|
20161
|
-
]
|
|
20162
|
-
}
|
|
20163
|
-
);
|
|
20164
|
-
}
|
|
20165
|
-
if (!manifoldJSModule) {
|
|
20166
|
-
return /* @__PURE__ */ jsx11("div", { style: { padding: "1em" }, children: "Loading Manifold module..." });
|
|
20167
|
-
}
|
|
20168
19350
|
if (builderError) {
|
|
20169
19351
|
return /* @__PURE__ */ jsxs8(
|
|
20170
19352
|
"div",
|
|
@@ -20182,7 +19364,7 @@ var CadViewerManifold = ({
|
|
|
20182
19364
|
}
|
|
20183
19365
|
);
|
|
20184
19366
|
}
|
|
20185
|
-
if (builderIsLoading
|
|
19367
|
+
if (builderIsLoading && !geoms) {
|
|
20186
19368
|
return /* @__PURE__ */ jsx11("div", { style: { padding: "1em" }, children: "Processing board geometry..." });
|
|
20187
19369
|
}
|
|
20188
19370
|
return /* @__PURE__ */ jsxs8(
|
|
@@ -20216,10 +19398,10 @@ var CadViewerManifold = ({
|
|
|
20216
19398
|
var CadViewerManifold_default = CadViewerManifold;
|
|
20217
19399
|
|
|
20218
19400
|
// src/hooks/useContextMenu.ts
|
|
20219
|
-
import { useState as
|
|
19401
|
+
import { useState as useState9, useCallback as useCallback5, useRef as useRef7, useEffect as useEffect6 } from "react";
|
|
20220
19402
|
var useContextMenu = ({ containerRef }) => {
|
|
20221
|
-
const [menuVisible, setMenuVisible] =
|
|
20222
|
-
const [menuPos, setMenuPos] =
|
|
19403
|
+
const [menuVisible, setMenuVisible] = useState9(false);
|
|
19404
|
+
const [menuPos, setMenuPos] = useState9({
|
|
20223
19405
|
x: 0,
|
|
20224
19406
|
y: 0
|
|
20225
19407
|
});
|
|
@@ -20288,7 +19470,7 @@ var useContextMenu = ({ containerRef }) => {
|
|
|
20288
19470
|
setMenuVisible(false);
|
|
20289
19471
|
}
|
|
20290
19472
|
}, []);
|
|
20291
|
-
|
|
19473
|
+
useEffect6(() => {
|
|
20292
19474
|
if (menuVisible) {
|
|
20293
19475
|
document.addEventListener("mousedown", handleClickAway);
|
|
20294
19476
|
return () => document.removeEventListener("mousedown", handleClickAway);
|
|
@@ -20320,7 +19502,7 @@ var useContextMenu = ({ containerRef }) => {
|
|
|
20320
19502
|
// src/CadViewer.tsx
|
|
20321
19503
|
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
20322
19504
|
var CadViewer = (props) => {
|
|
20323
|
-
const [engine, setEngine] =
|
|
19505
|
+
const [engine, setEngine] = useState10("jscad");
|
|
20324
19506
|
const containerRef = useRef8(null);
|
|
20325
19507
|
const {
|
|
20326
19508
|
menuVisible,
|
|
@@ -20333,13 +19515,13 @@ var CadViewer = (props) => {
|
|
|
20333
19515
|
setEngine(newEngine);
|
|
20334
19516
|
setMenuVisible(false);
|
|
20335
19517
|
};
|
|
20336
|
-
|
|
19518
|
+
useEffect7(() => {
|
|
20337
19519
|
const stored = window.localStorage.getItem("cadViewerEngine");
|
|
20338
19520
|
if (stored === "jscad" || stored === "manifold") {
|
|
20339
19521
|
setEngine(stored);
|
|
20340
19522
|
}
|
|
20341
19523
|
}, []);
|
|
20342
|
-
|
|
19524
|
+
useEffect7(() => {
|
|
20343
19525
|
window.localStorage.setItem("cadViewerEngine", engine);
|
|
20344
19526
|
}, [engine]);
|
|
20345
19527
|
const viewerKey = props.circuitJson ? JSON.stringify(props.circuitJson) : void 0;
|
|
@@ -20438,12 +19620,12 @@ var CadViewer = (props) => {
|
|
|
20438
19620
|
|
|
20439
19621
|
// src/convert-circuit-json-to-3d-svg.ts
|
|
20440
19622
|
var import_debug = __toESM(require_browser(), 1);
|
|
20441
|
-
import { su as
|
|
20442
|
-
import * as
|
|
19623
|
+
import { su as su7 } from "@tscircuit/soup-util";
|
|
19624
|
+
import * as THREE11 from "three";
|
|
20443
19625
|
import { SVGRenderer } from "three/examples/jsm/renderers/SVGRenderer.js";
|
|
20444
19626
|
|
|
20445
19627
|
// src/utils/create-geometry-from-polygons.ts
|
|
20446
|
-
import * as
|
|
19628
|
+
import * as THREE8 from "three";
|
|
20447
19629
|
import { BufferGeometry as BufferGeometry4, Float32BufferAttribute as Float32BufferAttribute4 } from "three";
|
|
20448
19630
|
function createGeometryFromPolygons(polygons) {
|
|
20449
19631
|
const geometry = new BufferGeometry4();
|
|
@@ -20457,12 +19639,12 @@ function createGeometryFromPolygons(polygons) {
|
|
|
20457
19639
|
...polygon2.vertices[i + 1]
|
|
20458
19640
|
// Third vertex
|
|
20459
19641
|
);
|
|
20460
|
-
const v1 = new
|
|
20461
|
-
const v2 = new
|
|
20462
|
-
const v3 = new
|
|
20463
|
-
const normal = new
|
|
20464
|
-
new
|
|
20465
|
-
new
|
|
19642
|
+
const v1 = new THREE8.Vector3(...polygon2.vertices[0]);
|
|
19643
|
+
const v2 = new THREE8.Vector3(...polygon2.vertices[i]);
|
|
19644
|
+
const v3 = new THREE8.Vector3(...polygon2.vertices[i + 1]);
|
|
19645
|
+
const normal = new THREE8.Vector3().crossVectors(
|
|
19646
|
+
new THREE8.Vector3().subVectors(v2, v1),
|
|
19647
|
+
new THREE8.Vector3().subVectors(v3, v1)
|
|
20466
19648
|
).normalize();
|
|
20467
19649
|
normals.push(
|
|
20468
19650
|
normal.x,
|
|
@@ -20488,22 +19670,22 @@ import { Footprinter3d as Footprinter3d2 } from "jscad-electronics";
|
|
|
20488
19670
|
import { convertCSGToThreeGeom as convertCSGToThreeGeom2 } from "jscad-fiber/three";
|
|
20489
19671
|
import { createJSCADRenderer as createJSCADRenderer2 } from "jscad-fiber";
|
|
20490
19672
|
import { executeJscadOperations as executeJscadOperations2, jscadPlanner as jscadPlanner2 } from "jscad-planner";
|
|
20491
|
-
import * as
|
|
19673
|
+
import * as THREE10 from "three";
|
|
20492
19674
|
|
|
20493
19675
|
// src/utils/load-model.ts
|
|
20494
|
-
import * as
|
|
19676
|
+
import * as THREE9 from "three";
|
|
20495
19677
|
import { OBJLoader as OBJLoader3 } from "three/examples/jsm/loaders/OBJLoader.js";
|
|
20496
19678
|
import { STLLoader as STLLoader2 } from "three/examples/jsm/loaders/STLLoader.js";
|
|
20497
19679
|
async function load3DModel(url) {
|
|
20498
19680
|
if (url.endsWith(".stl")) {
|
|
20499
19681
|
const loader = new STLLoader2();
|
|
20500
19682
|
const geometry = await loader.loadAsync(url);
|
|
20501
|
-
const material = new
|
|
19683
|
+
const material = new THREE9.MeshStandardMaterial({
|
|
20502
19684
|
color: 8947848,
|
|
20503
19685
|
metalness: 0.5,
|
|
20504
19686
|
roughness: 0.5
|
|
20505
19687
|
});
|
|
20506
|
-
return new
|
|
19688
|
+
return new THREE9.Mesh(geometry, material);
|
|
20507
19689
|
}
|
|
20508
19690
|
if (url.endsWith(".obj")) {
|
|
20509
19691
|
const loader = new OBJLoader3();
|
|
@@ -20530,9 +19712,9 @@ async function renderComponent(component, scene) {
|
|
|
20530
19712
|
}
|
|
20531
19713
|
if (component.rotation) {
|
|
20532
19714
|
model.rotation.set(
|
|
20533
|
-
|
|
20534
|
-
|
|
20535
|
-
|
|
19715
|
+
THREE10.MathUtils.degToRad(component.rotation.x ?? 0),
|
|
19716
|
+
THREE10.MathUtils.degToRad(component.rotation.y ?? 0),
|
|
19717
|
+
THREE10.MathUtils.degToRad(component.rotation.z ?? 0)
|
|
20536
19718
|
);
|
|
20537
19719
|
}
|
|
20538
19720
|
scene.add(model);
|
|
@@ -20545,13 +19727,13 @@ async function renderComponent(component, scene) {
|
|
|
20545
19727
|
component.model_jscad
|
|
20546
19728
|
);
|
|
20547
19729
|
const threeGeom = convertCSGToThreeGeom2(jscadObject);
|
|
20548
|
-
const material2 = new
|
|
19730
|
+
const material2 = new THREE10.MeshStandardMaterial({
|
|
20549
19731
|
color: 8947848,
|
|
20550
19732
|
metalness: 0.5,
|
|
20551
19733
|
roughness: 0.5,
|
|
20552
|
-
side:
|
|
19734
|
+
side: THREE10.DoubleSide
|
|
20553
19735
|
});
|
|
20554
|
-
const mesh2 = new
|
|
19736
|
+
const mesh2 = new THREE10.Mesh(threeGeom, material2);
|
|
20555
19737
|
if (component.position) {
|
|
20556
19738
|
mesh2.position.set(
|
|
20557
19739
|
component.position.x ?? 0,
|
|
@@ -20561,9 +19743,9 @@ async function renderComponent(component, scene) {
|
|
|
20561
19743
|
}
|
|
20562
19744
|
if (component.rotation) {
|
|
20563
19745
|
mesh2.rotation.set(
|
|
20564
|
-
|
|
20565
|
-
|
|
20566
|
-
|
|
19746
|
+
THREE10.MathUtils.degToRad(component.rotation.x ?? 0),
|
|
19747
|
+
THREE10.MathUtils.degToRad(component.rotation.y ?? 0),
|
|
19748
|
+
THREE10.MathUtils.degToRad(component.rotation.z ?? 0)
|
|
20567
19749
|
);
|
|
20568
19750
|
}
|
|
20569
19751
|
scene.add(mesh2);
|
|
@@ -20576,13 +19758,13 @@ async function renderComponent(component, scene) {
|
|
|
20576
19758
|
for (const operation of jscadOperations) {
|
|
20577
19759
|
const jscadObject = executeJscadOperations2(import_modeling2.default, operation);
|
|
20578
19760
|
const threeGeom = convertCSGToThreeGeom2(jscadObject);
|
|
20579
|
-
const material2 = new
|
|
19761
|
+
const material2 = new THREE10.MeshStandardMaterial({
|
|
20580
19762
|
color: 4473924,
|
|
20581
19763
|
metalness: 0.2,
|
|
20582
19764
|
roughness: 0.8,
|
|
20583
|
-
side:
|
|
19765
|
+
side: THREE10.DoubleSide
|
|
20584
19766
|
});
|
|
20585
|
-
const mesh2 = new
|
|
19767
|
+
const mesh2 = new THREE10.Mesh(threeGeom, material2);
|
|
20586
19768
|
if (component.position) {
|
|
20587
19769
|
mesh2.position.set(
|
|
20588
19770
|
component.position.x ?? 0,
|
|
@@ -20592,22 +19774,22 @@ async function renderComponent(component, scene) {
|
|
|
20592
19774
|
}
|
|
20593
19775
|
if (component.rotation) {
|
|
20594
19776
|
mesh2.rotation.set(
|
|
20595
|
-
|
|
20596
|
-
|
|
20597
|
-
|
|
19777
|
+
THREE10.MathUtils.degToRad(component.rotation.x ?? 0),
|
|
19778
|
+
THREE10.MathUtils.degToRad(component.rotation.y ?? 0),
|
|
19779
|
+
THREE10.MathUtils.degToRad(component.rotation.z ?? 0)
|
|
20598
19780
|
);
|
|
20599
19781
|
}
|
|
20600
19782
|
scene.add(mesh2);
|
|
20601
19783
|
}
|
|
20602
19784
|
return;
|
|
20603
19785
|
}
|
|
20604
|
-
const geometry = new
|
|
20605
|
-
const material = new
|
|
19786
|
+
const geometry = new THREE10.BoxGeometry(0.5, 0.5, 0.5);
|
|
19787
|
+
const material = new THREE10.MeshStandardMaterial({
|
|
20606
19788
|
color: 16711680,
|
|
20607
19789
|
transparent: true,
|
|
20608
19790
|
opacity: 0.25
|
|
20609
19791
|
});
|
|
20610
|
-
const mesh = new
|
|
19792
|
+
const mesh = new THREE10.Mesh(geometry, material);
|
|
20611
19793
|
if (component.position) {
|
|
20612
19794
|
mesh.position.set(
|
|
20613
19795
|
component.position.x ?? 0,
|
|
@@ -20628,11 +19810,11 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
|
|
|
20628
19810
|
padding = 20,
|
|
20629
19811
|
zoom = 1.5
|
|
20630
19812
|
} = options;
|
|
20631
|
-
const scene = new
|
|
19813
|
+
const scene = new THREE11.Scene();
|
|
20632
19814
|
const renderer = new SVGRenderer();
|
|
20633
19815
|
renderer.setSize(width, height);
|
|
20634
|
-
renderer.setClearColor(new
|
|
20635
|
-
const camera = new
|
|
19816
|
+
renderer.setClearColor(new THREE11.Color(backgroundColor), 1);
|
|
19817
|
+
const camera = new THREE11.OrthographicCamera();
|
|
20636
19818
|
const aspect = width / height;
|
|
20637
19819
|
const frustumSize = 100;
|
|
20638
19820
|
const halfFrustumSize = frustumSize / 2 / zoom;
|
|
@@ -20646,14 +19828,14 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
|
|
|
20646
19828
|
camera.position.set(position.x, position.y, position.z);
|
|
20647
19829
|
camera.up.set(0, 1, 0);
|
|
20648
19830
|
const lookAt = options.camera?.lookAt ?? { x: 0, y: 0, z: 0 };
|
|
20649
|
-
camera.lookAt(new
|
|
19831
|
+
camera.lookAt(new THREE11.Vector3(lookAt.x, lookAt.y, lookAt.z));
|
|
20650
19832
|
camera.updateProjectionMatrix();
|
|
20651
|
-
const ambientLight = new
|
|
19833
|
+
const ambientLight = new THREE11.AmbientLight(16777215, Math.PI / 2);
|
|
20652
19834
|
scene.add(ambientLight);
|
|
20653
|
-
const pointLight = new
|
|
19835
|
+
const pointLight = new THREE11.PointLight(16777215, Math.PI / 4);
|
|
20654
19836
|
pointLight.position.set(-10, -10, 10);
|
|
20655
19837
|
scene.add(pointLight);
|
|
20656
|
-
const components =
|
|
19838
|
+
const components = su7(circuitJson).cad_component.list();
|
|
20657
19839
|
for (const component of components) {
|
|
20658
19840
|
await renderComponent(component, scene);
|
|
20659
19841
|
}
|
|
@@ -20661,8 +19843,8 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
|
|
|
20661
19843
|
if (boardGeom) {
|
|
20662
19844
|
for (const geom of boardGeom) {
|
|
20663
19845
|
const geometry = createGeometryFromPolygons(geom.polygons);
|
|
20664
|
-
const material = new
|
|
20665
|
-
color: new
|
|
19846
|
+
const material = new THREE11.MeshStandardMaterial({
|
|
19847
|
+
color: new THREE11.Color(
|
|
20666
19848
|
geom.color?.[0] ?? 0,
|
|
20667
19849
|
geom.color?.[1] ?? 0,
|
|
20668
19850
|
geom.color?.[2] ?? 0
|
|
@@ -20671,18 +19853,18 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
|
|
|
20671
19853
|
roughness: 0.8,
|
|
20672
19854
|
opacity: 0.9,
|
|
20673
19855
|
transparent: true,
|
|
20674
|
-
side:
|
|
19856
|
+
side: THREE11.DoubleSide
|
|
20675
19857
|
});
|
|
20676
|
-
const mesh = new
|
|
19858
|
+
const mesh = new THREE11.Mesh(geometry, material);
|
|
20677
19859
|
scene.add(mesh);
|
|
20678
19860
|
}
|
|
20679
19861
|
}
|
|
20680
|
-
const gridHelper = new
|
|
19862
|
+
const gridHelper = new THREE11.GridHelper(100, 100);
|
|
20681
19863
|
gridHelper.rotation.x = Math.PI / 2;
|
|
20682
19864
|
scene.add(gridHelper);
|
|
20683
|
-
const box = new
|
|
20684
|
-
const center = box.getCenter(new
|
|
20685
|
-
const size = box.getSize(new
|
|
19865
|
+
const box = new THREE11.Box3().setFromObject(scene);
|
|
19866
|
+
const center = box.getCenter(new THREE11.Vector3());
|
|
19867
|
+
const size = box.getSize(new THREE11.Vector3());
|
|
20686
19868
|
scene.position.sub(center);
|
|
20687
19869
|
const maxDim = Math.max(size.x, size.y, size.z);
|
|
20688
19870
|
if (maxDim > 0) {
|
|
@@ -20699,7 +19881,7 @@ async function convertCircuitJsonTo3dSvg(circuitJson, options = {}) {
|
|
|
20699
19881
|
}
|
|
20700
19882
|
|
|
20701
19883
|
// src/hooks/exporter/gltf.ts
|
|
20702
|
-
import { useEffect as
|
|
19884
|
+
import { useEffect as useEffect8, useState as useState11, useMemo as useMemo9, useCallback as useCallback7 } from "react";
|
|
20703
19885
|
function useSaveGltfAs(options = {}) {
|
|
20704
19886
|
const parse = useParser(options);
|
|
20705
19887
|
const link = useMemo9(() => document.createElement("a"), []);
|
|
@@ -20712,7 +19894,7 @@ function useSaveGltfAs(options = {}) {
|
|
|
20712
19894
|
link.dispatchEvent(new MouseEvent("click"));
|
|
20713
19895
|
URL.revokeObjectURL(url);
|
|
20714
19896
|
};
|
|
20715
|
-
|
|
19897
|
+
useEffect8(
|
|
20716
19898
|
() => () => {
|
|
20717
19899
|
link.remove();
|
|
20718
19900
|
instance = null;
|
|
@@ -20727,13 +19909,13 @@ function useSaveGltfAs(options = {}) {
|
|
|
20727
19909
|
}
|
|
20728
19910
|
function useExportGltfUrl(options = {}) {
|
|
20729
19911
|
const parse = useParser(options);
|
|
20730
|
-
const [url, setUrl] =
|
|
20731
|
-
const [error, setError] =
|
|
19912
|
+
const [url, setUrl] = useState11();
|
|
19913
|
+
const [error, setError] = useState11();
|
|
20732
19914
|
const ref = useCallback7(
|
|
20733
19915
|
(instance) => parse(instance).then(setUrl).catch(setError),
|
|
20734
19916
|
[]
|
|
20735
19917
|
);
|
|
20736
|
-
|
|
19918
|
+
useEffect8(() => () => URL.revokeObjectURL(url), [url]);
|
|
20737
19919
|
return [ref, url, error];
|
|
20738
19920
|
}
|
|
20739
19921
|
function useParser(options = {}) {
|