Skip to content

Instantly share code, notes, and snippets.

@sethfischer
Created August 24, 2022 09:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sethfischer/62cd4efe2be82df7422718b1fb9fb09d to your computer and use it in GitHub Desktop.
Save sethfischer/62cd4efe2be82df7422718b1fb9fb09d to your computer and use it in GitHub Desktop.
AEC 2020 V-slot Aluminium Extrusion profile
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