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.

293 lines
9.7 KiB

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