@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.
- package/dist/index.cjs +657 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +660 -0
- package/dist/index.js.map +1 -0
- package/package.json +2 -1
- package/.storybook/main.ts +0 -29
- package/.storybook/preview.tsx +0 -24
- package/ai-reference/soup-reference.md +0 -1301
- package/bun.lockb +0 -0
- package/index.html +0 -13
- package/src/App.tsx +0 -17
- package/src/App2.tsx +0 -48
- package/src/App3.tsx +0 -209
- package/src/App4.tsx +0 -88
- package/src/CadViewer.tsx +0 -56
- package/src/CadViewerContainer.tsx +0 -73
- package/src/GeomContext.ts +0 -3
- package/src/bug-pads-and-traces.json +0 -1516
- package/src/geoms/constants.ts +0 -9
- package/src/geoms/plated-hole.ts +0 -45
- package/src/hooks/index.ts +0 -1
- package/src/hooks/render/hooks.tsx +0 -62
- package/src/hooks/render/index.tsx +0 -440
- package/src/hooks/use-convert-children-to-soup.ts +0 -10
- package/src/hooks/use-stls-from-geom.ts +0 -54
- package/src/index.tsx +0 -1
- package/src/main.tsx +0 -9
- package/src/modules.d.ts +0 -3
- package/src/plated-hole-board.json +0 -213
- package/src/soup-to-3d/index.ts +0 -162
- package/src/themes.ts +0 -7
- package/src/three-components/GeomModel.tsx +0 -13
- package/src/three-components/MixedStlModel.tsx +0 -61
- package/src/three-components/STLModel.tsx +0 -33
- package/src/three-components/cube-with-labeled-sides.tsx +0 -116
- package/src/vite-env.d.ts +0 -1
- package/stories/Simple.stories.tsx +0 -10
- package/stories/assets/soic-with-traces.json +0 -1802
- package/tsconfig.json +0 -39
- package/vendor/@jscadui/format-three/index.ts +0 -252
- 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
|