jscad-electronics 0.0.179 → 0.0.181

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/vanilla.d.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  import { BufferGeometry } from 'three';
2
2
  import * as jscadModeling from '@jscad/modeling';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import { NemaMotorModelPropsInput, HexSocketBoltModelPropsInput, SheetMetalModelPropsInput } from '@tscircuit/modelprinter';
5
+ import * as _jscad_modeling_src_geometries_types from '@jscad/modeling/src/geometries/types';
3
6
 
4
7
  type VNode = {
5
8
  type: any;
@@ -21,6 +24,59 @@ type RenderResult = {
21
24
 
22
25
  declare function convertCSGToThreeGeom(csg: any): BufferGeometry;
23
26
 
27
+ type NemaMotorProps = NemaMotorModelPropsInput & {
28
+ bodyColor?: string;
29
+ capColor?: string;
30
+ shaftColor?: string;
31
+ };
32
+ type NemaFrameMotorProps = Omit<NemaMotorProps, "nemaSize">;
33
+ /** Mounting face at Z=0, body along -Z, shaft along +Z; all lengths in mm.
34
+ * Parameters follow the modelprinter NEMA contract; JSCAD builds all geometry.
35
+ */
36
+ declare function NemaMotor({ bodyColor, capColor, shaftColor, ...props }: NemaMotorProps): react_jsx_runtime.JSX.Element;
37
+ declare const NEMA8: (props: NemaFrameMotorProps) => react_jsx_runtime.JSX.Element;
38
+ declare const NEMA17: (props: NemaFrameMotorProps) => react_jsx_runtime.JSX.Element;
39
+ declare const NEMA23: (props: NemaFrameMotorProps) => react_jsx_runtime.JSX.Element;
40
+
41
+ /** Indexed triangles, counterclockwise from outside; millimeters, Z up. */
42
+ interface HexSocketBoltMesh {
43
+ positions: number[];
44
+ indices: number[];
45
+ }
46
+ /**
47
+ * Head bearing plane at Z=0; tip at -length; head extends along +Z.
48
+ * Produces a closed surface with a blind hex socket and a right-hand,
49
+ * truncated 60-degree helical thread. Threads are a visual approximation.
50
+ * No renderer dependencies are needed to consume the indexed mesh.
51
+ */
52
+ declare const createHexSocketBoltMesh: (input: HexSocketBoltModelPropsInput) => HexSocketBoltMesh;
53
+
54
+ type HexSocketBoltProps = HexSocketBoltModelPropsInput & {
55
+ color?: string;
56
+ };
57
+ /** Bearing plane Z=0, tip along -Z, head along +Z. Threads are approximate. */
58
+ declare function createHexSocketBoltGeom(input: HexSocketBoltModelPropsInput): _jscad_modeling_src_geometries_types.Geom3;
59
+ declare function HexSocketBolt({ color, ...props }: HexSocketBoltProps): react_jsx_runtime.JSX.Element;
60
+
61
+ interface SheetMetalMesh {
62
+ positions: number[];
63
+ indices: number[];
64
+ }
65
+ /** Watertight sheet-metal plate, 90-degree angle or U-channel. Millimeters,
66
+ * right-handed +Z up. Base midsurface at Z=0, centered in X/Y; flanges rise
67
+ * above it. Lengths measure straight tangent-to-tangent sections. Bend skins
68
+ * use the specified inside radius and constant material thickness.
69
+ * Holes are through-cut on straight panels only; no bend allowance/unfolding.
70
+ */
71
+ declare function createSheetMetalMesh(input: SheetMetalModelPropsInput): SheetMetalMesh;
72
+
73
+ type SheetMetalProps = SheetMetalModelPropsInput & {
74
+ color?: string;
75
+ };
76
+ /** Base midsurface Z=0; bends and flanges rise along +Z. Dimensions in mm. */
77
+ declare function createSheetMetalGeom(input: SheetMetalModelPropsInput): _jscad_modeling_src_geometries_types.Geom3;
78
+ declare function SheetMetal({ color, ...props }: SheetMetalProps): react_jsx_runtime.JSX.Element;
79
+
24
80
  declare function getJscadModelForFootprint(footprint: string, jscad: typeof jscadModeling): RenderResult;
25
81
  declare function getJscadModelForFootprintWithPads(footprint: string, jscad: typeof jscadModeling): RenderResult;
26
82
  declare function createJSCADRenderer(jscad: typeof jscadModeling): {
@@ -29,4 +85,4 @@ declare function createJSCADRenderer(jscad: typeof jscadModeling): {
29
85
  };
30
86
  };
31
87
 
32
- export { type ColoredGeom, Fragment, type RenderResult, type VNode, convertCSGToThreeGeom, createJSCADRenderer, getJscadModelForFootprint, getJscadModelForFootprintWithPads, h };
88
+ export { type ColoredGeom, Fragment, HexSocketBolt, type HexSocketBoltMesh, type HexSocketBoltProps, NEMA17, NEMA23, NEMA8, type NemaFrameMotorProps, NemaMotor, type NemaMotorProps, type RenderResult, SheetMetal, type SheetMetalMesh, type SheetMetalProps, type VNode, convertCSGToThreeGeom, createHexSocketBoltGeom, createHexSocketBoltMesh, createJSCADRenderer, createSheetMetalGeom, createSheetMetalMesh, getJscadModelForFootprint, getJscadModelForFootprintWithPads, h };
package/dist/vanilla.js CHANGED
@@ -19,6 +19,7 @@ function h(type, props, ...restChildren) {
19
19
  }
20
20
 
21
21
  // lib/ExtrudedPads.tsx
22
+ import { mp } from "@tscircuit/modelprinter";
22
23
  import { fp } from "@tscircuit/footprinter";
23
24
 
24
25
  // lib/vanilla/primitives.ts
@@ -36,6 +37,7 @@ var Colorize = /* @__PURE__ */ Symbol("Colorize");
36
37
  var Polygon = /* @__PURE__ */ Symbol("Polygon");
37
38
  var ExtrudeLinear = /* @__PURE__ */ Symbol("ExtrudeLinear");
38
39
  var RoundedCylinder = /* @__PURE__ */ Symbol("RoundedCylinder");
40
+ var Custom = /* @__PURE__ */ Symbol("Custom");
39
41
 
40
42
  // lib/vanilla/react-shim.ts
41
43
  var Fragment2 = Fragment;
@@ -170,6 +172,12 @@ var ExtrudedPads = ({
170
172
  circuitJson,
171
173
  footprint
172
174
  }) => {
175
+ if (!circuitJson && footprint && ["nema", "hexsocketbolt", "sheetmetal"].includes(
176
+ mp.string(footprint.split("_", 1)[0]).params().fn
177
+ )) {
178
+ mp.string(footprint).json();
179
+ return null;
180
+ }
173
181
  if (!circuitJson && footprint) {
174
182
  circuitJson = fp.string(footprint).circuitJson();
175
183
  }
@@ -193,6 +201,395 @@ var ExtrudedPads = ({
193
201
  ] });
194
202
  };
195
203
 
204
+ // lib/mechanical/hex-socket-bolt-mesh.ts
205
+ import {
206
+ hexSocketBoltDimensions,
207
+ hexSocketBoltModelPropsSchema
208
+ } from "@tscircuit/modelprinter";
209
+ var createHexSocketBoltMesh = (input) => {
210
+ const props = hexSocketBoltModelPropsSchema.parse(input);
211
+ const {
212
+ diameter,
213
+ threadPitch: pitch,
214
+ headDiameter,
215
+ headHeight,
216
+ socketWidth,
217
+ socketDepth
218
+ } = hexSocketBoltDimensions[props.metricSize];
219
+ const { length, showThreads } = props;
220
+ const segments = 96;
221
+ const steps = Math.max(2, Math.ceil(length / pitch * 24));
222
+ if (steps > 24e3)
223
+ throw new Error(
224
+ "Bolt length exceeds mesh resolution limit (1000 thread turns)"
225
+ );
226
+ const positions = [];
227
+ const indices = [];
228
+ const radius = diameter / 2;
229
+ const depth = pitch * 0.61343;
230
+ const bevel = Math.min(0.2, headHeight * 0.08);
231
+ const tipBevel = Math.min(pitch * 0.6, length / 3);
232
+ const ring = (z, radiusAt) => {
233
+ const start = positions.length / 3;
234
+ for (let i = 0; i < segments; i++) {
235
+ const angle = i * Math.PI * 2 / segments;
236
+ const r = radiusAt(angle);
237
+ positions.push(r * Math.cos(angle), r * Math.sin(angle), z);
238
+ }
239
+ return start;
240
+ };
241
+ const connect = (a, b) => {
242
+ for (let i = 0; i < segments; i++) {
243
+ const next = (i + 1) % segments;
244
+ indices.push(a + i, a + next, b + next, a + i, b + next, b + i);
245
+ }
246
+ };
247
+ const cap = (start, z, upward) => {
248
+ const center = positions.length / 3;
249
+ positions.push(0, 0, z);
250
+ for (let i = 0; i < segments; i++) {
251
+ const a = start + i;
252
+ const b = start + (i + 1) % segments;
253
+ indices.push(center, upward ? a : b, upward ? b : a);
254
+ }
255
+ };
256
+ let previous = -1;
257
+ for (let step = 0; step <= steps; step++) {
258
+ const z = -length + length * step / steps;
259
+ const current = ring(z, (angle) => {
260
+ const phase = ((z / pitch - angle / (2 * Math.PI)) % 1 + 1) % 1;
261
+ const distance2 = Math.min(phase, 1 - phase);
262
+ const groove = Math.min(
263
+ depth,
264
+ Math.max(0, (distance2 - 1 / 16) * pitch * Math.sqrt(3))
265
+ );
266
+ const endRamp = Math.min(
267
+ 1,
268
+ (z + length) / tipBevel,
269
+ -z / Math.min(pitch / 2, length / 3)
270
+ );
271
+ const tipCut = tipBevel * Math.max(0, 1 - (z + length) / tipBevel);
272
+ return radius - (showThreads ? groove * endRamp : 0) - tipCut;
273
+ });
274
+ if (previous < 0) cap(current, z, false);
275
+ else connect(previous, current);
276
+ previous = current;
277
+ }
278
+ const addRing = (z, radiusAt) => {
279
+ const current = ring(z, radiusAt);
280
+ connect(previous, current);
281
+ previous = current;
282
+ };
283
+ addRing(0, () => headDiameter / 2 - bevel);
284
+ addRing(bevel, () => headDiameter / 2);
285
+ addRing(headHeight - bevel, () => headDiameter / 2);
286
+ addRing(headHeight, () => headDiameter / 2 - bevel);
287
+ const hexRadius = (angle, acrossFlats) => acrossFlats / 2 / Math.cos((angle + Math.PI / 6) % (Math.PI / 3) - Math.PI / 6);
288
+ addRing(headHeight, (angle) => hexRadius(angle, socketWidth + bevel));
289
+ addRing(headHeight - bevel / 2, (angle) => hexRadius(angle, socketWidth));
290
+ addRing(headHeight - socketDepth, (angle) => hexRadius(angle, socketWidth));
291
+ cap(previous, headHeight - socketDepth, true);
292
+ return { positions, indices };
293
+ };
294
+
295
+ // lib/mechanical/indexedMeshToGeom3.ts
296
+ import jscad from "@jscad/modeling";
297
+ function indexedMeshToGeom3(mesh) {
298
+ const polygons = [];
299
+ for (let i = 0; i < mesh.indices.length; i += 3)
300
+ polygons.push(
301
+ mesh.indices.slice(i, i + 3).map((index) => [
302
+ mesh.positions[index * 3],
303
+ mesh.positions[index * 3 + 1],
304
+ mesh.positions[index * 3 + 2]
305
+ ])
306
+ );
307
+ return jscad.geometries.geom3.fromPoints(polygons);
308
+ }
309
+
310
+ // lib/HexSocketBolt.tsx
311
+ function createHexSocketBoltGeom(input) {
312
+ return indexedMeshToGeom3(createHexSocketBoltMesh(input));
313
+ }
314
+ function HexSocketBolt({
315
+ color = "#737e8f",
316
+ ...props
317
+ }) {
318
+ return /* @__PURE__ */ jsx(Colorize, { color, children: /* @__PURE__ */ jsx(Custom, { geometry: createHexSocketBoltGeom(props) }) });
319
+ }
320
+
321
+ // lib/mechanical/sheet-metal-mesh.ts
322
+ import earcut from "earcut";
323
+ import {
324
+ sheetMetalModelPropsSchema
325
+ } from "@tscircuit/modelprinter";
326
+ function holeOutline(hole) {
327
+ const radius = (hole.shape === "round" ? hole.diameter : hole.width) / 2;
328
+ const straight = hole.shape === "slot" ? (hole.length - hole.width) / 2 : 0;
329
+ if (straight < 0) throw new Error("Slot length must be at least its width");
330
+ return Array.from({ length: 64 }, (_, i) => {
331
+ if (hole.shape === "round") {
332
+ const angle2 = i * Math.PI / 32;
333
+ return {
334
+ u: hole.u + radius * Math.cos(angle2),
335
+ v: hole.v + radius * Math.sin(angle2)
336
+ };
337
+ }
338
+ const half = i < 32 ? 1 : -1;
339
+ const angle = -Math.PI / 2 + (i < 32 ? i : i - 32) * Math.PI / 31 + (i < 32 ? 0 : Math.PI);
340
+ const along = half * straight + radius * Math.cos(angle);
341
+ const across = radius * Math.sin(angle);
342
+ return hole.shape === "slot" && hole.axis === "v" ? { u: hole.u - across, v: hole.v + along } : { u: hole.u + along, v: hole.v + across };
343
+ });
344
+ }
345
+ function createSheetMetalMesh(input) {
346
+ const props = sheetMetalModelPropsSchema.parse(input);
347
+ const { width: width10, baseLength, flangeHeight, thickness, profile } = props;
348
+ const radius = props.insideBendRadius + thickness / 2;
349
+ const positions = [];
350
+ const indices = [];
351
+ const addTriangle = (vertices, reverse = false) => {
352
+ const start = positions.length / 3;
353
+ for (const vertex of vertices) positions.push(...vertex);
354
+ indices.push(start, start + (reverse ? 2 : 1), start + (reverse ? 1 : 2));
355
+ };
356
+ const addQuad = (vertices, reverse = false) => {
357
+ addTriangle([vertices[0], vertices[1], vertices[2]], reverse);
358
+ addTriangle([vertices[0], vertices[2], vertices[3]], reverse);
359
+ };
360
+ const panels = profile === "plate" ? ["base"] : profile === "angle" ? ["base", "right"] : ["base", "left", "right"];
361
+ for (const hole of props.holes) {
362
+ if (!panels.includes(hole.panel))
363
+ throw new Error(`No ${hole.panel} panel on ${profile}`);
364
+ }
365
+ for (const panel of panels) {
366
+ const length = panel === "base" ? baseLength : flangeHeight;
367
+ const holes = props.holes.filter((hole) => hole.panel === panel).map(holeOutline);
368
+ const bounds = holes.map((outline2) => ({
369
+ minU: Math.min(...outline2.map((p) => p.u)),
370
+ maxU: Math.max(...outline2.map((p) => p.u)),
371
+ minV: Math.min(...outline2.map((p) => p.v)),
372
+ maxV: Math.max(...outline2.map((p) => p.v))
373
+ }));
374
+ for (const [i, box] of bounds.entries()) {
375
+ if (box.minU <= -length / 2 || box.maxU >= length / 2 || box.minV <= -width10 / 2 || box.maxV >= width10 / 2)
376
+ throw new Error(`Hole crosses ${panel} panel edge or bend tangent`);
377
+ for (const previous of bounds.slice(0, i))
378
+ if (box.minU <= previous.maxU && box.maxU >= previous.minU && box.minV <= previous.maxV && box.maxV >= previous.minV)
379
+ throw new Error("Hole bounding boxes must not overlap");
380
+ }
381
+ const outline = [
382
+ { u: -length / 2, v: -width10 / 2 },
383
+ { u: length / 2, v: -width10 / 2 },
384
+ { u: length / 2, v: width10 / 2 },
385
+ { u: -length / 2, v: width10 / 2 }
386
+ ];
387
+ const loops = [outline, ...holes.map((hole) => [...hole].reverse())];
388
+ const points = loops.flat();
389
+ const starts = holes.map((_, i) => 4 + i * 64);
390
+ const triangles = earcut(
391
+ points.flatMap((p) => [p.u, p.v]),
392
+ starts
393
+ );
394
+ const project = (point, offset) => panel === "base" ? [point.u, point.v, offset] : [
395
+ (panel === "left" ? -1 : 1) * (baseLength / 2 + radius - offset),
396
+ point.v,
397
+ radius + flangeHeight / 2 + point.u
398
+ ];
399
+ const reverse = panel === "left";
400
+ for (let i = 0; i < triangles.length; i += 3) {
401
+ const face2 = triangles.slice(i, i + 3).map((index) => points[index]);
402
+ addTriangle(
403
+ face2.map((p) => project(p, thickness / 2)),
404
+ reverse
405
+ );
406
+ addTriangle(
407
+ face2.map((p) => project(p, -thickness / 2)),
408
+ !reverse
409
+ );
410
+ }
411
+ for (const [loopIndex, loop] of loops.entries())
412
+ for (let i = 0; i < loop.length; i++) {
413
+ const a = loop[i], b = loop[(i + 1) % loop.length];
414
+ if (loopIndex === 0 && a.u === b.u && (panel === "base" && (a.u > 0 && profile !== "plate" || a.u < 0 && profile === "channel") || panel !== "base" && a.u < 0))
415
+ continue;
416
+ addQuad(
417
+ [
418
+ project(a, -thickness / 2),
419
+ project(b, -thickness / 2),
420
+ project(b, thickness / 2),
421
+ project(a, thickness / 2)
422
+ ],
423
+ reverse
424
+ );
425
+ }
426
+ }
427
+ const sides = profile === "plate" ? [] : profile === "angle" ? [1] : [-1, 1];
428
+ for (const side of sides) {
429
+ const project = (angle, crossSection) => {
430
+ const r = radius - crossSection.offset;
431
+ return [
432
+ side * (baseLength / 2 + r * Math.sin(angle)),
433
+ crossSection.y,
434
+ radius - r * Math.cos(angle)
435
+ ];
436
+ };
437
+ for (let i = 0; i < 32; i++) {
438
+ const a = i * Math.PI / 64, b = (i + 1) * Math.PI / 64;
439
+ for (const offset of [-thickness / 2, thickness / 2]) {
440
+ addQuad(
441
+ [
442
+ project(a, { y: -width10 / 2, offset }),
443
+ project(b, { y: -width10 / 2, offset }),
444
+ project(b, { y: width10 / 2, offset }),
445
+ project(a, { y: width10 / 2, offset })
446
+ ],
447
+ offset < 0 !== side < 0
448
+ );
449
+ }
450
+ for (const y of [-width10 / 2, width10 / 2]) {
451
+ addQuad(
452
+ [
453
+ project(a, { y, offset: -thickness / 2 }),
454
+ project(b, { y, offset: -thickness / 2 }),
455
+ project(b, { y, offset: thickness / 2 }),
456
+ project(a, { y, offset: thickness / 2 })
457
+ ],
458
+ y > 0 !== side < 0
459
+ );
460
+ }
461
+ }
462
+ }
463
+ return { positions, indices };
464
+ }
465
+
466
+ // lib/SheetMetal.tsx
467
+ function createSheetMetalGeom(input) {
468
+ return indexedMeshToGeom3(createSheetMetalMesh(input));
469
+ }
470
+ function SheetMetal({ color = "#737e8f", ...props }) {
471
+ return /* @__PURE__ */ jsx(Colorize, { color, children: /* @__PURE__ */ jsx(Custom, { geometry: createSheetMetalGeom(props) }) });
472
+ }
473
+
474
+ // lib/NemaMotor.tsx
475
+ import jscad2 from "@jscad/modeling";
476
+
477
+ // lib/utils/nemaMotorParameters.ts
478
+ import { mp as mp2, nemaMotorModelPropsSchema } from "@tscircuit/modelprinter";
479
+ var resolveNemaMotorProps = nemaMotorModelPropsSchema.parse.bind(
480
+ nemaMotorModelPropsSchema
481
+ );
482
+
483
+ // lib/utils/nemaMotorGeometry.ts
484
+ var circle = (radius, x = 0, y = 0) => Array.from({ length: 96 }, (_, i) => [
485
+ x + radius * Math.cos(i * Math.PI / 48),
486
+ y + radius * Math.sin(i * Math.PI / 48)
487
+ ]);
488
+ var face = (width10, chamfer) => {
489
+ const h2 = width10 / 2, c = chamfer;
490
+ if (c === 0)
491
+ return [
492
+ [-h2, -h2],
493
+ [h2, -h2],
494
+ [h2, h2],
495
+ [-h2, h2]
496
+ ];
497
+ return [
498
+ [-h2 + c, -h2],
499
+ [h2 - c, -h2],
500
+ [h2, -h2 + c],
501
+ [h2, h2 - c],
502
+ [h2 - c, h2],
503
+ [-h2 + c, h2],
504
+ [-h2, h2 - c],
505
+ [-h2, -h2 + c]
506
+ ];
507
+ };
508
+ function createNemaMotorSections(input) {
509
+ const p = resolveNemaMotorProps(input);
510
+ const sections = [];
511
+ const add = (name, outline, zMin, zMax, holes2 = []) => {
512
+ if (zMax > zMin) sections.push({ name, outline, holes: holes2, zMin, zMax });
513
+ };
514
+ const front = face(p.bodyWidth, p.faceCornerChamfer);
515
+ const core = face(p.bodyWidth, p.bodyCornerChamfer);
516
+ const h2 = p.mountingHoleSpacing / 2;
517
+ const centers = [
518
+ [-h2, -h2],
519
+ [h2, -h2],
520
+ [h2, h2],
521
+ [-h2, h2]
522
+ ];
523
+ const holes = centers.map(
524
+ ([x, y]) => circle(p.mountingHoleDiameter / 2, x, y)
525
+ );
526
+ const holeDepth = p.mountingHoleThrough ? p.frontCapLength : p.mountingHoleDepth;
527
+ add("frontCap", front, -holeDepth, 0, holes);
528
+ add("frontCap", front, -p.frontCapLength, -holeDepth);
529
+ add("body", core, -p.bodyLength + p.rearCapLength, -p.frontCapLength);
530
+ add(
531
+ "rearCap",
532
+ p.mountingHoleThrough ? core : front,
533
+ -p.bodyLength,
534
+ -p.bodyLength + p.rearCapLength
535
+ );
536
+ add("pilot", circle(p.pilotDiameter / 2), 0, p.pilotLength);
537
+ const r = p.shaftDiameter / 2;
538
+ const round = circle(r);
539
+ if (p.shaftShape === "round")
540
+ add("shaft", round, p.pilotLength, p.shaftLength);
541
+ else {
542
+ const cut = r - p.shaftFlatDepth;
543
+ const angle = Math.acos(cut / r);
544
+ const profile = Array.from({ length: 97 }, (_, i) => {
545
+ const a = angle + (2 * Math.PI - 2 * angle) * i / 96;
546
+ return [r * Math.cos(a), r * Math.sin(a)];
547
+ });
548
+ const rotation = p.shaftFlatAngle * Math.PI / 180;
549
+ const d = profile.map(([x, y]) => [
550
+ x * Math.cos(rotation) - y * Math.sin(rotation),
551
+ x * Math.sin(rotation) + y * Math.cos(rotation)
552
+ ]);
553
+ add("shaft", round, p.pilotLength, p.shaftLength - p.shaftFlatLength);
554
+ add("shaft", d, p.shaftLength - p.shaftFlatLength, p.shaftLength);
555
+ }
556
+ return sections;
557
+ }
558
+
559
+ // lib/NemaMotor.tsx
560
+ function NemaMotor({
561
+ bodyColor = "#242830",
562
+ capColor = "#aeb5bd",
563
+ shaftColor = "#b8bec6",
564
+ ...props
565
+ }) {
566
+ return /* @__PURE__ */ jsx(Fragment2, { children: createNemaMotorSections(props).map((section, index) => {
567
+ const outline = jscad2.geometries.geom2.fromPoints(section.outline);
568
+ const profile = section.holes.length ? jscad2.booleans.subtract(
569
+ outline,
570
+ ...section.holes.map((h2) => jscad2.geometries.geom2.fromPoints(h2))
571
+ ) : outline;
572
+ const geometry = jscad2.transforms.translate(
573
+ [0, 0, section.zMin],
574
+ jscad2.extrusions.extrudeLinear(
575
+ { height: section.zMax - section.zMin },
576
+ profile
577
+ )
578
+ );
579
+ return /* @__PURE__ */ jsx(
580
+ Colorize,
581
+ {
582
+ color: section.name === "body" ? bodyColor : section.name === "shaft" ? shaftColor : capColor,
583
+ children: /* @__PURE__ */ jsx(Custom, { geometry })
584
+ },
585
+ `${section.name}-${index}`
586
+ );
587
+ }) });
588
+ }
589
+ var NEMA8 = (props) => /* @__PURE__ */ jsx(NemaMotor, { ...props, nemaSize: 8 });
590
+ var NEMA17 = (props) => /* @__PURE__ */ jsx(NemaMotor, { ...props, nemaSize: 17 });
591
+ var NEMA23 = (props) => /* @__PURE__ */ jsx(NemaMotor, { ...props, nemaSize: 23 });
592
+
196
593
  // lib/LGA.tsx
197
594
  function LGA(p) {
198
595
  for (const k of [
@@ -489,7 +886,7 @@ function renderFootprinterBodyModel(p) {
489
886
 
490
887
  // lib/Footprinter3d.tsx
491
888
  import { fp as fp10 } from "@tscircuit/footprinter";
492
- import { mp } from "@tscircuit/modelprinter";
889
+ import { mp as mp3 } from "@tscircuit/modelprinter";
493
890
 
494
891
  // lib/ChipBody.tsx
495
892
  var ChipBody = ({
@@ -961,6 +1358,66 @@ var A0603 = ({ color = "#333" }) => {
961
1358
  ] });
962
1359
  };
963
1360
 
1361
+ // lib/SmdLED.tsx
1362
+ var smdLedDimensions = {
1363
+ "0402": { length: 1, width: 0.5, height: 0.5, terminalLength: 0.2 },
1364
+ "0603": { length: 1.62, width: 0.8, height: 0.615, terminalLength: 0.21 },
1365
+ "0805": { length: 2, width: 1.25, height: 0.75, terminalLength: 0.35 }
1366
+ };
1367
+ function SmdLED({
1368
+ footprint,
1369
+ color = "#ffe8a0"
1370
+ }) {
1371
+ const p = smdLedDimensions[footprint];
1372
+ if (footprint === "0603")
1373
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
1374
+ /* @__PURE__ */ jsx(Colorize, { color: "#f0eee7", children: /* @__PURE__ */ jsx(
1375
+ RoundedCuboid,
1376
+ {
1377
+ size: [1.2, 0.79, 0.6],
1378
+ roundRadius: 0.02,
1379
+ center: [0, 0, 0.31]
1380
+ }
1381
+ ) }),
1382
+ /* @__PURE__ */ jsx(Colorize, { color: "#c7c9ca", children: [-1, 1].map((sign) => /* @__PURE__ */ jsx(
1383
+ Cuboid,
1384
+ {
1385
+ size: [0.21, 0.8, 0.22],
1386
+ center: [sign * 0.705, 0, 0.11]
1387
+ },
1388
+ sign
1389
+ )) }),
1390
+ /* @__PURE__ */ jsx(Colorize, { color, children: /* @__PURE__ */ jsx(Cuboid, { size: [0.72, 0.52, 0.01], center: [0, 0, 0.61] }) })
1391
+ ] });
1392
+ const terminalThickness = Math.min(0.1, p.height / 4);
1393
+ const apertureHeight = 0.02;
1394
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
1395
+ /* @__PURE__ */ jsx(Colorize, { color: "#f0eee7", children: /* @__PURE__ */ jsx(Translate, { z: (p.height + terminalThickness - apertureHeight) / 2, children: /* @__PURE__ */ jsx(
1396
+ Cuboid,
1397
+ {
1398
+ size: [
1399
+ p.length,
1400
+ p.width,
1401
+ p.height - terminalThickness - apertureHeight
1402
+ ]
1403
+ }
1404
+ ) }) }),
1405
+ /* @__PURE__ */ jsx(Colorize, { color: "#c7c9ca", children: [-1, 1].map((sign) => /* @__PURE__ */ jsx(
1406
+ Translate,
1407
+ {
1408
+ offset: [
1409
+ sign * (p.length - p.terminalLength) / 2,
1410
+ 0,
1411
+ terminalThickness / 2
1412
+ ],
1413
+ children: /* @__PURE__ */ jsx(Cuboid, { size: [p.terminalLength, p.width, terminalThickness] })
1414
+ },
1415
+ sign
1416
+ )) }),
1417
+ /* @__PURE__ */ jsx(Colorize, { color, children: /* @__PURE__ */ jsx(Translate, { z: p.height - apertureHeight / 2, children: /* @__PURE__ */ jsx(Cuboid, { size: [p.length * 0.45, p.width * 0.65, apertureHeight] }) }) })
1418
+ ] });
1419
+ }
1420
+
964
1421
  // lib/A0805.tsx
