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.
- b3dkit/__init__.py +10 -0
- b3dkit/antichamfer.py +79 -0
- b3dkit/ball_socket.py +177 -0
- b3dkit/basic_shapes.py +397 -0
- b3dkit/bolt_fittings.py +351 -0
- b3dkit/click_fit.py +78 -0
- b3dkit/dovetail.py +1338 -0
- b3dkit/hexwall.py +102 -0
- b3dkit/high_top_slide_box.py +504 -0
- b3dkit/point.py +196 -0
- b3dkit/slide_box.py +210 -0
- b3dkit/twist_snap.py +302 -0
- b3dkit-0.1.0.dist-info/METADATA +54 -0
- b3dkit-0.1.0.dist-info/RECORD +16 -0
- b3dkit-0.1.0.dist-info/WHEEL +4 -0
- b3dkit-0.1.0.dist-info/licenses/LICENSE +7 -0
b3dkit/bolt_fittings.py
ADDED
|
@@ -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
|
+
)
|