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

344 行
11 KiB

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