Created
August 24, 2022 09:08
-
-
Save sethfischer/62cd4efe2be82df7422718b1fb9fb09d to your computer and use it in GitHub Desktop.
AEC 2020 V-slot Aluminium Extrusion profile
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from math import radians, sqrt, tan | |
from typing import Tuple | |
import cadquery as cq | |
def reflect_xy(point: Tuple[float, float]) -> Tuple[float, float]: | |
"""Reflect point in axis x=y. | |
Reflect by reversing tuple. | |
""" | |
return point[::-1] | |
def reflect_x(point: Tuple[float, float]) -> Tuple[float, float]: | |
"""Reflect point in the x-axis.""" | |
return tuple((point[0], point[1] * -1)) | |
class Vslot2020Profile: | |
"""2020 V-slot Aluminium Extrusion profile. | |
:Manufacturer: Aluminium Extrusion Company | |
:Web: https://www.alexco.co.nz/ | |
:Part: AEC 2020 | |
""" | |
WIDTH = 20 | |
CORE_WIDTH = 8 | |
RIB_THICKNESS = 1.6627 | |
SLOT_WIDTH = 10.9 | |
SLOT_CHAMFER = 2.62 | |
SLOT_DEPTH = 4.3 | |
CENTER_BORE_DIAMETER = 4.2 | |
HALF_BETWEEN_V_LOWER_VERTICES = 2.7 | |
BORE_CHANNEL_WIDTH = 3 | |
BORE_CHANNEL_DEPTH = 1.325 | |
BORE_GROOVE_ANGLE_OBTUSE = 45 | |
def __init__(self) -> None: | |
"""Initialise dimensions.""" | |
self.half_width = self.WIDTH / 2 | |
self.half_core_width = self.CORE_WIDTH / 2 | |
self.half_rib_thickness = self.RIB_THICKNESS / 2 | |
self.half_slot_width = self.SLOT_WIDTH / 2 | |
self.center_bore_radius = self.CENTER_BORE_DIAMETER / 2 | |
self.half_bore_channel_width = self.BORE_CHANNEL_WIDTH / 2 | |
self.core_positive_x_axis_vertex = (self.half_core_width, 0) | |
self.core_rib_vertex_y = self.half_core_width - sqrt( | |
self.half_rib_thickness**2 + self.half_rib_thickness**2 | |
) | |
self.core_rib_vertex = (self.half_core_width, self.core_rib_vertex_y) | |
self.rib_wall_vertex_x = self.half_core_width + self.SLOT_CHAMFER | |
self.rib_slot_vertex = (self.rib_wall_vertex_x, self.half_slot_width) | |
self.slot_retainer_vertex = ( | |
self.half_core_width + self.SLOT_DEPTH, | |
self.half_slot_width, | |
) | |
self.v_lower_vertex = ( | |
self.slot_retainer_vertex[0], | |
self.half_slot_width - self.HALF_BETWEEN_V_LOWER_VERTICES, | |
) | |
self.v_upper_vertex = ( | |
self.half_width, | |
(self.half_width - self.v_lower_vertex[0]) + self.v_lower_vertex[1], | |
) | |
def make(self): | |
"""Make profile.""" | |
sketch = self._make_main_sketch() | |
sketch_bore_slot = self._make_bore_slot_sketch() | |
sketch = sketch.face(sketch_bore_slot, mode="s") | |
sketch = self._make_center_lines(sketch) | |
sketch = self._tag_vertices(sketch) | |
sketch = self._fillet(sketch) | |
return sketch | |
def _make_main_sketch(self): | |
"""Main sketch.""" | |
sketch = ( | |
cq.Sketch() | |
.parray(0, 0, 360, 4) | |
# quadrant 1 | |
.segment(self.core_positive_x_axis_vertex, self.core_rib_vertex) | |
.segment(self.rib_slot_vertex) | |
.segment(self.slot_retainer_vertex) | |
.segment(self.v_lower_vertex) | |
.segment(self.v_upper_vertex) | |
.segment((self.half_width, self.half_width)) | |
.segment(reflect_xy(self.v_upper_vertex)) | |
.segment(reflect_xy(self.v_lower_vertex)) | |
.segment(reflect_xy(self.slot_retainer_vertex)) | |
.segment(reflect_xy(self.rib_slot_vertex)) | |
.segment(reflect_xy(self.core_rib_vertex)) | |
.segment((0, self.half_core_width)) | |
.segment((0, 0)) | |
.close() | |
.assemble() | |
.clean() | |
) | |
sketch = sketch.reset().circle(self.center_bore_radius, mode="s") | |
return sketch | |
def _make_bore_slot_sketch(self): | |
"""Make bore slot sketch. | |
Bore slot consists of a channel and a groove. | |
""" | |
core_channel_vertex_q2 = (-self.half_core_width, self.half_bore_channel_width) | |
channel_groove_vertex = ( | |
-self.half_core_width + self.BORE_CHANNEL_DEPTH, | |
self.half_bore_channel_width, | |
) | |
bore_groove_depth = self.half_bore_channel_width * tan( | |
radians(self.BORE_GROOVE_ANGLE_OBTUSE) | |
) | |
bore_groove_vertex = ( | |
-self.half_core_width + self.BORE_CHANNEL_DEPTH + bore_groove_depth, | |
0, | |
) | |
sketch = ( | |
cq.Sketch() | |
.segment((-self.half_core_width, 0), core_channel_vertex_q2) | |
.segment(channel_groove_vertex) | |
.segment(bore_groove_vertex) | |
.segment(reflect_x(channel_groove_vertex)) | |
.segment(reflect_x(core_channel_vertex_q2)) | |
.close() | |
.assemble() | |
) | |
return sketch | |
def _make_center_lines(self, profile): | |
points = [ | |
(self.half_core_width, 0), | |
(0, self.half_core_width), | |
(0, -self.half_core_width), | |
] | |
profile = profile.reset().push(points).circle(0.25, mode="s") | |
return profile | |
def _tag_vertices(self, profile): | |
"""Tag all vertices.""" | |
profile = self._tag_vertices_centerbore_groove(profile) | |
profile = self._tag_vertices_bounding_box(profile) | |
profile = self._tag_vertices_slot_retainer(profile) | |
profile = self._tag_vertices_v_lower(profile) | |
profile = self._tag_vertices_v_upper(profile) | |
profile = self._tag_vertices_rib_slot(profile) | |
profile = self._tag_vertices_core_rib(profile) | |
profile = self._tag_vertices_core_channel(profile) | |
profile = self._tag_vertices_channel_groove(profile) | |
profile = self._tag_vertices_cline(profile) | |
return profile | |
@staticmethod | |
def _tag_vertices_centerbore_groove(profile): | |
"""Tag centerbore grove.""" | |
return profile.reset().vertices(">>X[9]").tag("centerbore_groove_vertices") | |
@staticmethod | |
def _tag_vertices_bounding_box(profile): | |
"""Tag bounding box vertices.""" | |
profile = ( | |
profile.reset() | |
.vertices("(>X and <Y) or (>X and >Y) or (<X and <Y) or (<X and >Y)") | |
.tag("bounding_box_vertices") | |
) | |
return profile | |
@staticmethod | |
def _tag_vertices_slot_retainer(profile): | |
"""Tag slot retainer.""" | |
selector = { | |
"q1": "(>>Y[1] and >>X[3]) or (>>X[1] and >>Y[3])", | |
"q2": "(<<Y[1] and >>X[3]) or (>>X[1] and <<Y[3])", | |
"q3": "(<<Y[3] and <<X[1]) or (<<Y[1] and <<X[3])", | |
"q4": "(>>Y[3] and <<X[1]) or (>>Y[1] and <<X[3])", | |
} | |
profile = ( | |
profile.reset() | |
.vertices(" or ".join(selector.values())) | |
.tag("slot_retainer_vertices") | |
) | |
return profile | |
@staticmethod | |
def _tag_vertices_v_lower(profile): | |
"""Tag V-slot lower verticies.""" | |
selector = { | |
"q1": "(<<X[1] and <<Y[7]) or (<<X[7] and <<Y[1])", | |
"q2": "(>>X[1] and <<Y[7]) or (>>X[7] and <<Y[1])", | |
"q3": "(>>X[1] and >>Y[7]) or (>>X[7] and >>Y[1])", | |
"q4": "(<<X[1] and >>Y[7]) or (<<X[7] and >>Y[1])", | |
} | |
profile = ( | |
profile.reset() | |
.vertices(" or ".join(selector.values())) | |
.tag("v_lower_vertices") | |
) | |
return profile | |
@staticmethod | |
def _tag_vertices_v_upper(profile): | |
"""Tag V-slot upper vertices.""" | |
selector = { | |
"q1": "(<<X[0] and <<Y[4]) or (<<X[4] and <<Y[0])", | |
"q2": "(>>X[0] and <<Y[4]) or (>>X[4] and <<Y[0])", | |
"q3": "(>>X[0] and >>Y[4]) or (>>X[4] and >>Y[0])", | |
"q4": "(<<X[0] and >>Y[4]) or (<<X[4] and >>Y[0])", | |
} | |
profile = ( | |
profile.reset() | |
.vertices(" or ".join(selector.values())) | |
.tag("v_upper_vertices") | |
) | |
return profile | |
@staticmethod | |
def _tag_vertices_rib_slot(profile): | |
"""Tag rib slot vertices.""" | |
profile = ( | |
profile.reset() | |
.vertices("<<X[2] or >>X[2] or <<Y[2] or >>Y[2]") | |
.tag("rib_slot_vertices") | |
) | |
return profile | |
@staticmethod | |
def _tag_vertices_core_rib(profile): | |
"""Tag core rib vertices.""" | |
selector = { | |
"q1": "(<<X[5] and <<Y[6]) or (<<X[6] and <<Y[5])", | |
"q2": "(>>X[5] and <<Y[6]) or (>>X[6] and <<Y[5])", | |
"q3": "(>>X[5] and >>Y[6]) or (>>X[6] and >>Y[5])", | |
"q4": "(<<X[5] and >>Y[6]) or (<<X[6] and >>Y[5])", | |
} | |
profile = ( | |
profile.reset() | |
.vertices(" or ".join(selector.values())) | |
.tag("core_rib_vertices") | |
) | |
return profile | |
@staticmethod | |
def _tag_vertices_core_channel(profile): | |
"""Tag core channel verticies.""" | |
profile = ( | |
profile.reset() | |
.vertices("(>>X[5] and <<Y[8]) or (>>X[5] and >>Y[8])") | |
.tag("core_channel_vertices") | |
) | |
return profile | |
@staticmethod | |
def _tag_vertices_channel_groove(profile): | |
"""Tag channel groove vertices.""" | |
profile = profile.reset().vertices(">>X[8]").tag("channel_groove_vertices") | |
return profile | |
@staticmethod | |
def _tag_vertices_cline(profile): | |
"""Tag center line vertices.""" | |
selector = { | |
"positive_x": "(<<X[5] and <<Y[12]) or (<<X[5] and >>Y[12])", | |
"positive_y": "(<<X[11] and <<Y[5]) or (<<X[9] and <<Y[5])", | |
"negative_y": "(<<X[11] and >>Y[5]) or (<<X[9] and >>Y[5])", | |
} | |
profile = ( | |
profile.reset() | |
.vertices(" or ".join(selector.values())) | |
.tag("cline_vertices") | |
) | |
return profile | |
@staticmethod | |
def _fillet(profile): | |
"""Fillet tagged vertices.""" | |
profile.vertices(tag="centerbore_groove_vertices").fillet(0.3) | |
profile.vertices(tag="bounding_box_vertices").fillet(0.5) | |
profile.vertices(tag="slot_retainer_vertices").fillet(0.25) | |
profile.vertices(tag="v_lower_vertices").fillet(0.25) | |
profile.vertices(tag="v_upper_vertices").fillet(0.3) | |
profile.vertices(tag="rib_slot_vertices").fillet(0.3) | |
profile.vertices(tag="core_rib_vertices").fillet(0.3) | |
profile.vertices(tag="core_channel_vertices").fillet(0.3) | |
profile.vertices(tag="channel_groove_vertices").fillet(0.3) | |
profile.vertices(tag="cline_vertices").fillet(0.3) | |
return profile | |
if "show_object" in locals(): | |
profile = Vslot2020Profile() | |
# show_object(profile.make()) | |
show_object(cq.Workplane().placeSketch(profile.make()).extrude(10)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment