Pip-Boy inspired phone holder built around the PINE64 PinePhone
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

321 行
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. }