965
1422
  var fullLength3 = 2;
966
1423
  var width3 = 1.25;
@@ -8135,10 +8592,22 @@ var FlexScreen = (props) => {
8135
8592
 
8136
8593
  // lib/Footprinter3d.tsx
8137
8594
  var Footprinter3d = ({ footprint }) => {
8138
- const modelFn = mp.string(footprint.split("_", 1)[0]).params().fn;
8139
- if (mp.getModelNames().includes(modelFn)) {
8140
- const model = mp.string(footprint).json();
8595
+ const modelFn = mp3.string(footprint.split("_", 1)[0]).params().fn;
8596
+ if (mp3.getModelNames().includes(modelFn)) {
8597
+ const model = mp3.string(footprint).json();
8141
8598
  switch (model.fn) {
8599
+ case "nema": {
8600
+ const { fn, ...props } = model;
8601
+ return /* @__PURE__ */ jsx(NemaMotor, { ...props });
8602
+ }
8603
+ case "hexsocketbolt": {
8604
+ const { fn, ...props } = model;
8605
+ return /* @__PURE__ */ jsx(HexSocketBolt, { ...props });
8606
+ }
8607
+ case "sheetmetal": {
8608
+ const { fn, ...props } = model;
8609
+ return /* @__PURE__ */ jsx(SheetMetal, { ...props });
8610
+ }
8142
8611
  case "flexscreen": {
8143
8612
  const { fn: _, ...flexScreenProps } = model;
8144
8613
  return /* @__PURE__ */ jsx(FlexScreen, { ...flexScreenProps });
@@ -8306,6 +8775,12 @@ var Footprinter3d = ({ footprint }) => {
8306
8775
  );
8307
8776
  case "led5050":
8308
8777
  return /* @__PURE__ */ jsx(Led5050, { color });
8778
+ case "led": {
8779
+ const imperial = String(fpJson.imperial);
8780
+ if (imperial === "0402" || imperial === "0603" || imperial === "0805")
8781
+ return /* @__PURE__ */ jsx(SmdLED, { footprint: imperial, color });
8782
+ break;
8783
+ }
8309
8784
  case "cap": {
8310
8785
  switch (fpJson.imperial) {
8311
8786
  case "0402":
@@ -8876,6 +9351,9 @@ function renderNode(node, colorCtx, renderCtx) {
8876
9351
  else geom = hulls.hull(geoms);
8877
9352
  return [{ geom, color: colorCtx }];
8878
9353
  }
9354
+ if (type === Custom) {
9355
+ return [{ geom: props.geometry, color: colorCtx }];
9356
+ }
8879
9357
  if (type === Polygon) {
8880
9358
  const points = props?.points ?? [];
8881
9359
  const g2 = geometries.geom2.fromPoints(points);
@@ -8936,8 +9414,8 @@ function renderNode(node, colorCtx, renderCtx) {
8936
9414
  }
8937
9415
  return (children ?? []).flatMap((c) => renderNode(c, colorCtx, renderCtx));
8938
9416
  }
8939
- function render(root, jscad) {
8940
- const geometries = renderNode(root, void 0, { jscad });
9417
+ function render(root, jscad3) {
9418
+ const geometries = renderNode(root, void 0, { jscad: jscad3 });
8941
9419
  return { geometries };
8942
9420
  }
8943
9421
 
@@ -9025,24 +9503,24 @@ function convertCSGToThreeGeom(csg) {
9025
9503
  }
9026
9504
 
9027
9505
  // lib/vanilla/index.ts
9028
- function getJscadModelForFootprint(footprint, jscad) {
9506
+ function getJscadModelForFootprint(footprint, jscad3) {
9029
9507
  const vnode = h(Footprinter3d, { footprint });
9030
- return render(vnode, jscad);
9508
+ return render(vnode, jscad3);
9031
9509
  }
9032
- function getJscadModelForFootprintWithPads(footprint, jscad) {
9510
+ function getJscadModelForFootprintWithPads(footprint, jscad3) {
9033
9511
  const vnode = h(
9034
9512
  Fragment,
9035
9513
  {},
9036
9514
  h(Footprinter3d, { footprint }),
9037
9515
  h(ExtrudedPads, { footprint })
9038
9516
  );
9039
- return render(vnode, jscad);
9517
+ return render(vnode, jscad3);
9040
9518
  }
9041
- function createJSCADRenderer(jscad) {
9519
+ function createJSCADRenderer(jscad3) {
9042
9520
  function createJSCADRoot(container) {
9043
9521
  return {
9044
9522
  render(element) {
9045
- const { geometries } = render(element, jscad);
9523
+ const { geometries } = render(element, jscad3);
9046
9524
  container.splice(0, container.length, ...geometries);
9047
9525
  }
9048
9526
  };
@@ -9051,8 +9529,18 @@ function createJSCADRenderer(jscad) {
9051
9529
  }
9052
9530
  export {
9053
9531
  Fragment,
9532
+ HexSocketBolt,
9533
+ NEMA17,
9534
+ NEMA23,
9535
+ NEMA8,
9536
+ NemaMotor,
9537
+ SheetMetal,
9054
9538
  convertCSGToThreeGeom,
9539
+ createHexSocketBoltGeom,
9540
+ createHexSocketBoltMesh,
9055
9541
  createJSCADRenderer,
9542
+ createSheetMetalGeom,
9543
+ createSheetMetalMesh,
9056
9544
  getJscadModelForFootprint,
9057
9545
  getJscadModelForFootprintWithPads,
9058
9546
  h