{
 "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": 12,
   "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": 5,
   "id": "c22da25a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "580"
      ]
     },
     "execution_count": 5,
     "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": 10,
   "id": "98eeee57",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(620, 1400, 431)\n"
     ]
    }
   ],
   "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",
    "    print(interior_size)\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(True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cec5b185",
   "metadata": {},
   "source": [
    "## D) Lower Part (Cap)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "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.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
