Skip to content

Commit

Permalink
Added fasteners class per suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
jmwright committed Aug 29, 2024
1 parent 7227230 commit f262bed
Show file tree
Hide file tree
Showing 4 changed files with 249 additions and 72 deletions.
98 changes: 98 additions & 0 deletions nimble_build_system/cad/fasteners.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"""
Holds fastener classes containing the information needed to generate CAD models of fasteners.
"""

class Fastener:
"""
Class that defines a generic fastener that can be used in the assembly of a device and/or rack.
"""
_name = None
_position = (0.0, 0.0, 0.0)
_explode_translation = (0.0, 0.0, 0.0)
_size = "M3-0.5"
_fastener_type = "iso7380_1"
_direction_axis = "-Z"

def __init__(self, name, position, explode_translation, size, fastener_type, direction_axis):
"""
Generic fastener constructor that sets common attributes for all faster types.
"""
self._name = name
self._position = position
self._explode_translation = explode_translation
self._size = size
self._fastener_type = fastener_type
self._direction_axis = direction_axis

@property
def name(self):
"""
Getter for the name of the fastener.
"""
return self._name

@name.setter
def name(self, name):
"""
Setter for the name of the fastener.
"""
self._name = name

@property
def position(self):
"""
Getter for the position of the fastener.
"""
return self._position

@property
def explode_translation(self):
"""
Getter for the explosion translation of the fastener.
"""
return self._explode_translation

@property
def size(self):
"""
Getter for the size of the fastener.
"""
return self._size

@property
def fastener_type(self):
"""
Getter for the fastener_type of the fastener.
"""
return self._fastener_type

@property
def direction_axis(self):
"""
Getter for the direction axis of the fastener.
"""
return self._direction_axis


class Screw(Fastener):
"""
Specific type of fastener that adds screw-specific parameters like length.
"""
_length = 6 # mm

def __init__(self, name, position, explode_translation, size, fastener_type, axis, length):
"""
Screw constructor that additionally sets the length of the screw.
"""
# pylint: disable=too-many-arguments

self._length = length

super().__init__(name, position, explode_translation, size, fastener_type, axis)

@property
def length(self):
"""
Getter for the length of the screw.
"""
return self._length
150 changes: 79 additions & 71 deletions nimble_build_system/cad/shelf.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from nimble_build_system.cad import RackParameters
from nimble_build_system.cad.device_placeholder import generate_placeholder
from nimble_build_system.cad.shelf_builder import ShelfBuilder, ziptie_shelf
from nimble_build_system.cad.fasteners import Screw
from nimble_build_system.orchestration.device import Device
from nimble_build_system.orchestration.paths import REL_MECH_DIR

Expand Down Expand Up @@ -115,22 +116,14 @@ def __init__(self,
assembly_key: str,
position: tuple[float, float, float],
color: str,
rack_params: RackParameters,
device_depth_axis: float = "X",
device_offset: tuple[float, float, float] = (0, 0, 0),
fasteners: list[dict] = None,
device_explode_translation: tuple[float, float, float] = (0, 0, 0)
rack_params: RackParameters
):

# pylint: disable=too-many-arguments

self._rack_params = rack_params

self._device = device
self._device_depth_axis = device_depth_axis
self._device_offset = device_offset
self._fasteners = fasteners
self._device_explode_translation = device_explode_translation

#Note that "assembled shelf" is the CadOrchestrator AssembledComponent
# object not the full calculation in CadQuery of the physical assembly!
Expand Down Expand Up @@ -291,27 +284,31 @@ def generate_assembly_model(self, explode=False):
name="shelf",
color=cq.Color(0.565, 0.698, 0.278, 1.0),
metadata={
"explode_translation": cq.Location(
(self._device_explode_translation[0],
self._device_explode_translation[1],
self._device_explode_translation[2]))
"explode_translation": cq.Location(self._device_explode_translation)
})

