Pip-Boy inspired phone holder built around the PINE64 PinePhone
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

321 lignes
9.0 KiB

  1. // dependency: https://github.com/revarbat/BOSL
  2. include <BOSL/constants.scad>
  3. use <BOSL/transforms.scad>
  4. use <BOSL/shapes.scad>
  5. use <BOSL/masks.scad>
  6. use <BOSL/sliders.scad>
  7. // DEBUG:
  8. //validation = 1;
  9. //xray = 1;
  10. // EXPORT:
  11. MODEL_HARNESS_LEFT=1;
  12. MODEL_HARNESS_RIGHT=2;
  13. MODEL_HARNESS_TOP=3;
  14. MODEL_HARNESS_SLIDER_FRONT=4;
  15. MODEL_HARNESS_SLIDER_BACK=5;
  16. export=0; // [0:5]
  17. function is_not_export() = is_undef(export) || export == 0;
  18. function is_export() = !is_not_export();
  19. function is_model_strict(m) = is_export() && export == m;
  20. function is_model(m) = is_not_export() || export == m;
  21. function get_phone_size() = [160, 76.7, 10];
  22. module cubi(s, fillet=0, edges=EDGES_ALL) {
  23. if ($preview) {
  24. cube(s, center=true);
  25. } else {
  26. cuboid(s, fillet=fillet, edges=edges);
  27. }
  28. }
  29. module phone() {
  30. s=get_phone_size();
  31. color("gray")
  32. cubi(s, fillet=5);
  33. }
  34. module arm() {
  35. $fn=6;
  36. s=[210, 55, 50];
  37. color("SaddleBrown")
  38. down(s.z/2 + get_phone_size().z/2) cubi(s, fillet=20);
  39. }
  40. module phone_harness() {
  41. ops=get_phone_size();
  42. ps=[ops.x+0.2, ops.y+0.2, ops.z+0.2];
  43. shell_wall=3;
  44. shell_rim_wall=3;
  45. rim_edge_thickness=1.2;
  46. rim_edge_tolerance=0.4;
  47. expose_back_camera=false;
  48. screwmount_screw=3;
  49. harness_divider=[16, 0, 1.3];
  50. harness_size=[ps.x+shell_wall*2, ps.y+shell_wall*2, ps.z+shell_wall*2];
  51. rim_size=[harness_size.x+2*shell_rim_wall, harness_size.y+2*shell_rim_wall, 6];
  52. w=shell_wall;
  53. f=2;
  54. rf=5;
  55. hs=harness_size;
  56. backplane_rim_size=[30, ps.y*0.95, shell_wall/2];
  57. // ==== SHELL ====
  58. module shell() {
  59. $fn = $preview ? 6 : 30;
  60. difference() {
  61. cubi(hs, fillet=f);
  62. cubi(ps, fillet=f);
  63. }
  64. shell_rim();
  65. shell_screwmounts();
  66. }
  67. module shell_rim() {
  68. $fn = $preview ? 6 : 30;
  69. w=shell_rim_wall;
  70. s=rim_size;
  71. intersection() {
  72. difference() {
  73. cubi(s, fillet=rf, edges=EDGES_Z_ALL);
  74. cubi(ps, fillet=f, edges=EDGES_Z_ALL);
  75. }
  76. cubi(s, fillet=1);
  77. }
  78. rail_extension=10;
  79. fwd(hs.y/2) xrot(90) harness_rail(slider_len_front+rail_extension);
  80. back(hs.y/2) xrot(-90) harness_rail(slider_len_back+rail_extension);
  81. }
  82. module shell_rim_edge(thickness, groove, tolerance=rim_edge_tolerance) {
  83. $fn = $preview ? 6 : 30;
  84. w=thickness + (groove ? tolerance : 0);
  85. offset=(groove ? 0 : tolerance/2);
  86. res=[hs.x+2*(w+offset), hs.y+2*(w+offset), 3];
  87. resi=[hs.x+2*offset, hs.y+2*offset, res.z+1];
  88. up(0.0) difference() {
  89. intersection() {
  90. cubi(res, fillet=rf, edges=EDGES_Z_ALL);
  91. cubi(res, fillet=(groove?1:1.5), edges=EDGES_BOTTOM);
  92. }
  93. cubi(resi, fillet=rf, edges=EDGES_Z_ALL);
  94. }
  95. }
  96. module foreach_screwmount() {
  97. for(x = [0:1]) for (y = [0:1]) {
  98. translate([(-0.5+x)*ps.x, (-0.5+y)*ps.y, -hs.z/2])
  99. children();
  100. }
  101. }
  102. module shell_screwmounts() {
  103. $fn = $preview ? 6 : 30;
  104. r=3;
  105. foreach_screwmount() {
  106. cylinder(r=r, h=hs.z);
  107. }
  108. }
  109. module shell_screwmounts_holes() {
  110. $fn = $preview ? 6 : 30;
  111. foreach_screwmount() {
  112. up(w) cylinder(d2=screwmount_screw, d1=screwmount_screw*0.5, h=hs.z+1);
  113. up(hs.z-1) cylinder(d=screwmount_screw+1.5, h=5);
  114. }
  115. }
  116. module shell_backplane(groove) {
  117. down(hs.z/2 - shell_wall/6) up(backplane_rim_size.z/2) right(harness_divider.x) scale(groove ? 1.01 : 1) cubi(backplane_rim_size, fillet=5, edges=EDGES_Z_LF);
  118. }
  119. // ==== SHELL SLIDER AND RAIL ====
  120. rail_angle=36;
  121. rail_wall=1;
  122. slider_slop=0.1;
  123. slider_len_back=hs.x*0.3;
  124. slider_len_front=hs.x*0.3;
  125. module harness_slider(l) {
  126. base=2;
  127. ss=[l, shell_rim_wall, rim_size.z+rail_wall*2];
  128. up(ss.y + base) xrot(180) slider(l=ss.x, w=ss.z, h=ss.y, base=base, chamfer=0.9, wall=2, ang=rail_angle, slop=slider_slop, orient=ORIENT_X, align=V_UP);
  129. }
  130. module harness_rail(l) {
  131. sw=2;
  132. ss=[l, shell_rim_wall, rim_size.z+rail_wall*2];
  133. rail(l=ss.x, w=ss.z, h=ss.y, chamfer=0.5, ang=rail_angle, orient=ORIENT_X, align=V_UP);
  134. }
  135. // ==== SHELL CUTOUTS ====
  136. module shell_cutouts() {
  137. top_cutout();
  138. bottom_cutout();
  139. left_cutout();
  140. right_cutout();
  141. front_cutout();
  142. shell_screwmounts_holes();
  143. }
  144. // -- TOP --
  145. module screen_cutout() {
  146. s=[138, 69, 100];
  147. up(s.z/2) cubi(s, fillet=3, edges=EDGES_Z_ALL);
  148. }
  149. top_offset=6.5;
  150. module top_speaker_cutout() {
  151. s=[3, 12, 100];
  152. right(ps.x/2 - top_offset) up(s.z/2 - ps.z/3) cubi(s, fillet=1);
  153. }
  154. module front_camera_cutout() {
  155. $fn = $preview ? 6 : 30;
  156. d=5;
  157. s=[d, d, 100];
  158. right(ps.x/2 - top_offset) fwd(11.5) cylinder(d=s.x, h=s.z);
  159. }
  160. module leds_cutout() {
  161. s=[4, 14, 100];
  162. right(ps.x/2 - top_offset) up(s.z/2 - ps.z/3) back(23) cubi(s, fillet=1);
  163. }
  164. module top_cutout() {
  165. screen_cutout();
  166. top_speaker_cutout();
  167. front_camera_cutout();
  168. leds_cutout();
  169. }
  170. // -- BOTTOM --
  171. module back_camera_cutout() {
  172. s=[13, 25, expose_back_camera ? 100 : 11];
  173. right(ps.x/2 - 7.5) fwd(ps.y/2 - 23) down(s.z/2 - ps.z/3) cubi(s, fillet=6, edges=EDGES_Z_ALL);
  174. }
  175. module bottom_speaker_cutout() {
  176. s=[8, 49, 100];
  177. left(ps.x/2 - 13)
  178. difference() {
  179. down(s.z/2 - ps.z/3) cubi(s, fillet=1);
  180. bars=8;
  181. down(ps.z/2)
  182. for (i = [0:bars-1]) {
  183. bs=[s.x+2, 1, 2];
  184. step=s.y/bars;
  185. fwd(i*step - s.y/2 + step/2) down(w) cube(bs, center=true);
  186. }
  187. }
  188. }
  189. module bottom_cutout() {
  190. back_camera_cutout();
  191. bottom_speaker_cutout();
  192. }
  193. // -- RIGHT --
  194. module headphones_cutout() {
  195. $fn = $preview ? 10 : 30;
  196. d=8.8;
  197. p=[ps.x/2, ps.y/2-24, -0.88];
  198. h=100;
  199. translate(p) yrot(90) down(10) cylinder(h=h, d=d);
  200. }
  201. module right_cutout() {
  202. headphones_cutout();
  203. }
  204. // -- LEFT --
  205. module usbc_cutout() {
  206. s=[100, 20, 6];
  207. left(ps.x/2) down(2) cubi(s, fillet=1);
  208. }
  209. module left_cutout() {
  210. usbc_cutout();
  211. }
  212. // -- FRONT --
  213. module buttons_cutout() {
  214. s=[42, 100, 5];
  215. fwd(ps.y/2) right(ps.x/2 - 44.5) cubi(s, fillet=1);
  216. }
  217. module front_cutout() {
  218. buttons_cutout();
  219. }
  220. // ==== MODEL ====
  221. divider=harness_divider;
  222. module model_harness_left() {
  223. intersection() {
  224. intersection() {
  225. translate(divider) downcube([1000, 1000, 1000]);
  226. translate(divider) leftcube([1000, 1000, 1000]);
  227. }
  228. difference() {
  229. shell();
  230. shell_rim_edge(rim_edge_thickness, groove=true, tolerance=rim_edge_tolerance);
  231. shell_cutouts();
  232. shell_backplane(groove=true);
  233. }
  234. }
  235. }
  236. module model_harness_right() {
  237. intersection() {
  238. intersection() {
  239. translate(divider) downcube([1000, 1000, 1000]);
  240. translate(divider) rightcube([1000, 1000, 1000]);
  241. }
  242. difference() {
  243. shell();
  244. shell_rim_edge(rim_edge_thickness, groove=true, tolerance=rim_edge_tolerance);
  245. shell_cutouts();
  246. }
  247. }
  248. shell_backplane(groove=false);
  249. }
  250. module model_harness_top() {
  251. intersection() {
  252. translate(divider) upcube([1000, 1000, 1000]);
  253. difference() {
  254. shell();
  255. shell_cutouts();
  256. }
  257. }
  258. difference() {
  259. shell_rim_edge(rim_edge_thickness, groove=false);
  260. shell_cutouts();
  261. }
  262. }
  263. module model_harness_slider_front() {
  264. difference() {
  265. fwd(hs.y/2) xrot(90) harness_slider(slider_len_front);
  266. front_cutout();
  267. }
  268. }
  269. module model_harness_slider_back() {
  270. back(hs.y/2) xrot(-90) harness_slider(slider_len_back);
  271. }
  272. if (is_model(MODEL_HARNESS_LEFT)) model_harness_left();
  273. if (is_model(MODEL_HARNESS_RIGHT)) model_harness_right();
  274. if (is_model(MODEL_HARNESS_TOP)) model_harness_top();
  275. if (is_model(MODEL_HARNESS_SLIDER_FRONT)) model_harness_slider_front();
  276. if (is_model(MODEL_HARNESS_SLIDER_BACK)) model_harness_slider_back();
  277. }
  278. module model() {
  279. phone_harness();
  280. }
  281. if (!is_undef(validation)) {
  282. intersection() {
  283. phone();
  284. model();
  285. }
  286. } else {
  287. intersection() {
  288. if (!is_undef(xray)) {
  289. span_cube([0,1000*xray], [0,1000*xray], [-1000,1000]);
  290. }
  291. union() {
  292. if (is_not_export()) {
  293. //phone();
  294. //arm();
  295. }
  296. model();
  297. }
  298. }
  299. }