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/dovetail.py
ADDED
|
@@ -0,0 +1,1338 @@
|
|
|
1
|
+
from enum import Enum, auto
|
|
2
|
+
from math import atan, atan2, degrees, radians, tan
|
|
3
|
+
from typing import Tuple
|
|
4
|
+
from build123d import (
|
|
5
|
+
Align,
|
|
6
|
+
Axis,
|
|
7
|
+
BuildLine,
|
|
8
|
+
BuildPart,
|
|
9
|
+
BuildSketch,
|
|
10
|
+
Box,
|
|
11
|
+
Compound,
|
|
12
|
+
Cylinder,
|
|
13
|
+
FilletPolyline,
|
|
14
|
+
GridLocations,
|
|
15
|
+
Line,
|
|
16
|
+
Location,
|
|
17
|
+
Mode,
|
|
18
|
+
Part,
|
|
19
|
+
Plane,
|
|
20
|
+
PolarLocations,
|
|
21
|
+
Polyline,
|
|
22
|
+
add,
|
|
23
|
+
extrude,
|
|
24
|
+
fillet,
|
|
25
|
+
loft,
|
|
26
|
+
make_face,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# it's a bad habit, but I keep some simple test code under __main__
|
|
30
|
+
# to make creating test object easy -- this adds ".b3dkit" to the path
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
import sys, os
|
|
33
|
+
|
|
34
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
from b3dkit.point import (
|
|
38
|
+
Point,
|
|
39
|
+
midpoint,
|
|
40
|
+
shifted_midpoint,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
from b3dkit.click_fit import Divot
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class DovetailPart(Enum):
|
|
47
|
+
TAIL = auto()
|
|
48
|
+
SOCKET = auto()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class DovetailStyle(Enum):
|
|
52
|
+
TRADITIONAL = auto()
|
|
53
|
+
SNUGTAIL = auto()
|
|
54
|
+
T_SLOT = auto()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def subpart_outline_boundary(
|
|
58
|
+
start: Point,
|
|
59
|
+
end: Point,
|
|
60
|
+
max_dimension: float,
|
|
61
|
+
section: DovetailPart = DovetailPart.TAIL,
|
|
62
|
+
tolerance: float = 0.1,
|
|
63
|
+
scarf_offset: float = 0,
|
|
64
|
+
) -> Line:
|
|
65
|
+
direction_multiplier = 1 if section == DovetailPart.TAIL else -1
|
|
66
|
+
base_angle = start.angle_to(end)
|
|
67
|
+
dovetail_tolerance = -(abs(tolerance / 2)) * direction_multiplier
|
|
68
|
+
adjusted_start_point = start.related_point(base_angle - 90, scarf_offset)
|
|
69
|
+
adjusted_end_point = end.related_point(base_angle - 90, scarf_offset)
|
|
70
|
+
toleranced_start_point = adjusted_start_point.related_point(
|
|
71
|
+
base_angle - 90, dovetail_tolerance
|
|
72
|
+
)
|
|
73
|
+
toleranced_end_point = adjusted_end_point.related_point(
|
|
74
|
+
base_angle - 90, dovetail_tolerance
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
with BuildLine() as border:
|
|
78
|
+
Polyline(
|
|
79
|
+
*[
|
|
80
|
+
tuple(toleranced_start_point),
|
|
81
|
+
tuple(
|
|
82
|
+
toleranced_start_point.related_point(
|
|
83
|
+
base_angle + 180, max_dimension
|
|
84
|
+
)
|
|
85
|
+
),
|
|
86
|
+
tuple(
|
|
87
|
+
toleranced_start_point.related_point(
|
|
88
|
+
base_angle - 225 * direction_multiplier, max_dimension
|
|
89
|
+
)
|
|
90
|
+
),
|
|
91
|
+
tuple(
|
|
92
|
+
toleranced_end_point.related_point(
|
|
93
|
+
base_angle + 45 * direction_multiplier, max_dimension
|
|
94
|
+
)
|
|
95
|
+
),
|
|
96
|
+
tuple(toleranced_end_point.related_point(base_angle, max_dimension)),
|
|
97
|
+
tuple(toleranced_end_point),
|
|
98
|
+
]
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
return border.line
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def snugtail_subpart_outline(
|
|
105
|
+
start: Point,
|
|
106
|
+
end: Point,
|
|
107
|
+
max_dimension: float = 1000,
|
|
108
|
+
section: DovetailPart = DovetailPart.TAIL,
|
|
109
|
+
tolerance: float = 0.025,
|
|
110
|
+
tail_angle_offset: float = 15,
|
|
111
|
+
taper_distance: float = 0,
|
|
112
|
+
length_ratio: float = 0.8,
|
|
113
|
+
depth_ratio: float = 0.15,
|
|
114
|
+
scarf_offset: float = 0,
|
|
115
|
+
straighten_dovetail: bool = False,
|
|
116
|
+
) -> Line:
|
|
117
|
+
"""
|
|
118
|
+
given a part and a start and end point on the XY plane, returns an outline to build an intersection Part to generate the subpart
|
|
119
|
+
args:
|
|
120
|
+
- part: the part to split
|
|
121
|
+
- start: the start point along the XY Plane for the dovetail line
|
|
122
|
+
- end: the end point along the XY Plane for the dovetail line
|
|
123
|
+
- section: the section of the dovetail to create (DovetailPart.TAIL or DovetailPart.SOCKET)
|
|
124
|
+
- tolerance: the tolerance for the split
|
|
125
|
+
- tail_angle_offset: the adjustment pitch of angle of the dovetail (0 will result in a square dovetail)
|
|
126
|
+
- taper_distance: an extra shrinking factor for the dovetail size, allows for easier assembly
|
|
127
|
+
- length_ratio: the ratio of the length of the tongue to the total length of the dovetail
|
|
128
|
+
- depth_ratio: the ratio of the depth of the tongue to the total length of the dovetail
|
|
129
|
+
- scarf_offset: setting this to a non-zero value will shift the dovetail to allow for tilt adjustemnt between the top & bottom outlines
|
|
130
|
+
- straighten_dovetail: setting this to True will draw the straight line of the cut,
|
|
131
|
+
allowing for the correct tolerances for the section
|
|
132
|
+
"""
|
|
133
|
+
if (length_ratio + depth_ratio > 1) and not straighten_dovetail:
|
|
134
|
+
raise ValueError(
|
|
135
|
+
"the combined length_ratio and depth_ratio must be not exceed 1"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
direction_multiplier = 1 if section == DovetailPart.TAIL else -1
|
|
139
|
+
base_angle = start.angle_to(end)
|
|
140
|
+
opposite_angle = 180 if base_angle == 0 else -base_angle
|
|
141
|
+
dovetail_tolerance = -(abs(tolerance / 2)) * direction_multiplier
|
|
142
|
+
adjusted_start_point = start.related_point(base_angle - 90, scarf_offset)
|
|
143
|
+
adjusted_end_point = end.related_point(base_angle - 90, scarf_offset)
|
|
144
|
+
toleranced_start_point = adjusted_start_point.related_point(
|
|
145
|
+
base_angle - 90, dovetail_tolerance
|
|
146
|
+
)
|
|
147
|
+
toleranced_end_point = adjusted_end_point.related_point(
|
|
148
|
+
base_angle - 90, dovetail_tolerance
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
cut_length = start.distance_to(end)
|
|
152
|
+
tail_depth = cut_length * depth_ratio
|
|
153
|
+
tail_length = cut_length * length_ratio
|
|
154
|
+
|
|
155
|
+
cut_start = toleranced_start_point
|
|
156
|
+
cut_end = toleranced_end_point
|
|
157
|
+
|
|
158
|
+
fin_join = cut_start.related_point(
|
|
159
|
+
base_angle, tail_depth / 2 + dovetail_tolerance
|
|
160
|
+
).related_point(base_angle - 90, tail_depth / 2 + dovetail_tolerance)
|
|
161
|
+
fin_depart = cut_end.related_point(
|
|
162
|
+
base_angle - 180, tail_depth / 2 + dovetail_tolerance
|
|
163
|
+
).related_point(base_angle - 90, tail_depth / 2 + dovetail_tolerance)
|
|
164
|
+
|
|
165
|
+
start_fin = cut_start.related_point(
|
|
166
|
+
base_angle, tail_depth / 2 + dovetail_tolerance
|
|
167
|
+
).related_point(base_angle + 90, dovetail_tolerance)
|
|
168
|
+
end_fin = cut_end.related_point(
|
|
169
|
+
base_angle - 180, tail_depth / 2 + dovetail_tolerance
|
|
170
|
+
).related_point(base_angle + 90, dovetail_tolerance)
|
|
171
|
+
|
|
172
|
+
start_snugtail = start_fin.related_point(
|
|
173
|
+
base_angle + 90, cut_length - tail_depth / 2
|
|
174
|
+
)
|
|
175
|
+
end_snugtail = end_fin.related_point(base_angle + 90, cut_length - tail_depth / 2)
|
|
176
|
+
|
|
177
|
+
fin_connect = start_fin.related_point(
|
|
178
|
+
base_angle + 90, cut_length - dovetail_tolerance
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
fin_disconnect = end_fin.related_point(
|
|
182
|
+
base_angle + 90, cut_length - dovetail_tolerance
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
start_tail_line = fin_connect.related_point(
|
|
186
|
+
base_angle,
|
|
187
|
+
abs(dovetail_tolerance) * (4 if section == DovetailPart.TAIL else 6)
|
|
188
|
+
- dovetail_tolerance * 2,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
end_tail_line = fin_disconnect.related_point(
|
|
192
|
+
opposite_angle,
|
|
193
|
+
abs(dovetail_tolerance) * (4 if section == DovetailPart.TAIL else 6)
|
|
194
|
+
- dovetail_tolerance * 2,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
with BuildLine() as tail_line:
|
|
198
|
+
add(
|
|
199
|
+
subpart_outline_boundary(
|
|
200
|
+
start=start,
|
|
201
|
+
end=end,
|
|
202
|
+
max_dimension=max_dimension,
|
|
203
|
+
section=section,
|
|
204
|
+
tolerance=tolerance,
|
|
205
|
+
scarf_offset=scarf_offset,
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
FilletPolyline(
|
|
210
|
+
*[cut_start, fin_join, start_fin],
|
|
211
|
+
radius=abs(dovetail_tolerance) * (3 if section == DovetailPart.TAIL else 2),
|
|
212
|
+
)
|
|
213
|
+
if straighten_dovetail:
|
|
214
|
+
Line(start_fin, start_snugtail)
|
|
215
|
+
else:
|
|
216
|
+
add(
|
|
217
|
+
dovetail_split_line(
|
|
218
|
+
start=start_fin.related_point(base_angle, -dovetail_tolerance),
|
|
219
|
+
end=start_snugtail.related_point(base_angle, -dovetail_tolerance),
|
|
220
|
+
linear_offset=-tail_depth / 2,
|
|
221
|
+
section=section,
|
|
222
|
+
tolerance=tolerance,
|
|
223
|
+
tail_angle_offset=tail_angle_offset,
|
|
224
|
+
taper_distance=taper_distance,
|
|
225
|
+
length_ratio=length_ratio,
|
|
226
|
+
depth_ratio=depth_ratio,
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
FilletPolyline(
|
|
230
|
+
*[start_snugtail, fin_connect, start_tail_line],
|
|
231
|
+
radius=abs(dovetail_tolerance) * (2 if section == DovetailPart.TAIL else 3),
|
|
232
|
+
)
|
|
233
|
+
if straighten_dovetail:
|
|
234
|
+
Line(
|
|
235
|
+
start_tail_line,
|
|
236
|
+
end_tail_line,
|
|
237
|
+
)
|
|
238
|
+
else:
|
|
239
|
+
add(
|
|
240
|
+
dovetail_split_line(
|
|
241
|
+
start=start_tail_line.related_point(
|
|
242
|
+
base_angle - 90, -dovetail_tolerance
|
|
243
|
+
),
|
|
244
|
+
end=end_tail_line.related_point(
|
|
245
|
+
base_angle - 90, -dovetail_tolerance
|
|
246
|
+
),
|
|
247
|
+
section=section,
|
|
248
|
+
linear_offset=0,
|
|
249
|
+
tolerance=tolerance,
|
|
250
|
+
tail_angle_offset=tail_angle_offset,
|
|
251
|
+
taper_distance=taper_distance,
|
|
252
|
+
length_ratio=length_ratio,
|
|
253
|
+
depth_ratio=depth_ratio,
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
FilletPolyline(
|
|
257
|
+
*[end_tail_line, fin_disconnect, end_snugtail],
|
|
258
|
+
radius=abs(dovetail_tolerance) * (2 if section == DovetailPart.TAIL else 3),
|
|
259
|
+
)
|
|
260
|
+
if straighten_dovetail:
|
|
261
|
+
Line(end_snugtail, end_fin)
|
|
262
|
+
else:
|
|
263
|
+
add(
|
|
264
|
+
dovetail_split_line(
|
|
265
|
+
start=end_snugtail.related_point(base_angle, dovetail_tolerance),
|
|
266
|
+
end=end_fin.related_point(base_angle, dovetail_tolerance),
|
|
267
|
+
section=section,
|
|
268
|
+
linear_offset=tail_depth / 2,
|
|
269
|
+
tolerance=tolerance,
|
|
270
|
+
tail_angle_offset=tail_angle_offset,
|
|
271
|
+
taper_distance=taper_distance,
|
|
272
|
+
length_ratio=length_ratio,
|
|
273
|
+
depth_ratio=depth_ratio,
|
|
274
|
+
)
|
|
275
|
+
)
|
|
276
|
+
FilletPolyline(
|
|
277
|
+
*[end_fin, fin_depart, cut_end],
|
|
278
|
+
radius=abs(dovetail_tolerance) * (3 if section == DovetailPart.TAIL else 2),
|
|
279
|
+
)
|
|
280
|
+
return tail_line.line
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def dovetail_subpart_outline(
|
|
284
|
+
start: Point,
|
|
285
|
+
end: Point,
|
|
286
|
+
max_dimension: float = 1000,
|
|
287
|
+
section: DovetailPart = DovetailPart.TAIL,
|
|
288
|
+
style: DovetailStyle = DovetailStyle.TRADITIONAL,
|
|
289
|
+
linear_offset: float = 0,
|
|
290
|
+
tolerance: float = 0.025,
|
|
291
|
+
tail_angle_offset: float = 15,
|
|
292
|
+
taper_distance: float = 0,
|
|
293
|
+
length_ratio: float = 1 / 3,
|
|
294
|
+
depth_ratio: float = 1 / 6,
|
|
295
|
+
slot_count: int = 1,
|
|
296
|
+
depth: float = 2,
|
|
297
|
+
scarf_offset: float = 0,
|
|
298
|
+
straighten_dovetail: bool = False,
|
|
299
|
+
) -> Line:
|
|
300
|
+
"""
|
|
301
|
+
given a part and a start and end point on the XY plane, returns an outline to build an intersection Part to generate the subpart
|
|
302
|
+
args:
|
|
303
|
+
- part: the part to split
|
|
304
|
+
- start: the start point along the XY Plane for the dovetail line
|
|
305
|
+
- end: the end point along the XY Plane for the dovetail line
|
|
306
|
+
- section: the section of the dovetail to create (DovetailPart.TAIL or DovetailPart.SOCKET)
|
|
307
|
+
- style: valid styles are DovetailStyle.TRADITIONAL, DovetailStyle.T_SLOT
|
|
308
|
+
- linear_offset: offsets the center of the tail or socket along the line by the ammount specified
|
|
309
|
+
- tolerance: the tolerance for the split
|
|
310
|
+
- tail_angle_offset: the adjustment pitch of angle of the dovetail (0 will result in a square dovetail)
|
|
311
|
+
- taper_distance: an extra shrinking factor for the dovetail size, allows for easier assembly
|
|
312
|
+
- length_ratio: the ratio of the length of the tongue to the total length of the dovetail
|
|
313
|
+
- depth_ratio: the ratio of the depth of the tongue to the total length of the dovetail
|
|
314
|
+
- scarf_offset: setting this to a non-zero value will shift the dovetail to allow for tilt adjustemnt between the top & bottom outlines
|
|
315
|
+
- straighten_dovetail: setting this to True will draw the straight line of the cut,
|
|
316
|
+
allowing for the correct tolerances for the section
|
|
317
|
+
"""
|
|
318
|
+
if style not in (DovetailStyle.TRADITIONAL, DovetailStyle.T_SLOT):
|
|
319
|
+
raise ValueError(f"Invalid style: {style}")
|
|
320
|
+
direction_multiplier = 1 if section == DovetailPart.TAIL else -1
|
|
321
|
+
base_angle = start.angle_to(end)
|
|
322
|
+
dovetail_tolerance = -(abs(tolerance / 2)) * direction_multiplier
|
|
323
|
+
adjusted_start_point = start.related_point(base_angle - 90, scarf_offset)
|
|
324
|
+
adjusted_end_point = end.related_point(base_angle - 90, scarf_offset)
|
|
325
|
+
toleranced_start_point = adjusted_start_point.related_point(
|
|
326
|
+
base_angle - 90, dovetail_tolerance
|
|
327
|
+
)
|
|
328
|
+
toleranced_end_point = adjusted_end_point.related_point(
|
|
329
|
+
base_angle - 90, dovetail_tolerance
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
with BuildLine() as tail_line:
|
|
333
|
+
add(
|
|
334
|
+
subpart_outline_boundary(
|
|
335
|
+
start=start,
|
|
336
|
+
end=end,
|
|
337
|
+
max_dimension=max_dimension,
|
|
338
|
+
section=section,
|
|
339
|
+
tolerance=tolerance,
|
|
340
|
+
scarf_offset=scarf_offset,
|
|
341
|
+
)
|
|
342
|
+
)
|
|
343
|
+
if straighten_dovetail:
|
|
344
|
+
Line(toleranced_start_point, toleranced_end_point)
|
|
345
|
+
elif style == DovetailStyle.T_SLOT:
|
|
346
|
+
add(
|
|
347
|
+
tslot_split_line(
|
|
348
|
+
start=adjusted_start_point,
|
|
349
|
+
end=adjusted_end_point,
|
|
350
|
+
section=section,
|
|
351
|
+
slot_count=slot_count,
|
|
352
|
+
depth=depth,
|
|
353
|
+
tolerance=tolerance,
|
|
354
|
+
taper_distance=taper_distance,
|
|
355
|
+
)
|
|
356
|
+
)
|
|
357
|
+
else:
|
|
358
|
+
add(
|
|
359
|
+
dovetail_split_line(
|
|
360
|
+
start=adjusted_start_point,
|
|
361
|
+
end=adjusted_end_point,
|
|
362
|
+
section=section,
|
|
363
|
+
linear_offset=linear_offset,
|
|
364
|
+
tolerance=tolerance,
|
|
365
|
+
tail_angle_offset=tail_angle_offset,
|
|
366
|
+
taper_distance=taper_distance,
|
|
367
|
+
length_ratio=length_ratio,
|
|
368
|
+
depth_ratio=depth_ratio,
|
|
369
|
+
)
|
|
370
|
+
)
|
|
371
|
+
return tail_line.line
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
def subpart_outline(
|
|
375
|
+
start: Point,
|
|
376
|
+
end: Point,
|
|
377
|
+
max_dimension: float = 1000,
|
|
378
|
+
section: DovetailPart = DovetailPart.TAIL,
|
|
379
|
+
style: DovetailStyle = DovetailStyle.SNUGTAIL,
|
|
380
|
+
linear_offset: float = 0,
|
|
381
|
+
tolerance: float = 0.025,
|
|
382
|
+
tail_angle_offset: float = 15,
|
|
383
|
+
taper_distance: float = 0,
|
|
384
|
+
length_ratio: float = 1 / 3,
|
|
385
|
+
depth_ratio: float = 1 / 6,
|
|
386
|
+
scarf_offset: float = 0,
|
|
387
|
+
slot_count: int = 2,
|
|
388
|
+
depth: float = 2,
|
|
389
|
+
straighten_dovetail: bool = False,
|
|
390
|
+
) -> Line:
|
|
391
|
+
"""
|
|
392
|
+
given a part and a start and end point on the XY plane, returns an outline to build an intersection Part to generate the subpart
|
|
393
|
+
args:
|
|
394
|
+
- start: the start point along the XY Plane for the dovetail line
|
|
395
|
+
- end: the end point along the XY Plane for the dovetail line
|
|
396
|
+
- max_dimension: the maximum dimension of the part to split, used to determine the size of the outline
|
|
397
|
+
- section: the section of the dovetail to create (DovetailPart.TAIL or DovetailPart.SOCKET)
|
|
398
|
+
- linear_offset: offsets the center of the tail or socket along the line by the ammount specified
|
|
399
|
+
- tolerance: the tolerance for the split
|
|
400
|
+
- tail_angle_offset: the adjustment pitch of angle of the dovetail (0 will result in a square dovetail)
|
|
401
|
+
- taper_distance: an extra shrinking factor for the dovetail size, allows for easier assembly
|
|
402
|
+
- length_ratio: the ratio of the length of the tongue to the total length of the dovetail
|
|
403
|
+
- depth_ratio: the ratio of the depth of the tongue to the total length of the dovetail
|
|
404
|
+
- scarf_offset: setting this to a non-zero value will shift the dovetail to allow for tilt adjustemnt between the top & bottom outlines
|
|
405
|
+
- straighten_dovetail: setting this to True will draw the straight line of the cut,
|
|
406
|
+
allowing for the correct tolerances for the section
|
|
407
|
+
"""
|
|
408
|
+
if style == DovetailStyle.SNUGTAIL:
|
|
409
|
+
return snugtail_subpart_outline(
|
|
410
|
+
start=start,
|
|
411
|
+
end=end,
|
|
412
|
+
max_dimension=max_dimension,
|
|
413
|
+
section=section,
|
|
414
|
+
tolerance=tolerance,
|
|
415
|
+
tail_angle_offset=tail_angle_offset,
|
|
416
|
+
taper_distance=taper_distance,
|
|
417
|
+
length_ratio=length_ratio,
|
|
418
|
+
scarf_offset=scarf_offset,
|
|
419
|
+
straighten_dovetail=straighten_dovetail,
|
|
420
|
+
)
|
|
421
|
+
else:
|
|
422
|
+
return dovetail_subpart_outline(
|
|
423
|
+
start=start,
|
|
424
|
+
end=end,
|
|
425
|
+
max_dimension=max_dimension,
|
|
426
|
+
section=section,
|
|
427
|
+
style=style,
|
|
428
|
+
linear_offset=linear_offset,
|
|
429
|
+
tolerance=tolerance,
|
|
430
|
+
tail_angle_offset=tail_angle_offset,
|
|
431
|
+
taper_distance=taper_distance,
|
|
432
|
+
length_ratio=length_ratio,
|
|
433
|
+
depth_ratio=depth_ratio,
|
|
434
|
+
scarf_offset=scarf_offset,
|
|
435
|
+
slot_count=slot_count,
|
|
436
|
+
depth=depth,
|
|
437
|
+
straighten_dovetail=straighten_dovetail,
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def traditional_subpart_divots(
|
|
442
|
+
subpart: Part,
|
|
443
|
+
start: Point,
|
|
444
|
+
end: Point,
|
|
445
|
+
section: DovetailPart = DovetailPart.TAIL,
|
|
446
|
+
linear_offset: float = 0,
|
|
447
|
+
tolerance: float = 0.025,
|
|
448
|
+
vertical_tolerance: float = 0.2,
|
|
449
|
+
scarf_angle: float = 0,
|
|
450
|
+
taper_angle: float = 0,
|
|
451
|
+
depth_ratio: float = 1 / 6,
|
|
452
|
+
vertical_offset: float = 0,
|
|
453
|
+
click_fit_radius: float = 0,
|
|
454
|
+
):
|
|
455
|
+
"""
|
|
456
|
+
adds/subtracts click-fit divots to subpart and returns it
|
|
457
|
+
----------
|
|
458
|
+
Arguments:
|
|
459
|
+
- subpart: the part to add divots to
|
|
460
|
+
- start: the start point along the XY Plane for the dovetail line
|
|
461
|
+
- end: the end point along the XY Plane for the dovetail line
|
|
462
|
+
- section: the section of the dovetail to create (DovetailPart.TAIL or DovetailPart.SOCKET)
|
|
463
|
+
- linear_offset: offsets the center of the tail or socket along the line by the ammount specified
|
|
464
|
+
- tolerance: the tolerance for the split
|
|
465
|
+
- scarf_angle: the scarf angle of the dovetail
|
|
466
|
+
- taper_angle: an extra shrinking factor for the dovetail size, allows for easier assembly
|
|
467
|
+
- length_ratio: the ratio of the length of the tongue to the total length of the dovetail
|
|
468
|
+
- vertical_offset: the vertical offset of the dovetail
|
|
469
|
+
- click_fit_radius: the radius of the click-fit divots
|
|
470
|
+
"""
|
|
471
|
+
|
|
472
|
+
cut_angle = start.angle_to(end)
|
|
473
|
+
|
|
474
|
+
# how much of an offset is there along the top and bottom of the subparts
|
|
475
|
+
scarf_offset = (subpart.bounding_box().size.Z) * tan(radians(scarf_angle)) / 2
|
|
476
|
+
|
|
477
|
+
tailtop_z = subpart.bounding_box().max.Z + (
|
|
478
|
+
vertical_offset if vertical_offset < 0 else 0
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
adjusted_top_divot_angle = scarf_angle - taper_angle
|
|
482
|
+
|
|
483
|
+
taper_offset = (subpart.bounding_box().size.Z - abs(vertical_offset)) * tan(
|
|
484
|
+
radians(adjusted_top_divot_angle)
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
topmode = (
|
|
488
|
+
Mode.SUBTRACT
|
|
489
|
+
if ((section == DovetailPart.TAIL) == (vertical_offset < 0))
|
|
490
|
+
else Mode.ADD
|
|
491
|
+
)
|
|
492
|
+
bottommode = (
|
|
493
|
+
Mode.ADD
|
|
494
|
+
if ((section == DovetailPart.SOCKET) == (vertical_offset >= 0))
|
|
495
|
+
else Mode.SUBTRACT
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
top_divot_center = shifted_midpoint(start, end, linear_offset).related_point(
|
|
499
|
+
cut_angle - 90,
|
|
500
|
+
start.distance_to(end) * depth_ratio
|
|
501
|
+
- scarf_offset
|
|
502
|
+
+ taper_offset
|
|
503
|
+
- click_fit_radius / 2,
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
with BuildPart() as divotedpart:
|
|
507
|
+
add(subpart, mode=Mode.ADD)
|
|
508
|
+
with BuildPart(
|
|
509
|
+
Location(
|
|
510
|
+
(
|
|
511
|
+
top_divot_center.x,
|
|
512
|
+
top_divot_center.y,
|
|
513
|
+
tailtop_z - click_fit_radius * 2,
|
|
514
|
+
)
|
|
515
|
+
),
|
|
516
|
+
mode=topmode,
|
|
517
|
+
):
|
|
518
|
+
add(
|
|
519
|
+
Divot(
|
|
520
|
+
click_fit_radius,
|
|
521
|
+
positive=topmode == Mode.ADD,
|
|
522
|
+
extend_base=True,
|
|
523
|
+
)
|
|
524
|
+
.rotate(
|
|
525
|
+
Axis.X,
|
|
526
|
+
(90 * (-1 if vertical_offset < 0 else 1))
|
|
527
|
+
+ adjusted_top_divot_angle,
|
|
528
|
+
)
|
|
529
|
+
.rotate(Axis.Y, cut_angle),
|
|
530
|
+
)
|
|
531
|
+
#####################################
|
|
532
|
+
# Bottom divots
|
|
533
|
+
#####################################
|
|
534
|
+
start_side = start.related_point(cut_angle, click_fit_radius * 2).related_point(
|
|
535
|
+
cut_angle + 90,
|
|
536
|
+
scarf_offset - ((click_fit_radius * 2) * tan(radians(scarf_angle))),
|
|
537
|
+
)
|
|
538
|
+
end_side = end.related_point(cut_angle, -click_fit_radius * 2).related_point(
|
|
539
|
+
cut_angle - 90,
|
|
540
|
+
-scarf_offset + ((click_fit_radius * 2) * tan(radians(scarf_angle))),
|
|
541
|
+
)
|
|
542
|
+
with BuildPart(
|
|
543
|
+
Location(
|
|
544
|
+
(
|
|
545
|
+
start_side.x,
|
|
546
|
+
start_side.y,
|
|
547
|
+
click_fit_radius * 2,
|
|
548
|
+
)
|
|
549
|
+
),
|
|
550
|
+
mode=bottommode,
|
|
551
|
+
):
|
|
552
|
+
add(
|
|
553
|
+
Divot(
|
|
554
|
+
click_fit_radius,
|
|
555
|
+
positive=bottommode == Mode.ADD,
|
|
556
|
+
extend_base=True,
|
|
557
|
+
)
|
|
558
|
+
.rotate(
|
|
559
|
+
Axis.X,
|
|
560
|
+
(90 * (1 if vertical_offset < 0 else -1)) + scarf_angle,
|
|
561
|
+
)
|
|
562
|
+
.rotate(Axis.Y, cut_angle),
|
|
563
|
+
)
|
|
564
|
+
with BuildPart(
|
|
565
|
+
Location((end_side.x, end_side.y, click_fit_radius * 2)),
|
|
566
|
+
mode=bottommode,
|
|
567
|
+
):
|
|
568
|
+
add(
|
|
569
|
+
Divot(click_fit_radius, positive=True, extend_base=True)
|
|
570
|
+
.rotate(
|
|
571
|
+
Axis.X,
|
|
572
|
+
(90 * (1 if vertical_offset < 0 else -1)) + scarf_angle,
|
|
573
|
+
)
|
|
574
|
+
.rotate(Axis.Y, cut_angle),
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
return divotedpart.part
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
def snugtail_divots(
|
|
581
|
+
subpart: Part,
|
|
582
|
+
start: Point,
|
|
583
|
+
end: Point,
|
|
584
|
+
section: DovetailPart = DovetailPart.TAIL,
|
|
585
|
+
tolerance: float = 0.025,
|
|
586
|
+
scarf_angle: float = 0,
|
|
587
|
+
depth_ratio: float = 1 / 10,
|
|
588
|
+
length_ratio: float = 1 / 5,
|
|
589
|
+
vertical_offset: float = 0,
|
|
590
|
+
click_fit_radius: float = 0,
|
|
591
|
+
) -> Part:
|
|
592
|
+
part_width = start.distance_to(end)
|
|
593
|
+
tail_depth = part_width * depth_ratio
|
|
594
|
+
direction_multiplier = -1 if section == DovetailPart.TAIL else 1
|
|
595
|
+
inner_width = (
|
|
596
|
+
part_width - (part_width * depth_ratio * 2) - (tolerance * direction_multiplier)
|
|
597
|
+
)
|
|
598
|
+
cut_angle = start.angle_to(end)
|
|
599
|
+
with BuildPart() as divotedpart:
|
|
600
|
+
add(subpart, mode=Mode.ADD)
|
|
601
|
+
with BuildPart(
|
|
602
|
+
Location(
|
|
603
|
+
(
|
|
604
|
+
0,
|
|
605
|
+
part_width * length_ratio * 1.5 + midpoint(start, end).Y,
|
|
606
|
+
click_fit_radius * 2,
|
|
607
|
+
)
|
|
608
|
+
),
|
|
609
|
+
mode=Mode.SUBTRACT if section == DovetailPart.SOCKET else Mode.ADD,
|
|
610
|
+
):
|
|
611
|
+
with PolarLocations(inner_width / 2, 2, start_angle=cut_angle):
|
|
612
|
+
Divot(
|
|
613
|
+
radius=click_fit_radius,
|
|
614
|
+
positive=True,
|
|
615
|
+
extend_base=True,
|
|
616
|
+
).rotate(Axis.Y, -90)
|
|
617
|
+
return divotedpart.part
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
def subpart_divots(
|
|
621
|
+
subpart: Part,
|
|
622
|
+
start: Point,
|
|
623
|
+
end: Point,
|
|
624
|
+
section: DovetailPart = DovetailPart.TAIL,
|
|
625
|
+
style: DovetailStyle = DovetailStyle.SNUGTAIL,
|
|
626
|
+
linear_offset: float = 0,
|
|
627
|
+
tolerance: float = 0.025,
|
|
628
|
+
vertical_tolerance: float = 0.2,
|
|
629
|
+
scarf_angle: float = 0,
|
|
630
|
+
taper_angle: float = 0,
|
|
631
|
+
depth_ratio: float = 1 / 6,
|
|
632
|
+
length_ratio: float = 1 / 3,
|
|
633
|
+
vertical_offset: float = 0,
|
|
634
|
+
click_fit_radius: float = 0,
|
|
635
|
+
):
|
|
636
|
+
"""
|
|
637
|
+
adds/subtracts click-fit divots to subpart and returns it
|
|
638
|
+
----------
|
|
639
|
+
Arguments:
|
|
640
|
+
- subpart: the part to add divots to
|
|
641
|
+
- start: the start point along the XY Plane for the dovetail line
|
|
642
|
+
- end: the end point along the XY Plane for the dovetail line
|
|
643
|
+
- section: the section of the dovetail to create (DovetailPart.TAIL or DovetailPart.SOCKET)
|
|
644
|
+
- style: create a traditional dovetal or a cut that wraps around 3 sides of the object and creates a tighter fit
|
|
645
|
+
- linear_offset: offsets the center of the tail or socket along the line by the ammount specified
|
|
646
|
+
- tolerance: the tolerance for the split
|
|
647
|
+
- scarf_angle: the scarf angle of the dovetail
|
|
648
|
+
- taper_angle: an extra shrinking factor for the dovetail size, allows for easier assembly
|
|
649
|
+
- depth_ratio: the ratio of the depth of the tongue to the total length of the dovetail
|
|
650
|
+
- length_ratio: the ratio of the length of the tongue to the total length of the dovetail
|
|
651
|
+
- vertical_offset: the vertical offset of the dovetail
|
|
652
|
+
- click_fit_radius: the radius of the click-fit divots
|
|
653
|
+
"""
|
|
654
|
+
if style == DovetailStyle.TRADITIONAL:
|
|
655
|
+
return traditional_subpart_divots(
|
|
656
|
+
subpart=subpart,
|
|
657
|
+
start=start,
|
|
658
|
+
end=end,
|
|
659
|
+
section=section,
|
|
660
|
+
linear_offset=linear_offset,
|
|
661
|
+
tolerance=tolerance,
|
|
662
|
+
vertical_tolerance=vertical_tolerance,
|
|
663
|
+
scarf_angle=scarf_angle,
|
|
664
|
+
taper_angle=taper_angle,
|
|
665
|
+
depth_ratio=depth_ratio,
|
|
666
|
+
vertical_offset=vertical_offset,
|
|
667
|
+
click_fit_radius=click_fit_radius,
|
|
668
|
+
)
|
|
669
|
+
else:
|
|
670
|
+
return snugtail_divots(
|
|
671
|
+
subpart=subpart,
|
|
672
|
+
start=start,
|
|
673
|
+
end=end,
|
|
674
|
+
section=section,
|
|
675
|
+
tolerance=tolerance,
|
|
676
|
+
scarf_angle=scarf_angle,
|
|
677
|
+
depth_ratio=1 / 10,
|
|
678
|
+
length_ratio=1 / 5,
|
|
679
|
+
vertical_offset=vertical_offset,
|
|
680
|
+
click_fit_radius=click_fit_radius,
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def subpart_section(
|
|
685
|
+
start: Point,
|
|
686
|
+
end: Point,
|
|
687
|
+
max_dimension: float,
|
|
688
|
+
section: DovetailPart = DovetailPart.TAIL,
|
|
689
|
+
style: DovetailStyle = DovetailStyle.SNUGTAIL,
|
|
690
|
+
floor_z: float = 0,
|
|
691
|
+
floor_taper_distance: float = 0,
|
|
692
|
+
floor_scarf_offset: float = 0,
|
|
693
|
+
top_z: float = 0,
|
|
694
|
+
top_taper_distance: float = 0,
|
|
695
|
+
top_scarf_offset: float = 0,
|
|
696
|
+
tolerance: float = 0.1,
|
|
697
|
+
slot_count: int = 1,
|
|
698
|
+
depth: float = 2,
|
|
699
|
+
linear_offset: float = 0,
|
|
700
|
+
tail_angle_offset: float = 20,
|
|
701
|
+
length_ratio: float = 1 / 3,
|
|
702
|
+
depth_ratio: float = 1 / 6,
|
|
703
|
+
straighten_dovetail: bool = False,
|
|
704
|
+
) -> Part:
|
|
705
|
+
with BuildPart() as intersect:
|
|
706
|
+
with BuildSketch(Plane.XY.offset(floor_z)):
|
|
707
|
+
with BuildLine() as baseline:
|
|
708
|
+
add(
|
|
709
|
+
subpart_outline(
|
|
710
|
+
start=start,
|
|
711
|
+
end=end,
|
|
712
|
+
max_dimension=max_dimension,
|
|
713
|
+
section=section,
|
|
714
|
+
style=style,
|
|
715
|
+
tolerance=tolerance,
|
|
716
|
+
taper_distance=floor_taper_distance,
|
|
717
|
+
slot_count=slot_count,
|
|
718
|
+
depth=depth,
|
|
719
|
+
scarf_offset=floor_scarf_offset,
|
|
720
|
+
straighten_dovetail=straighten_dovetail,
|
|
721
|
+
)
|
|
722
|
+
)
|
|
723
|
+
make_face()
|
|
724
|
+
with BuildSketch(Plane.XY.offset(top_z)) as topline:
|
|
725
|
+
with BuildLine():
|
|
726
|
+
add(
|
|
727
|
+
subpart_outline(
|
|
728
|
+
start=start,
|
|
729
|
+
end=end,
|
|
730
|
+
max_dimension=max_dimension,
|
|
731
|
+
section=section,
|
|
732
|
+
style=style,
|
|
733
|
+
tolerance=tolerance,
|
|
734
|
+
taper_distance=top_taper_distance,
|
|
735
|
+
slot_count=slot_count,
|
|
736
|
+
depth=depth,
|
|
737
|
+
scarf_offset=top_scarf_offset,
|
|
738
|
+
straighten_dovetail=straighten_dovetail,
|
|
739
|
+
)
|
|
740
|
+
)
|
|
741
|
+
make_face()
|
|
742
|
+
loft()
|
|
743
|
+
return intersect.part
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
def dovetail_subpart(
|
|
747
|
+
part: Part,
|
|
748
|
+
start: Point,
|
|
749
|
+
end: Point,
|
|
750
|
+
section: DovetailPart = DovetailPart.TAIL,
|
|
751
|
+
style: DovetailStyle = DovetailStyle.SNUGTAIL,
|
|
752
|
+
linear_offset: float = 0,
|
|
753
|
+
tolerance: float = 0.025,
|
|
754
|
+
vertical_tolerance: float = 0.2,
|
|
755
|
+
slot_count: int = 1,
|
|
756
|
+
depth: float = 2,
|
|
757
|
+
tail_angle_offset: float = 15,
|
|
758
|
+
taper_angle: float = 0,
|
|
759
|
+
length_ratio: float = 1 / 3,
|
|
760
|
+
depth_ratio: float = 1 / 6,
|
|
761
|
+
scarf_angle: float = 0,
|
|
762
|
+
vertical_offset: float = 0,
|
|
763
|
+
click_fit_radius: float = 0,
|
|
764
|
+
) -> Part:
|
|
765
|
+
"""
|
|
766
|
+
given a part and a start and end point on the XY plane, returns a Part for the appropriate split section
|
|
767
|
+
args:
|
|
768
|
+
- part: the part to split
|
|
769
|
+
- start: the start point along the XY Plane for the dovetail line
|
|
770
|
+
- end: the end point along the XY Plane for the dovetail line
|
|
771
|
+
- section: the section of the dovetail to create (DovetailPart.TAIL or DovetailPart.SOCKET)
|
|
772
|
+
- style: create a traditional dovetal or a cut that wraps around 3 sides of the object and creates a tighter fit
|
|
773
|
+
- linear_offset: offsets the center of the tail or socket along the line by the ammount specified
|
|
774
|
+
- tolerance: the tolerance for the split
|
|
775
|
+
- tail_angle_offset: the adjustment pitch of angle of the dovetail (0 will result in a square dovetail)
|
|
776
|
+
- taper_angle: an extra shrinking factor for the dovetail size, allows for easier assembly
|
|
777
|
+
- length_ratio: the ratio of the length of the tongue to the total length of the dovetail
|
|
778
|
+
- depth_ratio: the ratio of the depth of the tongue to the total length of the dovetail
|
|
779
|
+
- scarf_angle: setting this to a non-zero value will tilt the dovetail along the Z axis which may improve part stability
|
|
780
|
+
- vertical_offset: offsets the dovetail along the Z axis by the ammount specified, which results in a straight line
|
|
781
|
+
cut on one side, and provides a hard stop for fitting. A positive number results in a straight cut on the bottom
|
|
782
|
+
of the part passed, a negagive number results in a straight cut on the top of the part passed
|
|
783
|
+
"""
|
|
784
|
+
if start == end:
|
|
785
|
+
raise ValueError("start and end points cannot be the same")
|
|
786
|
+
if abs(vertical_offset) > part.bounding_box().size.Z:
|
|
787
|
+
raise ValueError("Vertical offset cannot be greater than the part's height")
|
|
788
|
+
if vertical_offset < 0 and taper_angle < 0:
|
|
789
|
+
raise ValueError(
|
|
790
|
+
"a negative taper_angle and a positive vertical_offset will result in an invalid dovetail"
|
|
791
|
+
)
|
|
792
|
+
if vertical_offset > 0 and taper_angle > 0:
|
|
793
|
+
raise ValueError(
|
|
794
|
+
"a positive taper_angle and a positive vertical_offset will result in an invalid dovetail"
|
|
795
|
+
)
|
|
796
|
+
|
|
797
|
+
max_dimension = (
|
|
798
|
+
max(
|
|
799
|
+
part.bounding_box().size.X,
|
|
800
|
+
part.bounding_box().size.Y,
|
|
801
|
+
part.bounding_box().size.Z,
|
|
802
|
+
)
|
|
803
|
+
* 3
|
|
804
|
+
)
|
|
805
|
+
|
|
806
|
+
vertical_tolerance_adjustment = (
|
|
807
|
+
vertical_tolerance
|
|
808
|
+
* (1 if section == DovetailPart.TAIL else -1)
|
|
809
|
+
* (1 if vertical_offset > 0 else -1)
|
|
810
|
+
)
|
|
811
|
+
scarf_offset = (part.bounding_box().size.Z) * tan(radians(scarf_angle)) / 2
|
|
812
|
+
vertical_scarf_offset = (
|
|
813
|
+
abs(vertical_offset) - vertical_tolerance_adjustment
|
|
814
|
+
) * tan(radians(scarf_angle))
|
|
815
|
+
|
|
816
|
+
taper_offset = (
|
|
817
|
+
part.bounding_box().size.Z
|
|
818
|
+
- abs(vertical_offset)
|
|
819
|
+
- vertical_tolerance_adjustment
|
|
820
|
+
) * tan(radians(abs(taper_angle)))
|
|
821
|
+
with BuildPart() as intersect:
|
|
822
|
+
if vertical_offset > 0:
|
|
823
|
+
add(
|
|
824
|
+
subpart_section(
|
|
825
|
+
start=start,
|
|
826
|
+
end=end,
|
|
827
|
+
max_dimension=max_dimension,
|
|
828
|
+
section=section,
|
|
829
|
+
style=style,
|
|
830
|
+
floor_z=part.bounding_box().min.Z,
|
|
831
|
+
floor_taper_distance=0, # fix taper_offset if (taper_angle < 0) else 0,
|
|
832
|
+
floor_scarf_offset=-scarf_offset,
|
|
833
|
+
top_z=part.bounding_box().min.Z
|
|
834
|
+
+ vertical_offset
|
|
835
|
+
+ vertical_tolerance_adjustment,
|
|
836
|
+
top_taper_distance=0, # fix
|
|
837
|
+
linear_offset=linear_offset,
|
|
838
|
+
top_scarf_offset=-scarf_offset + vertical_scarf_offset,
|
|
839
|
+
tolerance=tolerance,
|
|
840
|
+
slot_count=slot_count,
|
|
841
|
+
depth=depth,
|
|
842
|
+
straighten_dovetail=True,
|
|
843
|
+
)
|
|
844
|
+
)
|
|
845
|
+
current_floor = (
|
|
846
|
+
part.bounding_box().min.Z
|
|
847
|
+
if vertical_offset <= 0
|
|
848
|
+
else part.bounding_box().min.Z + abs(vertical_offset) + vertical_tolerance
|
|
849
|
+
)
|
|
850
|
+
add(
|
|
851
|
+
subpart_section(
|
|
852
|
+
start=start,
|
|
853
|
+
end=end,
|
|
854
|
+
max_dimension=max_dimension,
|
|
855
|
+
section=section,
|
|
856
|
+
style=style,
|
|
857
|
+
floor_z=current_floor,
|
|
858
|
+
floor_taper_distance=taper_offset if (taper_angle < 0) else 0,
|
|
859
|
+
floor_scarf_offset=(
|
|
860
|
+
-scarf_offset
|
|
861
|
+
if vertical_offset <= 0
|
|
862
|
+
else -scarf_offset + vertical_scarf_offset
|
|
863
|
+
),
|
|
864
|
+
top_z=part.bounding_box().max.Z
|
|
865
|
+
+ (
|
|
866
|
+
0
|
|
867
|
+
if vertical_offset >= 0
|
|
868
|
+
else vertical_offset + vertical_tolerance_adjustment
|
|
869
|
+
),
|
|
870
|
+
top_taper_distance=taper_offset,
|
|
871
|
+
top_scarf_offset=scarf_offset
|
|
872
|
+
- (0 if vertical_offset >= 0 else vertical_scarf_offset),
|
|
873
|
+
tolerance=tolerance,
|
|
874
|
+
slot_count=slot_count,
|
|
875
|
+
depth=depth,
|
|
876
|
+
linear_offset=linear_offset,
|
|
877
|
+
tail_angle_offset=tail_angle_offset,
|
|
878
|
+
length_ratio=length_ratio,
|
|
879
|
+
depth_ratio=depth_ratio,
|
|
880
|
+
straighten_dovetail=False,
|
|
881
|
+
)
|
|
882
|
+
)
|
|
883
|
+
if vertical_offset < 0:
|
|
884
|
+
current_floor = part.bounding_box().max.Z + (
|
|
885
|
+
0
|
|
886
|
+
if vertical_offset >= 0
|
|
887
|
+
else vertical_offset + vertical_tolerance_adjustment
|
|
888
|
+
)
|
|
889
|
+
add(
|
|
890
|
+
subpart_section(
|
|
891
|
+
start=start,
|
|
892
|
+
end=end,
|
|
893
|
+
max_dimension=max_dimension,
|
|
894
|
+
section=section,
|
|
895
|
+
style=style,
|
|
896
|
+
floor_z=current_floor,
|
|
897
|
+
floor_taper_distance=0,
|
|
898
|
+
floor_scarf_offset=scarf_offset - vertical_scarf_offset,
|
|
899
|
+
top_z=part.bounding_box().max.Z,
|
|
900
|
+
top_taper_distance=0,
|
|
901
|
+
top_scarf_offset=scarf_offset,
|
|
902
|
+
tolerance=tolerance,
|
|
903
|
+
slot_count=slot_count,
|
|
904
|
+
depth=depth,
|
|
905
|
+
linear_offset=linear_offset,
|
|
906
|
+
tail_angle_offset=tail_angle_offset,
|
|
907
|
+
length_ratio=length_ratio,
|
|
908
|
+
depth_ratio=depth_ratio,
|
|
909
|
+
straighten_dovetail=True,
|
|
910
|
+
)
|
|
911
|
+
)
|
|
912
|
+
add(part, mode=Mode.INTERSECT)
|
|
913
|
+
if click_fit_radius != 0:
|
|
914
|
+
intersect.part = subpart_divots(
|
|
915
|
+
subpart=intersect.part,
|
|
916
|
+
start=start,
|
|
917
|
+
end=end,
|
|
918
|
+
section=section,
|
|
919
|
+
style=style,
|
|
920
|
+
tolerance=tolerance,
|
|
921
|
+
vertical_tolerance=vertical_tolerance,
|
|
922
|
+
scarf_angle=scarf_angle,
|
|
923
|
+
linear_offset=linear_offset,
|
|
924
|
+
taper_angle=taper_angle,
|
|
925
|
+
depth_ratio=depth_ratio,
|
|
926
|
+
length_ratio=length_ratio,
|
|
927
|
+
vertical_offset=vertical_offset,
|
|
928
|
+
click_fit_radius=click_fit_radius,
|
|
929
|
+
)
|
|
930
|
+
|
|
931
|
+
return intersect.part
|
|
932
|
+
|
|
933
|
+
|
|
934
|
+
def tslot_split_line(
|
|
935
|
+
start: Point,
|
|
936
|
+
end: Point,
|
|
937
|
+
section: DovetailPart = DovetailPart.TAIL,
|
|
938
|
+
slot_count: int = 1,
|
|
939
|
+
depth: float = 2,
|
|
940
|
+
tolerance: float = 0.1,
|
|
941
|
+
taper_distance=0,
|
|
942
|
+
) -> Line:
|
|
943
|
+
"""
|
|
944
|
+
given a start and end point, returns a tslot split line as a Line object
|
|
945
|
+
-------
|
|
946
|
+
arguments:
|
|
947
|
+
- start: the start point for the dovetail line
|
|
948
|
+
- end: the end point for the dovetail line
|
|
949
|
+
- section: the section of the dovetail to create (DovetailPart.TAIL or DovetailPart.SOCKET)
|
|
950
|
+
- linear_offset: offsets the center of the tail or socket along the line by the ammount specified
|
|
951
|
+
- tolerance: the tolerance for the split
|
|
952
|
+
- tail_angle_offset: the adjustment pitch of angle of the dovetail (0 will result in a square dovetail)
|
|
953
|
+
- taper_distance: an extra shrinking factor for the dovetail size, allows for easier assembly
|
|
954
|
+
- length_ratio: the ratio of the length of the tongue to the total length of the dovetail
|
|
955
|
+
- depth_ratio: the ratio of the depth of the tongue to the total length of the dovetail
|
|
956
|
+
"""
|
|
957
|
+
base_angle = start.angle_to(end)
|
|
958
|
+
length = start.distance_to(end)
|
|
959
|
+
base_width = depth * 2
|
|
960
|
+
next_distance = (length - (base_width * slot_count)) / (slot_count + 1)
|
|
961
|
+
dovetail_tolerance = (
|
|
962
|
+
-(abs(tolerance / 2)) if section == DovetailPart.TAIL else abs(tolerance / 2)
|
|
963
|
+
)
|
|
964
|
+
|
|
965
|
+
adjusted_start_point = start.related_point(base_angle - 90, dovetail_tolerance)
|
|
966
|
+
adjusted_end_point = adjusted_start_point.related_point(base_angle, length)
|
|
967
|
+
|
|
968
|
+
last_point = adjusted_start_point
|
|
969
|
+
|
|
970
|
+
with BuildLine() as tslot_outline:
|
|
971
|
+
|
|
972
|
+
for slot_index in range(slot_count):
|
|
973
|
+
root_start = last_point.related_point(base_angle, next_distance)
|
|
974
|
+
trunk_start = root_start.related_point(
|
|
975
|
+
base_angle, depth / 2 + dovetail_tolerance + taper_distance
|
|
976
|
+
)
|
|
977
|
+
branch_start = trunk_start.related_point(
|
|
978
|
+
base_angle + 90, depth / 2 + dovetail_tolerance * 2 + taper_distance
|
|
979
|
+
)
|
|
980
|
+
mid_trunk_start = midpoint(trunk_start, branch_start)
|
|
981
|
+
branch_start_inner = branch_start.related_point(base_angle, -depth / 2)
|
|
982
|
+
branch_start_inner_mid = midpoint(branch_start_inner, branch_start)
|
|
983
|
+
branch_start_outer = branch_start_inner.related_point(
|
|
984
|
+
base_angle + 90, depth / 2 - dovetail_tolerance * 2 - taper_distance * 2
|
|
985
|
+
)
|
|
986
|
+
branch_start_mid = midpoint(branch_start_inner, branch_start_outer)
|
|
987
|
+
branch_end_inner = branch_start_inner.related_point(
|
|
988
|
+
base_angle, depth * 2 - dovetail_tolerance * 2 - taper_distance * 2
|
|
989
|
+
)
|
|
990
|
+
branch_end_outer = branch_start_outer.related_point(
|
|
991
|
+
base_angle, depth * 2 - dovetail_tolerance * 2 - taper_distance * 2
|
|
992
|
+
)
|
|
993
|
+
branch_mid = midpoint(branch_start_outer, branch_end_outer)
|
|
994
|
+
branch_end_mid = midpoint(branch_end_inner, branch_end_outer)
|
|
995
|
+
branch_end = branch_start.related_point(
|
|
996
|
+
base_angle, depth - dovetail_tolerance * 2 - taper_distance * 2
|
|
997
|
+
)
|
|
998
|
+
branch_end_inner_mid = midpoint(branch_end_inner, branch_end)
|
|
999
|
+
trunk_end = trunk_start.related_point(
|
|
1000
|
+
base_angle, depth - dovetail_tolerance * 2 - taper_distance * 2
|
|
1001
|
+
)
|
|
1002
|
+
mid_trunk_end = midpoint(trunk_end, branch_end)
|
|
1003
|
+
|
|
1004
|
+
root_end = root_start.related_point(base_angle, depth * 2)
|
|
1005
|
+
FilletPolyline(
|
|
1006
|
+
last_point,
|
|
1007
|
+
trunk_start,
|
|
1008
|
+
mid_trunk_start,
|
|
1009
|
+
radius=abs(tolerance) * (2 if section == DovetailPart.TAIL else 3),
|
|
1010
|
+
)
|
|
1011
|
+
FilletPolyline(
|
|
1012
|
+
mid_trunk_start,
|
|
1013
|
+
branch_start,
|
|
1014
|
+
branch_start_inner_mid,
|
|
1015
|
+
radius=abs(tolerance) * (2 if section == DovetailPart.TAIL else 3),
|
|
1016
|
+
)
|
|
1017
|
+
FilletPolyline(
|
|
1018
|
+
branch_start_inner_mid,
|
|
1019
|
+
branch_start_inner,
|
|
1020
|
+
branch_start_mid,
|
|
1021
|
+
radius=abs(tolerance) * (3 if section == DovetailPart.TAIL else 2),
|
|
1022
|
+
)
|
|
1023
|
+
FilletPolyline(
|
|
1024
|
+
branch_start_mid,
|
|
1025
|
+
branch_start_outer,
|
|
1026
|
+
branch_mid,
|
|
1027
|
+
radius=abs(tolerance) * (3 if section == DovetailPart.TAIL else 2),
|
|
1028
|
+
)
|
|
1029
|
+
FilletPolyline(
|
|
1030
|
+
branch_mid,
|
|
1031
|
+
branch_end_outer,
|
|
1032
|
+
branch_end_mid,
|
|
1033
|
+
radius=abs(tolerance) * (3 if section == DovetailPart.TAIL else 2),
|
|
1034
|
+
)
|
|
1035
|
+
FilletPolyline(
|
|
1036
|
+
branch_end_mid,
|
|
1037
|
+
branch_end_inner,
|
|
1038
|
+
branch_end_inner_mid,
|
|
1039
|
+
radius=abs(tolerance) * (3 if section == DovetailPart.TAIL else 2),
|
|
1040
|
+
)
|
|
1041
|
+
FilletPolyline(
|
|
1042
|
+
branch_end_inner_mid,
|
|
1043
|
+
branch_end,
|
|
1044
|
+
mid_trunk_end,
|
|
1045
|
+
radius=abs(tolerance) * (2 if section == DovetailPart.TAIL else 3),
|
|
1046
|
+
)
|
|
1047
|
+
FilletPolyline(
|
|
1048
|
+
mid_trunk_end,
|
|
1049
|
+
trunk_end,
|
|
1050
|
+
root_end,
|
|
1051
|
+
radius=abs(tolerance) * (2 if section == DovetailPart.TAIL else 3),
|
|
1052
|
+
)
|
|
1053
|
+
last_point = root_end
|
|
1054
|
+
Polyline(last_point, adjusted_end_point)
|
|
1055
|
+
return tslot_outline.line
|
|
1056
|
+
|
|
1057
|
+
|
|
1058
|
+
def dovetail_split_line(
|
|
1059
|
+
start: Point,
|
|
1060
|
+
end: Point,
|
|
1061
|
+
section: DovetailPart = DovetailPart.TAIL,
|
|
1062
|
+
linear_offset: float = 0,
|
|
1063
|
+
tolerance: float = 0.025,
|
|
1064
|
+
tail_angle_offset: float = 15,
|
|
1065
|
+
taper_distance: float = 0,
|
|
1066
|
+
length_ratio: float = 1 / 3,
|
|
1067
|
+
depth_ratio: float = 1 / 6,
|
|
1068
|
+
) -> Line:
|
|
1069
|
+
"""
|
|
1070
|
+
given a start and end point, returns a dovetail split line as a Line object
|
|
1071
|
+
-------
|
|
1072
|
+
arguments:
|
|
1073
|
+
- start: the start point for the dovetail line
|
|
1074
|
+
- end: the end point for the dovetail line
|
|
1075
|
+
- section: the section of the dovetail to create (DovetailPart.TAIL or DovetailPart.SOCKET)
|
|
1076
|
+
- linear_offset: offsets the center of the tail or socket along the line by the ammount specified
|
|
1077
|
+
- tolerance: the tolerance for the split
|
|
1078
|
+
- tail_angle_offset: the adjustment pitch of angle of the dovetail (0 will result in a square dovetail)
|
|
1079
|
+
- taper_distance: an extra shrinking factor for the dovetail size, allows for easier assembly
|
|
1080
|
+
- length_ratio: the ratio of the length of the tongue to the total length of the dovetail
|
|
1081
|
+
- depth_ratio: the ratio of the depth of the tongue to the total length of the dovetail
|
|
1082
|
+
"""
|
|
1083
|
+
dovetail_tolerance = (
|
|
1084
|
+
-(abs(tolerance / 2)) if section == DovetailPart.TAIL else abs(tolerance / 2)
|
|
1085
|
+
)
|
|
1086
|
+
|
|
1087
|
+
base_angle = start.angle_to(end)
|
|
1088
|
+
tail_angle_tolerance_adjustment = dovetail_tolerance * tan(
|
|
1089
|
+
radians(tail_angle_offset)
|
|
1090
|
+
)
|
|
1091
|
+
base_angle = start.angle_to(end)
|
|
1092
|
+
length = start.distance_to(end)
|
|
1093
|
+
tongue_length = length * length_ratio + (dovetail_tolerance * 2)
|
|
1094
|
+
tongue_depth = length * depth_ratio
|
|
1095
|
+
tail_angle_extension = (tongue_depth) * tan(radians(tail_angle_offset))
|
|
1096
|
+
adjusted_start_point = start.related_point(base_angle - 90, dovetail_tolerance)
|
|
1097
|
+
|
|
1098
|
+
tail_end_start = adjusted_start_point.related_point(
|
|
1099
|
+
base_angle,
|
|
1100
|
+
length / 2
|
|
1101
|
+
- tongue_length / 2
|
|
1102
|
+
- tail_angle_tolerance_adjustment
|
|
1103
|
+
+ linear_offset
|
|
1104
|
+
+ taper_distance,
|
|
1105
|
+
).related_point(base_angle - 90, tongue_depth - taper_distance / 2)
|
|
1106
|
+
|
|
1107
|
+
tail_end = adjusted_start_point.related_point(
|
|
1108
|
+
base_angle,
|
|
1109
|
+
length / 2
|
|
1110
|
+
+ tongue_length / 2
|
|
1111
|
+
+ tail_angle_tolerance_adjustment
|
|
1112
|
+
+ linear_offset
|
|
1113
|
+
- taper_distance,
|
|
1114
|
+
).related_point(base_angle - 90, tongue_depth - taper_distance / 2)
|
|
1115
|
+
|
|
1116
|
+
tail_base_start = adjusted_start_point.related_point(
|
|
1117
|
+
base_angle,
|
|
1118
|
+
length / 2
|
|
1119
|
+
- tongue_length / 2
|
|
1120
|
+
+ tail_angle_extension
|
|
1121
|
+
- tail_angle_tolerance_adjustment
|
|
1122
|
+
+ linear_offset
|
|
1123
|
+
+ taper_distance / 2,
|
|
1124
|
+
)
|
|
1125
|
+
|
|
1126
|
+
tail_base_resume = adjusted_start_point.related_point(
|
|
1127
|
+
base_angle,
|
|
1128
|
+
length / 2
|
|
1129
|
+
+ tongue_length / 2
|
|
1130
|
+
- tail_angle_extension
|
|
1131
|
+
+ tail_angle_tolerance_adjustment
|
|
1132
|
+
+ linear_offset
|
|
1133
|
+
- taper_distance / 2,
|
|
1134
|
+
)
|
|
1135
|
+
|
|
1136
|
+
adjusted_end_point = adjusted_start_point.related_point(base_angle, length)
|
|
1137
|
+
|
|
1138
|
+
with BuildLine() as dovetail_outline:
|
|
1139
|
+
FilletPolyline(
|
|
1140
|
+
adjusted_start_point,
|
|
1141
|
+
tail_base_start,
|
|
1142
|
+
midpoint(tail_base_start, tail_end_start),
|
|
1143
|
+
radius=abs(dovetail_tolerance) * (2 if section == DovetailPart.TAIL else 3),
|
|
1144
|
+
)
|
|
1145
|
+
FilletPolyline(
|
|
1146
|
+
midpoint(tail_base_start, tail_end_start),
|
|
1147
|
+
tail_end_start,
|
|
1148
|
+
midpoint(tail_end_start, tail_end),
|
|
1149
|
+
radius=abs(dovetail_tolerance) * (3 if section == DovetailPart.TAIL else 2),
|
|
1150
|
+
)
|
|
1151
|
+
FilletPolyline(
|
|
1152
|
+
midpoint(tail_end_start, tail_end),
|
|
1153
|
+
tail_end,
|
|
1154
|
+
midpoint(tail_end, tail_base_resume),
|
|
1155
|
+
radius=abs(dovetail_tolerance) * (3 if section == DovetailPart.TAIL else 2),
|
|
1156
|
+
)
|
|
1157
|
+
FilletPolyline(
|
|
1158
|
+
midpoint(tail_end, tail_base_resume),
|
|
1159
|
+
tail_base_resume,
|
|
1160
|
+
adjusted_end_point,
|
|
1161
|
+
radius=abs(dovetail_tolerance) * (2 if section == DovetailPart.TAIL else 3),
|
|
1162
|
+
)
|
|
1163
|
+
|
|
1164
|
+
return dovetail_outline.line
|
|
1165
|
+
|
|
1166
|
+
|
|
1167
|
+
# if __name__ == "__main__":
|
|
1168
|
+
# from ocp_vscode import show, Camera
|
|
1169
|
+
# with BuildPart(mode=Mode.PRIVATE) as test:
|
|
1170
|
+
# Box(40, 200, 78.7, align=(Align.CENTER, Align.CENTER, Align.MIN))
|
|
1171
|
+
|
|
1172
|
+
# splines = snugtail_subpart_outline(
|
|
1173
|
+
# test.part,
|
|
1174
|
+
# Point(-20, 0),
|
|
1175
|
+
# Point(20, 0),
|
|
1176
|
+
# section=DovetailPart.SOCKET,
|
|
1177
|
+
# taper_distance=0,
|
|
1178
|
+
# tolerance=0.8,
|
|
1179
|
+
# length_ratio=.6,
|
|
1180
|
+
# depth_ratio=.3,
|
|
1181
|
+
# tail_angle_offset=35,
|
|
1182
|
+
# # scarf_angle=20,
|
|
1183
|
+
# # straighten_dovetail=True,
|
|
1184
|
+
# )
|
|
1185
|
+
# spline = snugtail_subpart_outline(
|
|
1186
|
+
# test.part,
|
|
1187
|
+
# Point(-20, 0),
|
|
1188
|
+
# Point(20, 0),
|
|
1189
|
+
# section=DovetailPart.TAIL,
|
|
1190
|
+
# taper_distance=4,
|
|
1191
|
+
# tolerance=0.8,
|
|
1192
|
+
# length_ratio=.6,
|
|
1193
|
+
# depth_ratio=.3,
|
|
1194
|
+
# tail_angle_offset=35,
|
|
1195
|
+
# # scarf_angle=20,
|
|
1196
|
+
# # straighten_dovetail=True,
|
|
1197
|
+
# )
|
|
1198
|
+
# splines.color = (0.5, 0.5, 0.5)
|
|
1199
|
+
|
|
1200
|
+
# show(spline, splines, reset_camera=Camera.KEEP)
|
|
1201
|
+
|
|
1202
|
+
if __name__ == "__main__":
|
|
1203
|
+
from ocp_vscode import show, Camera
|
|
1204
|
+
|
|
1205
|
+
with BuildPart(mode=Mode.PRIVATE) as test:
|
|
1206
|
+
Box(40, 200, 78.7, align=(Align.CENTER, Align.CENTER, Align.MIN))
|
|
1207
|
+
with BuildPart(
|
|
1208
|
+
Plane.XY.offset(73.6),
|
|
1209
|
+
mode=Mode.SUBTRACT,
|
|
1210
|
+
):
|
|
1211
|
+
Cylinder(
|
|
1212
|
+
25,
|
|
1213
|
+
200,
|
|
1214
|
+
rotation=(90, 0, 0),
|
|
1215
|
+
)
|
|
1216
|
+
|
|
1217
|
+
tl = dovetail_subpart(
|
|
1218
|
+
test.part,
|
|
1219
|
+
Point(-20, 0),
|
|
1220
|
+
Point(20, 0),
|
|
1221
|
+
section=DovetailPart.TAIL,
|
|
1222
|
+
style=DovetailStyle.T_SLOT,
|
|
1223
|
+
tolerance=0.1,
|
|
1224
|
+
depth=2,
|
|
1225
|
+
slot_count=1,
|
|
1226
|
+
taper_angle=0.25,
|
|
1227
|
+
scarf_angle=2,
|
|
1228
|
+
vertical_offset=-14.33333,
|
|
1229
|
+
tail_angle_offset=25,
|
|
1230
|
+
length_ratio=0.7,
|
|
1231
|
+
depth_ratio=0.3,
|
|
1232
|
+
).move(Location((0, 0, 0)))
|
|
1233
|
+
sckt = dovetail_subpart(
|
|
1234
|
+
test.part,
|
|
1235
|
+
Point(-20, 0),
|
|
1236
|
+
Point(20, 0),
|
|
1237
|
+
section=DovetailPart.SOCKET,
|
|
1238
|
+
style=DovetailStyle.T_SLOT,
|
|
1239
|
+
tolerance=0.1,
|
|
1240
|
+
depth=2,
|
|
1241
|
+
slot_count=1,
|
|
1242
|
+
taper_angle=0.25,
|
|
1243
|
+
scarf_angle=2,
|
|
1244
|
+
vertical_offset=-14.33333,
|
|
1245
|
+
tail_angle_offset=25,
|
|
1246
|
+
length_ratio=0.7,
|
|
1247
|
+
depth_ratio=0.3,
|
|
1248
|
+
)
|
|
1249
|
+
sckt.color = (0.5, 0.5, 0.5)
|
|
1250
|
+
splines = dovetail_subpart_outline(
|
|
1251
|
+
Point(-20, 0),
|
|
1252
|
+
Point(20, 0),
|
|
1253
|
+
section=DovetailPart.SOCKET,
|
|
1254
|
+
style=DovetailStyle.T_SLOT,
|
|
1255
|
+
taper_distance=0,
|
|
1256
|
+
tolerance=0.1,
|
|
1257
|
+
length_ratio=0.6,
|
|
1258
|
+
depth_ratio=0.3,
|
|
1259
|
+
tail_angle_offset=35,
|
|
1260
|
+
slot_count=1,
|
|
1261
|
+
depth=2,
|
|
1262
|
+
# scarf_angle=20,
|
|
1263
|
+
# straighten_dovetail=True,
|
|
1264
|
+
)
|
|
1265
|
+
spline = dovetail_subpart_outline(
|
|
1266
|
+
Point(-20, 0),
|
|
1267
|
+
Point(20, 0),
|
|
1268
|
+
section=DovetailPart.TAIL,
|
|
1269
|
+
style=DovetailStyle.T_SLOT,
|
|
1270
|
+
taper_distance=0,
|
|
1271
|
+
tolerance=0.1,
|
|
1272
|
+
length_ratio=0.6,
|
|
1273
|
+
depth_ratio=0.3,
|
|
1274
|
+
tail_angle_offset=35,
|
|
1275
|
+
slot_count=1,
|
|
1276
|
+
depth=2,
|
|
1277
|
+
# scarf_angle=20,
|
|
1278
|
+
# straighten_dovetail=True,
|
|
1279
|
+
)
|
|
1280
|
+
# spline = snugtail_subpart_outline(
|
|
1281
|
+
# Point(-20, 0),
|
|
1282
|
+
# Point(20, 0),
|
|
1283
|
+
# section=DovetailPart.TAIL,
|
|
1284
|
+
# taper_distance=0,
|
|
1285
|
+
# tolerance=0.8,
|
|
1286
|
+
# length_ratio=0.6,
|
|
1287
|
+
# depth_ratio=0.3,
|
|
1288
|
+
# tail_angle_offset=35,
|
|
1289
|
+
# # scarf_angle=20,
|
|
1290
|
+
# # straighten_dovetail=True,
|
|
1291
|
+
# )
|
|
1292
|
+
# splines = dovetail_subpart_outline(
|
|
1293
|
+
# test.part,
|
|
1294
|
+
# Point(-20, 0),
|
|
1295
|
+
# Point(20, 0),
|
|
1296
|
+
# section=DovetailPart.SOCKET,
|
|
1297
|
+
# taper_distance=0,
|
|
1298
|
+
# tolerance=0.8,
|
|
1299
|
+
# length_ratio=.6,
|
|
1300
|
+
# depth_ratio=.3,
|
|
1301
|
+
# tail_angle_offset=35,
|
|
1302
|
+
# # scarf_angle=20,
|
|
1303
|
+
# # straighten_dovetail=True,
|
|
1304
|
+
# )
|
|
1305
|
+
# spline = dovetail_subpart_outline(
|
|
1306
|
+
# test.part,
|
|
1307
|
+
# Point(-20, 0),
|
|
1308
|
+
# Point(20, 0),
|
|
1309
|
+
# section=DovetailPart.TAIL,
|
|
1310
|
+
# taper_distance=0,
|
|
1311
|
+
# tolerance=0.8,
|
|
1312
|
+
# length_ratio=.6,
|
|
1313
|
+
# depth_ratio=.3,
|
|
1314
|
+
# tail_angle_offset=35,
|
|
1315
|
+
# # scarf_angle=20,
|
|
1316
|
+
# # straighten_dovetail=True,
|
|
1317
|
+
# )
|
|
1318
|
+
splines.color = (0.5, 0.5, 0.5)
|
|
1319
|
+
with BuildSketch() as sks:
|
|
1320
|
+
add(splines)
|
|
1321
|
+
make_face()
|
|
1322
|
+
sks.color = (0.5, 0.5, 0.5)
|
|
1323
|
+
with BuildSketch() as sk:
|
|
1324
|
+
add(spline)
|
|
1325
|
+
make_face()
|
|
1326
|
+
from build123d import export_stl
|
|
1327
|
+
|
|
1328
|
+
show(
|
|
1329
|
+
tl,
|
|
1330
|
+
sckt,
|
|
1331
|
+
# sk,
|
|
1332
|
+
# sks,
|
|
1333
|
+
# spline,
|
|
1334
|
+
# splines,
|
|
1335
|
+
reset_camera=Camera.KEEP,
|
|
1336
|
+
)
|
|
1337
|
+
# export_stl(tl, "tail.stl")
|
|
1338
|
+
# export_stl(sckt, "socket.stl")
|