# Add the screws to the assembly
for i, screw in enumerate(self._fasteners):
cur_screw = ButtonHeadScrew(size=screw["size"],
fastener_type=screw["type"],
length=screw["length"],
# Create a button head screw model
cur_screw = ButtonHeadScrew(size=screw.size,
fastener_type=screw.fastener_type,
length=screw.length,
simple=True).cq_object

# Figure out what the name of the screw should be
if screw.name is None:
screw.name = f"screw_{i}"

# Add the screw to the assembly
assy.add(cur_screw,
name=f"screw_{i}",
loc=cq.Location(*screw["position"]),
name=screw.name,
loc=cq.Location(*screw.position),
color=cq.Color(0.5, 0.5, 0.5, 1.0),
metadata={
"explode_translation": cq.Location(
(screw["explode_translation"][0],
screw["explode_translation"][1],
screw["explode_translation"][2]))
(screw.explode_translation[0],
screw.explode_translation[1],
screw.explode_translation[2]))
})

self._shelf_assembly_model = assy
Expand All @@ -328,14 +325,32 @@ def generate_assembly_model(self, explode=False):
return self._shelf_assembly_model


def get_render(self, assy, camera_pos, image_format="png"):
def get_render(self, model, camera_pos, annotate=False, image_format="png", image_path=None):
"""
Generates a render of the assembly.
parameters:
model (cadquery): The model to render, can be either a single part or an assembly
camera_pos (tuple): The position of the camera when capturing the render
annotate (bool): Whether or not to annotate the render using cq-annotate
image_format (str): The format of the image to render (png, svg, gltf, etc)
returns:
render_path (str): The path to the rendered image
"""

# pylint: disable=unused-argument

# TODO - Use the PNG functionality in CadQuery to generate a PNG render
# TODO - Maybe also need other formats such as glTF
#pylint: disable=unused-argument
# TODO - Return a bitmap buffer instead of a file path

# Check to see if we are dealing with a single part or an assembly
if isinstance(model, cq.Assembly):
print("We have an assembly")
else:
print("We have a part")

return NotImplemented


Expand Down Expand Up @@ -612,23 +627,47 @@ def __init__(self,
assembly_key: str,
position: tuple[float, float, float],
color: str,
rack_params: RackParameters,
device_depth_axis: float = "X",
device_offset: tuple[float, float, float] = (0, 0, 0),
fasteners: list[dict] = None,
device_explode_translation: tuple[float, float, float] = (0, 0, 0)):

#pylint: disable=too-many-arguments
rack_params: RackParameters):

self._device_depth_axis = "Y"
self._device_offset = (11.5, 42.5, 6.2)
self._device_explode_translation = (0.0, 0.0, -25.0)
self._fasteners = [
Screw(name=None,
position=(-13.0, 23.5, 7.0),
explode_translation=(0.0, 0.0, 35.0),
size="M3-0.5",
fastener_type="iso7380_1",
axis="-Z",
length=6),
Screw(name=None,
position=(36.0, 23.5, 7.0),
explode_translation=(0.0, 0.0, 35.0),
size="M3-0.5",
fastener_type="iso7380_1",
axis="-Z",
length=6),
Screw(name=None,
position=(-13.0, 81.5, 7.0),
explode_translation=(0.0, 0.0, 35.0),
size="M3-0.5",
fastener_type="iso7380_1",
axis="-Z",
length=6),
Screw(name=None,
position=(36.0, 81.5, 7.0),
explode_translation=(0.0, 0.0, 35.0),
size="M3-0.5",
fastener_type="iso7380_1",
axis="-Z",
length=6)
]

super().__init__(device,
assembly_key,
position,
color,
rack_params,
device_depth_axis,
device_offset,
fasteners,
device_explode_translation)
rack_params)


