{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "71c5d64a", "metadata": {}, "outputs": [], "source": [ "from solid import *\n", "import viewscad\n", "import numpy as np\n", "\n", "\n", "def yp(y:float) -> OpenSCADObject:\n", " return translate((0, y, 0))\n", "\n", "def ym(y: float) -> OpenSCADObject:\n", " return translate((0, -y, 0))\n", "\n", "def xp(x: float) -> OpenSCADObject:\n", " return translate((x, 0, 0))\n", "\n", "def xm(x: float) -> OpenSCADObject:\n", " return translate((-x, 0, 0))\n", "\n", "def zp(z: float) -> OpenSCADObject:\n", " return translate((0, 0, z))\n", "\n", "def zm(z: float) -> OpenSCADObject:\n", " return translate((0, 0, -z))\n", "\n", "def wedge(height_dir : int, width_dir: int, start_point, width, height, extent, straight_section=0):\n", " dtype = type(start_point[0])\n", " e_width = np.array([0, 0, 0], dtype=dtype)\n", " e_width[width_dir] = width\n", " e_height = np.array([0, 0, 0], dtype=dtype)\n", " e_height[height_dir] = height\n", " e_height_s = np.array([0, 0, 0], dtype=dtype)\n", " e_height_s[height_dir] = straight_section\n", " outward_dir = ({0, 1, 2} - {height_dir, width_dir}).pop()\n", " e_outward = np.array([0, 0, 0], dtype=dtype)\n", " e_outward[outward_dir] = extent\n", "\n", " start_point = np.array(start_point, dtype=dtype)\n", " start_point += e_height_s\n", " \n", " p0 = start_point \n", " p1 = start_point + e_height\n", " p2 = p1 + e_width\n", " p3 = p2 - e_height\n", " p4 = p3 + e_outward\n", " p5 = p0 + e_outward\n", " \n", " faces = [\n", " (0, 3, 4, 5), # top\n", " (3, 0, 1, 2),\n", " (4, 2, 1, 5), # \n", " (0, 5, 1), # front\n", " (4, 3, 2), # back\n", " ]\n", " result = polyhedron(points = [p0, p1, p2, p3, p4, p5], faces=faces)\n", " if straight_section > 0:\n", " cube_origin = start_point - e_height_s\n", " result += translate(cube_origin)(cube(e_outward + e_height_s + e_width))\n", " return result\n", "\n", "def wedge_by_angle(height_dir : int, width_dir: int, start_point, width, degrees, extent, straight_section=0):\n", " height = abs(int(extent / np.tan(np.deg2rad(degrees))))\n", " \n", " return wedge(height_dir, width_dir, start_point, width, height, extent, straight_section)\n", "\n", "\n", "r = viewscad.Renderer()\n", "\n", "#r.render(wedge_by_angle(1, 2, (0, 0, 0), 40, degrees=20, extent=20, straight_section=15))" ] }, { "cell_type": "markdown", "id": "0612a43c", "metadata": {}, "source": [ "# SwimTracker Case\n", "\n", "all units in tenth of mm\n", "\n", "## A) Dimensions of contained parts" ] }, { "cell_type": "markdown", "id": "69f72329", "metadata": {}, "source": [ "### Load Cell\n", "\n", "load cell is aligned such that cable outlet is at top right, \n", "\n", "$z$ axis is parallel to screws, $y$ is the larger size, $x$ the smaller size" ] }, { "cell_type": "code", "execution_count": 2, "id": "b979fa4a", "metadata": {}, "outputs": [], "source": [ "def make_load_cell_dims():\n", " size = (400, 1300, 220)\n", " d = 125 # distance between screw and next side\n", " screw_midpoints = [\n", " (d, d), (size[0] - d, d), # bottom two screws\n", " (d, size[1] - d), (size[0] -d, size[1] - d), # top two screws,\n", " (size[0] / 2, 2 * d), # bottom middle screw\n", " (size[0] / 2, size[1] - 2 * d), # top middle screw\n", " ]\n", " return {'size': size, \n", " 'screw_midpoints': screw_midpoints}\n", "\n", "load_cell_dims = make_load_cell_dims()" ] }, { "cell_type": "markdown", "id": "198286e2", "metadata": {}, "source": [ "### PCB" ] }, { "cell_type": "code", "execution_count": 3, "id": "4b17b3f1", "metadata": {}, "outputs": [], "source": [ "def make_pcb_dims():\n", " pcb_thickness = 16\n", " pin_space_below = 25\n", " component_max_height = 100\n", " total_height = pcb_thickness + pin_space_below + component_max_height\n", " \n", " return {\n", " 'size': (400, 800, total_height),\n", " 'pin_space_below': pin_space_below,\n", " 'pcb_thickness': pcb_thickness,\n", " }\n", "pcb_dims = make_pcb_dims()" ] }, { "cell_type": "markdown", "id": "f36ade54", "metadata": {}, "source": [ "## B) Parameters" ] }, { "cell_type": "code", "execution_count": 4, "id": "6ac51708", "metadata": {}, "outputs": [], "source": [ "wall_thickness = 30\n", "wedge_angle = 20 # in degrees\n", "\n", "# Tolerances\n", "tol_around_lcell = 20\n", "tol_pcb_slide = 5\n", "\n", "bottom_lip = 100 # how much longer the case is in y direction below the size of the load cell\n", "cable_space = 60 # how much space left/right to put the cable in\n", "separator_size = (wall_thickness, 190, 70) # 4 walls that fix load cell at the top and separate load cell & cable\n", "#pcb_rails_size = (50, pcb_dims['size'][1], wall_thickness) # 4 rails that hold pcb in place\n", "#pcb_rail_stopper_height = 0 # straight part after PCB rails (if 0 wedge starts right away)\n", "\n", "\n", "interior_size = (load_cell_dims['size'][0] + 2 * tol_around_lcell + 2 * cable_space + 2 * separator_size[0],\n", " load_cell_dims['size'][1] + bottom_lip,\n", " load_cell_dims['size'][2] + 2 * tol_around_lcell + pcb_dims['size'][2] + wall_thickness)\n", "size = tuple(s + 2 * wall_thickness for s in interior_size)" ] }, { "cell_type": "code", "execution_count": 11, "id": "c22da25a", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "580" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "load_cell_dims['size'][0] + 2 * cable_space + 2 * separator_size[0]" ] }, { "cell_type": "markdown", "id": "9778b756", "metadata": {}, "source": [ "## C) Upper Part" ] }, { "cell_type": "code", "execution_count": 5, "id": "98eeee57", "metadata": {}, "outputs": [], "source": [ "def make_upper_part(with_screw_holes):\n", " # shortcuts\n", " lcell_size = load_cell_dims['size']\n", " wt = wall_thickness\n", " \n", " # dims\n", " bottom_lip = 100 # how much longer the case is in y direction below the size of the load cell\n", " cable_space = 60 # how much space left/right to put the cable in\n", " separator_size = (wall_thickness, 190, 70) # 4 walls that fix load cell at the top and separate load cell & cable\n", " pcb_rails_size = (70, pcb_dims['size'][1], wt) # 4 rails that hold pcb in place\n", " pcb_rail_stopper_height = 0\n", " \n", " interior_size = (lcell_size[0] + 2 * tol_around_lcell + 2 * cable_space + 2 * separator_size[0],\n", " lcell_size[1] + bottom_lip,\n", " lcell_size[2] + 2 * tol_around_lcell + pcb_dims['size'][2] + wt)\n", " size = tuple(s + 2 * wall_thickness for s in interior_size)\n", " \n", " # outer wall\n", " def interior_coords(e):\n", " return translate([wt, bottom_lip + wt, wt])(e)\n", " outer_wall = cube(size) - translate([wt, 0, wt])(cube([interior_size[0], interior_size[1] + wt, interior_size[2]]))\n", " \n", " # pcb rails\n", " rail_space_to_wall = min(pcb_rails_size[2], pcb_dims['pin_space_below'] - pcb_rails_size[2])\n", " rail_z_extent = 2 * pcb_rails_size[2] + pcb_dims['pcb_thickness'] + tol_pcb_slide + rail_space_to_wall\n", " \n", " rail_wedge_height = pcb_rails_size[1] + pcb_rail_stopper_height\n", " rail_wedge_left = wedge_by_angle(1, 2, (0, rail_wedge_height, interior_size[2] - rail_z_extent), rail_z_extent, wedge_angle, pcb_rails_size[0])\n", " rail_wedge_right = wedge_by_angle(1, 2, (interior_size[0], rail_wedge_height, interior_size[2] - rail_z_extent), rail_z_extent, wedge_angle, -pcb_rails_size[0])\n", "\n", " stopper = yp(pcb_rails_size[1])(cube([pcb_rails_size[0], pcb_rail_stopper_height, rail_z_extent]))\n", " pcb_rails_left = zp(interior_size[2] - rail_space_to_wall - wt)(cube([pcb_rails_size[0], pcb_rails_size[1], rail_space_to_wall + pcb_rails_size[2]]) + zm(pcb_dims['pcb_thickness'] + tol_pcb_slide + pcb_rails_size[2])(stopper + cube(pcb_rails_size)))\n", " pcb_rails_right = xp(interior_size[0] - pcb_rails_size[0])(pcb_rails_left)\n", " \n", " pcb_rails = interior_coords(pcb_rails_left + pcb_rails_right + rail_wedge_left + rail_wedge_right)\n", " \n", " # load cell cap\n", " y_off = lcell_size[1] - separator_size[1]\n", " x_off_right = interior_size[0] - cable_space - separator_size[0]\n", " \n", " load_cell_cap_gen = yp(y_off)(cube([separator_size[0], separator_size[1], interior_size[2]]))\n", " load_cell_cap_left = xp(cable_space)(load_cell_cap_gen)\n", " load_cell_cap_right = xp(x_off_right)(load_cell_cap_gen)\n", " cable_cutout = xp(x_off_right)(yp(y_off)(zp(separator_size[2])(cube([separator_size[0], separator_size[1], lcell_size[2] + 2 * tol_around_lcell - 2 * separator_size[2]])))) \n", " load_cell_cap_cross = translate([0, y_off, lcell_size[2] + 2 * tol_around_lcell])((cube([interior_size[0], separator_size[1], separator_size[0]])))\n", " load_cell_cap = interior_coords(load_cell_cap_left + (load_cell_cap_right - cable_cutout) + load_cell_cap_cross)\n", " \n", " \n", " # wedges for cap\n", " cap_wedge_y = 0\n", " cap_wedge_width = load_cell_dims['size'][2] + 2 * tol_around_lcell\n", " cap_wedge_straight_thickness = 50\n", " cap_wedge_x = cable_space + wt\n", " cap_wedge_left = wedge_by_angle(1, 2, (0, 0, cap_wedge_y), cap_wedge_width, wedge_angle, cap_wedge_x, cap_wedge_straight_thickness)\n", " cap_wedge_right = wedge_by_angle(1, 2, (interior_size[0], 0, cap_wedge_y), cap_wedge_width, wedge_angle, -cap_wedge_x, cap_wedge_straight_thickness)\n", " cap_wedges = interior_coords(cap_wedge_left + cap_wedge_right)\n", "\n", " # screw holes\n", " lower_screw = load_cell_dims['screw_midpoints'][-2]\n", " upper_screw = load_cell_dims['screw_midpoints'][-1]\n", " screw_x_offset = cable_space + wt + tol_around_lcell\n", " hole_eps = 2\n", " screw_diameter = 75\n", " screw_hole_upper = translate([screw_x_offset + upper_screw[0], upper_screw[1], -hole_eps / 2 + interior_size[2]])(cylinder(d=screw_diameter, center=False, h= wt + 2* hole_eps))\n", " screw_hole_lower = translate([screw_x_offset + lower_screw[0], lower_screw[1], -hole_eps / 2 - wt])(cylinder(d=screw_diameter, center=False, h= wt + 2* hole_eps))\n", " screw_holes = interior_coords(screw_hole_upper + screw_hole_lower)\n", " \n", " if with_screw_holes:\n", " return (outer_wall + hole()(screw_holes)) + pcb_rails + load_cell_cap + cap_wedges\n", " else:\n", " return outer_wall + pcb_rails + load_cell_cap + cap_wedges\n", "\n", "#upper_part = make_upper_part(tr)" ] }, { "cell_type": "markdown", "id": "cec5b185", "metadata": {}, "source": [ "## D) Lower Part (Cap)" ] }, { "cell_type": "code", "execution_count": 6, "id": "a377d2ff", "metadata": {}, "outputs": [], "source": [ "def make_lower_part():\n", " return cube([interior_size[0], wall_thickness, interior_size[2]])\n", "\n", "lower_part = make_lower_part()\n", "#done directly in blender" ] }, { "cell_type": "markdown", "id": "e57a5d44", "metadata": {}, "source": [ "# Renders" ] }, { "cell_type": "code", "execution_count": 7, "id": "19c5c826", "metadata": {}, "outputs": [], "source": [ "display_scale = 0.001 # just for display in notebook\n", "output_scale = 0.1 # in mm" ] }, { "cell_type": "code", "execution_count": 8, "id": "caafb3cd", "metadata": {}, "outputs": [], "source": [ "#scad_render_to_file(scale(output_scale)(lower_part), \"lower_part.scad\")\n", "#r.render(scale(display_scale)(lower_part))\n", "#!openscad -o lower_part.stl lower_part.scad" ] }, { "cell_type": "code", "execution_count": 9, "id": "b9b5f0fd", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "3791607ee3e84c9ca1943887389ae504", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox(children=(HTML(value=''), Renderer(background='#cccc88', background_opacity=0.0, camera=PerspectiveCamera…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "scad_render_to_file(scale(output_scale)(make_upper_part(True)), \"upper_part_holes.scad\")\n", "r.render(scale(display_scale)(make_upper_part(True)))\n", "!openscad -o upper_part_holes.stl upper_part_holes.scad" ] }, { "cell_type": "code", "execution_count": 10, "id": "b556b39b", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "bbb678c04348422da6ee48a7e4f6efd2", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox(children=(HTML(value=''), Renderer(background='#cccc88', background_opacity=0.0, camera=PerspectiveCamera…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "scad_render_to_file(scale(output_scale)(make_upper_part(False)), \"upper_part_no_holes.scad\")\n", "r.render(scale(display_scale)(make_upper_part(False)))\n", "!openscad -o upper_part_no_holes.stl upper_part_no_holes.scad" ] }, { "cell_type": "code", "execution_count": null, "id": "b2577f94", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "3e5d1f1e", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5" } }, "nbformat": 4, "nbformat_minor": 5 }