@code3d/screws 0.0.1-alpha.4 → 0.0.1-alpha.7

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.
Files changed (158) hide show
  1. package/README.md +228 -40
  2. package/bld/chunks/chunk-2DGVQRQ5.js +78 -0
  3. package/bld/chunks/chunk-2DGVQRQ5.js.map +7 -0
  4. package/bld/chunks/chunk-4FMUSJBB.js +27 -0
  5. package/bld/chunks/chunk-4FMUSJBB.js.map +7 -0
  6. package/bld/chunks/chunk-7UGU5EFV.js +87 -0
  7. package/bld/chunks/chunk-7UGU5EFV.js.map +7 -0
  8. package/bld/chunks/chunk-DKS4VANI.js +27 -0
  9. package/bld/chunks/chunk-DKS4VANI.js.map +7 -0
  10. package/bld/chunks/chunk-ES62SR3Q.js +21 -0
  11. package/bld/chunks/chunk-ES62SR3Q.js.map +7 -0
  12. package/bld/chunks/chunk-ESLSQKZW.js +25 -0
  13. package/bld/chunks/chunk-ESLSQKZW.js.map +7 -0
  14. package/bld/chunks/chunk-GAX6375G.js +25 -0
  15. package/bld/chunks/chunk-GAX6375G.js.map +7 -0
  16. package/bld/chunks/chunk-GIIOR3XJ.js +49 -0
  17. package/bld/chunks/chunk-GIIOR3XJ.js.map +7 -0
  18. package/bld/chunks/chunk-HPPYZYCK.js +23 -0
  19. package/bld/chunks/chunk-HPPYZYCK.js.map +7 -0
  20. package/bld/chunks/chunk-IXFHRZSY.js +121 -0
  21. package/bld/chunks/chunk-IXFHRZSY.js.map +7 -0
  22. package/bld/chunks/chunk-MEIKYZ23.js +84 -0
  23. package/bld/chunks/chunk-MEIKYZ23.js.map +7 -0
  24. package/bld/chunks/chunk-O34BMYJV.js +84 -0
  25. package/bld/chunks/chunk-O34BMYJV.js.map +7 -0
  26. package/bld/chunks/chunk-P3M5KQ5U.js +46 -0
  27. package/bld/chunks/chunk-P3M5KQ5U.js.map +7 -0
  28. package/bld/chunks/chunk-PFFNLWWR.js +25 -0
  29. package/bld/chunks/chunk-PFFNLWWR.js.map +7 -0
  30. package/bld/chunks/chunk-SDUJLXOE.js +46 -0
  31. package/bld/chunks/chunk-SDUJLXOE.js.map +7 -0
  32. package/bld/chunks/chunk-SDYJF2XK.js +25 -0
  33. package/bld/chunks/chunk-SDYJF2XK.js.map +7 -0
  34. package/bld/chunks/chunk-SGDNOA44.js +92 -0
  35. package/bld/chunks/chunk-SGDNOA44.js.map +7 -0
  36. package/bld/chunks/chunk-SPFHOFBC.js +125 -0
  37. package/bld/chunks/chunk-SPFHOFBC.js.map +7 -0
  38. package/bld/chunks/chunk-VVX3WOBG.js +160 -0
  39. package/bld/chunks/chunk-VVX3WOBG.js.map +7 -0
  40. package/bld/chunks/chunk-W62ASGIP.js +25 -0
  41. package/bld/chunks/chunk-W62ASGIP.js.map +7 -0
  42. package/bld/chunks/chunk-Z5LEGL77.js +464 -0
  43. package/bld/chunks/chunk-Z5LEGL77.js.map +7 -0
  44. package/bld/chunks/chunk-ZNWKI54A.js +25 -0
  45. package/bld/chunks/chunk-ZNWKI54A.js.map +7 -0
  46. package/bld/library/common.d.ts +104 -0
  47. package/bld/library/common.d.ts.map +1 -0
  48. package/bld/library/gb-2672.d.ts +8 -0
  49. package/bld/library/gb-2672.d.ts.map +1 -0
  50. package/bld/library/gb-2672.js +17 -0
  51. package/bld/library/gb-2672.js.map +7 -0
  52. package/bld/library/gb-5281.d.ts +8 -0
  53. package/bld/library/gb-5281.d.ts.map +1 -0
  54. package/bld/library/gb-5281.js +15 -0
  55. package/bld/library/gb-5281.js.map +7 -0
  56. package/bld/library/gb-5782.d.ts +8 -0
  57. package/bld/library/gb-5782.d.ts.map +1 -0
  58. package/bld/library/gb-5782.js +19 -0
  59. package/bld/library/gb-5782.js.map +7 -0
  60. package/bld/library/gb-5783.d.ts +8 -0
  61. package/bld/library/gb-5783.d.ts.map +1 -0
  62. package/bld/library/gb-5783.js +19 -0
  63. package/bld/library/gb-5783.js.map +7 -0
  64. package/bld/library/gb-70-1.d.ts +8 -0
  65. package/bld/library/gb-70-1.d.ts.map +1 -0
  66. package/bld/library/gb-70-1.js +17 -0
  67. package/bld/library/gb-70-1.js.map +7 -0
  68. package/bld/library/gb-70-2.d.ts +8 -0
  69. package/bld/library/gb-70-2.d.ts.map +1 -0
  70. package/bld/library/gb-70-2.js +17 -0
  71. package/bld/library/gb-70-2.js.map +7 -0
  72. package/bld/library/gb-70-3.d.ts +8 -0
  73. package/bld/library/gb-70-3.d.ts.map +1 -0
  74. package/bld/library/gb-70-3.js +17 -0
  75. package/bld/library/gb-70-3.js.map +7 -0
  76. package/bld/library/gb-70-4.d.ts +8 -0
  77. package/bld/library/gb-70-4.d.ts.map +1 -0
  78. package/bld/library/gb-70-4.js +17 -0
  79. package/bld/library/gb-70-4.js.map +7 -0
  80. package/bld/library/gb-80.d.ts +8 -0
  81. package/bld/library/gb-80.d.ts.map +1 -0
  82. package/bld/library/gb-80.js +13 -0
  83. package/bld/library/gb-80.js.map +7 -0
  84. package/bld/library/gb-818.d.ts +8 -0
  85. package/bld/library/gb-818.d.ts.map +1 -0
  86. package/bld/library/gb-818.js +17 -0
  87. package/bld/library/gb-818.js.map +7 -0
  88. package/bld/library/hex-head.d.ts +18 -0
  89. package/bld/library/hex-head.d.ts.map +1 -0
  90. package/bld/library/index.d.ts +19 -0
  91. package/bld/library/index.d.ts.map +1 -1
  92. package/bld/library/index.js +80 -455
  93. package/bld/library/index.js.map +3 -3
  94. package/bld/library/iso-10642.d.ts +51 -0
  95. package/bld/library/iso-10642.d.ts.map +1 -0
  96. package/bld/library/iso-10642.js +16 -0
  97. package/bld/library/iso-10642.js.map +7 -0
  98. package/bld/library/iso-14583.d.ts +35 -0
  99. package/bld/library/iso-14583.d.ts.map +1 -0
  100. package/bld/library/iso-14583.js +16 -0
  101. package/bld/library/iso-14583.js.map +7 -0
  102. package/bld/library/iso-4014.d.ts +20 -0
  103. package/bld/library/iso-4014.d.ts.map +1 -0
  104. package/bld/library/iso-4014.js +18 -0
  105. package/bld/library/iso-4014.js.map +7 -0
  106. package/bld/library/iso-4017.d.ts +20 -0
  107. package/bld/library/iso-4017.d.ts.map +1 -0
  108. package/bld/library/iso-4017.js +18 -0
  109. package/bld/library/iso-4017.js.map +7 -0
  110. package/bld/library/iso-4029.d.ts +32 -0
  111. package/bld/library/iso-4029.d.ts.map +1 -0
  112. package/bld/library/iso-4029.js +12 -0
  113. package/bld/library/iso-4029.js.map +7 -0
  114. package/bld/library/iso-4762.d.ts +10 -152
  115. package/bld/library/iso-4762.d.ts.map +1 -1
  116. package/bld/library/iso-4762.js +16 -0
  117. package/bld/library/iso-4762.js.map +7 -0
  118. package/bld/library/iso-7045.d.ts +33 -0
  119. package/bld/library/iso-7045.d.ts.map +1 -0
  120. package/bld/library/iso-7045.js +16 -0
  121. package/bld/library/iso-7045.js.map +7 -0
  122. package/bld/library/iso-7379.d.ts +56 -0
  123. package/bld/library/iso-7379.d.ts.map +1 -0
  124. package/bld/library/iso-7379.js +14 -0
  125. package/bld/library/iso-7379.js.map +7 -0
  126. package/bld/library/iso-7380-1.d.ts +31 -0
  127. package/bld/library/iso-7380-1.d.ts.map +1 -0
  128. package/bld/library/iso-7380-1.js +16 -0
  129. package/bld/library/iso-7380-1.js.map +7 -0
  130. package/bld/library/iso-7380-2.d.ts +33 -0
  131. package/bld/library/iso-7380-2.d.ts.map +1 -0
  132. package/bld/library/iso-7380-2.js +16 -0
  133. package/bld/library/iso-7380-2.js.map +7 -0
  134. package/package.json +82 -2
  135. package/src/library/common.ts +367 -0
  136. package/src/library/gb-2672.ts +7 -0
  137. package/src/library/gb-5281.ts +7 -0
  138. package/src/library/gb-5782.ts +7 -0
  139. package/src/library/gb-5783.ts +7 -0
  140. package/src/library/gb-70-1.ts +7 -0
  141. package/src/library/gb-70-2.ts +7 -0
  142. package/src/library/gb-70-3.ts +7 -0
  143. package/src/library/gb-70-4.ts +7 -0
  144. package/src/library/gb-80.ts +7 -0
  145. package/src/library/gb-818.ts +7 -0
  146. package/src/library/hex-head.ts +53 -0
  147. package/src/library/index.ts +20 -0
  148. package/src/library/iso-10642.ts +173 -0
  149. package/src/library/iso-14583.ts +181 -0
  150. package/src/library/iso-4014.ts +64 -0
  151. package/src/library/iso-4017.ts +62 -0
  152. package/src/library/iso-4029.ts +101 -0
  153. package/src/library/iso-4762.ts +70 -300
  154. package/src/library/iso-7045.ts +134 -0
  155. package/src/library/iso-7379.ts +230 -0
  156. package/src/library/iso-7380-1.ts +110 -0
  157. package/src/library/iso-7380-2.ts +135 -0
  158. package/src/library/thread.ts +7 -1
