@tscircuit/3d-viewer 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/index.cjs +657 -0
  2. package/dist/index.cjs.map +1 -0
  3. package/dist/index.d.cts +10 -0
  4. package/dist/index.d.ts +10 -0
  5. package/dist/index.js +660 -0
  6. package/dist/index.js.map +1 -0
  7. package/package.json +2 -1
  8. package/.storybook/main.ts +0 -29
  9. package/.storybook/preview.tsx +0 -24
  10. package/ai-reference/soup-reference.md +0 -1301
  11. package/bun.lockb +0 -0
  12. package/index.html +0 -13
  13. package/src/App.tsx +0 -17
  14. package/src/App2.tsx +0 -48
  15. package/src/App3.tsx +0 -209
  16. package/src/App4.tsx +0 -88
  17. package/src/CadViewer.tsx +0 -56
  18. package/src/CadViewerContainer.tsx +0 -73
  19. package/src/GeomContext.ts +0 -3
  20. package/src/bug-pads-and-traces.json +0 -1516
  21. package/src/geoms/constants.ts +0 -9
  22. package/src/geoms/plated-hole.ts +0 -45
  23. package/src/hooks/index.ts +0 -1
  24. package/src/hooks/render/hooks.tsx +0 -62
  25. package/src/hooks/render/index.tsx +0 -440
  26. package/src/hooks/use-convert-children-to-soup.ts +0 -10
  27. package/src/hooks/use-stls-from-geom.ts +0 -54
  28. package/src/index.tsx +0 -1
  29. package/src/main.tsx +0 -9
  30. package/src/modules.d.ts +0 -3
  31. package/src/plated-hole-board.json +0 -213
  32. package/src/soup-to-3d/index.ts +0 -162
  33. package/src/themes.ts +0 -7
  34. package/src/three-components/GeomModel.tsx +0 -13
  35. package/src/three-components/MixedStlModel.tsx +0 -61
  36. package/src/three-components/STLModel.tsx +0 -33
  37. package/src/three-components/cube-with-labeled-sides.tsx +0 -116
  38. package/src/vite-env.d.ts +0 -1
  39. package/stories/Simple.stories.tsx +0 -10
  40. package/stories/assets/soic-with-traces.json +0 -1802
  41. package/tsconfig.json +0 -39
  42. package/vendor/@jscadui/format-three/index.ts +0 -252
  43. package/vite.config.ts +0 -17
