@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
@@ -1,136 +1,71 @@
1
+ import {cylinder, frustum, union} from '@code3d/core';
1
2
  import {
2
- cylinder,
3
- cut,
4
- frustum,
5
- regularPrism,
6
- union,
7
- type CanonicalElements,
8
- type Bound,
9
- type LineAnchor,
10
- type SolidModel,
11
- } from '@code3d/core';
12
- import {helicalThread} from './thread.js';
13
-
14
- export type ClearanceFit = 'close' | 'normal' | 'loose';
15
-
16
- export type Specification = Readonly<{
17
- designation: string;
18
- nominalDiameter: number;
19
- pitch: number;
20
- headDiameter: number;
21
- headHeight: number;
22
- hexSocketWidth: number;
23
- hexSocketDepth: number;
24
- underHeadRadius: number;
25
- clearance: Readonly<Record<ClearanceFit, number>>;
26
- counterboreDiameter: number;
27
- }>;
3
+ metric,
4
+ headedScrew,
5
+ validateHead,
6
+ hexSocket,
7
+ clearanceHole as buildClearanceHole,
8
+ type MetricSize,
9
+ type HeadSpecification,
10
+ type Screw,
11
+ type ClearanceHole,
12
+ type CounterboredHole,
13
+ type ClearanceHoleOptions,
14
+ type CounterboreOptions,
15
+ } from './common.js';
16
+ export type {
17
+ ClearanceFit,
18
+ ClearanceHoleOptions,
19
+ CounterboreOptions,
20
+ Screw,
21
+ ClearanceHole,
22
+ CounterboredHole,
23
+ ScrewElements as SocketCapScrewElements,
24
+ HoleElements as SocketCapHoleElements,
25
+ CounterboredHoleElements as CounterboredSocketCapHoleElements,
26
+ } from './common.js';
27
+
28
+ export type Specification = HeadSpecification &
29
+ Readonly<{
30
+ hexSocketWidth: number;
31
+ hexSocketDepth: number;
32
+ counterboreDiameter: number;
33
+ }>;
28
34
 
29
35
  // ISO 4762 coarse-thread socket-head cap screws; millimetres.
30
36
  // Clearance series follow ISO 273. Counterbores follow DIN 974-1 normal series.
37
+ function specification(
38
+ size: MetricSize,
39
+ headDiameter: number,
40
+ headHeight: number,
41
+ hexSocketWidth: number,
42
+ hexSocketDepth: number,
43
+ underHeadRadius: number,
44
+ counterboreDiameter: number,
45
+ ): Specification {
46
+ return {
47
+ ...metric(size),
48
+ headDiameter,
49
+ headHeight,
50
+ hexSocketWidth,
51
+ hexSocketDepth,
52
+ underHeadRadius,
53
+ counterboreDiameter,
54
+ };
55
+ }
31
56
  export const specifications = {
32
- M3: {
33
- designation: 'M3',
34
- nominalDiameter: 3,
35
- pitch: 0.5,
36
- headDiameter: 5.5,
37
- headHeight: 3,
38
- hexSocketWidth: 2.5,
39
- hexSocketDepth: 1.3,
40
- underHeadRadius: 0.1,
41
- clearance: {close: 3.2, normal: 3.4, loose: 3.6},
42
- counterboreDiameter: 6,
43
- },
44
- M4: {
45
- designation: 'M4',
46
- nominalDiameter: 4,
47
- pitch: 0.7,
48
- headDiameter: 7,
49
- headHeight: 4,
50
- hexSocketWidth: 3,
51
- hexSocketDepth: 2,
52
- underHeadRadius: 0.2,
53
- clearance: {close: 4.3, normal: 4.5, loose: 4.8},
54
- counterboreDiameter: 8,
55
- },
56
- M5: {
57
- designation: 'M5',
58
- nominalDiameter: 5,
59
- pitch: 0.8,
60
- headDiameter: 8.5,
61
- headHeight: 5,
62
- hexSocketWidth: 4,
63
- hexSocketDepth: 2.5,
64
- underHeadRadius: 0.2,
65
- clearance: {close: 5.3, normal: 5.5, loose: 5.8},
66
- counterboreDiameter: 10,
67
- },
68
- M6: {
69
- designation: 'M6',
70
- nominalDiameter: 6,
71
- pitch: 1,
72
- headDiameter: 10,
73
- headHeight: 6,
74
- hexSocketWidth: 5,
75
- hexSocketDepth: 3,
76
- underHeadRadius: 0.25,
77
- clearance: {close: 6.4, normal: 6.6, loose: 7},
78
- counterboreDiameter: 11,
79
- },
80
- M8: {
81
- designation: 'M8',
82
- nominalDiameter: 8,
83
- pitch: 1.25,
84
- headDiameter: 13,
85
- headHeight: 8,
86
- hexSocketWidth: 6,
87
- hexSocketDepth: 4,
88
- underHeadRadius: 0.4,
89
- clearance: {close: 8.4, normal: 9, loose: 10},
90
- counterboreDiameter: 15,
91
- },
92
- M10: {
93
- designation: 'M10',
94
- nominalDiameter: 10,
95
- pitch: 1.5,
96
- headDiameter: 16,
97
- headHeight: 10,
98
- hexSocketWidth: 8,
99
- hexSocketDepth: 5,
100
- underHeadRadius: 0.4,
101
- clearance: {close: 10.5, normal: 11, loose: 12},
102
- counterboreDiameter: 18,
103
- },
104
- M12: {
105
- designation: 'M12',
106
- nominalDiameter: 12,
107
- pitch: 1.75,
108
- headDiameter: 18,
109
- headHeight: 12,
110
- hexSocketWidth: 10,
111
- hexSocketDepth: 6,
112
- underHeadRadius: 0.6,
113
- clearance: {close: 13, normal: 13.5, loose: 14.5},
114
- counterboreDiameter: 20,
115
- },
116
- } as const satisfies Readonly<Record<string, Specification>>;
57
+ M3: specification('M3', 5.5, 3, 2.5, 1.3, 0.1, 6),
58
+ M4: specification('M4', 7, 4, 3, 2, 0.2, 8),
59
+ M5: specification('M5', 8.5, 5, 4, 2.5, 0.2, 10),
60
+ M6: specification('M6', 10, 6, 5, 3, 0.25, 11),
61
+ M8: specification('M8', 13, 8, 6, 4, 0.4, 15),
62
+ M10: specification('M10', 16, 10, 8, 5, 0.4, 18),
63
+ M12: specification('M12', 18, 12, 10, 6, 0.6, 20),
64
+ } as const;
117
65
 
