jscad-electronics 0.0.183 → 0.0.185
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/README.md +15 -0
- package/dist/index.d.ts +44 -2
- package/dist/index.js +1270 -834
- package/dist/index.js.map +1 -1
- package/dist/vanilla.d.ts +44 -2
- package/dist/vanilla.js +449 -21
- package/dist/vanilla.js.map +1 -1
- package/package.json +2 -2
package/dist/vanilla.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BufferGeometry } from 'three';
|
|
2
2
|
import * as jscadModeling from '@jscad/modeling';
|
|
3
3
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
-
import { NemaMotorModelPropsInput, HexSocketBoltModelPropsInput, SheetMetalModelPropsInput } from '@tscircuit/modelprinter';
|
|
4
|
+
import { NemaMotorModelPropsInput, HexSocketBoltModelPropsInput, SheetMetalModelPropsInput, SpurGearModelPropsInput, WormGearModelPropsInput } from '@tscircuit/modelprinter';
|
|
5
5
|
import * as _jscad_modeling_src_geometries_types from '@jscad/modeling/src/geometries/types';
|
|
6
6
|
|
|
7
7
|
type VNode = {
|
|
@@ -78,6 +78,48 @@ type SheetMetalProps = SheetMetalModelPropsInput & {
|
|
|
78
78
|
declare function createSheetMetalGeom(input: SheetMetalModelPropsInput): _jscad_modeling_src_geometries_types.Geom3;
|
|
79
79
|
declare function SheetMetal({ color, ...props }: SheetMetalProps): react_jsx_runtime.JSX.Element;
|
|
80
80
|
|
|
81
|
+
/** Indexed triangles, counterclockwise from outside; millimeters, Z up. */
|
|
82
|
+
interface SpurGearMesh {
|
|
83
|
+
positions: number[];
|
|
84
|
+
indices: number[];
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* An external involute spur gear with its lower face at Z=0. Tooth centers
|
|
88
|
+
* start on +X and rotate counterclockwise with phase. The optional hub extends
|
|
89
|
+
* above the upper face, and the round bore passes through both gear and hub.
|
|
90
|
+
* Flanks are sampled involutes; tips and root valleys follow circular arcs.
|
|
91
|
+
* Below the base circle the flanks extend radially, without a generated
|
|
92
|
+
* trochoidal root fillet or cutter undercut. No CSG or renderer is required.
|
|
93
|
+
*/
|
|
94
|
+
declare function createSpurGearMesh(input: SpurGearModelPropsInput): SpurGearMesh;
|
|
95
|
+
|
|
96
|
+
type SpurGearProps = SpurGearModelPropsInput & {
|
|
97
|
+
color?: string;
|
|
98
|
+
};
|
|
99
|
+
/** Axis +Z, lower face Z=0; optional hub extends above the gear face. */
|
|
100
|
+
declare function createSpurGearGeom(input: SpurGearModelPropsInput): _jscad_modeling_src_geometries_types.Geom3;
|
|
101
|
+
declare function SpurGear({ color, ...props }: SpurGearProps): react_jsx_runtime.JSX.Element;
|
|
102
|
+
|
|
103
|
+
/** Indexed triangles, counterclockwise from outside; millimeters, Z up. */
|
|
104
|
+
interface WormGearMesh {
|
|
105
|
+
positions: number[];
|
|
106
|
+
indices: number[];
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* A cylindrical worm from Z=0 to Z=length, with square-cut ends.
|
|
110
|
+
* The axial trapezoidal rack profile is swept around +Z. A right-hand crest
|
|
111
|
+
* winds counterclockwise as Z increases; phase rotates the entire solid.
|
|
112
|
+
* This is a faceted visual worm, not a hobbed wheel or a conjugate gear pair.
|
|
113
|
+
*/
|
|
114
|
+
declare const createWormGearMesh: (input: WormGearModelPropsInput) => WormGearMesh;
|
|
115
|
+
|
|
116
|
+
type WormGearProps = WormGearModelPropsInput & {
|
|
117
|
+
color?: string;
|
|
118
|
+
};
|
|
119
|
+
/** Square-cut worm along +Z, from Z=0 to length. Threads are approximate. */
|
|
120
|
+
declare function createWormGearGeom(input: WormGearModelPropsInput): _jscad_modeling_src_geometries_types.Geom3;
|
|
121
|
+
declare function WormGear({ color, ...props }: WormGearProps): react_jsx_runtime.JSX.Element;
|
|
122
|
+
|
|
81
123
|
declare function getJscadModelForFootprint(footprint: string, jscad: typeof jscadModeling): RenderResult;
|
|
82
124
|
declare function getJscadModelForFootprintWithPads(footprint: string, jscad: typeof jscadModeling): RenderResult;
|
|
83
125
|
declare function createJSCADRenderer(jscad: typeof jscadModeling): {
|
|
@@ -86,4 +128,4 @@ declare function createJSCADRenderer(jscad: typeof jscadModeling): {
|
|
|
86
128
|
};
|
|
87
129
|
};
|
|
88
130
|
|
|
89
|
-
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 };
|
|
131
|
+
export { type ColoredGeom, Fragment, HexSocketBolt, type HexSocketBoltMesh, type HexSocketBoltProps, NEMA17, NEMA23, NEMA8, type NemaFrameMotorProps, NemaMotor, type NemaMotorProps, type RenderResult, SheetMetal, type SheetMetalMesh, type SheetMetalProps, SpurGear, type SpurGearMesh, type SpurGearProps, type VNode, WormGear, type WormGearMesh, type WormGearProps, convertCSGToThreeGeom, createHexSocketBoltGeom, createHexSocketBoltMesh, createJSCADRenderer, createSheetMetalGeom, createSheetMetalMesh, createSpurGearGeom, createSpurGearMesh, createWormGearGeom, createWormGearMesh, getJscadModelForFootprint, getJscadModelForFootprintWithPads, h };
|
package/dist/vanilla.js
CHANGED
|
@@ -172,7 +172,7 @@ var ExtrudedPads = ({
|
|
|
172
172
|
circuitJson,
|
|
173
173
|
footprint
|
|
174
174
|
}) => {
|
|
175
|
-
if (!circuitJson && footprint && ["nema", "hexsocketbolt", "sheetmetal"].includes(
|
|
175
|
+
if (!circuitJson && footprint && ["nema", "hexsocketbolt", "sheetmetal", "spurgear", "wormgear"].includes(
|
|
176
176
|
mp.string(footprint.split("_", 1)[0]).params().fn
|
|
177
177
|
)) {
|
|
178
178
|
mp.string(footprint).json();
|
|
@@ -218,7 +218,7 @@ var createHexSocketBoltMesh = (input) => {
|
|
|
218
218
|
} = hexSocketBoltDimensions[props.metricSize];
|
|
219
219
|
const { length, showThreads } = props;
|
|
220
220
|
const segments = 96;
|
|
221
|
-
const steps = Math.max(2, Math.ceil(length / pitch * 24));
|
|
221
|
+
const steps = showThreads ? Math.max(2, Math.ceil(length / pitch * 24)) : 2;
|
|
222
222
|
if (steps > 24e3)
|
|
223
223
|
throw new Error(
|
|
224
224
|
"Bolt length exceeds mesh resolution limit (1000 thread turns)"
|
|
@@ -254,8 +254,11 @@ var createHexSocketBoltMesh = (input) => {
|
|
|
254
254
|
}
|
|
255
255
|
};
|
|
256
256
|
let previous = -1;
|
|
257
|
-
|
|
258
|
-
|
|
257
|
+
const shaftLevels = showThreads ? Array.from(
|
|
258
|
+
{ length: steps + 1 },
|
|
259
|
+
(_, step) => -length + length * step / steps
|
|
260
|
+
) : [-length, -length + tipBevel, 0];
|
|
261
|
+
for (const z of shaftLevels) {
|
|
259
262
|
const current = ring(z, (angle) => {
|
|
260
263
|
const phase = ((z / pitch - angle / (2 * Math.PI)) % 1 + 1) % 1;
|
|
261
264
|
const distance2 = Math.min(phase, 1 - phase);
|
|
@@ -471,8 +474,357 @@ function SheetMetal({ color = "#737e8f", ...props }) {
|
|
|
471
474
|
return /* @__PURE__ */ jsx(Colorize, { color, children: /* @__PURE__ */ jsx(Custom, { geometry: createSheetMetalGeom(props) }) });
|
|
472
475
|
}
|
|
473
476
|
|
|
477
|
+
// lib/mechanical/spur-gear-mesh.ts
|
|
478
|
+
import {
|
|
479
|
+
getSpurGearDimensions,
|
|
480
|
+
spurGearModelPropsSchema
|
|
481
|
+
} from "@tscircuit/modelprinter";
|
|
482
|
+
import earcut2 from "earcut";
|
|
483
|
+
function createSpurGearMesh(input) {
|
|
484
|
+
const props = spurGearModelPropsSchema.parse(input);
|
|
485
|
+
const dimensions = getSpurGearDimensions(props);
|
|
486
|
+
const pitchRadius = dimensions.pitchDiameter / 2;
|
|
487
|
+
const baseRadius = dimensions.baseDiameter / 2;
|
|
488
|
+
const outsideRadius = dimensions.outsideDiameter / 2;
|
|
489
|
+
const rootRadius = dimensions.rootDiameter / 2;
|
|
490
|
+
const pressureAngle = props.pressureAngle * Math.PI / 180;
|
|
491
|
+
const toothAngle = Math.PI * 2 / props.toothCount;
|
|
492
|
+
const phase = (props.phase % 360 + 360) % 360 * (Math.PI / 180);
|
|
493
|
+
const baseHalfAngle = dimensions.toothThickness / (2 * pitchRadius) + Math.tan(pressureAngle) - pressureAngle;
|
|
494
|
+
const startRadius = Math.max(baseRadius, rootRadius);
|
|
495
|
+
const startT = Math.sqrt(Math.max(0, (startRadius / baseRadius) ** 2 - 1));
|
|
496
|
+
const outsideT = Math.sqrt((outsideRadius / baseRadius) ** 2 - 1);
|
|
497
|
+
const halfAngle = (t) => baseHalfAngle - (t - Math.atan(t));
|
|
498
|
+
const rootHalfAngle = halfAngle(startT);
|
|
499
|
+
const tipHalfAngle = halfAngle(outsideT);
|
|
500
|
+
const flankSteps = Math.max(2, Math.ceil(props.segmentsPerTooth / 2));
|
|
501
|
+
const arcSteps = Math.max(1, Math.ceil(props.segmentsPerTooth / 4));
|
|
502
|
+
const rootValleyAngle = toothAngle - 2 * rootHalfAngle;
|
|
503
|
+
const innerRadius = Math.max(props.boreDiameter, props.hubDiameter) / 2;
|
|
504
|
+
const safeRootStep = innerRadius > 0 ? 1.8 * Math.acos(innerRadius / rootRadius) : Math.PI;
|
|
505
|
+
const rootArcSteps = Math.max(
|
|
506
|
+
arcSteps,
|
|
507
|
+
Math.ceil(rootValleyAngle / safeRootStep)
|
|
508
|
+
);
|
|
509
|
+
const pointsPerTooth = 2 * flankSteps + arcSteps + rootArcSteps + (rootRadius < baseRadius ? 2 : 0);
|
|
510
|
+
if (!Number.isFinite(rootArcSteps) || 2 * props.toothCount * pointsPerTooth + 1024 > 1e6)
|
|
511
|
+
throw new Error(
|
|
512
|
+
"Spur gear bore or hub clearance exceeds mesh resolution limit (1 million vertices)"
|
|
513
|
+
);
|
|
514
|
+
const outline = [];
|
|
515
|
+
const polar = (radius, angle) => {
|
|
516
|
+
outline.push([radius * Math.cos(angle), radius * Math.sin(angle)]);
|
|
517
|
+
};
|
|
518
|
+
for (let tooth = 0; tooth < props.toothCount; tooth++) {
|
|
519
|
+
const center = phase + tooth * toothAngle;
|
|
520
|
+
polar(rootRadius, center - rootHalfAngle);
|
|
521
|
+
if (rootRadius < baseRadius) polar(baseRadius, center - baseHalfAngle);
|
|
522
|
+
for (let step = 1; step <= flankSteps; step++) {
|
|
523
|
+
const t = startT + (outsideT - startT) * step / flankSteps;
|
|
524
|
+
polar(baseRadius * Math.sqrt(1 + t * t), center - halfAngle(t));
|
|
525
|
+
}
|
|
526
|
+
for (let step = 1; step <= arcSteps; step++)
|
|
527
|
+
polar(
|
|
528
|
+
outsideRadius,
|
|
529
|
+
center - tipHalfAngle + 2 * tipHalfAngle * step / arcSteps
|
|
530
|
+
);
|
|
531
|
+
for (let step = flankSteps - 1; step >= 0; step--) {
|
|
532
|
+
const t = startT + (outsideT - startT) * step / flankSteps;
|
|
533
|
+
polar(baseRadius * Math.sqrt(1 + t * t), center + halfAngle(t));
|
|
534
|
+
}
|
|
535
|
+
if (rootRadius < baseRadius) polar(rootRadius, center + baseHalfAngle);
|
|
536
|
+
for (let step = 1; step < rootArcSteps; step++)
|
|
537
|
+
polar(
|
|
538
|
+
rootRadius,
|
|
539
|
+
center + rootHalfAngle + rootValleyAngle * step / rootArcSteps
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
const positions = [];
|
|
543
|
+
const indices = [];
|
|
544
|
+
const ring = (points, z) => {
|
|
545
|
+
const start = positions.length / 3;
|
|
546
|
+
for (const [x, y] of points) positions.push(x, y, z);
|
|
547
|
+
return start;
|
|
548
|
+
};
|
|
549
|
+
const circleSegments = Math.max(48, Math.min(256, props.toothCount * 4));
|
|
550
|
+
const circle2 = (radius) => Array.from({ length: circleSegments }, (_, segment) => {
|
|
551
|
+
const angle = phase + segment * Math.PI * 2 / circleSegments;
|
|
552
|
+
return [radius * Math.cos(angle), radius * Math.sin(angle)];
|
|
553
|
+
});
|
|
554
|
+
const connect = (bottom2, top2, count, inward = false) => {
|
|
555
|
+
for (let i = 0; i < count; i++) {
|
|
556
|
+
const next = (i + 1) % count;
|
|
557
|
+
if (inward)
|
|
558
|
+
indices.push(
|
|
559
|
+
bottom2 + i,
|
|
560
|
+
top2 + next,
|
|
561
|
+
bottom2 + next,
|
|
562
|
+
bottom2 + i,
|
|
563
|
+
top2 + i,
|
|
564
|
+
top2 + next
|
|
565
|
+
);
|
|
566
|
+
else
|
|
567
|
+
indices.push(
|
|
568
|
+
bottom2 + i,
|
|
569
|
+
bottom2 + next,
|
|
570
|
+
top2 + next,
|
|
571
|
+
bottom2 + i,
|
|
572
|
+
top2 + next,
|
|
573
|
+
top2 + i
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
const cap = (outer, outerStart, upward, inner) => {
|
|
578
|
+
const points = inner ? [...outer, ...inner.points] : outer;
|
|
579
|
+
const triangles = earcut2(
|
|
580
|
+
points.flatMap(([x, y]) => [x, y]),
|
|
581
|
+
inner ? [outer.length] : []
|
|
582
|
+
);
|
|
583
|
+
const vertex = (index) => index < outer.length ? outerStart + index : inner.start + index - outer.length;
|
|
584
|
+
for (let i = 0; i < triangles.length; i += 3) {
|
|
585
|
+
const a = triangles[i];
|
|
586
|
+
const b = triangles[i + 1];
|
|
587
|
+
const c = triangles[i + 2];
|
|
588
|
+
const pa = points[a];
|
|
589
|
+
const pb = points[b];
|
|
590
|
+
const pc = points[c];
|
|
591
|
+
const area = (pb[0] - pa[0]) * (pc[1] - pa[1]) - (pb[1] - pa[1]) * (pc[0] - pa[0]);
|
|
592
|
+
if (area > 0 === upward) indices.push(vertex(a), vertex(b), vertex(c));
|
|
593
|
+
else indices.push(vertex(a), vertex(c), vertex(b));
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
const bottom = ring(outline, 0);
|
|
597
|
+
const top = ring(outline, props.faceWidth);
|
|
598
|
+
connect(bottom, top, outline.length);
|
|
599
|
+
const hasHub = props.hubDiameter > 0 && props.hubLength > 0;
|
|
600
|
+
const height10 = props.faceWidth + (hasHub ? props.hubLength : 0);
|
|
601
|
+
const bore = props.boreDiameter > 0 ? circle2(props.boreDiameter / 2) : void 0;
|
|
602
|
+
const boreBottom = bore ? ring(bore, 0) : void 0;
|
|
603
|
+
const boreTop = bore ? ring(bore, height10) : void 0;
|
|
604
|
+
if (bore) connect(boreBottom, boreTop, bore.length, true);
|
|
605
|
+
cap(
|
|
606
|
+
outline,
|
|
607
|
+
bottom,
|
|
608
|
+
false,
|
|
609
|
+
bore ? { points: bore, start: boreBottom } : void 0
|
|
610
|
+
);
|
|
611
|
+
if (hasHub) {
|
|
612
|
+
const hub = circle2(props.hubDiameter / 2);
|
|
613
|
+
const hubBottom = ring(hub, props.faceWidth);
|
|
614
|
+
const hubTop = ring(hub, height10);
|
|
615
|
+
cap(outline, top, true, { points: hub, start: hubBottom });
|
|
616
|
+
connect(hubBottom, hubTop, hub.length);
|
|
617
|
+
if (bore) connect(hubTop, boreTop, hub.length);
|
|
618
|
+
else cap(hub, hubTop, true);
|
|
619
|
+
} else
|
|
620
|
+
cap(
|
|
621
|
+
outline,
|
|
622
|
+
top,
|
|
623
|
+
true,
|
|
624
|
+
bore ? { points: bore, start: boreTop } : void 0
|
|
625
|
+
);
|
|
626
|
+
return { positions, indices };
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// lib/SpurGear.tsx
|
|
630
|
+
function createSpurGearGeom(input) {
|
|
631
|
+
return indexedMeshToGeom3(createSpurGearMesh(input));
|
|
632
|
+
}
|
|
633
|
+
function SpurGear({ color = "#737e8f", ...props }) {
|
|
634
|
+
return /* @__PURE__ */ jsx(Colorize, { color, children: /* @__PURE__ */ jsx(Custom, { geometry: createSpurGearGeom(props) }) });
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// lib/mechanical/worm-gear-mesh.ts
|
|
638
|
+
import {
|
|
639
|
+
getWormGearDimensions,
|
|
640
|
+
wormGearModelPropsSchema
|
|
641
|
+
} from "@tscircuit/modelprinter";
|
|
642
|
+
import earcut3 from "earcut";
|
|
643
|
+
var createWormGearMesh = (input) => {
|
|
644
|
+
const props = wormGearModelPropsSchema.parse(input);
|
|
645
|
+
const { outsideDiameter, rootDiameter, axialPitch, lead, toothThickness } = getWormGearDimensions(props);
|
|
646
|
+
const outsideRadius = outsideDiameter / 2;
|
|
647
|
+
const rootRadius = rootDiameter / 2;
|
|
648
|
+
const pitchRadius = props.pitchDiameter / 2;
|
|
649
|
+
const pressureTangent = Math.tan(props.pressureAngle * Math.PI / 180);
|
|
650
|
+
const tipHalfWidth = toothThickness / 2 - props.module * pressureTangent;
|
|
651
|
+
const rootHalfWidth = toothThickness / 2 + (props.module + props.clearance) * pressureTangent;
|
|
652
|
+
const boreRadius = props.boreDiameter / 2;
|
|
653
|
+
const angularStepLimit = boreRadius > 0 ? Math.acos(boreRadius / rootRadius) * 0.8 : Infinity;
|
|
654
|
+
const samplesPerPitch = Math.max(
|
|
655
|
+
Math.ceil(props.radialSegments / props.starts),
|
|
656
|
+
Math.ceil(Math.PI * 2 / (props.starts * angularStepLimit))
|
|
657
|
+
);
|
|
658
|
+
const stepsPerTurn = Math.max(
|
|
659
|
+
props.segmentsPerTurn,
|
|
660
|
+
props.radialSegments,
|
|
661
|
+
props.starts * 12,
|
|
662
|
+
Math.ceil(Math.PI * 2 / angularStepLimit)
|
|
663
|
+
);
|
|
664
|
+
const steps = Math.max(1, Math.ceil(props.length / lead * stepsPerTurn));
|
|
665
|
+
const extraVertices = boreRadius > 0 ? props.radialSegments * 2 : 2;
|
|
666
|
+
if (!Number.isFinite(samplesPerPitch) || !Number.isFinite(steps) || (steps + 1) * (samplesPerPitch + 6) * props.starts + extraVertices > 1e6) {
|
|
667
|
+
throw new Error(
|
|
668
|
+
"Worm gear exceeds mesh resolution limit (1000000 vertices); reduce length or resolution, or increase module or bore wall thickness"
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
const corners = [
|
|
672
|
+
0,
|
|
673
|
+
tipHalfWidth / axialPitch,
|
|
674
|
+
toothThickness / (2 * axialPitch),
|
|
675
|
+
rootHalfWidth / axialPitch,
|
|
676
|
+
1 - rootHalfWidth / axialPitch,
|
|
677
|
+
1 - toothThickness / (2 * axialPitch),
|
|
678
|
+
1 - tipHalfWidth / axialPitch,
|
|
679
|
+
1
|
|
680
|
+
];
|
|
681
|
+
const fractions = [];
|
|
682
|
+
for (let index = 0; index < corners.length - 1; index++) {
|
|
683
|
+
const from = corners[index];
|
|
684
|
+
const width10 = corners[index + 1] - from;
|
|
685
|
+
const subdivisions = Math.max(1, Math.ceil(width10 * samplesPerPitch));
|
|
686
|
+
for (let subdivision = 0; subdivision < subdivisions; subdivision++) {
|
|
687
|
+
const fraction = from + width10 * subdivision / subdivisions;
|
|
688
|
+
if (fractions.length === 0 || fraction - fractions[fractions.length - 1] > 1e-12)
|
|
689
|
+
fractions.push(fraction);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
const profile = [];
|
|
693
|
+
for (let start = 0; start < props.starts; start++) {
|
|
694
|
+
for (const fraction of fractions) {
|
|
695
|
+
const distance2 = Math.min(fraction, 1 - fraction) * axialPitch;
|
|
696
|
+
profile.push({
|
|
697
|
+
angle: (start + fraction) * Math.PI * 2 / props.starts,
|
|
698
|
+
radius: distance2 <= tipHalfWidth ? outsideRadius : distance2 >= rootHalfWidth ? rootRadius : pitchRadius + (toothThickness / 2 - distance2) / pressureTangent
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
const count = profile.length;
|
|
703
|
+
const positions = [];
|
|
704
|
+
const indices = [];
|
|
705
|
+
const phase = props.phase % 360 * Math.PI / 180;
|
|
706
|
+
const direction = props.handedness === "right" ? 1 : -1;
|
|
707
|
+
const ring = (z) => {
|
|
708
|
+
const first = positions.length / 3;
|
|
709
|
+
const rotation = phase + direction * (z / lead) * Math.PI * 2;
|
|
710
|
+
for (const sample of profile) {
|
|
711
|
+
const angle = sample.angle + rotation;
|
|
712
|
+
positions.push(
|
|
713
|
+
sample.radius * Math.cos(angle),
|
|
714
|
+
sample.radius * Math.sin(angle),
|
|
715
|
+
z
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
return first;
|
|
719
|
+
};
|
|
720
|
+
const connect = (lower, upper) => {
|
|
721
|
+
for (let index = 0; index < count; index++) {
|
|
722
|
+
const next = (index + 1) % count;
|
|
723
|
+
indices.push(
|
|
724
|
+
lower + index,
|
|
725
|
+
lower + next,
|
|
726
|
+
upper + next,
|
|
727
|
+
lower + index,
|
|
728
|
+
upper + next,
|
|
729
|
+
upper + index
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
const bottom = ring(0);
|
|
734
|
+
let previous = bottom;
|
|
735
|
+
for (let step = 1; step <= steps; step++) {
|
|
736
|
+
const current = ring(props.length * (step / steps));
|
|
737
|
+
connect(previous, current);
|
|
738
|
+
previous = current;
|
|
739
|
+
}
|
|
740
|
+
const top = previous;
|
|
741
|
+
if (props.boreDiameter === 0) {
|
|
742
|
+
const cap = (boundary, z, upward) => {
|
|
743
|
+
const center = positions.length / 3;
|
|
744
|
+
positions.push(0, 0, z);
|
|
745
|
+
for (let index = 0; index < count; index++) {
|
|
746
|
+
const next = (index + 1) % count;
|
|
747
|
+
indices.push(
|
|
748
|
+
center,
|
|
749
|
+
boundary + (upward ? index : next),
|
|
750
|
+
boundary + (upward ? next : index)
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
cap(bottom, 0, false);
|
|
755
|
+
cap(top, props.length, true);
|
|
756
|
+
} else {
|
|
757
|
+
const holeCount = props.radialSegments;
|
|
758
|
+
const boreRing = (z) => {
|
|
759
|
+
const first = positions.length / 3;
|
|
760
|
+
for (let index = 0; index < holeCount; index++) {
|
|
761
|
+
const angle = phase + index * Math.PI * 2 / holeCount;
|
|
762
|
+
positions.push(
|
|
763
|
+
boreRadius * Math.cos(angle),
|
|
764
|
+
boreRadius * Math.sin(angle),
|
|
765
|
+
z
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
return first;
|
|
769
|
+
};
|
|
770
|
+
const holeBottom = boreRing(0);
|
|
771
|
+
const holeTop = boreRing(props.length);
|
|
772
|
+
for (let index = 0; index < holeCount; index++) {
|
|
773
|
+
const next = (index + 1) % holeCount;
|
|
774
|
+
indices.push(
|
|
775
|
+
holeBottom + index,
|
|
776
|
+
holeTop + next,
|
|
777
|
+
holeBottom + next,
|
|
778
|
+
holeBottom + index,
|
|
779
|
+
holeTop + index,
|
|
780
|
+
holeTop + next
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
const cap = (boundary, hole, upward) => {
|
|
784
|
+
const vertices = Array.from(
|
|
785
|
+
{ length: count },
|
|
786
|
+
(_, index) => boundary + index
|
|
787
|
+
);
|
|
788
|
+
vertices.push(
|
|
789
|
+
...Array.from({ length: holeCount }, (_, index) => hole + index)
|
|
790
|
+
);
|
|
791
|
+
const flat = vertices.flatMap((vertex) => [
|
|
792
|
+
positions[vertex * 3],
|
|
793
|
+
positions[vertex * 3 + 1]
|
|
794
|
+
]);
|
|
795
|
+
const triangles = earcut3(flat, [count], 2);
|
|
796
|
+
for (let index = 0; index < triangles.length; index += 3) {
|
|
797
|
+
const a = vertices[triangles[index]];
|
|
798
|
+
const b = vertices[triangles[index + 1]];
|
|
799
|
+
const c = vertices[triangles[index + 2]];
|
|
800
|
+
const signedArea = (positions[b * 3] - positions[a * 3]) * (positions[c * 3 + 1] - positions[a * 3 + 1]) - (positions[b * 3 + 1] - positions[a * 3 + 1]) * (positions[c * 3] - positions[a * 3]);
|
|
801
|
+
indices.push(
|
|
802
|
+
a,
|
|
803
|
+
signedArea > 0 === upward ? b : c,
|
|
804
|
+
signedArea > 0 === upward ? c : b
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
cap(bottom, holeBottom, false);
|
|
809
|
+
cap(top, holeTop, true);
|
|
810
|
+
}
|
|
811
|
+
return { positions, indices };
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
// lib/WormGear.tsx
|
|
815
|
+
function createWormGearGeom(input) {
|
|
816
|
+
return indexedMeshToGeom3(createWormGearMesh(input));
|
|
817
|
+
}
|
|
818
|
+
function WormGear({ color = "#737e8f", ...props }) {
|
|
819
|
+
return /* @__PURE__ */ jsx(Colorize, { color, children: /* @__PURE__ */ jsx(Custom, { geometry: createWormGearGeom(props) }) });
|
|
820
|
+
}
|
|
821
|
+
|
|
474
822
|
// lib/NemaMotor.tsx
|
|
823
|
+
import jscad3 from "@jscad/modeling";
|
|
824
|
+
|
|
825
|
+
// lib/utils/nemaMotorWireGeometry.ts
|
|
475
826
|
import jscad2 from "@jscad/modeling";
|
|
827
|
+
import { getNemaMotorReferencePoints } from "@tscircuit/modelprinter";
|
|
476
828
|
|
|
477
829
|
// lib/utils/nemaMotorParameters.ts
|
|
478
830
|
import { mp as mp2, nemaMotorModelPropsSchema } from "@tscircuit/modelprinter";
|
|
@@ -480,6 +832,66 @@ var resolveNemaMotorProps = nemaMotorModelPropsSchema.parse.bind(
|
|
|
480
832
|
nemaMotorModelPropsSchema
|
|
481
833
|
);
|
|
482
834
|
|
|
835
|
+
// lib/utils/nemaMotorWireGeometry.ts
|
|
836
|
+
function createNemaMotorWireGeometry(input) {
|
|
837
|
+
const p = resolveNemaMotorProps(input);
|
|
838
|
+
if (p.wireConnection === "none") return [];
|
|
839
|
+
const { position } = getNemaMotorReferencePoints(p).wireside;
|
|
840
|
+
const angle = p.wireSideAngle * Math.PI / 180;
|
|
841
|
+
const place = (geometry) => jscad2.transforms.translate(
|
|
842
|
+
[position.x, position.y, position.z],
|
|
843
|
+
jscad2.transforms.rotateZ(angle, geometry)
|
|
844
|
+
);
|
|
845
|
+
if (p.wireConnection === "stubs") {
|
|
846
|
+
const colors = [
|
|
847
|
+
"#d53f3f",
|
|
848
|
+
"#2463af",
|
|
849
|
+
"#3b934b",
|
|
850
|
+
"#252830",
|
|
851
|
+
"#e5d049",
|
|
852
|
+
"#eeeeee",
|
|
853
|
+
"#ce7432",
|
|
854
|
+
"#803e91"
|
|
855
|
+
];
|
|
856
|
+
return Array.from({ length: p.wireCount }, (_, i) => ({
|
|
857
|
+
color: colors[i],
|
|
858
|
+
geometry: place(
|
|
859
|
+
jscad2.transforms.translate(
|
|
860
|
+
[
|
|
861
|
+
(p.wireLength - 0.15) / 2,
|
|
862
|
+
(i - (p.wireCount - 1) / 2) * p.wireDiameter * 1.5,
|
|
863
|
+
0
|
|
864
|
+
],
|
|
865
|
+
jscad2.transforms.rotateY(
|
|
866
|
+
Math.PI / 2,
|
|
867
|
+
jscad2.primitives.cylinder({
|
|
868
|
+
height: p.wireLength + 0.15,
|
|
869
|
+
radius: p.wireDiameter / 2,
|
|
870
|
+
segments: 24
|
|
871
|
+
})
|
|
872
|
+
)
|
|
873
|
+
)
|
|
874
|
+
)
|
|
875
|
+
}));
|
|
876
|
+
}
|
|
877
|
+
const shell = jscad2.booleans.subtract(
|
|
878
|
+
jscad2.primitives.cuboid({ size: [6.1, 13.9, 4.5], center: [2.95, 0, 0] }),
|
|
879
|
+
jscad2.primitives.cuboid({ size: [5.5, 12.7, 3.3], center: [3.55, 0, 0] })
|
|
880
|
+
);
|
|
881
|
+
return [
|
|
882
|
+
{ color: "#eeeeea", geometry: place(shell) },
|
|
883
|
+
...Array.from({ length: 6 }, (_, i) => ({
|
|
884
|
+
color: "#b8bec6",
|
|
885
|
+
geometry: place(
|
|
886
|
+
jscad2.primitives.cuboid({
|
|
887
|
+
size: [3.4, 0.5, 0.5],
|
|
888
|
+
center: [1.7, (i - 2.5) * 2, 0]
|
|
889
|
+
})
|
|
890
|
+
)
|
|
891
|
+
}))
|
|
892
|
+
];
|
|
893
|
+
}
|
|
894
|
+
|
|
483
895
|
// lib/utils/nemaMotorGeometry.ts
|
|
484
896
|
var circle = (radius, x = 0, y = 0) => Array.from({ length: 96 }, (_, i) => [
|
|
485
897
|
x + radius * Math.cos(i * Math.PI / 48),
|
|
@@ -579,6 +991,7 @@ function createNemaMotorSections(input) {
|
|
|
579
991
|
}
|
|
580
992
|
|
|
581
993
|
// lib/NemaMotor.tsx
|
|
994
|
+
var generalize = jscad3.modifiers.generalize;
|
|
582
995
|
function NemaMotor({
|
|
583
996
|
bodyColor = "#242830",
|
|
584
997
|
capColor = "#aeb5bd",
|
|
@@ -587,7 +1000,7 @@ function NemaMotor({
|
|
|
587
1000
|
...props
|
|
588
1001
|
}) {
|
|
589
1002
|
const p = resolveNemaMotorProps(props);
|
|
590
|
-
const screw = p.backFace === "screws" ?
|
|
1003
|
+
const screw = p.backFace === "screws" ? jscad3.transforms.rotateX(
|
|
591
1004
|
Math.PI,
|
|
592
1005
|
createHexSocketBoltGeom({
|
|
593
1006
|
metricSize: p.backFaceScrewSize,
|
|
@@ -598,14 +1011,14 @@ function NemaMotor({
|
|
|
598
1011
|
const halfPitch = p.backFaceHoleSpacing / 2;
|
|
599
1012
|
return /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
600
1013
|
createNemaMotorSections(p).map((section, index) => {
|
|
601
|
-
const outline =
|
|
602
|
-
const profile = section.holes.length ?
|
|
1014
|
+
const outline = jscad3.geometries.geom2.fromPoints(section.outline);
|
|
1015
|
+
const profile = section.holes.length ? jscad3.booleans.subtract(
|
|
603
1016
|
outline,
|
|
604
|
-
...section.holes.map((h2) =>
|
|
1017
|
+
...section.holes.map((h2) => jscad3.geometries.geom2.fromPoints(h2))
|
|
605
1018
|
) : outline;
|
|
606
|
-
const geometry =
|
|
1019
|
+
const geometry = jscad3.transforms.translate(
|
|
607
1020
|
[0, 0, section.zMin],
|
|
608
|
-
|
|
1021
|
+
jscad3.extrusions.extrudeLinear(
|
|
609
1022
|
{ height: section.zMax - section.zMin },
|
|
610
1023
|
profile
|
|
611
1024
|
)
|
|
@@ -614,18 +1027,19 @@ function NemaMotor({
|
|
|
614
1027
|
Colorize,
|
|
615
1028
|
{
|
|
616
1029
|
color: section.name === "body" ? bodyColor : section.name === "shaft" ? shaftColor : capColor,
|
|
617
|
-
children: /* @__PURE__ */ jsx(Custom, { geometry })
|
|
1030
|
+
children: /* @__PURE__ */ jsx(Custom, { geometry: generalize({ simplify: true }, geometry) })
|
|
618
1031
|
},
|
|
619
1032
|
`${section.name}-${index}`
|
|
620
1033
|
);
|
|
621
1034
|
}),
|
|
1035
|
+
createNemaMotorWireGeometry(p).map(({ geometry, color }, index) => /* @__PURE__ */ jsx(Colorize, { color, children: /* @__PURE__ */ jsx(Custom, { geometry: generalize({ simplify: true }, geometry) }) }, `wire-${index}`)),
|
|
622
1036
|
screw && [-halfPitch, halfPitch].flatMap(
|
|
623
1037
|
(x) => [-halfPitch, halfPitch].map((y) => /* @__PURE__ */ jsx(Colorize, { color: screwColor, children: /* @__PURE__ */ jsx(
|
|
624
1038
|
Custom,
|
|
625
1039
|
{
|
|
626
|
-
geometry:
|
|
1040
|
+
geometry: jscad3.transforms.translate(
|
|
627
1041
|
[x, y, -p.bodyLength],
|
|
628
|
-
screw
|
|
1042
|
+
generalize({ simplify: true }, screw)
|
|
629
1043
|
)
|
|
630
1044
|
}
|
|
631
1045
|
) }, `rear-screw-${x}-${y}`))
|
|
@@ -8654,6 +9068,14 @@ var Footprinter3d = ({ footprint }) => {
|
|
|
8654
9068
|
const { fn, ...props } = model;
|
|
8655
9069
|
return /* @__PURE__ */ jsx(SheetMetal, { ...props });
|
|
8656
9070
|
}
|
|
9071
|
+
case "spurgear": {
|
|
9072
|
+
const { fn, ...props } = model;
|
|
9073
|
+
return /* @__PURE__ */ jsx(SpurGear, { ...props });
|
|
9074
|
+
}
|
|
9075
|
+
case "wormgear": {
|
|
9076
|
+
const { fn, ...props } = model;
|
|
9077
|
+
return /* @__PURE__ */ jsx(WormGear, { ...props });
|
|
9078
|
+
}
|
|
8657
9079
|
case "flexscreen": {
|
|
8658
9080
|
const { fn: _, ...flexScreenProps } = model;
|
|
8659
9081
|
return /* @__PURE__ */ jsx(FlexScreen, { ...flexScreenProps });
|
|
@@ -9460,8 +9882,8 @@ function renderNode(node, colorCtx, renderCtx) {
|
|
|
9460
9882
|
}
|
|
9461
9883
|
return (children ?? []).flatMap((c) => renderNode(c, colorCtx, renderCtx));
|
|
9462
9884
|
}
|
|
9463
|
-
function render(root,
|
|
9464
|
-
const geometries = renderNode(root, void 0, { jscad:
|
|
9885
|
+
function render(root, jscad4) {
|
|
9886
|
+
const geometries = renderNode(root, void 0, { jscad: jscad4 });
|
|
9465
9887
|
return { geometries };
|
|
9466
9888
|
}
|
|
9467
9889
|
|
|
@@ -9549,24 +9971,24 @@ function convertCSGToThreeGeom(csg) {
|
|
|
9549
9971
|
}
|
|
9550
9972
|
|
|
9551
9973
|
// lib/vanilla/index.ts
|
|
9552
|
-
function getJscadModelForFootprint(footprint,
|
|
9974
|
+
function getJscadModelForFootprint(footprint, jscad4) {
|
|
9553
9975
|
const vnode = h(Footprinter3d, { footprint });
|
|
9554
|
-
return render(vnode,
|
|
9976
|
+
return render(vnode, jscad4);
|
|
9555
9977
|
}
|
|
9556
|
-
function getJscadModelForFootprintWithPads(footprint,
|
|
9978
|
+
function getJscadModelForFootprintWithPads(footprint, jscad4) {
|
|
9557
9979
|
const vnode = h(
|
|
9558
9980
|
Fragment,
|
|
9559
9981
|
{},
|
|
9560
9982
|
h(Footprinter3d, { footprint }),
|
|
9561
9983
|
h(ExtrudedPads, { footprint })
|
|
9562
9984
|
);
|
|
9563
|
-
return render(vnode,
|
|
9985
|
+
return render(vnode, jscad4);
|
|
9564
9986
|
}
|
|
9565
|
-
function createJSCADRenderer(
|
|
9987
|
+
function createJSCADRenderer(jscad4) {
|
|
9566
9988
|
function createJSCADRoot(container) {
|
|
9567
9989
|
return {
|
|
9568
9990
|
render(element) {
|
|
9569
|
-
const { geometries } = render(element,
|
|
9991
|
+
const { geometries } = render(element, jscad4);
|
|
9570
9992
|
container.splice(0, container.length, ...geometries);
|
|
9571
9993
|
}
|
|
9572
9994
|
};
|
|
@@ -9581,12 +10003,18 @@ export {
|
|
|
9581
10003
|
NEMA8,
|
|
9582
10004
|
NemaMotor,
|
|
9583
10005
|
SheetMetal,
|
|
10006
|
+
SpurGear,
|
|
10007
|
+
WormGear,
|
|
9584
10008
|
convertCSGToThreeGeom,
|
|
9585
10009
|
createHexSocketBoltGeom,
|
|
9586
10010
|
createHexSocketBoltMesh,
|
|
9587
10011
|
createJSCADRenderer,
|
|
9588
10012
|
createSheetMetalGeom,
|
|
9589
10013
|
createSheetMetalMesh,
|
|
10014
|
+
createSpurGearGeom,
|
|
10015
|
+
createSpurGearMesh,
|
|
10016
|
+
createWormGearGeom,
|
|
10017
|
+
createWormGearMesh,
|
|
9590
10018
|
getJscadModelForFootprint,
|
|
9591
10019
|
getJscadModelForFootprintWithPads,
|
|
9592
10020
|
h
|