@@ -0,0 +1,230 @@
1
+ import {
2
+ cylinder,
3
+ frustum,
4
+ union,
5
+ type Bound,
6
+ type CanonicalElements,
7
+ type LineAnchor,
8
+ type SolidModel,
9
+ } from '@code3d/core';
10
+ import {
11
+ metric,
12
+ atY,
13
+ externalThread,
14
+ hexSocket,
15
+ positive,
16
+ validateHead,
17
+ withCounterbore,
18
+ type HeadSpecification,
19
+ type MetricSize,
20
+ type ClearanceHole,
21
+ type ClearanceHoleOptions as CommonClearanceHoleOptions,
22
+ type CounterboredHole,
23
+ type CounterboreOptions,
24
+ } from './common.js';
25
+ export type {
26
+ ClearanceHole,
27
+ HoleElements,
28
+ CounterboredHole,
29
+ CounterboredHoleElements,
30
+ CounterboreOptions,
31
+ } from './common.js';
32
+
33
+ export type Specification = HeadSpecification &
34
+ Readonly<{
35
+ shoulderDiameter: number;
36
+ shoulderBodyDiameter: number;
37
+ threadLength: number;
38
+ neckDiameter: number;
39
+ headReliefLength: number;
40
+ threadReliefLength: number;
41
+ hexSocketWidth: number;
42
+ hexSocketDepth: number;
43
+ }>;
44
+ // ISO 7379:1983, plain heads, maximum h8 shoulder diameter and nominal b.
45
+ // https://www.finesz.com/ISO7379.php
46
+ function specification(
47
+ size: MetricSize,
48
+ shoulderDiameter: number,
49
+ shoulderBodyDiameter: number,
50
+ threadLength: number,
51
+ headDiameter: number,
52
+ headHeight: number,
53
+ neckDiameter: number,
54
+ threadReliefLength: number,
55
+ underHeadRadius: number,
56
+ hexSocketWidth: number,
57
+ hexSocketDepth: number,
58
+ ): Specification {
59
+ return {
60
+ ...metric(size),
61
+ shoulderDiameter,
62
+ shoulderBodyDiameter,
63
+ threadLength,
64
+ headDiameter,
65
+ headHeight,
66
+ neckDiameter,
67
+ headReliefLength: 2.5,
68
+ threadReliefLength,
69
+ underHeadRadius,
70
+ hexSocketWidth,
71
+ hexSocketDepth,
72
+ };
73
+ }
74
+ export const specifications = {
75
+ 6.5: specification('M5', 6.5, 6.49, 9.5, 10, 4.5, 3.86, 2, 0.25, 3, 2.4),
76
+ 8: specification('M6', 8, 7.99, 11, 13, 5.5, 4.58, 2.5, 0.4, 4, 3.3),
77
+ 10: specification('M8', 10, 9.99, 13, 16, 7, 6.25, 3.1, 0.6, 5, 4.2),
78
+ 13: specification('M10', 13, 12.98, 16, 18, 9, 7.91, 3.7, 0.6, 6, 4.9),
79
+ 16: specification('M12', 16, 15.98, 18, 24, 11, 9.57, 4.4, 0.6, 8, 6.6),
80
+ } as const;
81
+ export type Size = keyof typeof specifications;
82
+ export type ScrewInput = Size | Specification;
83
+ export type ScrewElements = CanonicalElements &
84
+ Readonly<{
85
+ headTop: Bound;
86
+ headBottom: Bound;
87
+ shoulderTop: Bound;
88
+ shoulderBottom: Bound;
89
+ shoulderAxis: LineAnchor;
90
+ threadTop: Bound;
91
+ threadBottom: Bound;
92
+ threadAxis: LineAnchor;
93
+ }>;
94
+ export type Screw = SolidModel<ScrewElements>;
95
+ export type ClearanceHoleOptions = Omit<CommonClearanceHoleOptions, 'fit'>;
96
+ export type PlainHoleOptions = ClearanceHoleOptions &
97
+ Readonly<{counterbore?: false}>;
98
+ export type CounterboredHoleOptions = ClearanceHoleOptions &
99
+ Readonly<{counterbore: true | CounterboreOptions}>;
100
+ export function resolveSpecification(input: ScrewInput): Specification {
101
+ return typeof input === 'number' ? specifications[input] : input;
102
+ }
103
+ /**
104
+ * Length is the shoulder length L; the fixed threaded projection b is additional.
105
+ * @code3d.arguments [8, 20]
106
+ * @code3d.param shoulderLength {kind: 'length'}
107
+ */
108
+ export function screw(input: ScrewInput, shoulderLength: number): Screw {
109
+ const spec = resolveSpecification(input);
110
+ validateHead(spec);
111
+ for (const key of [
112
+ 'shoulderDiameter',
113
+ 'shoulderBodyDiameter',
114
+ 'threadLength',
115
+ 'neckDiameter',
116
+ 'headReliefLength',
117
+ 'threadReliefLength',
118
+ ] as const)
119
+ positive(key, spec[key]);
120
+ positive('Shoulder length', shoulderLength);
121
+ if (shoulderLength <= spec.headReliefLength)
122
+ throw new Error('Shoulder length must exceed the head relief length.');
123
+ if (
124
+ spec.shoulderBodyDiameter <= spec.nominalDiameter ||
125
+ spec.shoulderBodyDiameter > spec.shoulderDiameter ||
126
+ spec.shoulderDiameter >= spec.headDiameter
127
+ )
128
+ throw new Error(
129
+ 'Shoulder diameter must lie between thread and head diameters.',
130
+ );
131
+ if (
132
+ spec.neckDiameter >= spec.nominalDiameter ||
133
+ spec.threadLength - spec.threadReliefLength < spec.pitch
134
+ )
135
+ throw new Error(
136
+ 'Thread relief must leave a reduced neck and at least one thread pitch.',
137
+ );
138
+ const chamfer = Math.min(0.3, spec.headHeight / 10);
139
+ const barrel = atY(
140
+ cylinder(spec.headDiameter / 2, spec.headHeight - chamfer),
141
+ (spec.headHeight - chamfer) / 2,
142
+ );
143
+ const crown = atY(
144
+ frustum(spec.headDiameter / 2, spec.headDiameter / 2 - chamfer, chamfer),
145
+ spec.headHeight - chamfer / 2,
146
+ );
147
+ const head = hexSocket(
148
+ union([barrel, crown]),
149
+ spec.hexSocketWidth,
150
+ spec.hexSocketDepth,
151
+ spec.headHeight,
152
+ );
153
+ const shoulderDatum = atY(
154
+ cylinder(spec.shoulderBodyDiameter / 2, shoulderLength),
155
+ -shoulderLength / 2,
156
+ );
157
+ const relief = atY(
158
+ cylinder(
159
+ spec.shoulderBodyDiameter / 2 - spec.underHeadRadius,
160
+ spec.headReliefLength,
161
+ ),
162
+ -spec.headReliefLength / 2,
163
+ );
164
+ const shoulder = atY(
165
+ cylinder(
166
+ spec.shoulderBodyDiameter / 2,
167
+ shoulderLength - spec.headReliefLength,
168
+ ),
169
+ -(shoulderLength + spec.headReliefLength) / 2,
170
+ );
171
+ const neck = atY(
172
+ cylinder(spec.neckDiameter / 2, spec.threadReliefLength + 0.05),
173
+ -shoulderLength - (spec.threadReliefLength + 0.05) / 2,
174
+ );
175
+ const thread = atY(
176
+ externalThread(spec, spec.threadLength - spec.threadReliefLength),
177
+ -shoulderLength - (spec.threadLength + spec.threadReliefLength) / 2,
178
+ );
179
+ return union([head, relief, shoulder, neck, thread]).expose({
180
+ headTop: head.up,
181
+ headBottom: head.down,
182
+ shoulderTop: shoulderDatum.up,
183
+ shoulderBottom: shoulderDatum.down,
184
+ shoulderAxis: shoulderDatum.axis,
185
+ threadTop: neck.up,
186
+ threadBottom: thread.down,
187
+ threadAxis: thread.axis,
188
+ });
189
+ }
190
+ /**
191
+ * Shoulder passage with 0.2 mm diametral clearance and an optional head recess.
192
+ * @code3d.arguments [8, 10]
193
+ */
194
+ export function clearanceHole(input: ScrewInput, depth: number): ClearanceHole;
195
+ export function clearanceHole(
196
+ input: ScrewInput,
197
+ options: PlainHoleOptions,
198
+ ): ClearanceHole;
199
+ export function clearanceHole(
200
+ input: ScrewInput,
201
+ options: CounterboredHoleOptions,
202
+ ): CounterboredHole;
203
+ export function clearanceHole(
204
+ input: ScrewInput,
205
+ options: number | ClearanceHoleOptions,
206
+ ): ClearanceHole | CounterboredHole;
207
+ export function clearanceHole(
208
+ input: ScrewInput,
209
+ depth: number | ClearanceHoleOptions,
210
+ ): ClearanceHole | CounterboredHole {
211
+ const spec = resolveSpecification(input);
212
+ const options = typeof depth === 'number' ? {depth} : depth;
213
+ positive('Hole depth', options.depth);
214
+ const diameter = options.diameter ?? spec.shoulderDiameter + 0.2;
215
+ positive('Clearance diameter', diameter);
216
+ if (diameter <= spec.shoulderBodyDiameter)
217
+ throw new Error(
218
+ 'Clearance diameter must exceed the shoulder body diameter.',
219
+ );
220
+ const shaft = cylinder(diameter / 2, options.depth);
221
+ return withCounterbore(
222
+ shaft.expose({
223
+ shaftTop: shaft.up,
224
+ shaftBottom: shaft.down,
225
+ shaftAxis: shaft.axis,
226
+ }),
227
+ spec,
228
+ options,
229
+ );
230
+ }
@@ -0,0 +1,110 @@
1
+ import {
2
+ metric,
3
+ validateHead,
4
+ headedScrew,
5
+ hexSocket,
6
+ roundHead,
7
+ clearanceHole as buildClearanceHole,
8
+ type HeadSpecification,
9
+ type MetricSize,
10
+ type Screw,
11
+ type ClearanceHole,
12
+ type CounterboredHole,
13
+ type ClearanceHoleOptions,
14
+ type PlainHoleOptions,
15
+ type CounterboredHoleOptions,
16
+ } from './common.js';
17
+ export type {
18
+ ClearanceFit,
19
+ Screw,
20
+ ScrewElements,
21
+ ClearanceHole,
22
+ HoleElements,
23
+ CounterboredHole,
24
+ CounterboredHoleElements,
25
+ ClearanceHoleOptions,
26
+ PlainHoleOptions,
27
+ CounterboredHoleOptions,
28
+ CounterboreOptions,
29
+ } from './common.js';
30
+
31
+ export type Specification = HeadSpecification &
32
+ Readonly<{hexSocketWidth: number; hexSocketDepth: number}>;
33
+
34
+ // ISO 7380-1, k/dk maxima and nominal drive dimensions; millimetres.
35
+ // https://eshop-ro.boellhoff.com/out/media/pdf/ISO_7380-1_Stahl_10.9_Innensechskant___en.pdf
36
+ function specification(
37
+ size: MetricSize,
38
+ headDiameter: number,
39
+ headHeight: number,
40
+ hexSocketWidth: number,
41
+ hexSocketDepth: number,
42
+ underHeadRadius: number,
43
+ ): Specification {
44
+ return {
45
+ ...metric(size),
46
+ headDiameter,
47
+ headHeight,
48
+ hexSocketWidth,
49
+ hexSocketDepth,
50
+ underHeadRadius,
51
+ };
52
+ }
53
+ export const specifications = {
54
+ M3: specification('M3', 5.7, 1.65, 2, 1.04, 0.1),
55
+ M4: specification('M4', 7.6, 2.2, 2.5, 1.3, 0.2),
56
+ M5: specification('M5', 9.5, 2.75, 3, 1.56, 0.2),
57
+ M6: specification('M6', 10.5, 3.3, 4, 2.08, 0.25),
58
+ M8: specification('M8', 14, 4.4, 5, 2.6, 0.4),
59
+ M10: specification('M10', 17.5, 5.5, 6, 3.12, 0.4),
60
+ M12: specification('M12', 21, 6.6, 8, 4.16, 0.6),
61
+ } as const;
62
+ export type Size = keyof typeof specifications;
63
+ export type ScrewInput = Size | Specification;
64
+ export function resolveSpecification(input: ScrewInput): Specification {
65
+ return typeof input === 'string' ? specifications[input] : input;
66
+ }
67
+ /** Nominal partial thread b; the builder limits it to the available shank. */
68
+ export function threadLength(spec: Specification, _length: number): number {
69
+ return 2 * spec.nominalDiameter + 12;
70
+ }
71
+ /**
72
+ * @code3d.arguments ['M6', 18]
73
+ * @code3d.param length {kind: 'length'}
74
+ */
75
+ export function screw(input: ScrewInput, length: number): Screw {
76
+ const spec = resolveSpecification(input);
77
+ validateHead(spec);
78
+ const blank = roundHead(
79
+ spec.headDiameter,
80
+ spec.headHeight,
81
+ Math.max(spec.nominalDiameter, spec.hexSocketWidth * 1.3),
82
+ );
83
+ return headedScrew(
84
+ hexSocket(blank, spec.hexSocketWidth, spec.hexSocketDepth, spec.headHeight),
85
+ spec,
86
+ length,
87
+ threadLength(spec, length),
88
+ );
89
+ }
90
+
91
+ /** @code3d.arguments ['M6', 10] */
92
+ export function clearanceHole(input: ScrewInput, depth: number): ClearanceHole;
93
+ export function clearanceHole(
94
+ input: ScrewInput,
95
+ options: PlainHoleOptions,
96
+ ): ClearanceHole;
97
+ export function clearanceHole(
98
+ input: ScrewInput,
99
+ options: CounterboredHoleOptions,
100
+ ): CounterboredHole;
101
+ export function clearanceHole(
102
+ input: ScrewInput,
103
+ options: ClearanceHoleOptions,
104
+ ): ClearanceHole | CounterboredHole;
105
+ export function clearanceHole(
106
+ input: ScrewInput,
107
+ options: number | ClearanceHoleOptions,
108
+ ): ClearanceHole | CounterboredHole {
109
+ return buildClearanceHole(resolveSpecification(input), options);
110
+ }
@@ -0,0 +1,135 @@
1
+ import {cylinder, union} from '@code3d/core';
2
+ import {
3
+ metric,
4
+ validateHead,
5
+ positive,
6
+ atY,
7
+ headedScrew,
8
+ hexSocket,
9
+ roundHead,
10
+ clearanceHole as buildClearanceHole,
11
+ type HeadSpecification,
12
+ type MetricSize,
13
+ type Screw,
14
+ type ClearanceHole,
15
+ type CounterboredHole,
16
+ type ClearanceHoleOptions,
17
+ type PlainHoleOptions,
18
+ type CounterboredHoleOptions,
19
+ } from './common.js';
20
+ export type {
21
+ ClearanceFit,
22
+ Screw,
23
+ ScrewElements,
24
+ ClearanceHole,
25
+ HoleElements,
26
+ CounterboredHole,
27
+ CounterboredHoleElements,
28
+ ClearanceHoleOptions,
29
+ PlainHoleOptions,
30
+ CounterboredHoleOptions,
31
+ CounterboreOptions,
32
+ } from './common.js';
33
+
34
+ export type Specification = HeadSpecification &
35
+ Readonly<{
36
+ hexSocketWidth: number;
37
+ hexSocketDepth: number;
38
+ buttonDiameter: number;
39
+ collarHeight: number;
40
+ }>;
41
+
42
+ // ISO 7380-2:2022, collar diameter dc, button diameter dk and total head height k.
43
+ // https://www.iso.org/standard/78700.html
44
+ // https://eshop-cz.boellhoff.com/out/media/pdf/ISO_7380-2_Edelstahl_A2_Innensechskant___en.pdf
45
+ function specification(
46
+ size: MetricSize,
47
+ headDiameter: number,
48
+ buttonDiameter: number,
49
+ headHeight: number,
50
+ collarHeight: number,
51
+ hexSocketWidth: number,
52
+ hexSocketDepth: number,
53
+ underHeadRadius: number,
54
+ ): Specification {
55
+ return {
56
+ ...metric(size),
57
+ headDiameter,
58
+ buttonDiameter,
59
+ headHeight,
60
+ collarHeight,
61
+ hexSocketWidth,
62
+ hexSocketDepth,
63
+ underHeadRadius,
64
+ };
65
+ }
66
+ export const specifications = {
67
+ M3: specification('M3', 6.9, 5.2, 1.65, 0.7, 2, 1.04, 0.1),
68
+ M4: specification('M4', 9.4, 7.2, 2.2, 0.8, 2.5, 1.3, 0.2),
69
+ M5: specification('M5', 11.8, 8.8, 2.75, 1, 3, 1.56, 0.2),
70
+ M6: specification('M6', 13.6, 10, 3.3, 1.2, 4, 2.08, 0.25),
71
+ M8: specification('M8', 17.8, 13.2, 4.4, 1.5, 5, 2.6, 0.4),
72
+ M10: specification('M10', 21.9, 16.5, 5.5, 2, 6, 3.12, 0.4),
73
+ M12: specification('M12', 26, 19.4, 6.6, 2.4, 8, 4.16, 0.6),
74
+ } as const;
75
+ export type Size = keyof typeof specifications;
76
+ export type ScrewInput = Size | Specification;
77
+ export function resolveSpecification(input: ScrewInput): Specification {
78
+ return typeof input === 'string' ? specifications[input] : input;
79
+ }
80
+ /** Nominal partial thread b; the builder limits it to the available shank. */
81
+ export function threadLength(spec: Specification, _length: number): number {
82
+ return 2 * spec.nominalDiameter + 12;
83
+ }
84
+ /**
85
+ * @code3d.arguments ['M6', 18]
86
+ * @code3d.param length {kind: 'length'}
87
+ */
88
+ export function screw(input: ScrewInput, length: number): Screw {
89
+ const spec = resolveSpecification(input);
90
+ validateHead(spec);
91
+ positive('Collar height', spec.collarHeight);
92
+ positive('Button diameter', spec.buttonDiameter);
93
+ if (
94
+ spec.collarHeight >= spec.headHeight ||
95
+ spec.buttonDiameter >= spec.headDiameter
96
+ )
97
+ throw new Error('Button and collar must fit the head envelope.');
98
+ const collar = cylinder(spec.headDiameter / 2, spec.collarHeight);
99
+ const crown = atY(
100
+ roundHead(
101
+ spec.buttonDiameter,
102
+ spec.headHeight - spec.collarHeight,
103
+ Math.max(spec.nominalDiameter, spec.hexSocketWidth * 1.3),
104
+ ),
105
+ spec.headHeight / 2,
106
+ );
107
+ const blank = union([collar, crown]);
108
+ return headedScrew(
109
+ hexSocket(blank, spec.hexSocketWidth, spec.hexSocketDepth, spec.headHeight),
110
+ spec,
111
+ length,
112
+ threadLength(spec, length),
113
+ );
114
+ }
115
+
116
+ /** @code3d.arguments ['M6', 10] */
117
+ export function clearanceHole(input: ScrewInput, depth: number): ClearanceHole;
118
+ export function clearanceHole(
119
+ input: ScrewInput,
120
+ options: PlainHoleOptions,
121
+ ): ClearanceHole;
122
+ export function clearanceHole(
123
+ input: ScrewInput,
124
+ options: CounterboredHoleOptions,
125
+ ): CounterboredHole;
126
+ export function clearanceHole(
127
+ input: ScrewInput,
128
+ options: ClearanceHoleOptions,
129
+ ): ClearanceHole | CounterboredHole;
130
+ export function clearanceHole(
131
+ input: ScrewInput,
132
+ options: number | ClearanceHoleOptions,
133
+ ): ClearanceHole | CounterboredHole {
134
+ return buildClearanceHole(resolveSpecification(input), options);
135
+ }
@@ -98,7 +98,13 @@ function makeHelicalThreadShape({
98
98
  const threaded = core.fuse(teeth);
99
99
  core.delete();
100
100
  teeth.delete();
101
- return threaded.rotate(-90, [0, 0, 0], [1, 0, 0]).translate([0, -y / 2, 0]);
101
+ // The axial profile width extends past the fade's quarter-pitch centreline.
102
+ // Face both ends. Keep the tool clear of the crest to avoid tangent slivers.
103
+ const envelope = makeCylinder(majorRadius + pitch, y);
104
+ const trimmed = threaded.intersect(envelope);
105
+ threaded.delete();
106
+ envelope.delete();
107
+ return trimmed.rotate(-90, [0, 0, 0], [1, 0, 0]).translate([0, -y / 2, 0]);
102
108
  }
103
109
 
104
110
  function threadProfile(