b3dkit 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,351 @@
1
+ from typing import Union
2
+ from build123d import (
3
+ Align,
4
+ Axis,
5
+ BasePartObject,
6
+ BuildPart,
7
+ BuildSketch,
8
+ Circle,
9
+ Cylinder,
10
+ Location,
11
+ Mode,
12
+ Part,
13
+ Box,
14
+ Plane,
15
+ RegularPolygon,
16
+ RotationLike,
17
+ add,
18
+ extrude,
19
+ loft,
20
+ tuplify,
21
+ validate_inputs,
22
+ )
23
+ from b3dkit.antichamfer import anti_chamfer
24
+ from b3dkit.basic_shapes import TeardropCylinder
25
+ from ocp_vscode import show, Camera
26
+
27
+
28
+ class TeardropBoltCutSinkhole(BasePartObject):
29
+
30
+ def __init__(
31
+ self,
32
+ shaft_radius: float = 1.65,
33
+ shaft_depth: float = 2,
34
+ head_radius: float = 3.1,
35
+ head_depth: float = 5,
36
+ chamfer_radius: float = 1,
37
+ extension_distance: float = 100,
38
+ teardrop_ratio: float = 1.1,
39
+ rotation: RotationLike = (0, 0, 0),
40
+ align: Union[None, Align, tuple[Align, Align, Align]] = None,
41
+ mode: Mode = Mode.ADD,
42
+ ):
43
+ """
44
+ template for the cutout for a hexagonal nutt
45
+ -------
46
+ arguments:
47
+ - shaft_radius: float
48
+ The radius of the bolt shaft
49
+ - shaft_depth: float
50
+ The depth of the shaft portion
51
+ - head_radius: float
52
+ The radius of the bolt head countersink
53
+ - head_depth: float
54
+ The depth of the head countersink
55
+ - chamfer_radius: float
56
+ The radius of the anti-chamfer at the top
57
+ - extension_distance: float
58
+ How far to extend the shaft beyond the head (for through-holes)
59
+ - teardrop_ratio: float
60
+ The ratio to stretch the teardrop shape (1.0 = circle, 1.1 = recommended teardrop)
61
+ - rotation: the rotation of the sinkhole
62
+ - align: the alignment of the sinkhole (default
63
+ is (Align.CENTER, Align.CENTER, Align.CENTER) )
64
+ - mode: the mode to use when adding the sinkhole
65
+ """
66
+ context: BuildPart = BuildPart._get_context()
67
+ validate_inputs(context, self)
68
+
69
+ with BuildPart(Location((0, 0, shaft_depth))) as sinkhole:
70
+ TeardropCylinder(
71
+ radius=shaft_radius,
72
+ height=shaft_depth,
73
+ peak_distance=shaft_radius * teardrop_ratio,
74
+ align=(Align.CENTER, Align.CENTER, Align.MAX),
75
+ ),
76
+ TeardropCylinder(
77
+ radius=head_radius,
78
+ height=head_depth,
79
+ peak_distance=head_radius * teardrop_ratio,
80
+ align=(Align.CENTER, Align.CENTER, Align.MIN),
81
+ ),
82
+ if extension_distance > 0:
83
+ # extrude(sinkhole.faces().sort_by(Axis.Z)[0], amount=extension_distance)
84
+ extrude(
85
+ sinkhole.faces().sort_by(Axis.Z)[-1],
86
+ amount=extension_distance,
87
+ )
88
+ final_template = anti_chamfer(
89
+ sinkhole.part.faces().sort_by(Axis.Z)[-1],
90
+ chamfer_radius,
91
+ )
92
+ super().__init__(
93
+ part=final_template,
94
+ rotation=rotation,
95
+ align=tuplify(align, 3),
96
+ mode=mode,
97
+ )
98
+
99
+
100
+ class BoltCutSinkhole(BasePartObject):
101
+
102
+ def __init__(
103
+ self,
104
+ shaft_radius: float = 1.65,
105
+ shaft_depth: float = 2,
106
+ head_radius: float = 3.1,
107
+ head_depth: float = 5,
108
+ chamfer_radius: float = 1,
109
+ extension_distance: float = 100,
110
+ rotation: RotationLike = (0, 0, 0),
111
+ align: Union[None, Align, tuple[Align, Align, Align]] = None,
112
+ mode: Mode = Mode.ADD,
113
+ ):
114
+ """create a cylindrical bolt hole with countersink
115
+ ----------
116
+ Arguments:
117
+ - shaft_radius: float
118
+ The radius of the bolt shaft
119
+ - shaft_depth: float
120
+ The depth of the shaft portion
121
+ - head_radius: float
122
+ The radius of the bolt head countersink
123
+ - head_depth: float
124
+ The depth of the head countersink
125
+ - chamfer_radius: float
126
+ The radius of the anti-chamfer at the top
127
+ - extension_distance: float
128
+ How far to extend the shaft beyond the head (for through-holes)
129
+ - rotation: the rotation of the sinkhole
130
+ - align: the alignment of the sinkhole (default
131
+ is (Align.CENTER, Align.CENTER, Align.CENTER) )
132
+ - mode: the mode to use when adding the sinkhole
133
+ Returns:
134
+ - Part: A cylindrical bolt hole part with countersink"""
135
+ pt = TeardropBoltCutSinkhole(
136
+ shaft_radius=shaft_radius,
137
+ shaft_depth=shaft_depth,
138
+ head_radius=head_radius,
139
+ head_depth=head_depth,
140
+ chamfer_radius=chamfer_radius,
141
+ extension_distance=extension_distance,
142
+ teardrop_ratio=1.0,
143
+ )
144
+ super().__init__(part=pt, rotation=rotation, align=tuplify(align, 3), mode=mode)
145
+
146
+
147
+ class SquareNutSinkhole(BasePartObject):
148
+
149
+ def __init__(
150
+ self,
151
+ bolt_radius: float = 1.65,
152
+ bolt_depth: float = 2,
153
+ nut_height: float = 2.1,
154
+ nut_legnth: float = 5.6,
155
+ nut_depth: float = 100,
156
+ bolt_extension: float = 1,
157
+ rotation: RotationLike = (0, 0, 0),
158
+ align: Union[None, Align, tuple[Align, Align, Align]] = None,
159
+ mode: Mode = Mode.ADD,
160
+ ):
161
+ """create a bolt hole with square nut trap
162
+ ----------
163
+ Arguments:
164
+ - bolt_radius: float
165
+ The radius of the bolt shaft
166
+ - bolt_depth: float
167
+ The depth of the bolt hole before the nut trap
168
+ - nut_height: float
169
+ The height (thickness) of the square nut
170
+ - nut_legnth: float
171
+ The side length of the square nut
172
+ - nut_depth: float
173
+ How far the nut trap extends
174
+ - bolt_extension: float
175
+ How far to extend the bolt hole beyond the nut trap
176
+ - rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0)
177
+ - align (Align | tuple[Align, Align, Align] | None, optional): align MIN, CENTER,
178
+ or MAX of object. Defaults to (Align.CENTER, Align.CENTER, Align.CENTER)
179
+ - mode (Mode, optional): combine mode. Defaults to Mode.ADD
180
+ """
181
+ with BuildPart() as sinkhole:
182
+ TeardropCylinder(
183
+ radius=bolt_radius,
184
+ height=bolt_depth,
185
+ peak_distance=bolt_radius * 1.1,
186
+ rotation=(-90, 0, 0),
187
+ align=(Align.CENTER, Align.CENTER, Align.MIN),
188
+ ),
189
+ with BuildPart(Location((0, bolt_depth, 0))) as nut:
190
+ Box(
191
+ nut_legnth,
192
+ nut_height,
193
+ nut_legnth,
194
+ align=(Align.CENTER, Align.MIN, Align.CENTER),
195
+ )
196
+ extrude(nut.part.faces().sort_by(Axis.Z)[-1], amount=nut_depth)
197
+ if bolt_extension > 0:
198
+ with BuildPart(Location((0, bolt_depth + nut_height, 0))) as nut:
199
+ TeardropCylinder(
200
+ radius=bolt_radius,
201
+ height=bolt_extension,
202
+ peak_distance=bolt_radius * 1.1,
203
+ rotation=(-90, 0, 0),
204
+ align=(Align.CENTER, Align.CENTER, Align.MIN),
205
+ ),
206
+ super().__init__(
207
+ part=sinkhole.part,
208
+ rotation=rotation,
209
+ align=tuplify(align, 3),
210
+ mode=mode,
211
+ )
212
+
213
+
214
+ class NutCut(BasePartObject):
215
+
216
+ def __init__(
217
+ self,
218
+ head_radius: float = 3,
219
+ head_depth: float = 5,
220
+ shaft_radius: float = 2.1,
221
+ shaft_length: float = 20,
222
+ rotation: RotationLike = (0, 0, 0),
223
+ align: Union[None, Align, tuple[Align, Align, Align]] = None,
224
+ mode: Mode = Mode.ADD,
225
+ ):
226
+ """
227
+ template for the cutout for a hexagonal nutt
228
+ -------
229
+ arguments:
230
+ - head_radius: the radius of the heatsink head
231
+ - head_depth: the depth of the heatsink head
232
+ - shaft_radius: the radius of the bolt shaft
233
+ - shaft_length: the length of the bolt shaft
234
+ - rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0)
235
+ - align (Align | tuple[Align, Align, Align] | None, optional): align MIN, CENTER,
236
+ or MAX of object. Defaults to (Align.CENTER, Align.CENTER, Align.CENTER)
237
+ - mode (Mode, optional): combine mode. Defaults to Mode.ADD
238
+ """
239
+ context: BuildPart = BuildPart._get_context()
240
+ validate_inputs(context, self)
241
+
242
+ with BuildPart(Location((0, 0, head_depth))) as cut:
243
+ with BuildSketch():
244
+ RegularPolygon(radius=head_radius, side_count=6)
245
+ extrude(amount=-head_depth)
246
+ Cylinder(
247
+ radius=shaft_radius,
248
+ height=shaft_length,
249
+ align=(Align.CENTER, Align.CENTER, Align.MIN),
250
+ )
251
+
252
+ super().__init__(
253
+ part=cut.part, rotation=rotation, align=tuplify(align, 3), mode=mode
254
+ )
255
+
256
+
257
+ class ScrewCut(BasePartObject):
258
+
259
+ def __init__(
260
+ self,
261
+ head_radius: float = 4.5,
262
+ head_depth: float = 1.4,
263
+ shaft_radius: float = 2.25,
264
+ shaft_length: float = 20,
265
+ bottom_clearance: float = 20,
266
+ rotation: RotationLike = (0, 0, 0),
267
+ align: Union[None, Align, tuple[Align, Align, Align]] = None,
268
+ mode: Mode = Mode.ADD,
269
+ ):
270
+ """
271
+ template for the cutout for a screwhead
272
+ -------
273
+ arguments:
274
+ - head_radius: the radius of the heatsink head
275
+ - head_depth: the depth of the heatsink head
276
+ - shaft_radius: the radius of the bolt shaft
277
+ - shaft_length: the length of the bolt shaft
278
+ - rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0)
279
+ - align (Align | tuple[Align, Align, Align] | None, optional): align MIN, CENTER,
280
+ or MAX of object. Defaults to (Align.CENTER, Align.CENTER, Align.CENTER)
281
+ - mode (Mode, optional): combine mode. Defaults to Mode.ADD
282
+ """
283
+ context: BuildPart = BuildPart._get_context()
284
+ validate_inputs(context, self)
285
+
286
+ if head_radius <= shaft_radius:
287
+ raise ValueError("head_radius must be larger than shaft_radius")
288
+ with BuildPart() as head:
289
+ with BuildSketch(Plane.XY.offset(-bottom_clearance)):
290
+ Circle(head_radius)
291
+ with BuildSketch():
292
+ Circle(head_radius)
293
+ with BuildSketch(Plane.XY.offset(head_depth)):
294
+ Circle(head_radius)
295
+ with BuildSketch(Plane.XY.offset(head_depth + head_radius - shaft_radius)):
296
+ Circle(shaft_radius)
297
+ with BuildSketch(Plane.XY.offset(shaft_length)):
298
+ Circle(shaft_radius)
299
+ loft(ruled=True)
300
+ super().__init__(
301
+ part=head.part, rotation=rotation, align=tuplify(align, 3), mode=mode
302
+ )
303
+
304
+
305
+ class HeatsinkCut(BasePartObject):
306
+
307
+ def __init__(
308
+ self,
309
+ head_radius: float = 3,
310
+ head_depth: float = 5,
311
+ shaft_radius: float = 2.1,
312
+ shaft_length: float = 20,
313
+ rotation: RotationLike = (0, 0, 0),
314
+ align: Union[None, Align, tuple[Align, Align, Align]] = None,
315
+ mode: Mode = Mode.ADD,
316
+ ):
317
+ """
318
+ template for the cutout for a heatsink and bolt
319
+ -------
320
+ arguments:
321
+ - head_radius: the radius of the heatsink head
322
+ - head_depth: the depth of the heatsink head
323
+ - shaft_radius: the radius of the bolt shaft
324
+ - shaft_length: the length of the bolt shaft
325
+ - rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0)
326
+ align (Align | tuple[Align, Align, Align] | None, optional): align MIN, CENTER,
327
+ or MAX of object. Defaults to (Align.CENTER, Align.CENTER, Align.CENTER)
328
+ - mode (Mode, optional): combine mode. Defaults to Mode.ADD
329
+ """
330
+ context: BuildPart = BuildPart._get_context()
331
+ validate_inputs(context, self)
332
+
333
+ with BuildPart(Location((0, 0, head_depth))) as cut:
334
+ Cylinder(
335
+ radius=head_radius,
336
+ height=head_depth,
337
+ align=(Align.CENTER, Align.CENTER, Align.MAX),
338
+ )
339
+ Cylinder(
340
+ radius=shaft_radius,
341
+ height=shaft_length,
342
+ align=(Align.CENTER, Align.CENTER, Align.MIN),
343
+ )
344
+
345
+ super().__init__(
346
+ part=cut.part, rotation=rotation, align=tuplify(align, 3), mode=mode
347
+ )
348
+
349
+
350
+ if __name__ == "__main__":
351
+ show(SquareNutSinkhole(), reset_camera=Camera.KEEP)
b3dkit/click_fit.py ADDED
@@ -0,0 +1,78 @@
1
+ """
2
+ click_fit.py allows for the creation of a preciscely fitting
3
+ snap fit connector that is easier to assemble and slower to
4
+ wear out than a simple half sphere.
5
+ """
6
+
7
+ from typing import Union
8
+ from build123d import (
9
+ Align,
10
+ Axis,
11
+ BasePartObject,
12
+ BuildPart,
13
+ BuildSketch,
14
+ Circle,
15
+ Mode,
16
+ Part,
17
+ Plane,
18
+ RotationLike,
19
+ chamfer,
20
+ extrude,
21
+ loft,
22
+ tuplify,
23
+ )
24
+
25
+ from ocp_vscode import show, Camera
26
+
27
+
28
+ class Divot(BasePartObject):
29
+
30
+ def __init__(
31
+ self,
32
+ radius: float = 0.5,
33
+ positive: bool = True,
34
+ extend_base=False,
35
+ rotation: RotationLike = (0, 0, 0),
36
+ align: Union[None, Align, tuple[Align, Align, Align]] = None,
37
+ mode: Mode = Mode.ADD,
38
+ ):
39
+ """Part Object: Divot
40
+ Create a divot that can be used to create a snap fit connector for 3d printing.
41
+ ----------
42
+ Arguments:
43
+ - radius: float
44
+ The radius of the divot.
45
+ - positive: bool
46
+ when True, reduces the size and shaping of the divot
47
+ for the extruded part. when False, deepens and widens the socket.
48
+ - extend_base: bool
49
+ when True, extends the base of the divot to allow for a clean
50
+ connection when attaching without precise placement.
51
+ - rotation (RotationLike, optional): angles to rotate about axes. Defaults to (0, 0, 0)
52
+ - align (Align | tuple[Align, Align, Align] | None, optional): align MIN, CENTER,
53
+ or MAX of object. Defaults to (Align.CENTER, Align.CENTER, Align.CENTER)
54
+ - mode (Mode, optional): combine mode. Defaults to Mode.ADD
55
+ """
56
+
57
+ tolerance = 0 if not positive else radius * 0.05
58
+ ratio = 0.5 if positive else 0.55
59
+ with BuildPart() as divot_part:
60
+ with BuildSketch():
61
+ Circle(radius - tolerance)
62
+ with BuildSketch(Plane.XY.offset(radius * ratio)) as sketch:
63
+ Circle(radius * ratio)
64
+ loft()
65
+ chamfer(divot_part.part.faces().sort_by(Axis.Z)[-1].edges(), radius * 0.1)
66
+ if extend_base:
67
+ extrude(divot_part.part.faces().sort_by(Axis.Z)[0], radius)
68
+ super().__init__(
69
+ divot_part.part, rotation=rotation, align=tuplify(align, 3), mode=mode
70
+ )
71
+
72
+
73
+ if __name__ == "__main__":
74
+ show(
75
+ Divot(10, extend_base=True),
76
+ Divot(10, positive=False),
77
+ reset_camera=Camera.KEEP,
78
+ )