Skip to content

Instantly share code, notes, and snippets.

@sethfischer
Created August 24, 2022 09:08
Show Gist options
  • Select an option

  • Save sethfischer/62cd4efe2be82df7422718b1fb9fb09d to your computer and use it in GitHub Desktop.

Select an option

Save sethfischer/62cd4efe2be82df7422718b1fb9fb09d to your computer and use it in GitHub Desktop.

Revisions

  1. sethfischer created this gist Aug 24, 2022.
    316 changes: 316 additions & 0 deletions aec_2020_vslot.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,316 @@
    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))