jscad-electronics 0.0.201 → 0.0.203

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.js CHANGED
@@ -183,24 +183,24 @@ var FootprintPlatedHole = ({
183
183
  import { buttonScrewModelDefinitionSchema } from "@tscircuit/modelprinter";
184
184
 
185
185
  // lib/model-registry.ts
186
- function defineModelRenderer(model16) {
187
- return model16;
186
+ function defineModelRenderer(model18) {
187
+ return model18;
188
188
  }
189
189
  var ModelRendererRegistry = class {
190
190
  models = /* @__PURE__ */ new Map();
191
- register(model16) {
192
- if (typeof model16.name !== "string" || !/^[a-z]+$/.test(model16.name)) {
191
+ register(model18) {
192
+ if (typeof model18.name !== "string" || !/^[a-z]+$/.test(model18.name)) {
193
193
  throw new Error(
194
194
  "Model renderer names must contain only lowercase letters"
195
195
  );
196
196
  }
197
- if (this.models.has(model16.name)) {
198
- throw new Error(`Model renderer "${model16.name}" is already registered`);
197
+ if (this.models.has(model18.name)) {
198
+ throw new Error(`Model renderer "${model18.name}" is already registered`);
199
199
  }
200
- if (model16.pads !== "none" && model16.pads !== "footprinter") {
200
+ if (model18.pads !== "none" && model18.pads !== "footprinter") {
201
201
  throw new Error('Model renderer pads must be "none" or "footprinter"');
202
202
  }
203
- const { name, schema, render: render2, pads } = model16;
203
+ const { name, schema, render: render2, pads } = model18;
204
204
  this.models.set(name, {
205
205
  name,
206
206
  schema,
@@ -454,6 +454,219 @@ function register2(registry) {
454
454
  registry.register(model2);
455
455
  }
456
456
 
457
+ // lib/models/compressionspring/register.tsx
458
+ import { compressionSpringModelDefinitionSchema } from "@tscircuit/modelprinter";
459
+
460
+ // lib/models/compressionspring/mesh.ts
461
+ import {
462
+ compressionSpringModelPropsSchema,
463
+ getCompressionSpringCenterlinePoint,
464
+ getCompressionSpringDimensions
465
+ } from "@tscircuit/modelprinter";
466
+
467
+ // lib/mechanical/clip-mesh-to-z-range.ts
468
+ import earcut from "earcut";
469
+ function clipMeshToZRange(mesh, lower, upper) {
470
+ const originals = [];
471
+ const tolerance = 32 * Number.EPSILON * (upper - lower);
472
+ for (let index = 0; index < mesh.positions.length / 3; index++)
473
+ originals.push({
474
+ key: `v${index}`,
475
+ coordinates: [
476
+ mesh.positions[index * 3],
477
+ mesh.positions[index * 3 + 1],
478
+ Math.abs(mesh.positions[index * 3 + 2] - lower) <= tolerance ? lower : Math.abs(mesh.positions[index * 3 + 2] - upper) <= tolerance ? upper : mesh.positions[index * 3 + 2]
479
+ ]
480
+ });
481
+ const intersections = /* @__PURE__ */ new Map();
482
+ const crossing = (a, b, plane) => {
483
+ if (a.coordinates[2] === plane) return a;
484
+ if (b.coordinates[2] === plane) return b;
485
+ const [first, second] = a.key < b.key ? [a, b] : [b, a];
486
+ const key = `${plane}:${first.key}:${second.key}`;
487
+ let point = intersections.get(key);
488
+ if (!point) {
489
+ const fraction = (plane - first.coordinates[2]) / (second.coordinates[2] - first.coordinates[2]);
490
+ point = {
491
+ key,
492
+ coordinates: [
493
+ first.coordinates[0] + fraction * (second.coordinates[0] - first.coordinates[0]),
494
+ first.coordinates[1] + fraction * (second.coordinates[1] - first.coordinates[1]),
495
+ plane
496
+ ]
497
+ };
498
+ intersections.set(key, point);
499
+ }
500
+ return point;
501
+ };
502
+ const clip = (polygon, plane, keepAbove) => {
503
+ const result = [];
504
+ const inside = (point) => keepAbove ? point.coordinates[2] >= plane : point.coordinates[2] <= plane;
505
+ for (let index = 0; index < polygon.length; index++) {
506
+ const a = polygon[index];
507
+ const b = polygon[(index + 1) % polygon.length];
508
+ if (inside(a)) result.push(a);
509
+ if (inside(a) !== inside(b)) result.push(crossing(a, b, plane));
510
+ }
511
+ return result.filter(
512
+ (point, index) => point.key !== result[(index + result.length - 1) % result.length]?.key
513
+ );
514
+ };
515
+ const positions = [];
516
+ const indices = [];
517
+ const pointIndices = /* @__PURE__ */ new Map();
518
+ const vertex = (point) => {
519
+ let index = pointIndices.get(point.key);
520
+ if (index === void 0) {
521
+ index = positions.length / 3;
522
+ positions.push(...point.coordinates);
523
+ pointIndices.set(point.key, index);
524
+ }
525
+ return index;
526
+ };
527
+ for (let index = 0; index < mesh.indices.length; index += 3) {
528
+ const triangle = mesh.indices.slice(index, index + 3).map((i) => originals[i]);
529
+ const polygon = clip(clip(triangle, lower, true), upper, false);
530
+ for (let corner = 1; corner + 1 < polygon.length; corner++) {
531
+ const points = [polygon[0], polygon[corner], polygon[corner + 1]];
532
+ if (new Set(points.map((point) => point.key)).size === 3)
533
+ indices.push(...points.map(vertex));
534
+ }
535
+ }
536
+ const edges = /* @__PURE__ */ new Map();
537
+ for (let index = 0; index < indices.length; index += 3)
538
+ for (let side = 0; side < 3; side++) {
539
+ const from = indices[index + side];
540
+ const to = indices[index + (side + 1) % 3];
541
+ const key = `${Math.min(from, to)},${Math.max(from, to)}`;
542
+ const prior = edges.get(key);
543
+ if (prior) prior.count++;
544
+ else edges.set(key, { from, to, count: 1 });
545
+ }
546
+ const boundary = [...edges.values()].filter((edge) => edge.count === 1);
547
+ const outgoing = new Map(boundary.map(({ from, to }) => [from, to]));
548
+ if (outgoing.size !== boundary.length)
549
+ throw new Error("Ground clipping produced an ambiguous boundary");
550
+ while (outgoing.size > 0) {
551
+ const start = outgoing.keys().next().value;
552
+ const loop = [];
553
+ let current = start;
554
+ do {
555
+ loop.push(current);
556
+ const next = outgoing.get(current);
557
+ if (next === void 0)
558
+ throw new Error("Ground clipping produced an open boundary");
559
+ outgoing.delete(current);
560
+ current = next;
561
+ } while (current !== start);
562
+ const plane = positions[3 * start + 2];
563
+ if (plane !== lower && plane !== upper || loop.some((i) => positions[3 * i + 2] !== plane))
564
+ throw new Error("Ground clipping boundary must lie on an end plane");
565
+ const coordinates = loop.flatMap((i) => [
566
+ positions[3 * i],
567
+ positions[3 * i + 1]
568
+ ]);
569
+ const cap = earcut(coordinates);
570
+ for (let index = 0; index < cap.length; index += 3) {
571
+ const a = loop[cap[index]];
572
+ let b = loop[cap[index + 1]];
573
+ let c = loop[cap[index + 2]];
574
+ const cross = (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]);
575
+ if (plane === lower && cross > 0 || plane === upper && cross < 0)
576
+ [b, c] = [c, b];
577
+ indices.push(a, b, c);
578
+ }
579
+ }
580
+ return { positions, indices };
581
+ }
582
+
583
+ // lib/models/compressionspring/mesh.ts
584
+ function createCompressionSpringMesh(input, options = {}) {
585
+ const props = compressionSpringModelPropsSchema.parse(input);
586
+ const dimensions = getCompressionSpringDimensions(props);
587
+ const segmentsPerTurn = options.segmentsPerTurn ?? 64;
588
+ const wireSegments = options.wireSegments ?? 24;
589
+ for (const [name, value, minimum, maximum] of [
590
+ ["segmentsPerTurn", segmentsPerTurn, 16, 256],
591
+ ["wireSegments", wireSegments, 8, 64]
592
+ ])
593
+ if (!Number.isInteger(value) || value < minimum || value > maximum || value % 4 !== 0)
594
+ throw new Error(
595
+ `${name} must be a multiple of four in [${minimum},${maximum}]`
596
+ );
597
+ const steps = props.totalTurns * segmentsPerTurn;
598
+ if (!Number.isSafeInteger(steps) || (steps + 1) * wireSegments > 25e4)
599
+ throw new Error("Spring exceeds mesh resolution limit (250000 vertices)");
600
+ const scale = Math.max(props.outerDiameter, props.freeLength);
601
+ if (dimensions.activePitch - props.wireDiameter <= scale * 1e-10 || props.wireDiameter <= scale * 1e-10 || dimensions.insideDiameter <= scale * 1e-10)
602
+ throw new Error("Spring clearance exceeds mesh resolution limit");
603
+ const positions = [];
604
+ const indices = [];
605
+ const wireRadius = props.wireDiameter / 2;
606
+ for (let step = 0; step <= steps; step++) {
607
+ const center = getCompressionSpringCenterlinePoint(
608
+ props,
609
+ step / segmentsPerTurn
610
+ );
611
+ const meanRadius = Math.hypot(center.x, center.y);
612
+ for (let sample = 0; sample < wireSegments; sample++) {
613
+ const angle = sample * 2 * Math.PI / wireSegments;
614
+ const cosine = sample === wireSegments / 4 || sample === 3 * wireSegments / 4 ? 0 : Math.cos(angle);
615
+ const sine = sample === 0 || sample === wireSegments / 2 ? 0 : Math.sin(angle);
616
+ const radialOffset = wireRadius * cosine;
617
+ positions.push(
618
+ center.x + radialOffset * center.x / meanRadius,
619
+ center.y + radialOffset * center.y / meanRadius,
620
+ center.z + wireRadius * sine
621
+ );
622
+ }
623
+ }
624
+ const triangle = (a, b, c) => {
625
+ if (props.hand === "left") indices.push(a, c, b);
626
+ else indices.push(a, b, c);
627
+ };
628
+ for (let step = 0; step < steps; step++)
629
+ for (let sample = 0; sample < wireSegments; sample++) {
630
+ const next = (sample + 1) % wireSegments;
631
+ const a = step * wireSegments;
632
+ const b = (step + 1) * wireSegments;
633
+ triangle(a + sample, b + sample, b + next);
634
+ triangle(a + sample, b + next, a + next);
635
+ }
636
+ for (let corner = 1; corner + 1 < wireSegments; corner++) {
637
+ triangle(0, corner, corner + 1);
638
+ const end = steps * wireSegments;
639
+ triangle(end, end + corner + 1, end + corner);
640
+ }
641
+ return clipMeshToZRange(
642
+ { positions, indices },
643
+ dimensions.lowerBearingZ,
644
+ dimensions.upperBearingZ
645
+ );
646
+ }
647
+
648
+ // lib/models/compressionspring/CompressionSpring.tsx
649
+ function createCompressionSpringGeom(input) {
650
+ return indexedMeshToGeom3(createCompressionSpringMesh(input));
651
+ }
652
+ function CompressionSpring({
653
+ color = "#737e8f",
654
+ ...props
655
+ }) {
656
+ return /* @__PURE__ */ jsx(Colorize, { color, children: /* @__PURE__ */ jsx(Custom, { geometry: createCompressionSpringGeom(props) }) });
657
+ }
658
+
659
+ // lib/models/compressionspring/register.tsx
660
+ var model3 = defineModelRenderer({
661
+ name: "compressionspring",
662
+ schema: compressionSpringModelDefinitionSchema,
663
+ render: ({ fn, ...props }) => /* @__PURE__ */ jsx(CompressionSpring, { ...props }),
664
+ pads: "none"
665
+ });
666
+ function register3(registry) {
667
+ registry.register(model3);
668
+ }
669
+
457
670
  // lib/models/flangedbushing/register.tsx
458
671
  import { flangedBushingModelDefinitionSchema } from "@tscircuit/modelprinter";
459
672
 
@@ -507,14 +720,14 @@ function FlangedBushing({
507
720
  }
508
721
 
509
722
  // lib/models/flangedbushing/register.tsx
510
- var model3 = defineModelRenderer({
723
+ var model4 = defineModelRenderer({
511
724
  name: "flangedbushing",
512
725
  schema: flangedBushingModelDefinitionSchema,
513
726
  render: ({ fn, ...props }) => /* @__PURE__ */ jsx(FlangedBushing, { ...props }),
514
727
  pads: "none"
515
728
  });
516
- function register3(registry) {
517
- registry.register(model3);
729
+ function register4(registry) {
730
+ registry.register(model4);
518
731
  }
519
732
 
520
733
  // lib/models/flatheadscrew/register.tsx
@@ -647,14 +860,14 @@ function FlatHeadScrew({
647
860
  }
648
861
 
649
862
  // lib/models/flatheadscrew/register.tsx
650
- var model4 = defineModelRenderer({
863
+ var model5 = defineModelRenderer({
651
864
  name: "flatheadscrew",
652
865
  schema: flatHeadScrewModelDefinitionSchema,
653
866
  render: ({ fn, ...props }) => /* @__PURE__ */ jsx(FlatHeadScrew, { ...props }),
654
867
  pads: "none"
655
868
  });
656
- function register4(registry) {
657
- registry.register(model4);
869
+ function register5(registry) {
870
+ registry.register(model5);
658
871
  }
659
872
 
660
873
  // lib/models/flexscreen/register.tsx
@@ -1332,14 +1545,14 @@ var FlexScreen = (props) => {
1332
1545
  };
1333
1546
 
1334
1547
  // lib/models/flexscreen/register.tsx
1335
- var model5 = defineModelRenderer({
1548
+ var model6 = defineModelRenderer({
1336
1549
  name: "flexscreen",
1337
1550
  schema: flexScreenModelDefinitionSchema,
1338
1551
  render: ({ fn, ...props }) => /* @__PURE__ */ jsx(FlexScreen, { ...props }),
1339
1552
  pads: "footprinter"
1340
1553
  });
1341
- function register5(registry) {
1342
- registry.register(model5);
1554
+ function register6(registry) {
1555
+ registry.register(model6);
1343
1556
  }
1344
1557
 
1345
1558
  // lib/models/helicalgear/register.tsx
@@ -1356,7 +1569,7 @@ import {
1356
1569
  getSpurGearDimensions,
1357
1570
  spurGearModelPropsSchema
1358
1571
  } from "@tscircuit/modelprinter";
1359
- import earcut from "earcut";
1572
+ import earcut2 from "earcut";
1360
1573
  function createSpurGearMesh(input) {
1361
1574
  return createTwistedSpurGearMesh(input);
1362
1575
  }
@@ -1462,7 +1675,7 @@ function createTwistedSpurGearMesh(input, twist = 0, segmentsPerTurn = 32) {
1462
1675
  };
1463
1676
  const cap = (outer, outerStart, upward, inner) => {
1464
1677
  const points = inner ? [...outer, ...inner.points] : outer;
1465
- const triangles = earcut(
1678
+ const triangles = earcut2(
1466
1679
  points.flatMap(([x, y]) => [x, y]),
1467
1680
  inner ? [outer.length] : []
1468
1681
  );
@@ -1545,14 +1758,14 @@ function HelicalGear({ color = "#737e8f", ...props }) {
1545
1758
  }
1546
1759
 
1547
1760
  // lib/models/helicalgear/register.tsx
1548
- var model6 = defineModelRenderer({
1761
+ var model7 = defineModelRenderer({
1549
1762
  name: "helicalgear",
1550
1763
  schema: helicalGearModelDefinitionSchema,
1551
1764
  render: ({ fn, ...props }) => /* @__PURE__ */ jsx(HelicalGear, { ...props }),
1552
1765
  pads: "none"
1553
1766
  });
1554
- function register6(registry) {
1555
- registry.register(model6);
1767
+ function register7(registry) {
1768
+ registry.register(model7);
1556
1769
  }
1557
1770
 
1558
1771
  // lib/models/hexbolt/register.tsx
@@ -1693,14 +1906,14 @@ function HexBolt({ color = "#737e8f", ...props }) {
1693
1906
  }
1694
1907
 
1695
1908
  // lib/models/hexbolt/register.tsx
1696
- var model7 = defineModelRenderer({
1909
+ var model8 = defineModelRenderer({
1697
1910
  name: "hexbolt",
1698
1911
  schema: hexBoltModelDefinitionSchema,
1699
1912
  render: ({ fn, ...props }) => /* @__PURE__ */ jsx(HexBolt, { ...props }),
1700
1913
  pads: "none"
1701
1914
  });
1702
- function register7(registry) {
1703
- registry.register(model7);
1915
+ function register8(registry) {
1916
+ registry.register(model8);
1704
1917
  }
1705
1918
 
1706
1919
  // lib/models/hexnut/register.tsx
@@ -1823,14 +2036,14 @@ function HexNut({ color = "#737e8f", ...props }) {
1823
2036
  }
1824
2037
 
1825
2038
  // lib/models/hexnut/register.tsx
1826
- var model8 = defineModelRenderer({
2039
+ var model9 = defineModelRenderer({
1827
2040
  name: "hexnut",
1828
2041
  schema: hexNutModelDefinitionSchema,
1829
2042
  render: ({ fn, ...props }) => /* @__PURE__ */ jsx(HexNut, { ...props }),
1830
2043
  pads: "none"
1831
2044
  });
1832
- function register8(registry) {
1833
- registry.register(model8);
2045
+ function register9(registry) {
2046
+ registry.register(model9);
1834
2047
  }
1835
2048
 
1836
2049
  // lib/models/hexsocketbolt/register.tsx
@@ -1942,14 +2155,14 @@ function HexSocketBolt({
1942
2155
  }
1943
2156
 
1944
2157
  // lib/models/hexsocketbolt/register.tsx
1945
- var model9 = defineModelRenderer({
2158
+ var model10 = defineModelRenderer({
1946
2159
  name: "hexsocketbolt",
1947
2160
  schema: hexSocketBoltModelDefinitionSchema,
1948
2161
  render: ({ fn, ...props }) => /* @__PURE__ */ jsx(HexSocketBolt, { ...props }),
1949
2162
  pads: "none"
1950
2163
  });
1951
- function register9(registry) {
1952
- registry.register(model9);
2164
+ function register10(registry) {
2165
+ registry.register(model10);
1953
2166
  }
1954
2167
 
1955
2168
  // lib/models/nema/register.tsx
@@ -2218,14 +2431,14 @@ var NEMA17 = (props) => /* @__PURE__ */ jsx(NemaMotor, { ...props, nemaSize: 17
2218
2431
  var NEMA23 = (props) => /* @__PURE__ */ jsx(NemaMotor, { ...props, nemaSize: 23 });
2219
2432
 
2220
2433
  // lib/models/nema/register.tsx
2221
- var model10 = defineModelRenderer({
2434
+ var model11 = defineModelRenderer({
2222
2435
  name: "nema",
2223
2436
  schema: nemaMotorModelDefinitionSchema,
2224
2437
  render: ({ fn, ...props }) => /* @__PURE__ */ jsx(NemaMotor, { ...props }),
2225
2438
  pads: "none"
2226
2439
  });
2227
- function register10(registry) {
2228
- registry.register(model10);
2440
+ function register11(registry) {
2441
+ registry.register(model11);
2229
2442
  }
2230
2443
 
2231
2444
  // lib/models/panscrew/register.tsx
@@ -2437,14 +2650,14 @@ function PanScrew({ color = "#737e8f", ...props }) {
2437
2650
  }
2438
2651
 
2439
2652
  // lib/models/panscrew/register.tsx
2440
- var model11 = defineModelRenderer({
2653
+ var model12 = defineModelRenderer({
2441
2654
  name: "panscrew",
2442
2655
  schema: panScrewModelDefinitionSchema,
2443
2656
  render: ({ fn, ...props }) => /* @__PURE__ */ jsx(PanScrew, { ...props }),
2444
2657
  pads: "none"
2445
2658
  });
2446
- function register11(registry) {
2447
- registry.register(model11);
2659
+ function register12(registry) {
2660
+ registry.register(model12);
2448
2661
  }
2449
2662
 
2450
2663
  // lib/models/plainbushing/register.tsx
@@ -2507,21 +2720,21 @@ function PlainBushing({
2507
2720
  }
2508
2721
 
2509
2722
  // lib/models/plainbushing/register.tsx
2510
- var model12 = defineModelRenderer({
2723
+ var model13 = defineModelRenderer({
2511
2724
  name: "plainbushing",
2512
2725
  schema: plainBushingModelDefinitionSchema,
2513
2726
  render: ({ fn, ...props }) => /* @__PURE__ */ jsx(PlainBushing, { ...props }),
2514
2727
  pads: "none"
2515
2728
  });
2516
- function register12(registry) {
2517
- registry.register(model12);
2729
+ function register13(registry) {
2730
+ registry.register(model13);
2518
2731
  }
2519
2732
 
2520
2733
  // lib/models/sheetmetal/register.tsx
2521
2734
  import { sheetMetalModelDefinitionSchema } from "@tscircuit/modelprinter";
2522
2735
 
2523
2736
  // lib/mechanical/sheet-metal-mesh.ts
2524
- import earcut2 from "earcut";
2737
+ import earcut3 from "earcut";
2525
2738
  import {
2526
2739
  sheetMetalModelPropsSchema
2527
2740
  } from "@tscircuit/modelprinter";
@@ -2589,7 +2802,7 @@ function createSheetMetalMesh(input) {
2589
2802
  const loops = [outline, ...holes.map((hole) => [...hole].reverse())];
2590
2803
  const points = loops.flat();
2591
2804
  const starts = holes.map((_, i) => 4 + i * 64);
2592
- const triangles = earcut2(
2805
+ const triangles = earcut3(
2593
2806
  points.flatMap((p) => [p.u, p.v]),
2594
2807
  starts
2595
2808
  );
@@ -2674,14 +2887,14 @@ function SheetMetal({ color = "#737e8f", ...props }) {
2674
2887
  }
2675
2888
 
2676
2889
  // lib/models/sheetmetal/register.tsx
2677
- var model13 = defineModelRenderer({
2890
+ var model14 = defineModelRenderer({
2678
2891
  name: "sheetmetal",
2679
2892
  schema: sheetMetalModelDefinitionSchema,
2680
2893
  render: ({ fn, ...props }) => /* @__PURE__ */ jsx(SheetMetal, { ...props }),
2681
2894
  pads: "none"
2682
2895
  });
2683
- function register13(registry) {
2684
- registry.register(model13);
2896
+ function register14(registry) {
2897
+ registry.register(model14);
2685
2898
  }
2686
2899
 
2687
2900
  // lib/models/spurgear/register.tsx
@@ -2696,14 +2909,152 @@ function SpurGear({ color = "#737e8f", ...props }) {
2696
2909
  }
2697
2910
 
2698
2911
  // lib/models/spurgear/register.tsx
2699
- var model14 = defineModelRenderer({
2912
+ var model15 = defineModelRenderer({
2700
2913
  name: "spurgear",
2701
2914
  schema: spurGearModelDefinitionSchema,
2702
2915
  render: ({ fn, ...props }) => /* @__PURE__ */ jsx(SpurGear, { ...props }),
2703
2916
  pads: "none"
2704
2917
  });
2705
- function register14(registry) {
2706
- registry.register(model14);
2918
+ function register15(registry) {
2919
+ registry.register(model15);
2920
+ }
2921
+
2922
+ // lib/models/tslotextrusion/register.tsx
2923
+ import { tSlotExtrusionModelDefinitionSchema } from "@tscircuit/modelprinter";
2924
+
2925
+ // lib/models/tslotextrusion/mesh.ts
2926
+ import {
2927
+ tSlotExtrusionModelPropsSchema
2928
+ } from "@tscircuit/modelprinter";
2929
+
2930
+ // lib/mechanical/extrude-planar-profile.ts
2931
+ import earcut4 from "earcut";
2932
+ function extrudePlanarProfile({
2933
+ outer,
2934
+ holes = [],
2935
+ start,
2936
+ end,
2937
+ project = (u, v, depth) => [u, v, depth],
2938
+ reverse = false,
2939
+ skipWall = () => false
2940
+ }) {
2941
+ const oriented = (loop, clockwise) => {
2942
+ loop = loop.filter((point, index) => {
2943
+ const previous = loop[(index + loop.length - 1) % loop.length];
2944
+ return point[0] !== previous[0] || point[1] !== previous[1];
2945
+ });
2946
+ const area = loop.reduce((sum, a, index) => {
2947
+ const b = loop[(index + 1) % loop.length];
2948
+ return sum + a[0] * b[1] - a[1] * b[0];
2949
+ }, 0);
2950
+ if (!Number.isFinite(area) || area === 0)
2951
+ throw new Error("Profile exceeds mesh resolution limits");
2952
+ return area < 0 === clockwise ? loop : [...loop].reverse();
2953
+ };
2954
+ const loops = [oriented(outer, false), ...holes.map((h2) => oriented(h2, true))];
2955
+ const points = loops.flat();
2956
+ const holeStarts = [];
2957
+ let count = loops[0].length;
2958
+ for (const hole of loops.slice(1)) {
2959
+ holeStarts.push(count);
2960
+ count += hole.length;
2961
+ }
2962
+ const faces = earcut4(points.flat(), holeStarts);
2963
+ const positions = [start, end].flatMap(
2964
+ (depth) => points.flatMap(([u, v]) => project(u, v, depth))
2965
+ );
2966
+ const indices = [];
2967
+ for (let i = 0; i < faces.length; i += 3) {
2968
+ const [a, b, c] = faces.slice(i, i + 3);
2969
+ indices.push(a, c, b, a + count, b + count, c + count);
2970
+ }
2971
+ let offset = 0;
2972
+ for (const [loopIndex, loop] of loops.entries()) {
2973
+ for (let i = 0; i < loop.length; i++) {
2974
+ const next = (i + 1) % loop.length;
2975
+ if (loopIndex === 0 && skipWall(loop[i], loop[next])) continue;
2976
+ const a = offset + i;
2977
+ const b = offset + next;
2978
+ indices.push(a, b, b + count, a, b + count, a + count);
2979
+ }
2980
+ offset += loop.length;
2981
+ }
2982
+ if (reverse)
2983
+ for (let i = 0; i < indices.length; i += 3)
2984
+ [indices[i + 1], indices[i + 2]] = [indices[i + 2], indices[i + 1]];
2985
+ return { positions, indices };
2986
+ }
2987
+ function circularProfile(x, y, radius, segments = 96) {
2988
+ return Array.from({ length: segments }, (_, index) => {
2989
+ const angle = index * 2 * Math.PI / segments;
2990
+ return [x + radius * Math.cos(angle), y + radius * Math.sin(angle)];
2991
+ });
2992
+ }
2993
+
2994
+ // lib/models/tslotextrusion/mesh.ts
2995
+ function createTSlotExtrusionMesh(input = {}) {
2996
+ const props = tSlotExtrusionModelPropsSchema.parse(input);
2997
+ const { width: width10, height: height10, length, cornerRadius, lipThickness, pocketDepth } = props;
2998
+ const slot = props.slotWidth / 2;
2999
+ const pocket = props.pocketWidth / 2;
3000
+ const depth = lipThickness + pocketDepth;
3001
+ const outer = [];
3002
+ for (let face2 = 0; face2 < 4; face2++) {
3003
+ const span = (face2 % 2 === 0 ? width10 : height10) / 2;
3004
+ const extent = (face2 % 2 === 0 ? height10 : width10) / 2;
3005
+ const local = [
3006
+ [-span + cornerRadius, -extent],
3007
+ [-slot, -extent],
3008
+ [-slot, -extent + lipThickness],
3009
+ [-pocket, -extent + lipThickness],
3010
+ [-pocket, -extent + depth],
3011
+ [pocket, -extent + depth],
3012
+ [pocket, -extent + lipThickness],
3013
+ [slot, -extent + lipThickness],
3014
+ [slot, -extent],
3015
+ [span - cornerRadius, -extent]
3016
+ ];
3017
+ if (cornerRadius > 0)
3018
+ for (let step = 1; step < 24; step++) {
3019
+ const angle = -Math.PI / 2 + step * Math.PI / 48;
3020
+ local.push([
3021
+ span - cornerRadius + cornerRadius * Math.cos(angle),
3022
+ -extent + cornerRadius + cornerRadius * Math.sin(angle)
3023
+ ]);
3024
+ }
3025
+ for (const [u, v] of local)
3026
+ outer.push(
3027
+ face2 === 0 ? [u, v] : face2 === 1 ? [-v, u] : face2 === 2 ? [-u, -v] : [v, -u]
3028
+ );
3029
+ }
3030
+ return extrudePlanarProfile({
3031
+ outer,
3032
+ holes: props.boreDiameter > 0 ? [circularProfile(0, 0, props.boreDiameter / 2)] : [],
3033
+ start: 0,
3034
+ end: length
3035
+ });
3036
+ }
3037
+
3038
+ // lib/models/tslotextrusion/TSlotExtrusion.tsx
3039
+ function createTSlotExtrusionGeom(input = {}) {
3040
+ return indexedMeshToGeom3(createTSlotExtrusionMesh(input));
3041
+ }
3042
+ function TSlotExtrusion({
3043
+ color = "#8995a3",
3044
+ ...props
3045
+ }) {
3046
+ return /* @__PURE__ */ jsx(Colorize, { color, children: /* @__PURE__ */ jsx(Custom, { geometry: createTSlotExtrusionGeom(props) }) });
3047
+ }
3048
+
3049
+ // lib/models/tslotextrusion/register.tsx
3050
+ var model16 = defineModelRenderer({
3051
+ name: "tslotextrusion",
3052
+ schema: tSlotExtrusionModelDefinitionSchema,
3053
+ render: ({ fn, ...props }) => /* @__PURE__ */ jsx(TSlotExtrusion, { ...props }),
3054
+ pads: "none"
3055
+ });
3056
+ function register16(registry) {
3057
+ registry.register(model16);
2707
3058
  }
2708
3059
 
2709
3060
  // lib/models/wormgear/register.tsx
@@ -2714,7 +3065,7 @@ import {
2714
3065
  getWormGearDimensions,
2715
3066
  wormGearModelPropsSchema
2716
3067
  } from "@tscircuit/modelprinter";
2717
- import earcut3 from "earcut";
3068
+ import earcut5 from "earcut";
2718
3069
  var createWormGearMesh = (input) => {
2719
3070
  const props = wormGearModelPropsSchema.parse(input);
2720
3071
  const { outsideDiameter, rootDiameter, axialPitch, lead, toothThickness } = getWormGearDimensions(props);
@@ -2867,7 +3218,7 @@ var createWormGearMesh = (input) => {
2867
3218
  positions[vertex * 3],
2868
3219
  positions[vertex * 3 + 1]
2869
3220
  ]);
2870
- const triangles = earcut3(flat, [count], 2);
3221
+ const triangles = earcut5(flat, [count], 2);
2871
3222
  for (let index = 0; index < triangles.length; index += 3) {
2872
3223
  const a = vertices[triangles[index]];
2873
3224
  const b = vertices[triangles[index + 1]];
@@ -2895,14 +3246,14 @@ function WormGear({ color = "#737e8f", ...props }) {
2895
3246
  }
2896
3247
 
2897
3248
  // lib/models/wormgear/register.tsx
2898
- var model15 = defineModelRenderer({
3249
+ var model17 = defineModelRenderer({
2899
3250
  name: "wormgear",
2900
3251
  schema: wormGearModelDefinitionSchema,
2901
3252
  render: ({ fn, ...props }) => /* @__PURE__ */ jsx(WormGear, { ...props }),
2902
3253
  pads: "none"
2903
3254
  });
2904
- function register15(registry) {
2905
- registry.register(model15);
3255
+ function register17(registry) {
3256
+ registry.register(model17);
2906
3257
  }
2907
3258
 
2908
3259
  // lib/generated/model-renderers.ts
@@ -2922,6 +3273,8 @@ function registerAllModelRenderers(registry) {
2922
3273
  register13(registry);
2923
3274
  register14(registry);
2924
3275
  register15(registry);
3276
+ register16(registry);
3277
+ register17(registry);
2925
3278
  }
2926
3279
 
2927
3280
  // lib/default-model-registry.ts
@@ -10478,10 +10831,10 @@ var SOT143 = ({ footprint = "sot143" }) => {
10478
10831
  var Footprinter3d = ({ footprint }) => {
10479
10832
  const modelFn = mp3.string(footprint.split("_", 1)[0]).params().fn;
10480
10833
  if (mp3.getModelNames().includes(modelFn)) {
10481
- const model16 = mp3.string(footprint).json();
10482
- const renderer = modelRendererRegistry.get(model16.fn);
10834
+ const model18 = mp3.string(footprint).json();
10835
+ const renderer = modelRendererRegistry.get(model18.fn);
10483
10836
  if (renderer) {
10484
- return renderer.render(model16);
10837
+ return renderer.render(model18);
10485
10838
  }
10486
10839
  }
10487
10840
  let normalizedFootprint = footprint;
@@ -11434,6 +11787,7 @@ function createJSCADRenderer(jscad4) {
11434
11787
  export {
11435
11788
  ButtonScrew,
11436
11789
  CableGrommet,
11790
+ CompressionSpring,
11437
11791
  FlangedBushing,
11438
11792
  FlatHeadScrew,
11439
11793
  Fragment,
@@ -11449,12 +11803,15 @@ export {
11449
11803
  PlainBushing,
11450
11804
  SheetMetal,
11451
11805
  SpurGear,
11806
+ TSlotExtrusion,
11452
11807
  WormGear,
11453
11808
  convertCSGToThreeGeom,
11454
11809
  createButtonScrewGeom,
11455
11810
  createButtonScrewMesh,
11456
11811
  createCableGrommetGeom,
11457
11812
  createCableGrommetMesh,
11813
+ createCompressionSpringGeom,
11814
+ createCompressionSpringMesh,
11458
11815
  createFlangedBushingGeom,
11459
11816
  createFlangedBushingMesh,
11460
11817
  createFlatHeadScrewGeom,
@@ -11476,6 +11833,8 @@ export {
11476
11833
  createSheetMetalMesh,
11477
11834
  createSpurGearGeom,
11478
11835
  createSpurGearMesh,
11836
+ createTSlotExtrusionGeom,
11837
+ createTSlotExtrusionMesh,
11479
11838
  createWormGearGeom,
11480
11839
  createWormGearMesh,
11481
11840
  getJscadModelForFootprint,