118
66
  export type Size = keyof typeof specifications;
119
67
  export type ScrewInput = Size | Specification;
120
68
 
121
- export type CounterboreOptions = Readonly<{
122
- diameter?: number;
123
- depth?: number;
124
- axialClearance?: number;
125
- }>;
126
-
127
- export type ClearanceHoleOptions = Readonly<{
128
- depth: number;
129
- fit?: ClearanceFit;
130
- diameter?: number;
131
- counterbore?: boolean | CounterboreOptions;
132
- }>;
133
-
134
69
  export type PlainClearanceHoleOptions = ClearanceHoleOptions &
135
70
  Readonly<{
136
71
  counterbore: false;
@@ -144,32 +79,6 @@ export type CounterboredHoleOptions = Omit<
144
79
  counterbore?: true | CounterboreOptions;
145
80
  }>;
146
81
 
147
- export type SocketCapScrewElements = CanonicalElements &
148
- Readonly<{
149
- headTop: Bound;
150
- headBottom: Bound;
151
- shankTop: Bound;
152
- shankBottom: Bound;
153
- shankAxis: LineAnchor;
154
- }>;
155
-
156
- export type SocketCapHoleElements = CanonicalElements &
157
- Readonly<{
158
- shaftTop: Bound;
159
- shaftBottom: Bound;
160
- shaftAxis: LineAnchor;
161
- }>;
162
-
163
- export type CounterboredSocketCapHoleElements = SocketCapHoleElements &
164
- Readonly<{
165
- counterboreTop: Bound;
166
- counterboreBottom: Bound;
167
- }>;
168
-
169
- export type Screw = SolidModel<SocketCapScrewElements>;
170
- export type ClearanceHole = SolidModel<SocketCapHoleElements>;
171
- export type CounterboredHole = SolidModel<CounterboredSocketCapHoleElements>;
172
-
173
82
  /**
174
83
  * @code3d.arguments ['M6', 18]
175
84
  * @code3d.arguments ['M8', 30]
@@ -177,77 +86,21 @@ export type CounterboredHole = SolidModel<CounterboredSocketCapHoleElements>;
177
86
  */
