codeCAD/smue2.ipynb
2026-02-27 02:05:50 +01:00

1813 lines
59 KiB
Plaintext

{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "11191589-d59e-453c-9473-98f7a5200e89",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting auto display for cadquery Workplane and Shape\n"
]
}
],
"source": [
"import timeit\n",
"tic = timeit.default_timer()\n",
"\n",
"import cadquery as cq\n",
"\n",
"\n",
"from jupyter_cadquery import (\n",
" versions,\n",
" show, PartGroup, Part, \n",
" get_viewer, close_viewer, get_viewers, close_viewers, open_viewer, set_defaults, get_defaults, open_viewer,\n",
" get_pick,\n",
")\n",
"from jupyter_cadquery.ocp_utils import webcol_to_cq\n",
"\n",
"from jupyter_cadquery.replay import replay, enable_replay, disable_replay\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "0bf54acd-c9dd-4d79-87a3-93245059765f",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Versions:\n",
"- jupyter_cadquery 3.5.2\n",
"- cad_viewer_widget 1.4.1\n",
"- open cascade 7.6.3\n",
"\n",
"Plugins loaded:\n",
"- build123d\n",
"- cadquery-massembly\n"
]
}
],
"source": [
"close_viewers()\n",
"versions()\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "ba2360de-f361-44ec-83a8-fa516edf320d",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"cv = open_viewer(\"smue2\",\n",
" anchor=\"right\", \n",
" glass=True, \n",
" theme=\"dark\",\n",
" tools=True) # sets default viewer\n",
"\n",
"\n",
"#cv.grid = [not g for g in cv.widget.grid]\n",
"\n",
"#cv.axes = not cv.axes\n",
"#cv.axes0 = not cv.axes0\n",
"#cv.transparent = not cv.transparent\n",
"#cv.black_edges = not cv.black_edges\n",
"\n",
"set_defaults(\n",
" cad_width=640, \n",
" height=480, \n",
" viewer=\"smue2\"\n",
")\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "6c0bf0b7-4559-487a-a059-95896c3ce3d0",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"from math import asin,pi\n",
"\n",
"def arcd(y):\n",
" return 2*asin(y*0.5)\n",
"\n",
"def row_phi(size, radius):\n",
" return arcd(size/radius)\n",
"\n",
"tol = 0.5\n",
"\n",
"column_width = 19.3\n",
"plate_thickness = 4.0\n",
"\n",
"#column_yoff = 20.2\n",
"column_yoff = 18.0\n",
"switch_height = 15.4\n",
"switch_travel = 3.9\n",
"pad = 1\n",
"buttonhole_size = 14.0\n",
"cap_size = 18.0\n",
"cap_height = 11.5\n",
"\n",
"y0 = 0#-column_width*0.5\n",
"z0 =30\n",
"x0=-55\n",
"\n",
"endz = 65\n",
"endx = 110\n",
"endy = 6*column_width+5*pad\n",
"\n",
"radii = [60,60,68,64,44,44]\n",
"#radii = [47,47,48,48,44,44]\n",
"# global angle to rotate everything around after generation\n",
"radius = max(radii) + switch_height\n",
"angle_y = row_phi(column_yoff, radius)/(pi)*180 * 2\n",
"\n",
"radmodp= lambda r : r+switch_height#-switch_travel+plate_thickness\n",
"radmoda = lambda r : r+switch_height- cap_height\n",
"\n",
"def column(radius, full=False, n_buttons=4, r=None, tol=0.0, overwidth=0.0):\n",
" \n",
" angle_button = row_phi(column_yoff, radmoda(radius))/(pi)*180\n",
" radius = radmodp(radius)\n",
" angle_rev = angle_button*n_buttons\n",
" if r:\n",
" angle_rev = r\n",
" \n",
" l = radius+plate_thickness+tol if full else plate_thickness\n",
" y0 = -tol if full else 0\n",
"\n",
" #radius = radius+tol if full else radius\n",
" \n",
" base = (cq.Workplane(\"YZ\")\n",
" .move(-tol, -plate_thickness+y0)\n",
" .rect(column_width+tol+overwidth, l, False)\n",
" .revolve(angle_rev,(-50,radius),(50,radius), False)\n",
" )\n",
" return base\n",
"\n",
"\n",
"def buttonhole(i, j,r):\n",
" \n",
" angle = row_phi(column_yoff, radmoda(r))/(pi)*180*(0.5+j)\n",
" r = radmodp(r)\n",
" # r = r+switch_height-switch_travel + plate_thickness\n",
" # h = 4\n",
" return (cq.Workplane(\"XY\")\n",
" .workplane(offset=switch_height/2)\n",
" .box(cap_size, cap_size ,switch_height)\n",
" .faces(\"<Z\")\n",
" .workplane()\n",
" .rect(buttonhole_size, buttonhole_size)\n",
" .extrude(7)\n",
" #.translate([0,0,h])\n",
" .rotate((0,-50,r),(0,50,r),angle)\n",
" .translate([0,(i+0.5)*(column_width)+i*pad, 0])\n",
" )\n",
"\n",
"def buttonholes(radii, n_buttons=4):\n",
" result = None\n",
" for i, r in enumerate(radii):\n",
" for j in range(n_buttons):\n",
" if not result:\n",
" result = buttonhole(i, j, r)\n",
" result += buttonhole(i, j, r)\n",
" return result\n",
"\n",
"def build_buttonplate(radii,full=False, phi=None):\n",
" radius = max(radii)\n",
" result=None\n",
" fingers = []\n",
" for i,r in enumerate(radii):\n",
" finger = column(r, full, 4, phi).translate([0,(column_width+pad)*i,0])\n",
" fingers.append(finger)\n",
" bridges = []\n",
" for i in range(len(fingers)-1):\n",
" left_finger_right_face = fingers[i].faces(\">Y\").val().outerWire()\n",
" right_finger_left_face = fingers[i+1].faces(\"<Y\").val().outerWire()\n",
" bridge = cq.Solid.makeLoft([left_finger_right_face, right_finger_left_face]) \n",
" bridges.append(bridge)\n",
" result = fingers[0]\n",
" for i in range(len(fingers)-1):\n",
" result += cq.Workplane().add(bridges[i]) + fingers[i+1]\n",
" return result\n",
"\n",
"def build_base_pcb_cutouts(radii):\n",
" cutouts = []\n",
" d = 1.5 # padding\n",
" h = 5 # pcb cutout height\n",
" for i, r in enumerate(radii):\n",
" phi = row_phi(column_yoff-(d*0.5), radmoda(r))/(pi)*180*4\n",
" r = radmodp(r)\n",
" cutout = (cq.Workplane(\"YZ\")\n",
" .move(d, -plate_thickness-h)\n",
" .rect(column_width-4, h+1, False)\n",
" .revolve(phi,(-50,r),(50,r), False)\n",
" .translate([0,(column_width+pad)*i,0])\n",
" #.faces(\"<Y or >Y\")#.edges(\">Z\")#.fillet(1)\n",
" \n",
" )\n",
" cutout = rotate_keyplate(cutout).faces(\"<Y or >Y\").edges(\"<Z\").fillet(2)\n",
" cutouts.append(cutout)\n",
" \n",
" result = cutouts[0]\n",
" for c in cutouts[1:]:\n",
" result += c\n",
" return result\n",
"\n",
"\n",
"def rotate_keyplate(part):\n",
" return part.rotate((0,-50,radius),(0,50,radius), -angle_y)\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0bb6d95c-eaf9-4915-b898-c1159ca0886a",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 5,
"id": "7a364cc4-dfd2-4a34-8a88-2a32408d1c7c",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"def build_screwholes(pos):\n",
" d = 10\n",
" hole= (cq.Workplane().cylinder(10,3/2))\n",
" countersunk = (cq.Workplane(\"XZ\")\n",
"\n",
" .polyline([(0,0),(0,d),(d,d),(0,0)])\n",
" .wire()\n",
" .revolve()\n",
" .translate([0,0,-2])\n",
" )\n",
" hole = (hole+countersunk).translate(pos)\n",
" stem = (cq.Workplane()\n",
" .workplane(offset=-10)\n",
" .cylinder(12, d+4)\n",
" .translate(pos)\n",
" )\n",
" return hole, stem\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "eb07d1a1-7512-464c-920d-0afd13fe092a",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"def build_handrest_cuttout(buttonplate):\n",
" h = plate_thickness\n",
" h1 = 50\n",
" dz=10\n",
" l = 0.7\n",
" sg = 0.04\n",
" y0=-30\n",
" pnts = [(y0, 5+z0),\n",
" ((1-l-sg)*(endy-y0)+y0,dz+z0),\n",
" ((l+sg)*(endy-y0)+y0, dz+z0),\n",
" (endy,5+z0)]\n",
" backpnts = [(y,z-h) for y,z in pnts][-1::-1]\n",
" backpnts_cutout = [(y,z+40) for y,z in pnts][-1::-1]\n",
"\n",
" top = (buttonplate.faces(\">X\")\n",
" .workplane(centerOption='CenterOfMass')\n",
" .moveTo(-endy/2+y0, switch_height)#plate_thickness*0.5)\n",
" .line(endy-y0,0)\n",
" .line(0,h1)\n",
" .line(-endy+y0,0)\n",
" .close()\n",
" .val()\n",
" )\n",
"\n",
" middle = (cq.Workplane(\"YZ\")\n",
" .workplane(offset=80)\n",
" .moveTo(-h, z0)\n",
" .spline(pnts, includeCurrent=False)\n",
" .line(0,30)\n",
" .line(-endy, 0)\n",
" .close()\n",
" .val()\n",
" )\n",
"\n",
" end = (cq.Workplane(\"YZ\")\n",
" .workplane(offset=endx)\n",
" .moveTo(y0,30-h)\n",
" .line(endy-y0,0)\n",
" .line(0,h1)\n",
" .line(-endy+y0,0)\n",
" .close()\n",
" .val()\n",
" )\n",
" \n",
" ans = cq.Solid.makeLoft([top, middle, end])\n",
" return ans\n",
"\n",
"#handrest_cutout = build_handrest_cuttout(raw_buttonplate)\n",
"#testrender()"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "8c526fc5-55a3-453d-afa5-878726a5c480",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"def build_xy_contour():\n",
" return (cq.Workplane(\"XY\")\n",
" .workplane(offset=-20)\n",
" .moveTo(x0,y0)\n",
" .line(0,endy)\n",
" .line(endx-x0,0)\n",
" .line(0,-endy) \n",
" .close()\n",
" .extrude(endz)\n",
" \n",
" )\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "314eae90-2aef-410a-9e46-97fa6b4f7d61",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"def build_electronics_cutout():\n",
" return (cq.Workplane(\"XY\")\n",
" .workplane(offset=-15)\n",
" .rect(50, 50)\n",
" .workplane(offset=30)\n",
" .rect(55,55)\n",
" .loft()\n",
" .fillet(5)\n",
" .translate([0, 56,0])\n",
" )\n"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "bc87c49c-9b74-4da6-9059-e86d44453b24",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"def build_keyplate_overhang_cutout(radii):\n",
" \n",
" def column(radius, n_buttons=4, r=None):\n",
" angle_button = row_phi(column_yoff, radmoda(radius))/(pi)*180\n",
" radius = radmodp(radius)\n",
" angle_rev = angle_button*(n_buttons)\n",
"\n",
"\n",
" base = (cq.Workplane(\"YZ\")\n",
" .move(0, -plate_thickness*5)\n",
" .rect(column_width, 5*plate_thickness, False)\n",
" .revolve(angle_rev,(-50,radius),(50,radius), False)\n",
" )\n",
" return base\n",
" \n",
" def plate(add=True):\n",
" n = 6 if add else 4\n",
" radius = max(radii)\n",
" \n",
" fingers = []\n",
" for i,r in enumerate(radii):\n",
"\n",
" finger = (column(r, n)\n",
" .translate([0,(column_width+pad)*i,0])\n",
" )\n",
" fingers.append(finger)\n",
" bridges = []\n",
" for i in range(len(fingers)-1):\n",
" left_finger_right_face = fingers[i].faces(\">Y\").val().outerWire()\n",
" right_finger_left_face = fingers[i+1].faces(\"<Y\").val().outerWire()\n",
" bridge = cq.Solid.makeLoft([left_finger_right_face, right_finger_left_face]) \n",
" bridges.append(bridge)\n",
" result = fingers[0]\n",
" for i in range(len(fingers)-1):\n",
" result += cq.Workplane().add(bridges[i]) + fingers[i+1]\n",
" #result += fingers[i+1]#cq.Workplane().add(bridges[i])# + fingers[i+1]\n",
" return result\n",
" \n",
" return plate(True)- plate(False)\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "387e92c7-5e92-4bdb-88d8-cbdc84e264ac",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"def build_bottom_cutout():\n",
" w = 100\n",
" h0 = 10\n",
" h = 30\n",
" x0 = 30\n",
" z0 = -30\n",
" d=0.3\n",
" pnts = [(x0, z0+h0),\n",
" (x0+0.3*w,h+z0),\n",
" (x0+0.5*w,15+h+z0),\n",
" (x0+0.9*w,15+h+z0)][-1::-1]\n",
" \n",
" \n",
" path = (cq.Workplane(\"YZ\")\n",
" .workplane(offset=x0+w*0.5)\n",
" .line(endx,0))\n",
" \n",
" straightpts = [(x0+0.75*w, z0+h0), (x0,z0+h0)]\n",
" return (cq.Workplane(\"XZ\")\n",
" .workplane(offset=-20)\n",
" .moveTo(x0,z0)\n",
" .line(w,0)\n",
" .line(0,h0)\n",
" .spline(straightpts, includeCurrent=True)\n",
" .close()\n",
" .workplane(offset=-30)\n",
" .moveTo(x0,z0)\n",
" .line(w,0)\n",
" .line(0, h+15)\n",
" .spline(pnts, includeCurrent=True)\n",
" .close()\n",
" .workplane(offset=-45)\n",
" .moveTo(x0,z0)\n",
" .line(w,0)\n",
" .line(0,h0)\n",
" .spline(straightpts, includeCurrent=True)\n",
" .close()\n",
" .loft()\n",
" )\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "bfbd1413-e793-4d44-a9a6-10f4790c9e96",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"def build_thumb_handrest_cuttout():\n",
" h1 = 50\n",
" base = (cq.Workplane(\"YZ\")\n",
" .transformed(rotate=[0,-15,0])\n",
" .transformed(offset=[endx+30,40, -38])\n",
" #.transformed(rotate=[-10,-15,-25])\n",
" \n",
" )\n",
" x1 = 0\n",
" ans = (base\n",
" .moveTo(x1,0)\n",
" .ellipse(22,22)\n",
" #.circle(10)\n",
" .workplane(offset=150)\n",
" .moveTo(x1,0)\n",
" .ellipse(37,22)\n",
" .loft(combine=True)\n",
" )\n",
" \n",
" return ans\n",
"#thumb_handrest_cutout = build_thumb_handrest_cuttout()\n",
"#based = contour - thumb_cutout - handrest_cutout - thumb_handrest_cutout\n",
"#testrender()\n",
"#based"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "139b0d8e-5e91-4664-9732-add85497d00d",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"def build_wire_tunnels():\n",
" d = 15\n",
" tunnel = cq.Workplane(\"XY\").workplane(offset=-8).move(0,20).rect(d,20).extrude(28).edges().fillet(3)\n",
" t1 = rotate_keyplate(tunnel).rotate((0,-50,radius),(0,50,radius), 10)\n",
" \n",
" r = radii[-1]+switch_height\n",
" t2 = rotate_keyplate(tunnel.rotate((0,-50,r),(0,50,r), 40)).translate([0,column_yoff*3.5,0])\n",
" t3 = rotate_keyplate(tunnel.rotate((0,-50,r),(0,50,r), 40)).translate([0,column_yoff*4.5,0])\n",
"\n",
" return t1 +t2 +t3\n",
"#wire_tunnels = build_wire_tunnels()\n",
"#testrender()"
]
},
{
"cell_type": "markdown",
"id": "dc9c2857-ad6c-4ab8-b72a-d24368469a1b",
"metadata": {},
"source": [
"# add thumb to base contour"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "2dc6984c-bdf0-48fb-8c5f-849a57834ced",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"def position_thumbplate(plate, r=44):\n",
" \n",
" phi = row_phi(column_yoff, radmoda(r))/(pi)*180\n",
" r = radmodp(r)\n",
" thumbplate = plate.translate([0,0,plate_thickness/2])\n",
" thumbplate = thumbplate.rotate((0,-r,r),(0,r,r), -phi)\n",
" thumbplate = thumbplate.rotate((0,0,0),(0,0,1), -60)\n",
" thumbplate = thumbplate.translate([35,-27,0])\n",
" return thumbplate\n",
"\n",
"\n",
"def position_second_col_thumbplate(plate, r):\n",
" phi = row_phi(column_yoff, radmoda(r))/(pi)*180\n",
" r = radmodp(r)\n",
" return (plate.rotate((0,-r,r),(0,r,r), phi)\n",
" .translate([0, column_width, -plate_thickness/2]))\n",
"\n",
"\n",
"def build_thumbplate(r=44):\n",
" c = column(r, full=False, n_buttons=2) \n",
" #r += switch_height\n",
" c1 = position_second_col_thumbplate(c, r)\n",
" f = c.faces(\">Y\").wires().val()\n",
" f1 = c1.faces(\"<Y\").wires().val()\n",
" #l = cq.Solid.makeLoft([f,f1])\n",
" thumbplate = c + c1 \n",
" thumbplate = position_thumbplate(thumbplate, r)\n",
" thumbplate = thumbplate.edges(\">>Y[0]\").fillet(5)\n",
" return thumbplate\n",
"\n",
"def build_thumbplate_holes(r=44):\n",
" #r += switch_height \n",
" h = buttonholes([r], n_buttons=2)\n",
" h1 = position_second_col_thumbplate(h, r)\n",
" holes = h + h1\n",
" holes = position_thumbplate(holes, r)\n",
" return holes\n",
"\n",
"def build_thumbplate_cutspace(r=44, phi=None, yoff=0, width=22): \n",
" phipad = row_phi(tol, radmoda(r))/(pi)*180\n",
" angle_button = row_phi(column_yoff, radmoda(r))/(pi)*180\n",
" angle_rev = angle_button*2+(phipad*2)\n",
" angle_2nd = 20\n",
" #r = radmodp(r)\n",
" dr = 30\n",
" r0 = r\n",
" c0 = (cq\n",
" .Workplane(\"YZ\")\n",
" .workplane(offset=0)#-tol)\n",
" .move(-column_width, -plate_thickness+y0-50)\n",
" .rect(column_width*5, 200, False)\n",
" .extrude(30))\n",
" #r = radmodp(r)\n",
" c = column(r, full=True, n_buttons=2, r=angle_rev, tol=3*tol)\n",
" \n",
" #.translate([0,yoff,0])\n",
" c = c.rotate((0,-r,r),(0,r,r), -phipad)\n",
" c1 = position_second_col_thumbplate(c, r0)#c.translate([0, width+tol, 0])\n",
" \n",
" \n",
" c = column(r, full=True, n_buttons=4, tol=tol).translate([0,yoff,0])\n",
" c = c.rotate((0,-r,r),(0,r,r), -phipad)\n",
" \n",
" f = c.faces(\">Y\").wires().val()\n",
" f1 = c1.faces(\"<Y\").wires().val()\n",
" # l = cq.Solid.makeLoft([f,f1])\n",
" cutspace = c.translate([0,-10,0])+ c0+c + c1 + c1.translate([0,-15,0])\n",
" #cutspace = cutspace.rotate((0,-r,r),(0,r,r), 20)\n",
" cutspace = position_thumbplate(cutspace, r0)\n",
" return cutspace\n",
"\n",
"\n",
"def build_thumbcontour():\n",
" # The points that the spline will pass through\n",
" s = cq.Workplane(\"XY\")\n",
" x0 = 40\n",
" y0 = -45\n",
" sPnts = [\n",
" (x0-33, y0+55),\n",
" (x0, y0-8),\n",
" (x0+24, y0+6),\n",
" (x0+40, y0+27),\n",
" (x0+70, y0+45),\n",
" ]\n",
" r = (s.polyline(sPnts)#.lineTo(x0, y0)\n",
" .close()\n",
" .extrude(endz)\n",
" .translate([0,0,-20])\n",
" #.faces(\">>X[2]\")\n",
" #.edges(\"|Z and >>X[2]\").fillet(10)\n",
" .edges(\"|Z and >>Y[2]\").fillet(30)\n",
" .edges(\"|Z and <<Y[4]\").fillet(50)\n",
" )\n",
" plate_aligned_cutout = position_thumbplate( \n",
" cq\n",
" .Workplane(\"YZ\")\n",
" .workplane(offset=-100)\n",
" .move(-column_width, -plate_thickness+y0)\n",
" .rect(column_width, 200, False)\n",
" .extrude(200))\n",
" \n",
" return r -plate_aligned_cutout\n",
"\n",
"\n",
"def build_thumb_cutout(r=44):\n",
" r0 = r+switch_travel \n",
" r = radmodp(r)\n",
" off = -3*column_yoff\n",
" return position_thumbplate(\n",
" #return (\n",
" #build_thumbplate_cutspace(40,360, yoff=-10, width=32)\n",
" cq.Workplane(\"XZ\")\n",
" .workplane(offset=off)\n",
" .move(-r, switch_travel)\n",
" #.move(-r,-switch_travel)#-r+switch_height)\n",
" #.cylinder(-off+30,r-switch_height+switch_travel, centered=False)\n",
" .cylinder(-3*off, r-switch_travel, centered=False)\n",
" .faces(\">Y\")\n",
" .workplane(centerOption='CenterOfMass')\n",
" #.wires().toPending()\n",
" #.move(0, -switch_height+switch_travel)\n",
" .circle(r-switch_travel)\n",
" .workplane(offset=150, centerOption='CenterOfMass')\n",
" .move(10,0)\n",
" .circle(20)\n",
" .loft()#.clean()\n",
" .edges(\">>Y[2]\").fillet(290)\n",
" .rotate((0,-50,r),(0,50,r), 75)\n",
" .translate([0,2*column_yoff,0])\n",
" )\n",
"\n",
"def build_thumb_overhang_cutout(radii):\n",
" \n",
" def column(radius, n_buttons=2, r=None):\n",
" #radius+=switch_height\n",
" angle_button = row_phi(column_yoff, radius)/(pi)*180\n",
" angle_rev = angle_button*(n_buttons)\n",
" s = 5\n",
"\n",
" base = (cq.Workplane(\"YZ\")\n",
" .move(-32, 0)#-plate_thickness*s)\n",
" .rect(column_width+28, s*plate_thickness, False)\n",
" .revolve(angle_rev,(-50,radius),(50,radius), False)\n",
" )\n",
" return base\n",
" \n",
" def plate(add=True):\n",
" n = 4 if add else 2\n",
" fingers = []\n",
" for i,r in enumerate(radii):\n",
"\n",
" finger = (column(r, n)\n",
" #.translate([0,(column_width+pad)*i,0])\n",
" )\n",
" finger = position_second_col_thumbplate(finger,r) if i == 1 else finger\n",
" fingers.append(finger)\n",
" bridges = []\n",
" for i in range(len(fingers)-1):\n",
" left_finger_right_face = fingers[i].faces(\">Y\").val().outerWire()\n",
" right_finger_left_face = fingers[i+1].faces(\"<Y\").val().outerWire()\n",
" bridge = cq.Solid.makeLoft([left_finger_right_face, right_finger_left_face]) \n",
" bridges.append(bridge)\n",
" result = fingers[0]\n",
" for i in range(len(fingers)-1):\n",
" result += cq.Workplane().add(bridges[i]) + fingers[i+1]\n",
" #result += fingers[i+1]#cq.Workplane().add(bridges[i])# + fingers[i+1]\n",
" return result\n",
" \n",
" return position_thumbplate(plate(True)- plate(False))\n",
" #return (build_thumbplate_cutspace(70))\n",
" \n",
"def build_thumb_pcb_cutouts(radii=[44,44]):\n",
" cutouts = []\n",
" d = 1.5 # padding\n",
" h = 5 # pcb cutout height\n",
" for i, r in enumerate(radii):\n",
" \n",
" phi = row_phi(column_yoff-d \\\n",
" #+(d*0.5) \\\n",
" ,radmoda( r))/(pi)*180*2\n",
" r = r+switch_height\n",
" phi = phi + 20 if i == 0 else phi\n",
" cutout = (cq.Workplane(\"YZ\")\n",
" .move(d, -plate_thickness-h)\n",
" .rect(column_width-2*d, h, False)\n",
" .revolve(phi,(-50,r),(50,r), False) \n",
" )\n",
" if i == 1:\n",
" cutout = (cq.Workplane(\"YZ\")\n",
" .move(-2*d, -plate_thickness-h)\n",
" .rect(column_width+d, h, False)\n",
" .revolve(phi,(-50,r),(50,r), False))\n",
" cutout = position_second_col_thumbplate(cutout, r-switch_height)\n",
" cutout = cutout.faces(\"<Y or >Y\").edges(\"<<Z[2]\").fillet(2)\n",
" cutout = cutout.rotate((0,-50,r),(0,50,r), 2.5)\n",
" cutout = position_thumbplate(cutout, r-switch_height) \n",
" #if i== 1:\n",
" cutouts.append(cutout)\n",
" \n",
" result = cutouts[0]\n",
" for c in cutouts[1:]:\n",
" result += c\n",
" return result\n",
"\n",
"#thumbplate_cutspace = build_thumbplate_cutspace()\n",
"#thumb_pcb_cutout = build_thumb_pcb_cutouts()\n",
"#thumbcontour = build_thumbcontour()\n",
"#thumb_cutout = build_thumb_cutout()\n",
"#thumb_switches = place_thumb_switches(mx_switch)\n",
"#thumbplate_cutspace = build_thumbplate_cutspace()\n",
"#thumb_caps = place_thumb_switches(xda_cap)\n",
"#thumbplate = build_thumbplate()\n",
"#thumb_pcb_cutout = build_thumb_pcb_cutouts()\n",
"#thumbplate_cutspace = build_thumbplate_cutspace()\n",
"#thumb_handrest_cutout = build_thumb_handrest_cuttout()\n",
"#thumb_overhang_cutout = build_thumb_overhang_cutout([44+switch_height-switch_travel])\n",
"#testrender()"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "348213f4-1705-454e-b921-014cf6e03f6e",
"metadata": {},
"outputs": [],
"source": [
"\n",
"#prt = column(44, 0, 1) - buttonhole(0,0,44)\n",
"#cq.exporters.export(prt, basepath+\"/singlebtnframe.step\")\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8e83077d-cabd-4740-9eb5-36896c3df032",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "4ce08cb1-2239-4ca9-a11c-882aebeaaac7",
"metadata": {},
"source": [
"# create bridge between buttonplate and thumb_buttonplate"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "181c0e74-d6d6-4967-8cea-2fa959b1078a",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"def build_buttonplate_bridge():\n",
" buttonplate_bridge_a = build_buttonplate([radii[0]],False, angle_y).translate([0,10,0])\n",
" buttonplate_bridge_a = rotate_keyplate(buttonplate_bridge_a)\n",
" f1 = buttonplate_bridge_a.faces(\"<Y\").val().outerWire()\n",
" buttonplate_bridge_b = position_thumbplate(column(44, full=False, n_buttons=2))\n",
" f2 = buttonplate_bridge_b.faces(\">Y\").val().outerWire()\n",
" buttonplate_bridge = cq.Solid.makeLoft([f2, f1])\n",
" return buttonplate_bridge\n",
" #return buttonplate_bridge_b\n"
]
},
{
"cell_type": "markdown",
"id": "c14ffb97-a514-4af9-ab58-a4434f9591f1",
"metadata": {},
"source": [
"# create slot"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "5792be7b-db30-4b0f-93ca-581c8991d5ea",
"metadata": {},
"outputs": [],
"source": [
"def build_slot():\n",
" box =( cq.Workplane(\"YZ\").workplane(offset=x0)\n",
" .moveTo(2.5*column_yoff,-10)\n",
" .sketch()\n",
" .rect(20,20)\n",
" .finalize()\n",
" .extrude(50)\n",
" .edges(\"<X\").edges(\"<Z\").fillet(10))\n",
" \n",
" slot = (cq.Workplane(\"YZ\").workplane(offset=x0)\n",
" .moveTo(2.5*column_yoff,-11)\n",
" .sketch()\n",
" .slot(12,8)\n",
" .finalize()\n",
" .extrude(10))\n",
" usb = (cq.Workplane(\"YZ\").workplane(offset=x0)\n",
" .moveTo(2.5*column_yoff,-11)\n",
" .sketch()\n",
" .slot(9,3.2)\n",
" .finalize()\n",
" .extrude(10))\n",
" \n",
" cutslot = (cq.Workplane(\"YZ\").workplane(offset=x0)\n",
" .moveTo(2.5*column_yoff,-11)\n",
" .sketch()\n",
" .slot(12,8)\n",
" .finalize()\n",
" .extrude(50))\n",
" return cutslot, (slot & box)-usb\n",
"#slot, usbsocket = build_slot()\n",
"#usbsocket"
]
},
{
"cell_type": "markdown",
"id": "dc985ea6-6982-4edc-9f43-39c85c7f3084",
"metadata": {},
"source": [
"# create instances of all components"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "e7a0ab16-5095-4dfc-bff3-f418940d25e8",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"raw_buttonplate = rotate_keyplate(build_buttonplate(radii)).faces(\"<Y or >Y\").edges(\"%LINE\").edges(\">>X[1] or >>X[0]\").fillet(8)\n",
"holes = rotate_keyplate(buttonholes(radii))\n",
"\n",
"buttonplate_cutspace = build_buttonplate(radii,True, angle_y*4).rotate((0,-50,radius),(0,50,radius), -angle_y-0.4)\n",
"buttonplate_cutspace1 = (build_buttonplate([radii[0]],True, angle_y*4)\n",
" .rotate((0,-50,radius),(0,50,radius), -angle_y-0.4)\n",
" #.rotate((0,-50,radii[0]),(0,50,radii[0]), angle_y*0.2)\n",
" .translate([0,-column_yoff*0.8,0]) )\n",
"#holes\n",
"hole1, stem1 = build_screwholes([0.,column_width+pad/2])\n",
"hole2, stem2 = build_screwholes([20.,5*(column_width+pad)-0.5,2.5])\n",
"slot, usbsocket = build_slot()\n",
"\n",
"xy_contour = build_xy_contour()\n",
"\n",
"handrest_cutout = build_handrest_cuttout(raw_buttonplate)\n",
"thumb_handrest_cutout = build_thumb_handrest_cuttout()\n",
"electronics_cutout = build_electronics_cutout()\n",
"overhang_cutout = rotate_keyplate(build_keyplate_overhang_cutout(radii))\n",
"bottom_cutout = build_bottom_cutout()\n",
"\n",
"\n",
"\n",
"pcb_cutouts = build_base_pcb_cutouts(radii)\n",
"wire_tunnels = build_wire_tunnels()\n",
"thumbplate = build_thumbplate()\n",
"thumbholes = build_thumbplate_holes()\n",
"thumbplate_cutspace = build_thumbplate_cutspace()\n",
"thumb_cutout = build_thumb_cutout()\n",
"thumbcontour = build_thumbcontour()\n",
"thumb_overhang_cutout = build_thumb_overhang_cutout([40])\n",
"#thumb_overhang_cutout -= thumb_overhang_cutout & buttonplate_cutspace\n",
"\n",
"thumb_pcb_cutout = build_thumb_pcb_cutouts()\n",
"\n",
"buttonplate_bridge = build_buttonplate_bridge()\n",
"\n",
"#raw_buttonplate += buttonplate_bridge\n",
"buttonplate = ((raw_buttonplate + buttonplate_bridge)- thumbholes - holes-hole1-hole2) \n",
"thumbplate = thumbplate - thumbholes - holes\n",
"buttonplate += thumbplate\n",
"\n",
"#testrender()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d8dbe4f9-6e2d-42aa-98e0-890c67faa7f1",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 18,
"id": "54dc0c80-cb21-4ed3-beb8-85192e7a52bf",
"metadata": {},
"outputs": [],
"source": [
"def build_contour():\n",
"# fuse both contours together and make seam smooth\n",
" r1 = 60\n",
" r2 = r1\n",
" contour = xy_contour + thumbcontour\n",
" contour = (contour.faces(\"-Y\").edges(\"|Z and >X\").fillet(r1)\n",
".edges(\"|Z\").edges(\"<<X[0]\").fillet(r2)\n",
" \n",
" )\n",
" return contour\n",
"contour = build_contour()\n",
"#contour"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7a0ff9b5-69e4-4efb-8932-7969ecbf1ec1",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "0a46d47e-d823-4191-85c0-af8ec6888369",
"metadata": {},
"source": [
"# build base"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "0c5aa503-6e41-4c3e-894e-87b571cf6fc5",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"def build_base():\n",
" base = contour - thumb_cutout- handrest_cutout- thumb_handrest_cutout\n",
" #return base\n",
" base = (base \n",
" - buttonplate_cutspace \n",
" - buttonplate_cutspace1\n",
" - overhang_cutout\n",
" #-thumb_overhang_cutout\n",
" )\n",
" #return base\n",
" base = base - pcb_cutouts - electronics_cutout - wire_tunnels\n",
" #return base\n",
" base = (base\n",
" .edges(\"<X\").edges(\"not <Z\")\n",
" #.edges(\"not |Z\")\n",
" .edges(\"|Y\")\n",
" .fillet(8) # front top edges\n",
" .edges(\"<X and |Z\").fillet(8) # front side edges\n",
")\n",
" \n",
" base = base-hole1-hole2\n",
" \n",
" # smooth handrest\n",
" #return base\n",
" r1 = 20\n",
" base = (base.edges(\"<<Z[0]\").fillet(r1)\n",
" .edges(\"<<Z[1]\").fillet(r1)\n",
" .edges(\"<<X[0]\").edges(\">Z\").fillet(12)\n",
" .edges(\"|Z and >>Y[0]\").fillet(5)\n",
" .faces(\"<Z\").fillet(10)\n",
")\n",
" \n",
" #return base\n",
" # add slot for cable\n",
" base = base - slot\n",
" base = base - thumbplate_cutspace \n",
" #base = (base.faces(\"<Z\").fillet(10)) # bottom edge\n",
" base = base -thumb_pcb_cutout\n",
" \n",
" #base = base \n",
" #base = base.edges(\">>Y[0]\").fillet(3)\n",
" base = base - thumbholes\n",
" return base\n",
"\n",
"base = build_base()\n",
"#testrender()\n",
"base\n",
"#keyboard = show_product()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "81768916-1d16-4d20-b337-9d1a59a42341",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "1af586cd-f2dc-4cd4-8daf-09957973b3c3",
"metadata": {},
"source": [
"# add ridge to base"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "c47df18b-fb87-4dc9-b4be-50347b26ffeb",
"metadata": {},
"outputs": [],
"source": [
"from build123d import *"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "dbd66d5c-ccc0-4921-914b-64a2ecab6f39",
"metadata": {},
"outputs": [],
"source": [
"\n",
"def add_ridge(contour, base):\n",
" path = contour.faces(\"<Z\").edges(\"<<X[2]\").translate([0,0,18.5]).val()\n",
" pathw = path.wrapped\n",
" path = Edge()\n",
" path.wrapped = pathw\n",
" with BuildPart() as signal:\n",
" with BuildLine(Plane.XY) as path_builder:\n",
" #path_builder.mode=Mode.PRIVATE\n",
" #Spline(path@0, ())\n",
" p1 = path@0\n",
" p1.Y = p1.Y+8.5\n",
" j0 = JernArc(start=p1, tangent=-(path % 0), radius=10, arc_size=-90)\n",
" j1 = JernArc(start=p1, tangent=path%0, radius=60, arc_size=-90)\n",
" #add(path)\n",
" #j1 = Spline(p1, path@1, tangents=[-(j0%0), path%1])\n",
" j2=JernArc(start=j1 @ 1, tangent=j1 % 1, radius=170, arc_size=90)\n",
"\n",
" with BuildSketch(Plane(j2 @ 0, z_dir=j2 % 0)) as x_section:\n",
" Circle(8)\n",
" sweep()\n",
" #sweep(sections=x_section, path=path_builder, transition=Transition.ROUND)\n",
" signal.label=\"decor\"\n",
" # convert base to build123d\n",
" base_b = Solid()\n",
" base_b.wrapped = base.val().wrapped\n",
"\n",
" with BuildPart() as pb:\n",
" add(base_b)\n",
" with Locations((0,0,0)):\n",
" add(signal.part, mode=Mode.SUBTRACT)\n",
" cut_selection = pb.edges(Select.LAST).filter_by(GeomType.CIRCLE, reverse=True).filter_by(GeomType.LINE, reverse=True).filter_by(GeomType.LINE, reverse=True).sort_by(Axis.Z)[1:]\n",
" cut_selection = cut_selection[:-3]+cut_selection[-2:]\n",
" fillet(cut_selection, radius=3)\n",
" # convert base back to cadquery \n",
" bases = cq.Solid.makeBox(1,1,1)\n",
" bases.wrapped = pb.part.solid().wrapped\n",
" return bases\n",
" \n",
"\n",
"#base = add_ridge(contour, base)bases\n",
"\n",
"#show(base, axes=True, axes0=True\n",
"# , ambient_intensity=1, theme=\"dark\", reset_camera=False\n",
"# ,tools=True, render_normals=False)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "520092cc-c728-4fa7-89ec-6cfdd14355c8",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"id": "c52f5db1-9205-4dc3-86a9-8def844d89eb",
"metadata": {},
"source": [
"# add switches and keycaps"
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "6c14a05b-612e-415d-97a4-eab445b680ca",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"mx_switch = (cq.importers.importStep(\"switch.step\")\n",
" .rotate([0,0,0],[1,0,0], 90)\n",
" .rotate([0,0,0],[0,0,1], 90)\n",
" )\n",
"\n",
"xda_cap = (cq.importers.importStep(\"xdacap.step\")\n",
" .translate([67, 0, 6])\n",
" .val().scale(18.0/17.7)\n",
" #.rotate([0,0,0],[0,0,1], 90)\n",
" )\n",
"\n",
"#xda_cap + mx_switch"
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "71e06b37-3747-4ad8-a777-1a289b329022",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"def place_switch(switch, i, j,r):\n",
" angle = row_phi(column_yoff, radmoda(r))/(pi)*180*(0.5+j)\n",
" r =radmodp(r)\n",
" return (cq.Workplane(\"XY\")\n",
" .workplane(offset=-plate_thickness*0.5)\n",
" .add(switch)\n",
" #.translate([0,0,0])\n",
" .translate([1,(i+0.5)*(column_width)+i*pad, 0])\n",
" .rotate((0,-50,r),(0,50,r),angle)\n",
" \n",
" )\n",
"\n",
"def build_switches(model):\n",
" switches = None\n",
" for i, r in enumerate(radii):\n",
" for j in range(4):\n",
" switch = place_switch(model, i, j, r)\n",
" switches = switches+switch if switches else switch\n",
" return switches\n",
"\n",
"\n",
"def place_thumb_switches(switch, r=44):\n",
" c1 = place_switch(switch, 0, 0,r)+place_switch(switch, 0, 1,r)\n",
" c2 = position_second_col_thumbplate(c1, r)\n",
" switches = c1 + c2\n",
" return position_thumbplate(switches, r)\n"
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "467a4986-63f0-4bd3-928a-1095f0619cf5",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"switches = rotate_keyplate(build_switches(mx_switch))\n",
"caps = rotate_keyplate(build_switches(xda_cap))"
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "189465d7-ba99-46d3-9642-6991935baa09",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"thumb_switches = place_thumb_switches(mx_switch)\n",
"thumb_caps = place_thumb_switches(xda_cap)"
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "68cd5bd3-3f2b-4efd-b8cf-461863ece5ba",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"100% ⋮————————————————————————————————————————————————————————————⋮ (2/2) 0.22s\n"
]
},
{
"data": {
"text/plain": [
"<cad_viewer_widget.widget.CadViewer at 0x736c913cc910>"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"def testrender():\n",
" return show(cq.Assembly()\n",
" #.add(xy_contour, color=webcol_to_cq(\"#11abdda4\"))\n",
" #.add(thumbcontour, color=webcol_to_cq(\"#ddab11a4\"))\n",
" #.add(contour, color=webcol_to_cq(\"#33ff6622\"))\n",
" .add(base, color=webcol_to_cq(\"#33ff66aa\"))\n",
" #.add(wire_tunnels, color=webcol_to_cq(\"#aa44ff99\"))\n",
" #.add(thumbplate, color=webcol_to_cq(\"#aa44ff99\"))\n",
" #.add(buttonplate_cutspace1, color=webcol_to_cq(\"#33abdd4a\"))\n",
" #.add(thumbplate_cutspace, color=webcol_to_cq(\"#1166ff33\")) \n",
" #.add(pcb_cutouts, color=webcol_to_cq(\"#aa44ff99\")) \n",
" #.add(thumb_cutout, color=webcol_to_cq(\"#ff66ff33\"))\n",
" #.add(handrest_cutout, color=webcol_to_cq(\"#aa110022\")) \n",
" .add(thumb_handrest_cutout, color=webcol_to_cq(\"#aa440022\"))\n",
" #.add(thumb_overhang_cutout, color=webcol_to_cq(\"#aa44ff99\"))\n",
" #.add(thumb_pcb_cutout, color=webcol_to_cq(\"#aa44ff99\"))\n",
" #.add(buttonplate, color=webcol_to_cq(\"#aa44ff99\"))\n",
" #.add(raw_buttonplate, color=webcol_to_cq(\"#aa44ff99\"))\n",
" #.add(hole1, color=webcol_to_cq(\"#ffa22aa44\"))\n",
" #.add(hole2, color=webcol_to_cq(\"#ffa22aa44\"))\n",
" #.add(holes, color=webcol_to_cq(\"#ffa22aa44\"))\n",
" #.add(thumbholes, color=webcol_to_cq(\"#ffa22aa44\"))\n",
" #.add(thumb_switches, color=webcol_to_cq(\"#00000055\"))\n",
" #.add(thumb_caps, color=webcol_to_cq(\"#efefef55\"))\n",
" #.add(buttonplate_bridge, color=webcol_to_cq(\"#ffaa4499\"))\n",
" #.add(overhang_cutout, color=webcol_to_cq(\"#aa44ff22\"))\n",
" , axes=True, axes0=True\n",
" , ambient_intensity=1, theme=\"dark\", reset_camera=False\n",
" ,tools=True, render_normals=False)\n",
"\n",
"testrender()"
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "3db7cb62-4476-48d9-a12c-b3efb73d6095",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"#base = build_base()\n",
"#handrest_top = (base.faces(\"<Z\")\n",
"# .workplane(offset=-26)\n",
"# .move(0, -40)\n",
"# .rect(120, endx)\n",
"# .wires()\n",
"## .split(keepBottom=True)\n",
"# .solids(\">X\")\n",
"# )\n",
"\n",
"#dowel = (handrest_top.faces(\"<Z\")\n",
"# .workplane()\n",
"# .moveTo(endx-10,0)\n",
"# .circle(3)\n",
"# .extrude(40))\n",
"\n",
"#bottom_base = base - handrest_top\n",
"\n",
"\n",
"#show(base, handrest_top)"
]
},
{
"cell_type": "markdown",
"id": "b02cf303-b55b-490d-97c3-1de1a6c88cbe",
"metadata": {},
"source": [
"# Assembly of product"
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "85894ad4-1f53-4b29-a312-acca8c33e4d0",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"\n",
"def show_product():\n",
" product = (\n",
" cq.Assembly(name=\"keyboard\")\n",
" #.add(bottom_base, name=\"base\", color=webcol_to_cq(\"#777744ff\")) # transparent alpha = 0x80/0xFF\n",
" #.add(handrest_top, name=\"blue box\", color=webcol_to_cq(\"#888844ff\")) # transparent, alpha = 0.3\n",
" .add(base, name=\"base\", color=webcol_to_cq(\"#777744ff\")) # transparent alpha = 0x80/0xFF\n",
" .add(buttonplate, name=\"plate\", color=webcol_to_cq(\"#121212\"))\n",
" .add(thumbplate, name=\"tplate\", color=webcol_to_cq(\"#121212\"))\n",
" .add(switches, name=\"switches\", color=webcol_to_cq(\"#000000ff\"))\n",
" .add(thumb_switches, name=\"thumbswitches\", color=webcol_to_cq(\"#000000ff\"))\n",
" .add(caps, name=\"caps\", color=webcol_to_cq(\"#bbbb99ff\"))\n",
" .add(thumb_caps, name=\"thumbcaps\", color=webcol_to_cq(\"#bbbb99ff\"))\n",
" .add(usbsocket, name=\"slot\", color=webcol_to_cq(\"#221111ff\"))\n",
" \n",
" )\n",
"\n",
" cv = show(product, axes=True, grid=[True, False, False], ortho=True, axes0=True, collapse=1)\n",
" return product"
]
},
{
"cell_type": "code",
"execution_count": 29,
"id": "2f697a70-ddaf-47b3-8950-9de820cabd15",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"100% ⋮————————————————————————————————————————————————————————————⋮ (8/8) 4.31s\n"
]
}
],
"source": [
"keyboard = show_product()"
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "fae0877e-16f1-46d4-9694-36a62eebd16d",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Total generator runtime: 40.31s\n"
]
}
],
"source": [
"toc = timeit.default_timer()\n",
"runtime = toc-tic\n",
"print(f\"Total generator runtime: {runtime:2.2f}s\")"
]
},
{
"cell_type": "code",
"execution_count": 31,
"id": "ff3054f9-f0bd-4665-9a5b-bfd658340008",
"metadata": {},
"outputs": [],
"source": [
"#assert False\n",
"#buttonplate = buttonplate - base\n",
"#buttonplate-buttonplate_cutspace"
]
},
{
"cell_type": "markdown",
"id": "c25d4553-ce80-4ff5-9679-09110c5fb86d",
"metadata": {},
"source": [
"# Export models"
]
},
{
"cell_type": "code",
"execution_count": 32,
"id": "4c271c8f-c19e-487d-829a-f0e8ffc1dcee",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[Errno 17] File exists: 'progress/2025-01-03'\n",
"\u001b[32;1m\n",
"*******************************************************************\n",
"****** Statistics on Transfer (Write) ******\u001b[0m\n",
"\u001b[32;1m\n",
"*******************************************************************\n",
"****** Transfer Mode = 0 I.E. As Is ******\u001b[0m\n",
"\u001b[32;1m****** Transferring Shape, ShapeType = 0 ******\u001b[0m\n",
"\u001b[32;1m** WorkSession : Sending all data\u001b[0m\n",
"\u001b[32;1m Step File Name : progress/2025-01-03/kbbase.step(28394 ents) Write Done\u001b[0m\n",
"\u001b[32;1m\n",
"*******************************************************************\n",
"****** Statistics on Transfer (Write) ******\u001b[0m\n",
"\u001b[32;1m\n",
"*******************************************************************\n",
"****** Transfer Mode = 0 I.E. As Is ******\u001b[0m\n",
"\u001b[32;1m****** Transferring Shape, ShapeType = 0 ******\u001b[0m\n",
"\u001b[32;1m** WorkSession : Sending all data\u001b[0m\n",
"\u001b[32;1m Step File Name : progress/2025-01-03/buttonplate.step(31618 ents) Write Done\u001b[0m\n",
"\u001b[32;1m\n",
"*******************************************************************\n",
"****** Statistics on Transfer (Write) ******\u001b[0m\n",
"\u001b[32;1m\n",
"*******************************************************************\n",
"****** Transfer Mode = 0 I.E. As Is ******\u001b[0m\n",
"\u001b[32;1m****** Transferring Shape, ShapeType = 0 ******\u001b[0m\n",
"\u001b[32;1m** WorkSession : Sending all data\u001b[0m\n",
"\u001b[32;1m Step File Name : progress/2025-01-03/usbsocket.step(1260 ents) Write Done\u001b[0m\n",
"\u001b[32;1m\n",
"*******************************************************************\n",
"****** Statistics on Transfer (Write) ******\u001b[0m\n",
"\u001b[32;1m\n",
"*******************************************************************\n",
"****** Transfer Mode = 0 I.E. As Is ******\u001b[0m\n",
"\u001b[32;1m****** Transferring Shape, ShapeType = 0 ******\u001b[0m\n",
"export runtime: 7.30s\n",
"\u001b[32;1m** WorkSession : Sending all data\u001b[0m\n",
"\u001b[32;1m Step File Name : progress/2025-01-03/thumbplate.step(4565 ents) Write Done\u001b[0m\n"
]
}
],
"source": [
"\n",
"import datetime\n",
"import os\n",
"\n",
"ticc = timeit.default_timer()\n",
"\n",
"today = datetime.date.today()\n",
"basepath = \"progress/\"+today.isoformat()\n",
"try:\n",
" os.mkdir(basepath)\n",
"except FileExistsError as e:\n",
" print(e)\n",
"\n",
"# render stl for rendering in blender\n",
"cq.exporters.export(base, basepath+\"/kbbase.stl\")\n",
"#cq.exporters.export(handrest_top, basepath+\"/handrest.stl\")\n",
"cq.exporters.export(switches+thumb_switches, basepath+'/switches.stl')\n",
"cq.exporters.export(caps+thumb_caps, basepath+'/caps.stl')\n",
"cq.exporters.export(buttonplate, basepath+'/buttonplate.stl')\n",
"cq.exporters.export(usbsocket, basepath+\"/usbsocket.stl\")\n",
"\n",
"# render steps for production\n",
"cq.exporters.export(base, basepath+\"/kbbase.step\")\n",
"cq.exporters.export(buttonplate, basepath+\"/buttonplate.step\")\n",
"cq.exporters.export(usbsocket, basepath+\"/usbsocket.step\")\n",
"cq.exporters.export(thumbplate, basepath+\"/thumbplate.step\")\n",
"\n",
"\n",
"toc = timeit.default_timer()\n",
"runtime = toc-ticc\n",
"print(f\"export runtime: {runtime:2.2f}s\")"
]
},
{
"cell_type": "markdown",
"id": "76401486-d51f-4e6d-87c8-792aac0fd28e",
"metadata": {},
"source": [
"# render svg graphics"
]
},
{
"cell_type": "code",
"execution_count": 33,
"id": "9371eb65-fd98-4bb6-a5f7-8e0f6b901458",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [
{
"ename": "AssertionError",
"evalue": "",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)",
"Cell \u001b[0;32mIn[33], line 2\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[38;5;66;03m#base = build_base()\u001b[39;00m\n\u001b[0;32m----> 2\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[1;32m 3\u001b[0m ticc \u001b[38;5;241m=\u001b[39m timeit\u001b[38;5;241m.\u001b[39mdefault_timer()\n\u001b[1;32m 4\u001b[0m base1 \u001b[38;5;241m=\u001b[39m base \u001b[38;5;241m+\u001b[39m buttonplate\u001b[38;5;241m.\u001b[39mtranslate([\u001b[38;5;241m0\u001b[39m,\u001b[38;5;241m0\u001b[39m,\u001b[38;5;241m74\u001b[39m]) \u001b[38;5;241m+\u001b[39m switches\u001b[38;5;241m.\u001b[39mtranslate([\u001b[38;5;241m0\u001b[39m,\u001b[38;5;241m0\u001b[39m,\u001b[38;5;241m130\u001b[39m])\n",
"\u001b[0;31mAssertionError\u001b[0m: "
]
}
],
"source": [
"#base = build_base()\n",
"assert False\n",
"ticc = timeit.default_timer()\n",
"base1 = base + buttonplate.translate([0,0,74]) + switches.translate([0,0,130])\n",
"base1 = base1 + thumb_switches.translate([0,0,130]) +thumb_caps.translate([0,0,180])+ caps.translate([0,0,180])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1ad8fe33-18c5-4fa2-834d-bab85827bceb",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"hidden_color = (0, 255, 255)\n",
"stroke_color = (255, 0, 255)\n",
"stroke_color = (0xc7, 0xf8, 255)\n",
"#c7f8ffff\n",
"cq.exporters.export(\n",
" base1,\n",
" basepath+\"/smue_explode.svg\",\n",
" opt={\n",
" \"width\": 300,\n",
" \"height\": 300,\n",
" \"marginLeft\": 10,\n",
" \"marginTop\": 10,\n",
" \"showAxes\": False,\n",
" \"projectionDir\": (100, -20, 70),\n",
" \"strokeWidth\": 0.25,\n",
" \"strokeColor\": stroke_color,\n",
" \"hiddenColor\": hidden_color,\n",
" \"showHidden\": False,\n",
" },\n",
")\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "201bab5c-213a-498f-9241-1077343430ce",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"toc = timeit.default_timer()\n",
"runtime = toc-ticc\n",
"total_runtime = toc -tic\n",
"print(f\"svg export runtime: {runtime:2.2f}s\")\n",
"print(f\"total runtime: {total_runtime:2.2f}s\")\n"
]
},
{
"cell_type": "markdown",
"id": "28f7ea15-de5d-4f03-b7cd-3a6930593144",
"metadata": {},
"source": [
"# real finger tip trajectory instead of circles"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "153074b2-8e5c-44e0-b34d-bfbe7ac1b749",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"assert False\n",
"\n",
"\n",
"from numpy import cos, sin, linspace, pi\n",
"import matplotlib.pyplot as plt\n",
"from math import atan2\n",
"\n",
"\n",
"# define finger segment sizes\n",
"40+20+18\n",
"50+40+20\n",
"proximal_phalange = 40\n",
"intermediate_phalange = 20\n",
"distal_phalange = 18\n",
"phi_start = pi/4 #45deg\n",
"phi_end = pi/9 #10deg\n",
"\n",
"phi = linspace(phi_start, phi_end)\n",
"\n",
"x_proximal = -proximal_phalange * cos(phi) \n",
"y_proximal = - proximal_phalange * sin(phi)\n",
"\n",
"x_intermediate = -intermediate_phalange * cos(2*phi)\n",
"y_intermediate = -intermediate_phalange * sin(2*phi)\n",
"\n",
"x_distal = -distal_phalange * cos(3*phi)\n",
"y_distal = -distal_phalange * sin(3*phi)\n",
"\n",
"x = x_proximal + x_intermediate + x_distal\n",
"y = y_proximal + y_intermediate + y_distal\n",
"\n",
"end_x_dir = x[0] - x[1]\n",
"end_y_dir = y[0] - y[1]\n",
"rho = atan2(end_y_dir, end_x_dir)\n",
"x = x - x[0]\n",
"y = y - y[0]\n",
"\n",
"apts = np.vstack([x, y]).T\n",
"lengths = np.sqrt(np.sum(np.diff(apts, axis=0)**2, axis=1)) # Length between corners\n",
"total_length = np.sum(lengths)\n",
"pts = [tuple(pt) for pt in apts]\n",
"print(f\"{total_length = :.2f}mm {rho = }\")\n",
"plt.plot(x,y)\n",
"\n",
"path = (cq.Workplane(\"XZ\")\n",
" .polyline(pts)\n",
")\n",
"\n",
"part = ( cq.Workplane(\"YZ\").rotateAboutCenter([0, 1, 0], rho*2*pi)\n",
" .rect(column_width, plate_thickness)\n",
" #.sweep(path)\n",
")\n",
"show(part, path)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ed64e437-0ba6-4957-b8fb-af831e742f55",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"path = (cq.Workplane(\"XZ\")\n",
" .polyline(pts)\n",
")\n",
"\n",
"part = ( cq.Workplane(\"YZ\").rotateAboutCenter([0, 1, 0], rho*2*pi)\n",
" .rect(column_width, plate_thickness)\n",
" #.sweep(path)\n",
")\n",
"show(part, path)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8b50622c-c504-4c87-94de-c9493d59d402",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [],
"source": [
"pts = [(0,0), (0,1), (3,1), (3,0)] # Corners of rectangle of height 1, length 3\n",
"apts = np.array(pts) # Make it a numpy array\n",
"lengths = np.sqrt(np.sum(np.diff(apts, axis=0)**2, axis=1)) # Length between corners\n",
"total_length = np.sum(lengths)\n",
"apts"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.10.8"
},
"name": "smue2.ipynb"
},
"nbformat": 4,
"nbformat_minor": 5
}