def generate_shelf_model(self):
Expand Down Expand Up @@ -704,41 +743,10 @@ def generate_shelf_model(self):
"hdd35": (HDD35Shelf, {}),
"dual-ssd": (DualSSDShelf, {}),
"raspi": (RaspberryPiShelf, {
"device_depth_axis": "Y",
"device_offset": (11.5, 42.5, 6.2),
"device_explode_translation": (0.0, 0.0, -25.0),
"fasteners": [
{
"position": (-13.0, 23.5, 7.0),
"explode_translation": (0.0, 0.0, 35.0),
"size": "M3-0.5",
"type": "iso7380_1",
"length": 6,
"axis": "-Z"
},
{
"position": (36.0, 23.5, 7.0),
"explode_translation": (0.0, 0.0, 35.0),
"size": "M3-0.5",
"type": "iso7380_1",
"length": 6,
"axis": "-Z"
},
{
"position": (-13.0, 81.5, 7.0),
"explode_translation": (0.0, 0.0, 35.0),
"size": "M3-0.5",
"type": "iso7380_1",
"length": 6,
"axis": "-Z"
},
{
"position": (36.0, 81.5, 7.0),
"explode_translation": (0.0, 0.0, 35.0),
"size": "M3-0.5",
"type": "iso7380_1",
"length": 6,
"axis": "-Z"
}]
# TODO: Hard code this into the shelf
# TODO: Abstract hole/screw position and use it for both shelf construction and fastener
# placement
# TODO: Create a fastener class that holds the fastener type, size, and position below
# TODO: Hard code as list of Fastener objects in the shelf constructor
})
}
2 changes: 1 addition & 1 deletion tests/test_cad.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pytest
from nimble_build_system.cad.shelf import Shelf, RaspberryPiShelf
from nimble_build_system.cad.shelf import RaspberryPiShelf
from nimble_build_system.orchestration.configuration import NimbleConfiguration

def test_shelf_generation():
Expand Down
71 changes: 71 additions & 0 deletions tests/test_rendering.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import tempfile
import os
import pytest
import cadquery as cq
from nimble_build_system.cad.shelf import RaspberryPiShelf
from nimble_build_system.orchestration.configuration import NimbleConfiguration


def test_model_png_rendering():
"""
Tests whether or not a PNG image can be output for a single part.
"""

# The configuration of hardware/shelves that we want to test against
test_config = ["Raspberry_Pi_4B"]

# Load the needed information to generate a Shelf object
config = NimbleConfiguration(test_config)

# Get the only shelf that we should have to deal with
rpi_shelf = config.shelves[0]

# Test the generated CAD assembly
shelf = rpi_shelf.generate_shelf_model().cq()

# Get the render of the shelf
rpi_shelf.get_render(shelf, (0, 0, 0))

# temp_dir = tempfile.gettempdir()

# Export the model to a PNG image
# cq.exporters.export(shelf, os.path.join(temp_dir, "test_model.png"))

# Check to make sure that the image was created
# assert os.path.exists(os.path.join(temp_dir, "test_model.png"))
assert True


def test_assembly_png_rendering():
"""
Tests whether or not a PNG image can be output for an entire assembly.
"""

# The configuration of hardware/shelves that we want to test against
test_config = ["Raspberry_Pi_4B"]

# Load the needed information to generate a Shelf object
config = NimbleConfiguration(test_config)

# Get the only shelf that we should have to deal with
rpi_shelf = config.shelves[0]

# Test the generated CAD assembly
shelf_assy = rpi_shelf.generate_assembly_model()

rpi_shelf.get_render(shelf_assy, (0, 0, 0))

# temp_dir = tempfile.gettempdir()

# Export the model to a PNG image
# cq.exporters.export(shelf_assy, os.path.join(temp_dir, "test_model.png"))

assert True


def test_annotated_assembly_png_rendering():
"""
Tests whether or not a PNG image can be output for an entire assembly with
annotations (i.e. assembly lines).
"""
assert True

0 comments on commit f262bed

Please sign in to comment.