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.

272 lines
9.0 KiB

  1. links_count=6; // [1..20]
  2. include_terminal=true; // [true,false]
  3. export=4; // [0:5]
  4. MODEL_DEMO=0;
  5. MODEL_LINKS=1;
  6. MODEL_CLIP=2;
  7. MODEL_CLIP_RATCHETING=3;
  8. MODEL_CLIP_RATCHETING_A=4;
  9. MODEL_CLIP_RATCHETING_B=5;
  10. //validation=1;
  11. VALIDATE_INTERSECTION=1;
  12. //xray=1;
  13. use <BOSL/transforms.scad>
  14. use <BOSL/shapes.scad>
  15. include <BOSL/constants.scad>
  16. function is_not_export() = is_undef(export) || export == 0;
  17. function is_export() = !is_not_export();
  18. function is_model_strict(m) = is_export() && export == m;
  19. function is_model(m) = is_not_export() || export == m;
  20. function get_link_segment_size() = [15, 30, 5];
  21. function get_link_pin_diameter() = 2;
  22. function get_link_socket_slack() = [1, 4, 1];
  23. function get_link_joiner_arm_size() = [get_link_segment_size().x - 2, 3, segment_size.z];
  24. function get_link_socket_size() = [get_link_segment_size().z,
  25. get_link_segment_size().y - get_link_joiner_arm_size().y*2,
  26. get_link_segment_size().z];
  27. function get_link_clip_size() = [get_link_pin_diameter()*5, get_link_segment_size().y - 5, get_link_segment_size().z];
  28. echo("===============================");
  29. echo(str("Strap length: ", (links_count * get_link_segment_size().x), " mm"));
  30. echo("===============================");
  31. assert(get_link_segment_size().x >= 10, "Link segment is too short.");
  32. assert(get_link_segment_size().z >= (get_link_pin_diameter() + get_link_socket_slack().z + 1), "Link segment is too thin.");
  33. module link(terminal=false) {
  34. segment_size=get_link_segment_size();
  35. module pin() {
  36. h=segment_size.y;
  37. d=get_link_pin_diameter();
  38. back(h/2) xrot(90) cylinder(h=h, d=d, $fn=$preview ? 10 : 30);
  39. }
  40. module pin_socket_area() {
  41. h=segment_size.y-get_link_socket_slack().y;
  42. d=segment_size.z;
  43. back(h/2) xrot(90) cylinder(h=h, d=d, $fn=$preview ? 10 : 30);
  44. }
  45. module pin_socket() {
  46. difference() {
  47. pin_socket_area();
  48. scaleup = [
  49. ((get_link_pin_diameter() + get_link_socket_slack().x) / get_link_pin_diameter()),
  50. ((get_link_pin_diameter() + get_link_socket_slack().y) / get_link_pin_diameter()),
  51. ((get_link_pin_diameter() + get_link_socket_slack().z) / get_link_pin_diameter())
  52. ];
  53. scale(scaleup) pin();
  54. }
  55. }
  56. module joiner() {
  57. gap=2;
  58. arm_size=get_link_joiner_arm_size();
  59. module arm() {
  60. fwd(segment_size.y/2 + arm_size.y/2 - 1) right(arm_size.x/2 - 1) cuboid(arm_size, fillet=1, edges=EDGES_FRONT + EDGES_Y_ALL);
  61. }
  62. module armFront() {
  63. arm();
  64. }
  65. module armBack() {
  66. scale([1, -1, 1]) arm();
  67. }
  68. module armJoiner() {
  69. joiner_size=[arm_size.x-get_link_socket_size().x, segment_size.y, segment_size.z];
  70. right(joiner_size.x/2 + get_link_socket_size().x/2 + gap) cuboid(joiner_size, fillet=1, edges=EDGES_Z_ALL + EDGES_BOTTOM);
  71. }
  72. module socketJoiner() {
  73. joiner_size=[segment_size.x-get_link_socket_size().x+get_link_socket_size().x*0.2,
  74. segment_size.y-4,
  75. segment_size.z];
  76. difference() {
  77. right(joiner_size.x/2 + 4.5) cuboid(joiner_size, fillet=1, edges=EDGES_Z_ALL + EDGES_BOTTOM);
  78. right(segment_size.x) scale([1, 2, 1]) pin_socket_area();
  79. }
  80. }
  81. module terminalArm() {
  82. left(5) {
  83. shift=arm_size.x - get_link_pin_diameter();
  84. right(segment_size.x + shift + gap/2) pin();
  85. right(segment_size.x + gap/2) {
  86. armFront();
  87. armBack();
  88. }
  89. right(arm_size.x - get_link_socket_size().x + gap) armJoiner();
  90. }
  91. }
  92. armFront();
  93. armBack();
  94. armJoiner();
  95. if (terminal) {
  96. terminalArm();
  97. } else {
  98. socketJoiner();
  99. }
  100. }
  101. pin();
  102. joiner();
  103. if (!terminal) {
  104. right(segment_size.x) pin_socket();
  105. }
  106. }
  107. module clip() {
  108. socket_size=get_link_socket_size();
  109. clip_size=get_link_clip_size();
  110. pin_diam=get_link_pin_diameter();
  111. slot_slack=get_link_socket_slack();
  112. size=[clip_size.x+socket_size.x, clip_size.y, clip_size.z];
  113. slot_size=[clip_size.x+slot_slack.x, size.y+1, pin_diam+slot_slack.z];
  114. entry_size=[slot_size.x-5, clip_size.y+1, clip_size.z];
  115. right(clip_size.x/2)
  116. difference() {
  117. cuboid(size, fillet=1);
  118. cuboid(slot_size, fillet=1);
  119. up(entry_size.z/2) cube(entry_size, center=true);
  120. }
  121. }
  122. module clip_ratcheting(ratchet_length=20) {
  123. socket_size=get_link_socket_size();
  124. clip_size=get_link_clip_size();
  125. pin_diam=get_link_pin_diameter();
  126. slot_slack=get_link_socket_slack();
  127. size=[clip_size.x+socket_size.x, clip_size.y, clip_size.z];
  128. slot_size=[clip_size.x+slot_slack.x, size.y+1, pin_diam+slot_slack.z];
  129. entry_size=[slot_size.x-5, clip_size.y+1, clip_size.z];
  130. //right(clip_size.x/2)
  131. module hook() {
  132. intersection() {
  133. difference() {
  134. cuboid(size, fillet=1);
  135. cuboid(slot_size, fillet=1);
  136. up(entry_size.z/2) cube(entry_size, center=true);
  137. }
  138. translate([0, -50, -50]) cube([100, 100, 100]);
  139. }
  140. }
  141. module hookRight() {
  142. hook();
  143. }
  144. module hookLeft() {
  145. scale([-1, 1, 1]) hook();
  146. }
  147. wall=2;
  148. tooth_size=[2, 2, 3];
  149. module tooth() {
  150. s=[tooth_size.x, tooth_size.z, tooth_size.y];
  151. up(tooth_size.z/2) xrot(-90) right_triangle(s);
  152. }
  153. module teethFwd() {
  154. tooth_count = ratchet_length/tooth_size.x;
  155. fwd((clip_size.y-wall*2)/2)
  156. right(ratchet_length/2) {
  157. for (i=[1:tooth_count]) {
  158. left(i*tooth_size.x) tooth();
  159. }
  160. cap_size=[ratchet_length, tooth_size.y, 1];
  161. up(tooth_size.z/2 + cap_size.z/2) left(cap_size.x/2) back(tooth_size.y/2) {
  162. difference() {
  163. cube(cap_size, center=true);
  164. translate([0.1, 0.1, 0]) back(tooth_size.y/2) right(ratchet_length/2) zrot(180) tooth();
  165. }
  166. }
  167. }
  168. }
  169. module teethBack() {
  170. scale([1, -1, 1]) teethFwd();
  171. }
  172. module ratchetLeft() {
  173. right(ratchet_length/2) {
  174. difference() {
  175. cube([ratchet_length, clip_size.y, clip_size.z], center=true);
  176. up(wall/2) cube([ratchet_length, clip_size.y-wall*2, clip_size.z], center=true);
  177. }
  178. teethFwd();
  179. teethBack();
  180. }
  181. }
  182. ratchet_tooth_count=3;
  183. ratchet_wall=1;
  184. module ratchetTeethFront() {
  185. module ratchetButton() {
  186. size=[3,wall*4,2];
  187. up(4) fwd(wall*2 - ratchet_wall) cuboid(size, fillet=1, edges=EDGES_FRONT);
  188. up(2) back(wall/2) cube([size.x, wall, tooth_size.z + size.z], center=true);
  189. }
  190. fwd((clip_size.y-wall*2)/2 - tooth_size.y - 0.5)
  191. left(ratchet_length) {
  192. for (i=[0:ratchet_tooth_count-1]) {
  193. right(i*tooth_size.x) scale([-1,-1,1]) tooth();
  194. }
  195. ratchetButton();
  196. }
  197. module ratchetWall() {
  198. up(tooth_size.z/2) left(ratchet_length/2 + wall - wall/2) fwd(clip_size.y/2-wall-tooth_size.y-ratchet_wall)
  199. down(wall/2+0.5) cube([ratchet_length + wall, ratchet_wall, tooth_size.z], center=true);
  200. }
  201. ratchetWall();
  202. }
  203. module ratchetTeethBack() {
  204. scale([1,-1,1]) ratchetTeethFront();
  205. }
  206. module ratchetRight() {
  207. left(wall/2) cube([wall, clip_size.y, clip_size.z], center=true);
  208. ratchetTeethFront();
  209. ratchetTeethBack();
  210. }
  211. if (is_model(MODEL_CLIP_RATCHETING_A) || is_model(MODEL_CLIP_RATCHETING)) {
  212. left(ratchet_length + wall + 1) {
  213. hookLeft();
  214. left(0.5) ratchetLeft();
  215. }
  216. }
  217. if (is_model(MODEL_CLIP_RATCHETING_B) || is_model(MODEL_CLIP_RATCHETING)) {
  218. ratchetRight();
  219. hookRight();
  220. }
  221. }
  222. // Export / Demo / Validation:
  223. segment_size=get_link_segment_size();
  224. if (!is_undef(validation)) {
  225. if (validation==VALIDATE_INTERSECTION) {
  226. intersection() {
  227. right(0*segment_size.x) link();
  228. right(1*segment_size.x) link();
  229. }
  230. intersection() {
  231. right(1*segment_size.x) link();
  232. right(2*segment_size.x) link();
  233. }
  234. }
  235. } else if (!is_undef(xray)) {
  236. intersection() {
  237. cube([1000, 1000, 1000]);
  238. for (i = [0:links_count-1]) {
  239. right(i*segment_size.x) link();
  240. }
  241. }
  242. } else {
  243. if (is_model(MODEL_CLIP)) {
  244. left(get_link_clip_size().x) clip();
  245. }
  246. if (is_model(MODEL_CLIP_RATCHETING) || is_model(MODEL_CLIP_RATCHETING_A) || is_model(MODEL_CLIP_RATCHETING_B)) {
  247. left(get_link_clip_size().x) clip_ratcheting();
  248. }
  249. if (is_model(MODEL_LINKS)) for (i = [0:links_count-1]) {
  250. terminal=(i==links_count-1) && include_terminal;
  251. right(i*segment_size.x) link(terminal=terminal);
  252. }
  253. }