Skip to content

Instantly share code, notes, and snippets.

@omry
Created December 7, 2024 05:23
Show Gist options
  • Save omry/5dda74e1232d397f368c5874dc620956 to your computer and use it in GitHub Desktop.
Save omry/5dda74e1232d397f368c5874dc620956 to your computer and use it in GitHub Desktop.
STL cooling rack generator
width: 600 # Width of the cooling rack in mm
depth: 800 # Depth of the cooling rack in mm
height: 2200 # Total height of the rack in mm
grid_spacing: 50 # Spacing between grid bars in mm
rack_bar_thickness: 2 # Thickness of the rack bars in mm
frame_thickness: 25 # Thickness of the frame and legs in mm
leg_height: 100 # Height of the legs in mm
num_levels: 8 # Number of stacked levels
file_path: "Cooling_Rack_With_Vertical_Frame.stl" # Output STL file path
import numpy as np
from stl import mesh
from mpl_toolkits.mplot3d import Axes3D, art3d
import matplotlib.pyplot as plt
import hydra
from omegaconf import DictConfig
def create_bar(start, end, thickness):
print(f"Creating bar from {start} to {end} with thickness {thickness}")
delta = np.array(end) - np.array(start)
if delta[0] != 0: # Bar extends along the x-axis
vertices = np.array([
[start[0], start[1] - thickness / 2, start[2] - thickness / 2],
[end[0], start[1] - thickness / 2, start[2] - thickness / 2],
[end[0], start[1] + thickness / 2, start[2] - thickness / 2],
[start[0], start[1] + thickness / 2, start[2] - thickness / 2],
[start[0], start[1] - thickness / 2, start[2] + thickness / 2],
[end[0], start[1] - thickness / 2, start[2] + thickness / 2],
[end[0], start[1] + thickness / 2, start[2] + thickness / 2],
[start[0], start[1] + thickness / 2, start[2] + thickness / 2],
])
elif delta[1] != 0: # Bar extends along the y-axis
vertices = np.array([
[start[0] - thickness / 2, start[1], start[2] - thickness / 2],
[end[0] - thickness / 2, end[1], start[2] - thickness / 2],
[end[0] + thickness / 2, end[1], start[2] - thickness / 2],
[start[0] + thickness / 2, start[1], start[2] - thickness / 2],
[start[0] - thickness / 2, start[1], start[2] + thickness / 2],
[end[0] - thickness / 2, end[1], start[2] + thickness / 2],
[end[0] + thickness / 2, end[1], start[2] + thickness / 2],
[start[0] + thickness / 2, start[1], start[2] + thickness / 2],
])
elif delta[2] != 0: # Bar extends along the z-axis
vertices = np.array([
[start[0] - thickness / 2, start[1] - thickness / 2, start[2]],
[start[0] + thickness / 2, start[1] - thickness / 2, start[2]],
[start[0] + thickness / 2, start[1] + thickness / 2, start[2]],
[start[0] - thickness / 2, start[1] + thickness / 2, start[2]],
[start[0] - thickness / 2, start[1] - thickness / 2, end[2]],
[start[0] + thickness / 2, start[1] - thickness / 2, end[2]],
[start[0] + thickness / 2, start[1] + thickness / 2, end[2]],
[start[0] - thickness / 2, start[1] + thickness / 2, end[2]],
])
else:
raise ValueError("Start and end coordinates must define a bar along x, y, or z axis.")
# Define faces using two triangles per rectangular face
faces = np.array([
[0, 1, 2], [0, 2, 3], # Bottom face
[4, 5, 6], [4, 6, 7], # Top face
[0, 1, 5], [0, 5, 4], # Front face
[1, 2, 6], [1, 6, 5], # Right face
[2, 3, 7], [2, 7, 6], # Back face
[3, 0, 4], [3, 4, 7], # Left face
])
bar_mesh = mesh.Mesh(np.zeros(faces.shape[0], dtype=mesh.Mesh.dtype))
for i, f in enumerate(faces):
for j in range(3):
bar_mesh.vectors[i][j] = vertices[f[j]]
return bar_mesh
# Define faces using the vertices
faces = np.array([
[0, 1, 2], [0, 2, 3], # Bottom face
[4, 5, 6], [4, 6, 7], # Top face
[0, 1, 5], [0, 5, 4], # Front face
[1, 2, 6], [1, 6, 5], # Right face
[2, 3, 7], [2, 7, 6], # Back face
[3, 0, 4], [3, 4, 7], # Left face
])
# Fix diagonal face alignment for better rendering
faces = np.concatenate([
[[0, 1, 4], [1, 5, 4]], # Adjust diagonals for Front
[[1, 2, 5], [2, 6, 5]], # Adjust diagonals for Right
[[2, 3, 6], [3, 7, 6]], # Adjust diagonals for Back
[[3, 0, 7], [0, 4, 7]], # Adjust diagonals for Left
])
# Create mesh
bar_mesh = mesh.Mesh(np.zeros(faces.shape[0], dtype=mesh.Mesh.dtype))
for i, f in enumerate(faces):
for j in range(3):
bar_mesh.vectors[i][j] = vertices[f[j]]
return bar_mesh
@hydra.main(config_path=".", config_name="config", version_base=None)
def main(cfg: DictConfig):
print("Configuration Loaded:", cfg)
width = cfg.width
depth = cfg.depth
height = cfg.height
grid_spacing = cfg.grid_spacing
rack_bar_thickness = cfg.rack_bar_thickness
frame_thickness = cfg.frame_thickness
leg_height = cfg.leg_height
num_levels = cfg.num_levels
print(f"Dimensions: width={width}, depth={depth}, height={height}")
print(f"Grid spacing: {grid_spacing}, Rack bar thickness: {rack_bar_thickness}, Frame thickness: {frame_thickness}")
print(f"Leg height: {leg_height}, Number of levels: {num_levels}")
bars = []
# Add horizontal grid levels
level_height = (height - leg_height) / (num_levels - 1)
print(f"Calculated level height: {level_height}")
for level in range(num_levels):
z = leg_height + level * level_height
print(f"Adding horizontal grid at z={z}")
for y in range(0, depth + grid_spacing, grid_spacing):
start = [0, y, z]
end = [width, y, z]
bars.append(create_bar(start, end, rack_bar_thickness))
for x in range(0, width + grid_spacing, grid_spacing):
start = [x, 0, z]
end = [x, depth, z]
bars.append(create_bar(start, end, rack_bar_thickness))
# Add vertical corner frames (four corners)
corner_positions = [(0, 0), (0, depth), (width, 0), (width, depth)]
for i, pos in enumerate(corner_positions):
start = [pos[0], pos[1], 0]
end = [pos[0], pos[1], height]
print(f"Adding vertical corner frame {i} from {start} to {end}")
bars.append(create_bar(start, end, frame_thickness))
# Combine all bars into one mesh
print(f"Total number of bars: {len(bars)}")
combined_mesh = mesh.Mesh(np.concatenate([bar.data for bar in bars]))
print(f"Combined mesh has {len(combined_mesh.vectors)} triangles.")
file_path = cfg.file_path
combined_mesh.save(file_path)
print(f"Cooling rack STL file saved at: {file_path}")
# Visualize the model
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection="3d")
ax.add_collection3d(
art3d.Poly3DCollection(combined_mesh.vectors, alpha=1.0, edgecolor="k")
)
ax.auto_scale_xyz([0, width], [0, depth], [0, height])
plt.savefig("visualization_debug_highres.png", dpi=300)
print("Visualization saved as visualization_debug_highres.png")
plt.show()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment