Pip-Boy inspired phone holder built around the PINE64 PinePhone
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

219 lines
5.6 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=2; // [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. rim_edge_thickness=0.8;
  20. rim_edge_tolerance=0.6;
  21. module phone() {
  22. s=get_phone_size();
  23. color("gray")
  24. cuboid(s, fillet=5);
  25. }
  26. module arm() {
  27. $fn=6;
  28. s=[210, 55, 50];
  29. color("SaddleBrown")
  30. down(s.z/2 + get_phone_size().z/2) cuboid(s, fillet=20);
  31. }
  32. module phone_harness() {
  33. ops=get_phone_size();
  34. ps=[ops.x+0.2, ops.y+0.2, ops.z+0.2];
  35. w=1;
  36. f=2;
  37. rf=5;
  38. hs=[ps.x+w*2, ps.y+w*2, ps.z+w*2];
  39. module shell() {
  40. $fn = $preview ? 6 : 30;
  41. difference() {
  42. cuboid(hs, fillet=f);
  43. cuboid(ps, fillet=f);
  44. }
  45. shell_rim();
  46. }
  47. module shell_rim() {
  48. $fn = $preview ? 6 : 30;
  49. w=2;
  50. s=[hs.x+2*w, hs.y+2*w, 6];
  51. intersection() {
  52. difference() {
  53. cuboid(s, fillet=rf, edges=EDGES_Z_ALL);
  54. cuboid(ps, fillet=f, edges=EDGES_Z_ALL);
  55. }
  56. cuboid(s, fillet=1);
  57. }
  58. }
  59. module shell_rim_edge(thickness, groove, tolerance=rim_edge_tolerance) {
  60. $fn = $preview ? 6 : 30;
  61. w=thickness + (groove ? tolerance : 0);
  62. offset=(groove ? 0 : tolerance/2);
  63. res=[hs.x+2*(w+offset), hs.y+2*(w+offset), 4.5];
  64. resi=[hs.x+2*offset, hs.y+2*offset, res.z+1];
  65. up(0.5) difference() {
  66. intersection() {
  67. cuboid(res, fillet=rf, edges=EDGES_Z_ALL);
  68. cuboid(res, fillet=(groove?1:2), edges=EDGES_BOTTOM);
  69. }
  70. cuboid(resi, fillet=rf, edges=EDGES_Z_ALL);
  71. }
  72. }
  73. module screen_cutout() {
  74. s=[138, 69, 10];
  75. up(s.z/2) cuboid(s, fillet=3, edges=EDGES_Z_ALL);
  76. }
  77. top_offset=6.5;
  78. module speaker_cutout() {
  79. s=[3, 12, 10];
  80. right(ps.x/2 - top_offset) up(ps.z/2) cuboid(s, fillet=1);
  81. }
  82. module front_camera_cutout() {
  83. $fn = $preview ? 6 : 30;
  84. d=5;
  85. s=[d, d, 10];
  86. right(ps.x/2 - top_offset) fwd(11) cylinder(d=s.x, h=s.z);
  87. }
  88. module leds_cutout() {
  89. s=[4, 14, 10];
  90. right(ps.x/2 - top_offset) up(ps.z/2) back(23) cuboid(s, fillet=1);
  91. }
  92. module top_cutout() {
  93. screen_cutout();
  94. speaker_cutout();
  95. front_camera_cutout();
  96. leds_cutout();
  97. }
  98. module back_camera_cutout() {
  99. s=[13, 25, 10];
  100. right(ps.x/2 - 8) fwd(ps.y/2 - 23) down(ps.z/2) cuboid(s, fillet=6, edges=EDGES_Z_ALL);
  101. }
  102. module bottom_cutout() {
  103. back_camera_cutout();
  104. }
  105. module headphones_cutout() {
  106. $fn = $preview ? 10 : 30;
  107. d=9;
  108. p=[ps.x/2, ps.y/2-24, -1];
  109. h=10;
  110. translate(p) yrot(90) down(h/2) cylinder(h=h, d=d);
  111. }
  112. module right_cutout() {
  113. headphones_cutout();
  114. }
  115. module usbc_cutout() {
  116. s=[10, 20, 6];
  117. left(ps.x/2) down(2) cuboid(s, fillet=1);
  118. }
  119. module left_cutout() {
  120. usbc_cutout();
  121. }
  122. module buttons_cutout() {
  123. s=[42, 10, 5];
  124. fwd(ps.y/2) right(ps.x/2 - 44.5) cuboid(s, fillet=1);
  125. }
  126. module front_cutout() {
  127. buttons_cutout();
  128. }
  129. module shell_cutouts() {
  130. top_cutout();
  131. bottom_cutout();
  132. left_cutout();
  133. right_cutout();
  134. front_cutout();
  135. }
  136. divider=[30, 0, 2.3];
  137. module model_harness_left() {
  138. intersection() {
  139. intersection() {
  140. translate(divider) downcube([1000, 1000, 1000]);
  141. translate(divider) leftcube([1000, 1000, 1000]);
  142. }
  143. difference() {
  144. shell();
  145. shell_rim_edge(rim_edge_thickness, groove=true, tolerance=rim_edge_tolerance);
  146. shell_cutouts();
  147. }
  148. }
  149. }
  150. module model_harness_right() {
  151. intersection() {
  152. intersection() {
  153. translate(divider) downcube([1000, 1000, 1000]);
  154. translate(divider) rightcube([1000, 1000, 1000]);
  155. }
  156. difference() {
  157. shell();
  158. shell_rim_edge(rim_edge_thickness, groove=true, tolerance=rim_edge_tolerance);
  159. shell_cutouts();
  160. }
  161. }
  162. }
  163. module model_harness_top() {
  164. intersection() {
  165. translate(divider) upcube([1000, 1000, 1000]);
  166. difference() {
  167. shell();
  168. shell_cutouts();
  169. }
  170. }
  171. difference() {
  172. shell_rim_edge(rim_edge_thickness, groove=false);
  173. shell_cutouts();
  174. }
  175. }
  176. if (is_model(MODEL_HARNESS_LEFT)) model_harness_left();
  177. if (is_model(MODEL_HARNESS_RIGHT)) model_harness_right();
  178. if (is_model(MODEL_HARNESS_TOP)) model_harness_top();
  179. }
  180. module model() {
  181. phone_harness();
  182. }
  183. if (!is_undef(validation)) {
  184. intersection() {
  185. phone();
  186. model();
  187. }
  188. } else {
  189. intersection() {
  190. if (!is_undef(xray)) {
  191. span_cube([0,1000*xray], [0,1000*xray], [-1000,1000]);
  192. }
  193. union() {
  194. if (is_not_export()) {
  195. //phone();
  196. //arm();
  197. }
  198. model();
  199. }
  200. }
  201. }