178
87
  export function screw(input: ScrewInput, length: number): Screw {
179
88
  const spec = resolveSpecification(input);
180
- validateSpec(spec);
181
- if (!Number.isFinite(length) || length <= spec.pitch + spec.underHeadRadius) {
182
- throw new Error(
183
- 'Screw length must leave room for at least one full thread pitch.',
184
- );
185
- }
186
-
89
+ validateHead(spec);
187
90
  const headChamfer = Math.min(spec.pitch / 2, spec.headHeight * 0.12);
188
- const headBarrel = cylinder(
189
- spec.headDiameter / 2,
190
- spec.headHeight - headChamfer,
191
- );
192
- const headTop = frustum(
91
+ const barrel = cylinder(spec.headDiameter / 2, spec.headHeight - headChamfer);
92
+ const crown = frustum(
193
93
  spec.headDiameter / 2,
194
94
  spec.headDiameter / 2 - headChamfer,
195
95
  headChamfer,
196
- ).relate(top => top.on(headBarrel.up));
197
- const headBlank = union([headBarrel, headTop]);
198
-
199
- const socketToolY = spec.hexSocketDepth + 0.2;
200
- const socketTool = regularPrism(
201
- spec.hexSocketWidth / Math.sqrt(3),
202
- socketToolY,
203
- 6,
204
- 30,
205
- ).relate(tool =>
206
- tool.center.on(headBlank.up).offset(0, -spec.hexSocketDepth / 2 + 0.1, 0),
96
+ ).relate(part => part.on(barrel.up));
97
+ const head = hexSocket(
98
+ union([barrel, crown]),
99
+ spec.hexSocketWidth,
100
+ spec.hexSocketDepth,
101
+ spec.headHeight,
207
102
  );
208
- const head = cut(headBlank, [socketTool]);
209
-
210
- const transition = frustum(
211
- spec.nominalDiameter / 2,
212
- spec.nominalDiameter / 2 + spec.underHeadRadius,
213
- spec.underHeadRadius,
214
- ).relate(part => part.on(head.down));
215
- const bodyLength = length - spec.underHeadRadius;
216
- const threadedLength = Math.min(bodyLength, threadLength(spec, length));
217
- const plainLength = bodyLength - threadedLength;
218
- const overlap = Math.min(0.08, spec.pitch / 10);
219
- const parts = [head, transition];
220
- let previous = transition;
221
-
222
- if (plainLength > overlap) {
223
- const shank = cylinder(
224
- spec.nominalDiameter / 2,
225
- plainLength + overlap,
226
- ).relate(part => part.on(previous.down).offset(0, -overlap, 0));
227
- parts.push(shank);
228
- previous = shank;
229
- }
230
-
231
- const fundamentalHeight = (Math.sqrt(3) / 2) * spec.pitch;
232
- const minorDiameter =
233
- spec.nominalDiameter - 2 * ((5 / 8) * fundamentalHeight);
234
- const thread = helicalThread({
235
- pitch: spec.pitch,
236
- y: threadedLength + overlap,
237
- majorDiameter: spec.nominalDiameter,
238
- minorDiameter,
239
- rootWidth: (3 / 4) * spec.pitch,
240
- crestWidth: (1 / 8) * spec.pitch,
241
- }).relate(part => part.on(previous.down).offset(0, -overlap, 0));
242
- parts.push(thread);
243
-
244
- return union(parts).expose({
245
- headTop: head.up,
246
- headBottom: head.down,
247
- shankTop: transition.up,
248
- shankBottom: thread.down,
249
- shankAxis: thread.axis,
250
- });
103
+ return headedScrew(head, spec, length, threadLength(spec, length));
251
104
  }
252
105
 
253
106
  /**
@@ -275,63 +128,7 @@ export function clearanceHole(
275
128
  input: ScrewInput,
276
129
  optionsOrDepth: ClearanceHoleOptions | number,
277
130
  ): ClearanceHole | CounterboredHole {
278
- const options: ClearanceHoleOptions =
279
- typeof optionsOrDepth === 'number'
280
- ? {depth: optionsOrDepth}
281
- : optionsOrDepth;
282
- const spec = resolveSpecification(input);
283
- validateSpec(spec);
284
- if (!Number.isFinite(options.depth) || options.depth <= 0) {
285
- throw new Error('Hole depth must be a positive finite number.');
286
- }
287
- const fit = options.fit ?? 'normal';
288
- const diameter = options.diameter ?? spec.clearance[fit];
289
- if (!Number.isFinite(diameter) || diameter <= spec.nominalDiameter) {
290
- throw new Error(
291
- 'Clearance-hole diameter must exceed the nominal screw diameter.',
292
- );
293
- }
294
- const shaft = cylinder(diameter / 2, options.depth);
295
- const counterboreOption = options.counterbore ?? true;
296
- if (counterboreOption === false) {
297
- return shaft.expose({
298
- shaftTop: shaft.up,
299
- shaftBottom: shaft.down,
300
- shaftAxis: shaft.axis,
301
- });
302
- }
303
-
304
- const counterbore = counterboreOption === true ? {} : counterboreOption;
305
- const axialClearance = counterbore.axialClearance ?? 0.5;
306
- const counterboreDepth =
307
- counterbore.depth ?? spec.headHeight + axialClearance;
308
- const counterboreDiameter = counterbore.diameter ?? spec.counterboreDiameter;
309
- if (
310
- !Number.isFinite(counterboreDepth) ||
311
- counterboreDepth <= 0 ||
312
- counterboreDepth > options.depth
313
- ) {
314
- throw new Error('Counterbore depth must be within the total hole depth.');
315
- }
316
- if (
317
- !Number.isFinite(counterboreDiameter) ||
318
- counterboreDiameter < spec.headDiameter
319
- ) {
320
- throw new Error('Counterbore diameter must accommodate the screw head.');
321
- }
322
- const recess = cylinder(
323
- counterboreDiameter / 2,
324
- counterboreDepth + 0.2,
325
- ).relate(tool =>
326
- tool.center.on(shaft.up).offset(0, -counterboreDepth / 2 + 0.1, 0),
327
- );
328
- return union([shaft, recess]).expose({
329
- shaftTop: shaft.up,
330
- shaftBottom: shaft.down,
331
- shaftAxis: shaft.axis,
332
- counterboreTop: recess.up,
333
- counterboreBottom: recess.down,
334
- });
131
+ return buildClearanceHole(resolveSpecification(input), optionsOrDepth, true);
335
132
  }
336
133
 
337
134
  export function resolveSpecification(input: ScrewInput): Specification {
@@ -343,30 +140,3 @@ export function threadLength(spec: Specification, length: number): number {
343
140
  if (length <= 200) return 2 * spec.nominalDiameter + 18;
344
141
  return 2 * spec.nominalDiameter + 31;
345
142
  }
346
-
347
- function validateSpec(spec: Specification): void {
348
- const values = [
349
- spec.nominalDiameter,
350
- spec.pitch,
351
- spec.headDiameter,
352
- spec.headHeight,
353
- spec.hexSocketWidth,
354
- spec.hexSocketDepth,
355
- spec.underHeadRadius,
356
- spec.clearance.close,
357
- spec.clearance.normal,
358
- spec.clearance.loose,
359
- spec.counterboreDiameter,
360
- ];
361
- if (values.some(value => !Number.isFinite(value) || value <= 0)) {
362
- throw new Error(
363
- 'Fastener specifications must contain positive finite dimensions.',
364
- );
365
- }
366
- if (spec.headDiameter <= spec.nominalDiameter) {
367
- throw new Error('Head diameter must exceed the nominal thread diameter.');
368
- }
369
- if (spec.hexSocketDepth >= spec.headHeight) {
370
- throw new Error('Hex socket depth must be smaller than the head height.');
371
- }
372
- }
@@ -0,0 +1,134 @@
1
+ import {
2
+ metric,
3
+ validateHead,
4
+ headedScrew,
5
+ panHead,
6
+ crossSocket,
7
+ clearanceHole as buildClearanceHole,
8
+ type CrossRecessType,
9
+ type CrossRecess,
10
+ type HeadSpecification,
11
+ type MetricSize,
12
+ type Screw,
13
+ type ClearanceHole,
14
+ type CounterboredHole,
15
+ type ClearanceHoleOptions,
16
+ type PlainHoleOptions,
17
+ type CounterboredHoleOptions,
18
+ } from './common.js';
19
+ export type {
20
+ CrossRecessType,
21
+ CrossRecess,
22
+ ClearanceFit,
23
+ Screw,
24
+ ScrewElements,
25
+ ClearanceHole,
26
+ HoleElements,
27
+ CounterboredHole,
28
+ CounterboredHoleElements,
29
+ ClearanceHoleOptions,
30
+ PlainHoleOptions,
31
+ CounterboredHoleOptions,
32
+ CounterboreOptions,
33
+ } from './common.js';
34
+
35
+ export type Specification = HeadSpecification &
36
+ Readonly<{
37
+ headCurvatureRadius: number;
38
+ recesses: Readonly<Record<CrossRecessType, CrossRecess>>;
39
+ }>;
40
+ export type ScrewOptions = Readonly<{recess?: CrossRecessType}>;
41
+ // ISO 7045:2011 (M3 and M5 heads differ from DIN 7985). H/Z driving envelopes.
42
+ // https://www.finesz.com/ISO7045.php
43
+ function specification(
44
+ size: MetricSize,
45
+ headDiameter: number,
46
+ headHeight: number,
47
+ headCurvatureRadius: number,
48
+ underHeadRadius: number,
49
+ number: number,
50
+ width: number,
51
+ hDiameter: number,
52
+ hDepth: number,
53
+ zDiameter: number,
54
+ zDepth: number,
55
+ ): Specification {
56
+ return {
57
+ ...metric(size),
58
+ headDiameter,
59
+ headHeight,
60
+ headCurvatureRadius,
61
+ underHeadRadius,
62
+ recesses: {
63
+ H: {number, width, diameter: hDiameter, depth: hDepth},
64
+ Z: {number, width, diameter: zDiameter, depth: zDepth},
65
+ },
66
+ };
67
+ }
68
+ export const specifications = {
69
+ M3: specification('M3', 5.6, 2.4, 5, 0.1, 1, 0.97, 3, 1.4, 2.8, 1.5),
70
+ M4: specification('M4', 8, 3.1, 6.5, 0.2, 2, 1.47, 4.4, 1.9, 4.3, 1.89),
71
+ M5: specification('M5', 9.5, 3.7, 8, 0.2, 2, 1.47, 4.9, 2.4, 4.7, 2.29),
72
+ M6: specification('M6', 12, 4.6, 10, 0.25, 3, 2.41, 6.9, 3.1, 6.7, 3.03),
73
+ M8: specification('M8', 16, 6, 13, 0.4, 4, 3.48, 9, 4, 8.8, 4.05),
74
+ M10: specification('M10', 20, 7.5, 16, 0.4, 4, 3.48, 10.1, 5.2, 9.9, 5.24),
75
+ } as const;
76
+ export type Size = keyof typeof specifications;
77
+ export type ScrewInput = Size | Specification;
78
+ export function resolveSpecification(input: ScrewInput): Specification {
79
+ return typeof input === 'string' ? specifications[input] : input;
80
+ }
81
+ /** ISO minimum b; shorter screws are threaded to the head transition. */
82
+ export function threadLength(spec: Specification, _length: number): number {
83
+ return spec.nominalDiameter <= 3 ? 25 : 38;
84
+ }
85
+ /**
86
+ * @code3d.arguments ['M6', 18]
87
+ * @code3d.param length {kind: 'length'}
88
+ */
89
+ export function screw(
90
+ input: ScrewInput,
91
+ length: number,
92
+ options: ScrewOptions = {},
93
+ ): Screw {
94
+ const spec = resolveSpecification(input);
95
+ validateHead(spec);
96
+ const type = options.recess ?? 'H';
97
+ if (type !== 'H' && type !== 'Z')
98
+ throw new Error('Cross recess type must be H or Z.');
99
+ const recess = spec.recesses[type];
100
+ if (recess.depth >= spec.headHeight || recess.width >= recess.diameter)
101
+ throw new Error('Cross recess must fit inside the head.');
102
+ const blank = panHead(
103
+ spec.headDiameter,
104
+ spec.headHeight,
105
+ recess.diameter * 1.05,
106
+ spec.headCurvatureRadius,
107
+ );
108
+ return headedScrew(
109
+ crossSocket(blank, recess, type),
110
+ spec,
111
+ length,
112
+ threadLength(spec, length),
113
+ );
114
+ }
115
+ /** @code3d.arguments ['M6', 10] */
116
+ export function clearanceHole(input: ScrewInput, depth: number): ClearanceHole;
117
+ export function clearanceHole(
118
+ input: ScrewInput,
119
+ options: PlainHoleOptions,
120
+ ): ClearanceHole;
121
+ export function clearanceHole(
122
+ input: ScrewInput,
123
+ options: CounterboredHoleOptions,
124
+ ): CounterboredHole;
125
+ export function clearanceHole(
126
+ input: ScrewInput,
127
+ options: ClearanceHoleOptions,
128
+ ): ClearanceHole | CounterboredHole;
129
+ export function clearanceHole(
130
+ input: ScrewInput,
131
+ options: number | ClearanceHoleOptions,
132
+ ): ClearanceHole | CounterboredHole {
133
+ return buildClearanceHole(resolveSpecification(input), options);
134
+ }