Pip-Boy inspired phone holder built around the PINE64 PinePhone
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

261 linhas
6.9 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. // DEBUG:
  7. //validation = 1;
  8. //xray = 1;
  9. // EXPORT:
  10. MODEL_HARNESS_LEFT=1;
  11. MODEL_HARNESS_RIGHT=2;
  12. MODEL_HARNESS_TOP=3;
  13. export=0; // [0:5]
  14. function is_not_export() = is_undef(export) || export == 0;
  15. function is_export() = !is_not_export();
  16. function is_model_strict(m) = is_export() && export == m;
  17. function is_model(m) = is_not_export() || export == m;
  18. function get_phone_size() = [160, 76.7, 10];
  19. shell_wall=3;
  20. shell_rim_wall=3;
  21. rim_edge_thickness=1.2;
  22. rim_edge_tolerance=0.4;
  23. expose_back_camera=false;
  24. screwmount_screw=3;
  25. harness_divider=[30, 0, 1.3];
  26. module phone() {
  27. s=get_phone_size();
  28. color("gray")
  29. cuboid(s, fillet=5);
  30. }
  31. module arm() {
  32. $fn=6;
  33. s=[210, 55, 50];
  34. color("SaddleBrown")
  35. down(s.z/2 + get_phone_size().z/2) cuboid(s, fillet=20);
  36. }
  37. module phone_harness() {
  38. ops=get_phone_size();
  39. ps=[ops.x+0.2, ops.y+0.2, ops.z+0.2];
  40. w=shell_wall;
  41. f=2;
  42. rf=5;
  43. hs=[ps.x+w*2, ps.y+w*2, ps.z+w*2];
  44. module shell() {
  45. $fn = $preview ? 6 : 30;
  46. difference() {
  47. cuboid(hs, fillet=f);
  48. cuboid(ps, fillet=f);
  49. }
  50. shell_rim();
  51. shell_screwmounts();
  52. }
  53. module shell_rim() {
  54. $fn = $preview ? 6 : 30;
  55. w=shell_rim_wall;
  56. s=[hs.x+2*w, hs.y+2*w, 6];
  57. intersection() {
  58. difference() {
  59. cuboid(s, fillet=rf, edges=EDGES_Z_ALL);
  60. cuboid(ps, fillet=f, edges=EDGES_Z_ALL);
  61. }
  62. cuboid(s, fillet=1);
  63. }
  64. }
  65. module shell_rim_edge(thickness, groove, tolerance=rim_edge_tolerance) {
  66. $fn = $preview ? 6 : 30;
  67. w=thickness + (groove ? tolerance : 0);
  68. offset=(groove ? 0 : tolerance/2);
  69. res=[hs.x+2*(w+offset), hs.y+2*(w+offset), 3];
  70. resi=[hs.x+2*offset, hs.y+2*offset, res.z+1];
  71. up(0.0) difference() {
  72. intersection() {
  73. cuboid(res, fillet=rf, edges=EDGES_Z_ALL);
  74. cuboid(res, fillet=(groove?1:1.5), edges=EDGES_BOTTOM);
  75. }
  76. cuboid(resi, fillet=rf, edges=EDGES_Z_ALL);
  77. }
  78. }
  79. module foreach_screwmount() {
  80. for(x = [0:1]) for (y = [0:1]) {
  81. translate([(-0.5+x)*ps.x, (-0.5+y)*ps.y, -hs.z/2])
  82. children();
  83. }
  84. }
  85. module shell_screwmounts() {
  86. $fn = $preview ? 6 : 30;
  87. r=3;
  88. foreach_screwmount() {
  89. cylinder(r=r, h=hs.z);
  90. }
  91. }
  92. module shell_screwmounts_holes() {
  93. $fn = $preview ? 6 : 30;
  94. foreach_screwmount() {
  95. up(w) cylinder(d2=screwmount_screw, d1=screwmount_screw*0.5, h=hs.z+1);
  96. up(hs.z-1) cylinder(d=screwmount_screw+1.5, h=5);
  97. }
  98. }
  99. module screen_cutout() {
  100. s=[138, 69, 100];
  101. up(s.z/2) cuboid(s, fillet=3, edges=EDGES_Z_ALL);
  102. }
  103. top_offset=6.5;
  104. module top_speaker_cutout() {
  105. s=[3, 12, 100];
  106. right(ps.x/2 - top_offset) up(s.z/2 - ps.z/3) cuboid(s, fillet=1);
  107. }
  108. module front_camera_cutout() {
  109. $fn = $preview ? 6 : 30;
  110. d=5;
  111. s=[d, d, 100];
  112. right(ps.x/2 - top_offset) fwd(11) cylinder(d=s.x, h=s.z);
  113. }
  114. module leds_cutout() {
  115. s=[4, 14, 100];
  116. right(ps.x/2 - top_offset) up(s.z/2 - ps.z/3) back(23) cuboid(s, fillet=1);
  117. }
  118. module top_cutout() {
  119. screen_cutout();
  120. top_speaker_cutout();
  121. front_camera_cutout();
  122. leds_cutout();
  123. }
  124. module back_camera_cutout() {
  125. s=[13, 25, expose_back_camera ? 100 : 11];
  126. right(ps.x/2 - 7.5) fwd(ps.y/2 - 23) down(s.z/2 - ps.z/3) cuboid(s, fillet=6, edges=EDGES_Z_ALL);
  127. }
  128. module bottom_speaker_cutout() {
  129. s=[8, 49, 100];
  130. left(ps.x/2 - 13)
  131. difference() {
  132. down(s.z/2 - ps.z/3) cuboid(s, fillet=1);
  133. bars=8;
  134. down(ps.z/2)
  135. for (i = [0:bars-1]) {
  136. bs=[s.x+2, 1, 2];
  137. step=s.y/bars;
  138. fwd(i*step - s.y/2 + step/2) down(w) cube(bs, center=true);
  139. }
  140. }
  141. }
  142. module bottom_cutout() {
  143. back_camera_cutout();
  144. bottom_speaker_cutout();
  145. }
  146. module headphones_cutout() {
  147. $fn = $preview ? 10 : 30;
  148. d=8.8;
  149. p=[ps.x/2, ps.y/2-24, -0.88];
  150. h=100;
  151. translate(p) yrot(90) down(10) cylinder(h=h, d=d);
  152. }
  153. module right_cutout() {
  154. headphones_cutout();
  155. }
  156. module usbc_cutout() {
  157. s=[100, 20, 6];
  158. left(ps.x/2) down(2) cuboid(s, fillet=1);
  159. }
  160. module left_cutout() {
  161. usbc_cutout();
  162. }
  163. module buttons_cutout() {
  164. s=[42, 100, 5];
  165. fwd(ps.y/2) right(ps.x/2 - 44.5) cuboid(s, fillet=1);
  166. }
  167. module front_cutout() {
  168. buttons_cutout();
  169. }
  170. module shell_cutouts() {
  171. top_cutout();
  172. bottom_cutout();
  173. left_cutout();
  174. right_cutout();
  175. front_cutout();
  176. shell_screwmounts_holes();
  177. }
  178. divider=harness_divider;
  179. module model_harness_left() {
  180. intersection() {
  181. intersection() {
  182. translate(divider) downcube([1000, 1000, 1000]);
  183. translate(divider) leftcube([1000, 1000, 1000]);
  184. }
  185. difference() {
  186. shell();
  187. shell_rim_edge(rim_edge_thickness, groove=true, tolerance=rim_edge_tolerance);
  188. shell_cutouts();
  189. }
  190. }
  191. }
  192. module model_harness_right() {
  193. intersection() {
  194. intersection() {
  195. translate(divider) downcube([1000, 1000, 1000]);
  196. translate(divider) rightcube([1000, 1000, 1000]);
  197. }
  198. difference() {
  199. shell();
  200. shell_rim_edge(rim_edge_thickness, groove=true, tolerance=rim_edge_tolerance);
  201. shell_cutouts();
  202. }
  203. }
  204. }
  205. module model_harness_top() {
  206. intersection() {
  207. translate(divider) upcube([1000, 1000, 1000]);
  208. difference() {
  209. shell();
  210. shell_cutouts();
  211. }
  212. }
  213. difference() {
  214. shell_rim_edge(rim_edge_thickness, groove=false);
  215. shell_cutouts();
  216. }
  217. }
  218. if (is_model(MODEL_HARNESS_LEFT)) model_harness_left();
  219. if (is_model(MODEL_HARNESS_RIGHT)) model_harness_right();
  220. if (is_model(MODEL_HARNESS_TOP)) model_harness_top();
  221. }
  222. module model() {
  223. phone_harness();
  224. }
  225. if (!is_undef(validation)) {
  226. intersection() {
  227. phone();
  228. model();
  229. }
  230. } else {
  231. intersection() {
  232. if (!is_undef(xray)) {
  233. span_cube([0,1000*xray], [0,1000*xray], [-1000,1000]);
  234. }
  235. union() {
  236. if (is_not_export()) {
  237. //phone();
  238. //arm();
  239. }
  240. model();
  241. }
  242. }
  243. }