package/dist/index.cjs ADDED
@@ -0,0 +1,657 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __commonJS = (cb, mod) => function __require() {
8
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
+ };
10
+ var __export = (target, all) => {
11
+ for (var name in all)
12
+ __defProp(target, name, { get: all[name], enumerable: true });
13
+ };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
+ mod
29
+ ));
30
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
31
+
32
+ // node_modules/@tscircuit/soup-util/dist/index.cjs
33
+ var require_dist = __commonJS({
34
+ "node_modules/@tscircuit/soup-util/dist/index.cjs"(exports2, module2) {
35
+ "use strict";
36
+ var __defProp2 = Object.defineProperty;
37
+ var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
38
+ var __getOwnPropNames2 = Object.getOwnPropertyNames;
39
+ var __hasOwnProp2 = Object.prototype.hasOwnProperty;
40
+ var __export2 = (target, all) => {
41
+ for (var name in all)
42
+ __defProp2(target, name, { get: all[name], enumerable: true });
43
+ };
44
+ var __copyProps2 = (to, from, except, desc) => {
45
+ if (from && typeof from === "object" || typeof from === "function") {
46
+ for (let key of __getOwnPropNames2(from))
47
+ if (!__hasOwnProp2.call(to, key) && key !== except)
48
+ __defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
49
+ }
50
+ return to;
51
+ };
52
+ var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
53
+ var soup_util_exports = {};
54
+ __export2(soup_util_exports, {
55
+ default: () => soup_util_default,
56
+ su: () => su3
57
+ });
58
+ module2.exports = __toCommonJS2(soup_util_exports);
59
+ var su3 = (soup) => {
60
+ const su22 = new Proxy(
61
+ {},
62
+ {
63
+ get: (proxy_target, component_type) => {
64
+ return {
65
+ get: (id) => soup.find(
66
+ (e) => e.type === component_type && e[`${component_type}_id`] === id
67
+ ),
68
+ getUsing: (using) => {
69
+ const keys = Object.keys(using);
70
+ if (keys.length !== 1) {
71
+ throw new Error(
72
+ "getUsing requires exactly one key, e.g. { pcb_component_id }"
73
+ );
74
+ }
75
+ const join_key = keys[0];
76
+ const join_type = join_key.replace("_id", "");
77
+ const joiner = soup.find(
78
+ (e) => e.type === join_type && e[join_key] === using[join_key]
79
+ );
80
+ if (!joiner)
81
+ return null;
82
+ return soup.find(
83
+ (e) => e.type === component_type && e[`${component_type}_id`] === joiner[`${component_type}_id`]
84
+ );
85
+ },
86
+ getWhere: (where) => {
87
+ const keys = Object.keys(where);
88
+ return soup.find(
89
+ (e) => e.type === component_type && keys.every((key) => e[key] === where[key])
90
+ );
91
+ },
92
+ list: (where) => {
93
+ const keys = !where ? [] : Object.keys(where);
94
+ return soup.filter(
95
+ (e) => e.type === component_type && keys.every((key) => e[key] === where[key])
96
+ );
97
+ },
98
+ select: (selector) => {
99
+ if (component_type === "source_component") {
100
+ return soup.find(
101
+ (e) => e.type === "source_component" && e.name === selector.replace(/\./g, "")
102
+ );
103
+ } else if (component_type === "pcb_port" || component_type === "source_port" || component_type === "schematic_port") {
104
+ const [component_name, port_selector] = selector.replace(/\./g, "").split(/[\s\>]+/);
105
+ const source_component = soup.find(
106
+ (e) => e.type === "source_component" && e.name === component_name
107
+ );
108
+ if (!source_component)
109
+ return null;
110
+ const source_port = soup.find(
111
+ (e) => e.type === "source_port" && e.source_component_id === source_component.source_component_id && (e.name === port_selector || (e.port_hints ?? []).includes(port_selector))
112
+ );
113
+ if (!source_port)
114
+ return null;
115
+ if (component_type === "source_port")
116
+ return source_port;
117
+ if (component_type === "pcb_port") {
118
+ return soup.find(
119
+ (e) => e.type === "pcb_port" && e.source_port_id === source_port.source_port_id
120
+ );
121
+ } else if (component_type === "schematic_port") {
122
+ return soup.find(
123
+ (e) => e.type === "schematic_port" && e.source_port_id === source_port.source_port_id
124
+ );
125
+ }
126
+ }
127
+ }
128
+ };
129
+ }
130
+ }
131
+ );
132
+ return su22;
133
+ };
134
+ su3.unparsed = su3;
135
+ var soup_util_default = su3;
136
+ }
137
+ });
138
+
139
+ // src/index.tsx
140
+ var src_exports = {};
141
+ __export(src_exports, {
142
+ CadViewer: () => CadViewer
143
+ });
144
+ module.exports = __toCommonJS(src_exports);
145
+
146
+ // src/hooks/use-convert-children-to-soup.ts
147
+ var useConvertChildrenToSoup = (children, defaultSoup) => {
148
+ return defaultSoup;
149
+ };
150
+
151
+ // src/CadViewer.tsx
152
+ var import_soup_util2 = __toESM(require_dist(), 1);
153
+ var import_react5 = require("react");
154
+
155
+ // src/soup-to-3d/index.ts
156
+ var import_soup_util = __toESM(require_dist(), 1);
157
+ var import_transforms = require("@jscad/modeling/src/operations/transforms");
158
+ var import_primitives2 = require("@jscad/modeling/src/primitives");
159
+ var import_colors2 = require("@jscad/modeling/src/colors");
160
+ var import_booleans2 = require("@jscad/modeling/src/operations/booleans");
161
+
162
+ // src/geoms/plated-hole.ts
163
+ var import_primitives = require("@jscad/modeling/src/primitives");
164
+ var import_colors = require("@jscad/modeling/src/colors");
165
+ var import_booleans = require("@jscad/modeling/src/operations/booleans");
166
+
167
+ // src/geoms/constants.ts
168
+ var M = 0.01;
169
+ var colors = {
170
+ copper: [0.9, 0.6, 0.2],
171
+ fr4Green: [5 / 255, 163 / 255, 46 / 255],
172
+ fr4GreenSolderWithMask: [0 / 255, 152 / 255, 19 / 255]
173
+ };
174
+
175
+ // src/geoms/plated-hole.ts
176
+ var platedHole = (plated_hole, ctx) => {
177
+ if (plated_hole.shape === "circle" || !plated_hole.shape) {
178
+ return (0, import_colors.colorize)(
179
+ colors.copper,
180
+ (0, import_booleans.subtract)(
181
+ (0, import_booleans.union)(
182
+ (0, import_primitives.cylinder)({
183
+ center: [plated_hole.x, plated_hole.y, 0],
184
+ radius: plated_hole.hole_diameter / 2,
185
+ height: 1.2
186
+ }),
187
+ (0, import_primitives.cylinder)({
188
+ center: [plated_hole.x, plated_hole.y, 1.2 / 2],
189
+ radius: plated_hole.outer_diameter / 2,
190
+ height: M
191
+ }),
192
+ (0, import_primitives.cylinder)({
193
+ center: [plated_hole.x, plated_hole.y, -1.2 / 2],
194
+ radius: plated_hole.outer_diameter / 2,
195
+ height: M
196
+ })
197
+ ),
198
+ (0, import_primitives.cylinder)({
199
+ center: [plated_hole.x, plated_hole.y, 0],
200
+ radius: plated_hole.hole_diameter / 2 - M,
201
+ height: 1.5
202
+ })
203
+ )
204
+ );
205
+ } else {
206
+ throw new Error(`Unsupported plated hole shape: ${plated_hole.shape}`);
207
+ }
208
+ };
209
+
210
+ // src/soup-to-3d/index.ts
211
+ var import_extrusions = require("@jscad/modeling/src/operations/extrusions");
212
+ var import_expansions = require("@jscad/modeling/src/operations/expansions");
213
+ var createBoardGeomFromSoup = (soup) => {
214
+ const board = (0, import_soup_util.su)(soup).pcb_board.list()[0];
215
+ if (!board) {
216
+ throw new Error("No pcb_board found");
217
+ }
218
+ const plated_holes = (0, import_soup_util.su)(soup).pcb_plated_hole.list();
219
+ const pads = (0, import_soup_util.su)(soup).pcb_smtpad.list();
220
+ const traces = (0, import_soup_util.su)(soup).pcb_trace.list();
221
+ const pcb_vias = (0, import_soup_util.su)(soup).pcb_via.list();
222
+ let boardGeom = (0, import_primitives2.cuboid)({ size: [board.width, board.height, 1.2] });
223
+ const platedHoleGeoms = [];
224
+ const padGeoms = [];
225
+ const traceGeoms = [];
226
+ const ctx = {
227
+ pcbThickness: 1.2
228
+ };
229
+ const addPlatedHole = (plated_hole) => {
230
+ if (plated_hole.shape === "circle" || !plated_hole.shape) {
231
+ const cyGeom = (0, import_primitives2.cylinder)({
232
+ center: [plated_hole.x, plated_hole.y, 0],
233
+ radius: plated_hole.hole_diameter / 2 + M
234
+ });
235
+ boardGeom = (0, import_booleans2.subtract)(boardGeom, cyGeom);
236
+ const platedHoleGeom = platedHole(plated_hole, ctx);
237
+ platedHoleGeoms.push(platedHoleGeom);
238
+ }
239
+ };
240
+ for (const plated_hole of plated_holes) {
241
+ addPlatedHole(plated_hole);
242
+ }
243
+ for (const pad of pads) {
244
+ if (pad.shape === "rect") {
245
+ const padGeom = (0, import_colors2.colorize)(
246
+ colors.copper,
247
+ (0, import_primitives2.cuboid)({
248
+ center: [pad.x, pad.y, 1.2 / 2 + M],
249
+ size: [pad.width, pad.height, M]
250
+ })
251
+ );
252
+ padGeoms.push(padGeom);
253
+ } else if (pad.shape === "circle") {
254
+ const padGeom = (0, import_colors2.colorize)(
255
+ colors.copper,
256
+ (0, import_primitives2.cylinder)({
257
+ center: [pad.x, pad.y, 1.2 / 2 + M],
258
+ radius: pad.radius,
259
+ height: M
260
+ })
261
+ );
262
+ padGeoms.push(padGeom);
263
+ }
264
+ }
265
+ for (const { route: mixedRoute } of traces) {
266
+ const subRoutes = mixedRoute.reduce(
267
+ (c, p) => {
268
+ const lastLayer = c.current?.[c.current.length - 1]?.layer;
269
+ if (p.route_type === "via" || p.route_type === "wire" && p.layer !== lastLayer) {
270
+ if (c.current.length > 2) {
271
+ c.allPrev.push(c.current);
272
+ }
273
+ c.current = p.route_type === "wire" ? [p] : [];
274
+ return c;
275
+ }
276
+ c.current.push(p);
277
+ return c;
278
+ },
279
+ {
280
+ current: [],
281
+ allPrev: []
282
+ }
283
+ );
284
+ for (const route of subRoutes.allPrev.concat([subRoutes.current])) {
285
+ const linePath = (0, import_primitives2.line)(route.map((p) => [p.x, p.y]));
286
+ const layer = route[0].route_type === "wire" ? route[0].layer : "top";
287
+ const layerSign = layer === "top" ? 1 : -1;
288
+ let traceGeom = (0, import_transforms.translate)(
289
+ [0, 0, layerSign * 1.2 / 2],
290
+ (0, import_extrusions.extrudeLinear)(
291
+ { height: M * layerSign },
292
+ (0, import_expansions.expand)({ delta: 0.1, corners: "edge" }, linePath)
293
+ )
294
+ );
295
+ for (const via of pcb_vias) {
296
+ traceGeom = (0, import_booleans2.subtract)(
297
+ traceGeom,
298
+ (0, import_primitives2.cylinder)({
299
+ center: [via.x, via.y, 0],
300
+ radius: via.outer_diameter / 2,
301
+ height: 5
302
+ })
303
+ );
304
+ }
305
+ traceGeom = (0, import_colors2.colorize)(colors.fr4GreenSolderWithMask, traceGeom);
306
+ traceGeoms.push(traceGeom);
307
+ }
308
+ for (const via of mixedRoute.filter((p) => p.route_type === "via")) {
309
+ if (via.route_type !== "via") continue;
310
+ addPlatedHole({
311
+ x: via.x,
312
+ y: via.y,
313
+ hole_diameter: 0.8,
314
+ outer_diameter: 1.6,
315
+ shape: "circle",
316
+ layers: ["top", "bottom"],
317
+ type: "pcb_plated_hole"
318
+ });
319
+ }
320
+ }
321
+ for (const via of pcb_vias) {
322
+ addPlatedHole({
323
+ x: via.x,
324
+ y: via.y,
325
+ hole_diameter: via.hole_diameter,
326
+ outer_diameter: via.outer_diameter,
327
+ shape: "circle",
328
+ layers: ["top", "bottom"],
329
+ type: "pcb_plated_hole"
330
+ });
331
+ }
332
+ boardGeom = (0, import_colors2.colorize)(colors.fr4Green, boardGeom);
333
+ return [boardGeom, ...platedHoleGeoms, ...padGeoms, ...traceGeoms];
334
+ };
335
+
336
+ // src/hooks/use-stls-from-geom.ts
337
+ var import_react = require("react");
338
+ var import_stl_serializer = __toESM(require("@jscad/stl-serializer"), 1);
339
+ function blobToBase64Url(blob) {
340
+ return new Promise((resolve, reject) => {
341
+ const reader = new FileReader();
342
+ reader.onload = () => {
343
+ resolve(reader.result);
344
+ };
345
+ reader.onerror = reject;
346
+ reader.readAsDataURL(blob);
347
+ });
348
+ }
349
+ var useStlsFromGeom = (geom) => {
350
+ const [stls, setStls] = (0, import_react.useState)([]);
351
+ const [loading, setLoading] = (0, import_react.useState)(true);
352
+ (0, import_react.useEffect)(() => {
353
+ const generateStls = async () => {
354
+ setLoading(true);
355
+ const geometries = Array.isArray(geom) ? geom : [geom];
356
+ const stlPromises = geometries.map(async (g) => {
357
+ const rawData = import_stl_serializer.default.serialize({ binary: true }, [g]);
358
+ const blobData = new Blob(rawData);
359
+ const stlUrl = await blobToBase64Url(blobData);
360
+ return { stlUrl, color: g.color };
361
+ });
362
+ try {
363
+ const generatedStls = await Promise.all(stlPromises);
364
+ setStls(generatedStls);
365
+ } catch (error) {
366
+ console.error("Error generating STLs:", error);
367
+ setStls([]);
368
+ } finally {
369
+ setLoading(false);
370
+ }
371
+ };
372
+ generateStls();
373
+ }, [geom]);
374
+ return { stls, loading };
375
+ };
376
+
377
+ // src/three-components/STLModel.tsx
378
+ var import_fiber = require("@react-three/fiber");
379
+ var import_react2 = require("react");
380
+ var import_three_stdlib = require("three-stdlib");
381
+ var import_jsx_runtime = require("react/jsx-runtime");
382
+ function STLModel({
383
+ stlUrl,
384
+ mtlUrl,
385
+ color,
386
+ opacity = 1
387
+ }) {
388
+ const geom = (0, import_fiber.useLoader)(import_three_stdlib.STLLoader, stlUrl);
389
+ const mesh = (0, import_react2.useRef)();
390
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("mesh", { ref: mesh, children: [
391
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("primitive", { object: geom, attach: "geometry" }),
392
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
393
+ "meshStandardMaterial",
394
+ {
395
+ color,
396
+ transparent: opacity !== 1,
397
+ opacity
398
+ }
399
+ )
400
+ ] });
401
+ }
402
+
403
+ // src/CadViewerContainer.tsx
404
+ var import_fiber3 = require("@react-three/fiber");
405
+ var import_drei2 = require("@react-three/drei");
406
+
407
+ // src/three-components/cube-with-labeled-sides.tsx
408
+ var import_react3 = require("react");
409
+ var import_fiber2 = require("@react-three/fiber");
410
+ var import_drei = require("@react-three/drei");
411
+ var THREE = __toESM(require("three"), 1);
412
+ var import_jsx_runtime2 = require("react/jsx-runtime");
413
+ window.TSCI_MAIN_CAMERA_ROTATION = new THREE.Euler(0, 0, 0);
414
+ function computePointInFront(rotationVector, distance) {
415
+ const quaternion = new THREE.Quaternion().setFromEuler(
416
+ new THREE.Euler(rotationVector.x, rotationVector.y, rotationVector.z)
417
+ );
418
+ const forwardVector = new THREE.Vector3(0, 0, 1);
419
+ forwardVector.applyQuaternion(quaternion);
420
+ const result = forwardVector.multiplyScalar(distance);
421
+ return result;
422
+ }
423
+ var CubeWithLabeledSides = ({}) => {
424
+ const ref = (0, import_react3.useRef)();
425
+ const rotationTrackingRef = (0, import_react3.useRef)({ lastRotation: new THREE.Euler() });
426
+ (0, import_fiber2.useFrame)((state, delta) => {
427
+ if (!ref.current) return;
428
+ const mainRot = window.TSCI_MAIN_CAMERA_ROTATION;
429
+ const cameraPosition = computePointInFront(mainRot, 2);
430
+ state.camera.position.copy(cameraPosition);
431
+ state.camera.lookAt(0, 0, 0);
432
+ });
433
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("mesh", { ref, rotation: [Math.PI / 2, 0, 0], children: [
434
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("boxGeometry", { args: [1, 1, 1] }),
435
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("meshStandardMaterial", { color: "white" }),
436
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_drei.Text, { position: [0, 0, 0.51], fontSize: 0.25, color: "black", children: "Front" }),
437
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
438
+ import_drei.Text,
439
+ {
440
+ position: [0, 0, -0.51],
441
+ fontSize: 0.25,
442
+ color: "black",
443
+ rotation: [0, Math.PI, 0],
444
+ children: "Back"
445
+ }
446
+ ),
447
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
448
+ import_drei.Text,
449
+ {
450
+ position: [0.51, 0, 0],
451
+ fontSize: 0.25,
452
+ color: "black",
453
+ rotation: [0, Math.PI / 2, 0],
454
+ children: "Right"
455
+ }
456
+ ),
457
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
458
+ import_drei.Text,
459
+ {
460
+ position: [-0.51, 0, 0],
461
+ fontSize: 0.25,
462
+ color: "black",
463
+ rotation: [0, -Math.PI / 2, 0],
464
+ children: "Left"
465
+ }
466
+ ),
467
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
468
+ import_drei.Text,
469
+ {
470
+ position: [0, 0.51, 0],
471
+ fontSize: 0.25,
472
+ color: "black",
473
+ rotation: [-Math.PI / 2, 0, 0],
474
+ children: "Top"
475
+ }
476
+ ),
477
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
478
+ import_drei.Text,
479
+ {
480
+ position: [0, -0.51, 0],
481
+ fontSize: 0.25,
482
+ color: "black",
483
+ rotation: [Math.PI / 2, 0, 0],
484
+ children: "Bottom"
485
+ }
486
+ ),
487
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
488
+ "lineSegments",
489
+ {
490
+ args: [new THREE.EdgesGeometry(new THREE.BoxGeometry(1, 1, 1))],
491
+ material: new THREE.LineBasicMaterial({
492
+ color: 0,
493
+ linewidth: 2
494
+ })
495
+ }
496
+ )
497
+ ] });
498
+ };
499
+
500
+ // src/CadViewerContainer.tsx
501
+ var import_jsx_runtime3 = require("react/jsx-runtime");
502
+ var RotationTracker = () => {
503
+ (0, import_fiber3.useFrame)(({ camera }) => {
504
+ window.TSCI_MAIN_CAMERA_ROTATION = camera.rotation;
505
+ });
506
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, {});
507
+ };
508
+ var CadViewerContainer = ({ children }) => {
509
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { position: "relative", width: "100%", height: "100%" }, children: [
510
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
511
+ "div",
512
+ {
513
+ style: {
514
+ position: "absolute",
515
+ top: 0,
516
+ left: 0,
517
+ width: 120,
518
+ height: 120
519
+ },
520
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
521
+ import_fiber3.Canvas,
522
+ {
523
+ camera: {
524
+ up: [0, 0, 1],
525
+ // rotation: [-Math.PI / 2, 0, 0],
526
+ // lookAt: new THREE.Vector3(0, 0, 0),
527
+ position: [1, 1, 1]
528
+ },
529
+ children: [
530
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ambientLight", { intensity: Math.PI / 2 }),
531
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CubeWithLabeledSides, {})
532
+ ]
533
+ }
534
+ )
535
+ }
536
+ ),
537
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
538
+ import_fiber3.Canvas,
539
+ {
540
+ scene: { up: [0, 0, 1] },
541
+ camera: { up: [0, 0, 1], position: [5, 5, 5] },
542
+ children: [
543
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RotationTracker, {}),
544
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_drei2.OrbitControls, { autoRotate: true, autoRotateSpeed: 1 }),
545
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ambientLight", { intensity: Math.PI / 2 }),
546
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
547
+ "pointLight",
548
+ {
549
+ position: [-10, -10, 10],
550
+ decay: 0,
551
+ intensity: Math.PI / 4
552
+ }
553
+ ),
554
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
555
+ import_drei2.Grid,
556
+ {
557
+ rotation: [Math.PI / 2, 0, 0],
558
+ infiniteGrid: true,
559
+ cellSize: 1,
560
+ sectionSize: 10
561
+ }
562
+ ),
563
+ children
564
+ ]
565
+ }
566
+ )
567
+ ] });
568
+ };
569
+
570
+ // src/three-components/MixedStlModel.tsx
571
+ var import_react4 = require("react");
572
+ var import_three_stdlib2 = require("three-stdlib");
573
+ var import_jsx_runtime4 = require("react/jsx-runtime");
574
+ function MixedStlModel({
575
+ url,
576
+ position,
577
+ rotation
578
+ }) {
579
+ const [obj, setObj] = (0, import_react4.useState)(null);
580
+ (0, import_react4.useEffect)(() => {
581
+ async function loadUrlContent() {
582
+ const response = await fetch(url);
583
+ const text = await response.text();
584
+ const mtlContent = text.match(/newmtl[\s\S]*?endmtl/g)?.join("\n").replace(/d 0\./g, "d 1.");
585
+ const objContent = text.replace(/newmtl[\s\S]*?endmtl/g, "");
586
+ const mtlLoader = new import_three_stdlib2.MTLLoader();
587
+ mtlLoader.setMaterialOptions({
588
+ invertTrProperty: true
589
+ });
590
+ const materials = mtlLoader.parse(
591
+ // Grayscale the colors, for some reason everything from JLCPCB is
592
+ // a bit red, it doesn't look right. The grayscale version looks OK,
593
+ // it's a HACK because we only take the second color rather than
594
+ // averaging the colors
595
+ mtlContent.replace(
596
+ /Kd\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)/g,
597
+ "Kd $2 $2 $2"
598
+ ),
599
+ "test.mtl"
600
+ );
601
+ const objLoader = new import_three_stdlib2.OBJLoader();
602
+ objLoader.setMaterials(materials);
603
+ setObj(objLoader.parse(objContent));
604
+ }
605
+ loadUrlContent();
606
+ }, [url]);
607
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("group", { rotation, position, children: obj && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("primitive", { object: obj }) });
608
+ }
609
+
610
+ // src/CadViewer.tsx
611
+ var import_jsx_runtime5 = require("react/jsx-runtime");
612
+ var CadViewer = ({ soup, children }) => {
613
+ soup ??= useConvertChildrenToSoup(children, soup);
614
+ const boardGeom = (0, import_react5.useMemo)(() => createBoardGeomFromSoup(soup), [soup]);
615
+ const { stls, loading } = useStlsFromGeom(boardGeom);
616
+ const cad_components = (0, import_soup_util2.su)(soup).cad_component.list();
617
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(CadViewerContainer, { children: [
618
+ stls.map(({ stlUrl, color }, index) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
619
+ STLModel,
620
+ {
621
+ stlUrl,
622
+ color,
623
+ opacity: index === 0 ? 0.95 : 1
624
+ },
625
+ stlUrl
626
+ )),
627
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
628
+ MixedStlModel,
629
+ {
630
+ url: "/easyeda-models/dc694c23844346e9981bdbac7bb76421",
631
+ position: [0, 0, 0.5],
632
+ rotation: [0, 0, Math.PI / 2]
633
+ }
634
+ ),
635
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
636
+ MixedStlModel,
637
+ {
638
+ url: "/easyeda-models/c7acac53bcbc44d68fbab8f60a747688",
639
+ position: [-5.65, 0, 0.5],
640
+ rotation: [0, 0, Math.PI / 2]
641
+ }
642
+ ),
643
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
644
+ MixedStlModel,
645
+ {
646
+ url: "/easyeda-models/c7acac53bcbc44d68fbab8f60a747688",
647
+ position: [6.75, 0, 0.5],
648
+ rotation: [0, 0, 0]
649
+ }
650
+ )
651
+ ] });
652
+ };
653
+ // Annotate the CommonJS export names for ESM import in node:
654
+ 0 && (module.exports = {
655
+ CadViewer
656
+ });
657
+ //# sourceMappingURL=index.cjs.map