jscad-electronics 0.0.184 → 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 +1191 -824
- package/dist/index.js.map +1 -1
- package/dist/vanilla.d.ts +44 -2
- package/dist/vanilla.js +360 -1
- 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();
|
|
@@ -474,6 +474,351 @@ function SheetMetal({ color = "#737e8f", ...props }) {
|
|
|
474
474
|
return /* @__PURE__ */ jsx(Colorize, { color, children: /* @__PURE__ */ jsx(Custom, { geometry: createSheetMetalGeom(props) }) });
|
|
475
475
|
}
|
|
476
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
|
+
|
|
477
822
|
// lib/NemaMotor.tsx
|
|
478
823
|
import jscad3 from "@jscad/modeling";
|
|
479
824
|
|
|
@@ -8723,6 +9068,14 @@ var Footprinter3d = ({ footprint }) => {
|
|
|
8723
9068
|
const { fn, ...props } = model;
|
|
8724
9069
|
return /* @__PURE__ */ jsx(SheetMetal, { ...props });
|
|
8725
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
|
+
}
|
|
8726
9079
|
case "flexscreen": {
|
|
8727
9080
|
const { fn: _, ...flexScreenProps } = model;
|
|
8728
9081
|
return /* @__PURE__ */ jsx(FlexScreen, { ...flexScreenProps });
|
|
@@ -9650,12 +10003,18 @@ export {
|
|
|
9650
10003
|
NEMA8,
|
|
9651
10004
|
NemaMotor,
|
|
9652
10005
|
SheetMetal,
|
|
10006
|
+
SpurGear,
|
|
10007
|
+
WormGear,
|
|
9653
10008
|
convertCSGToThreeGeom,
|
|
9654
10009
|
createHexSocketBoltGeom,
|
|
9655
10010
|
createHexSocketBoltMesh,
|
|
9656
10011
|
createJSCADRenderer,
|
|
9657
10012
|
createSheetMetalGeom,
|
|
9658
10013
|
createSheetMetalMesh,
|
|
10014
|
+
createSpurGearGeom,
|
|
10015
|
+
createSpurGearMesh,
|
|
10016
|
+
createWormGearGeom,
|
|
10017
|
+
createWormGearMesh,
|
|
9659
10018
|
getJscadModelForFootprint,
|
|
9660
10019
|
getJscadModelForFootprintWithPads,
|
|
9661
10020
|
h
|