chiark / gitweb /
fairphone-case: adjust "bumpers" comment
[reprap-play.git] / fairphone-case.scad
1 // -*- C -*-
2
3 phone = [ 145.0, 75.0 ];
4
5 bumpers = [ 1.5, 1.75 ];
6 // ^ both sides together.
7 // If no bumpers, is the gap around the phone.
8
9 phone_cnr_rad = 6.0;
10
11 button_cutout_depth = 9;
12
13 phone_edge_thick = 9.0;
14 phone_total_thick = 12.0;
15 phone_backside_slope_inner = 1.5; // larger means shallower
16 phone_backside_slope_outer = 1.0; // larger means shallower
17
18 camera_pos_tl = [  7.0, 13.0 ]; // measured from tl corner
19 camera_pos_br = [ 22.85,37.85]; // tl/br as seen from back
20
21 case_th_bottom = 2.5;
22 case_th_lid = 2.5;
23 case_th_side = 2;
24 case_th_lip = 1.2;
25
26 case_struts_count = 6;
27 case_struts_solid_below = 1.00;
28 case_struts_solid_above = 0.75;
29 case_struts_width = 0.10;
30
31 keeper_th_z = 0.75;
32 keeper_th_x = 0.75;
33 keeper_inner_width = 2.75;
34 keeper_inner_height = 2.75;
35 keeper_slant_slope = 2; // larger means steeper
36
37 keeper_gap_z_top = 0.25;
38 keeper_gap_z_bot = 0.75;
39 keeper_gap_x     = 0.25;
40 keeper_gap_x_holes = 0.75;
41
42 case_lip = 1.25;
43
44 lid_gap_x = 0.25;
45 lid_gap_z = 0.25;
46 lid_lip = 1.75;
47
48 $fa = 5;
49 $fs = 0.1;
50
51 button_l_fudge = 4.5;
52
53 strut_min_at_end = 1.5;
54
55 // ---------- calculated ----------
56
57 phone_height = (phone + bumpers)[0];
58 phone_width =  (phone + bumpers)[1];
59
60 // ----- could be changed -----
61 lid_buttoncover_gap = lid_gap_x;
62 lid_buttoncover_overlap = case_th_lip + keeper_gap_z_top;
63
64 phone_backside_slope_thick = phone_total_thick - phone_edge_thick;
65
66 //lid_lip_overlap_width xxx bad name = ;
67 //lid_lip_inner_slope = [ 5, 5 ]; // xxx
68
69 epp0 = [0,0];
70 epp1 = [0, -phone_edge_thick];
71 epp2i = epp1 + phone_backside_slope_thick * [ phone_backside_slope_inner, -1 ];
72 epp2o = epp1 + phone_backside_slope_thick * [ phone_backside_slope_outer, -1 ];
73 epp3 = epp2i + [10, 0];
74 epp5 = epp0 + [0,1] * (keeper_th_z + keeper_gap_z_top + case_lip);
75 epp4 = epp5 + [-1,0] * case_th_side;
76
77 kppe = [0,0];
78 kppd = kppe + [1,0] * keeper_inner_width;
79 kppc = kppd + [0,1] * keeper_th_z;
80 kppb = [ kppe[0] - keeper_th_x, kppc[1] ];
81 kppf = kppe - [0,1] * keeper_inner_height;
82 kppa = [ kppb[0], kppf[1] ];
83
84 lpp10 = [ epp5[0] + lid_gap_x, kppc[1] + lid_gap_z ];
85 lpp11 = [ lpp10[0],            epp5[1] + lid_gap_z ];
86
87 lp_r12 = case_th_lid - (lpp11[1] - lpp10[1]);
88
89 lpp12 = [ epp4[0] + lp_r12,    lpp11[1] ];
90 lpp13 = [ lpp12[0],            lpp12[1] + lp_r12 ];
91
92 echo(lpp13 - lpp10);
93
94 // button profile
95 bppM = epp4 + [0,5];
96 bppN = [ 0.5 * (epp0[0] + epp4[0]), bppM[1] ];
97 bppR = [ bppN[0] + lid_buttoncover_gap, -button_cutout_depth ];
98 bppS = [ epp1[0], bppR[1] ];
99 bppQ = [ bppM[0], bppR[1] - lid_buttoncover_overlap ];
100 bppP = bppQ + [0,1] * lid_buttoncover_gap;
101 bppO = [ bppN[0], bppP[1] ];
102 bppL = lpp10 + [5,0];
103 bppK = [ bppL[0], bppN[1] ];
104 bppJ = [ bppN[0], bppL[1] ];
105
106 module rectfromto(a,b) {
107   ab = b - a;
108   translate([min(a[0], b[0]), min(a[1], b[1])])
109     square([abs(ab[0]), abs(ab[1])]);
110 }
111 module circleat(c,r) { translate(c) circle(r); }
112
113 module KeeperProfile(slant=0){
114   use_e = kppe + [0,-1] * slant * keeper_inner_width / keeper_slant_slope;
115   polygon([use_e, kppd, kppc, kppb, kppa, kppf]);
116 }
117
118 module EdgeProfile(){
119   difference(){
120     hull(){
121       translate(epp3) square(case_th_bottom*2, center=true);
122       circleat(epp2o, r=case_th_bottom);
123       circleat(epp1, r=case_th_side);
124       rectfromto(epp0, epp4);
125     }
126     polygon([ epp5 + [0,10],
127               epp1,
128               epp2i,
129               epp3 + [10,0] ]);
130   }
131 }
132
133 module LidEdgeProfile(){
134   polygon([ lpp10,
135             lpp11,
136             lpp12,
137             lpp13,
138             lpp13 + [10, 0],
139             lpp10 + [10, 0]
140             ]);
141   intersection(){
142     circleat(lpp12, r=lp_r12);
143     rectfromto( lpp12 + [-10,   0],
144                 lpp12 + [+10, +10] );
145   }
146 }
147
148 module ButtonCoverProfile(){
149   intersection(){
150     polygon([ bppM, bppP, bppO, bppJ, bppL, bppK ]);
151     hull(){
152       EdgeProfile();
153       LidEdgeProfile();
154     }
155   }
156 }
157
158 module ButtonPlan(l, deep, cut){
159   epsilon =
160     (cut  ? 0 : lid_buttoncover_gap);
161
162   delta =
163     (deep ? lid_buttoncover_overlap : 0);
164
165   C = [0,0]; // by definition
166   T = [ 0, epp4[1] ];
167   G = T + [0,10];
168
169   B0 = C + [0,-1] * button_cutout_depth;
170   B1 = B0 + [0,1] * epsilon;
171
172   r0 = 0.5 * (T[1] - B0[1]);
173   A = [  -(l + button_l_fudge)/2 + r0, 0.5 * (T[1] + B0[1]) ];
174   H = A + [0,-1] * delta;
175
176   D = A + [-2,0] * r0;
177   F = D + [0,10];
178
179   E0 = 0.5 * (D + A);
180   E1 = E0 + [1,0] * epsilon;
181
182   I0 = [ E0[0], H[1] ];
183   I1 = [ E1[0], H[1] ];
184
185   hull(){
186     for (m=[0,1]) mirror([m,0])
187       circleat(H, r0 - epsilon);
188   }
189   for (m=[0,1]) mirror([m,0]) {
190     difference(){
191       polygon([ E1,
192                 I1,
193                 H,
194                 B1,
195                 G,
196                 F,
197                 D
198                 ]);
199       circleat(D, r0 + epsilon);
200     }
201   }
202 }
203
204 module CaseBase_rhsflip(yn=[0,1]) {
205   for (rhs=yn) {
206     translate([phone_width/2, 0, 0])
207       mirror([rhs,0,0])
208       translate([-phone_width/2, 0, 0])
209       children();
210   }
211 }
212
213 module CaseBase_botflip() {
214   for (bot=[0,1]) {
215     translate([0, -phone_height/2, 0])
216       mirror([0, bot, 0])
217       translate([0, phone_height/2, 0])
218       children();
219   }
220 }  
221
222 module AroundEdges(fill_zstart, fill_th, fill_downwards=0){
223   // sides
224   CaseBase_rhsflip(){
225     translate([0, -phone_cnr_rad, 0])
226       rotate([90,0,0])
227       linear_extrude(height = phone_height - phone_cnr_rad*2)
228       children(0);
229   }
230   // corners
231   CaseBase_rhsflip() CaseBase_botflip() {
232     translate([+1,-1] * phone_cnr_rad)
233       intersection(){
234         rotate_extrude()
235           intersection(){
236             mirror([1,0,0])
237               translate([-1,0] * phone_cnr_rad)
238               children(0);
239             rectfromto([0,-20],[10,20]);
240           }
241         translate([-10, 0, -20] + 0.01 * [+1,-1, 0] )
242           cube([10,10,40]);
243       }
244   }
245   // top and bottom
246   CaseBase_botflip(){
247     translate([ phone_width - phone_cnr_rad, 0,0 ])
248       rotate([90,0,-90])
249       linear_extrude(height = phone_width - phone_cnr_rad*2)
250       children(0);
251   }
252   // fill
253   translate([0,0, fill_zstart])
254     mirror([0,0, fill_downwards])
255     linear_extrude(height = fill_th)
256     rectfromto([+1,-1] * phone_cnr_rad,
257                [phone_width, -phone_height] + [-1,+1] * phone_cnr_rad);
258 }
259
260 module SideButton(y,l){
261   $button_l= l;
262   translate([0, -y, 0])
263     children();
264 }
265
266 module Buttons(){
267   CaseBase_rhsflip([1]) SideButton(20.6, 8.8) children(); // power
268 }
269
270 module Struts(x_start, z_min, th){
271   // if th is negative, starts at z_min and works towards -ve z
272   // and object should then be printed other way up
273   for (i= [1 : 1 : case_struts_count]) {
274     translate([0,
275                0,
276                z_min])
277       mirror([0,0, th<0 ? 1 : 0])
278       translate([0,
279                  -phone_height * i / (case_struts_count+1),
280                  case_struts_solid_below])
281       linear_extrude(height= abs(th)
282                      -(case_struts_solid_below+case_struts_solid_above))
283       rectfromto([               x_start, -0.5 * case_struts_width ],
284                  [ phone_width - x_start, +0.5 * case_struts_width ]);
285   }
286 }
287
288 module CaseBase(){
289   AroundEdges(epp3[1], case_th_bottom, 1)
290     EdgeProfile();
291 }
292
293 module Case(){ ////toplevel
294   difference(){
295     union(){
296       CaseBase();
297
298       // ledge (fixed keeper)
299       intersection(){
300         rotate([90, 0, 0])
301           linear_extrude(height = phone_height + phone_cnr_rad * 2)
302           KeeperProfile(1);
303
304         // outline of the whole case, to stop it protruding
305         translate([0,0, -25])
306           linear_extrude(height = 50)
307           hull()
308           // CaseBase_rhsflip() // actually, we only care about the LH
309           CaseBase_botflip()
310           circleat([+1,-1] * phone_cnr_rad, phone_cnr_rad + case_th_side/2);
311       }
312     }
313
314     // slot for keeper
315     CaseBase_rhsflip(1)
316       translate([0, -phone_cnr_rad, 0])
317       rotate([90, 0, 0])
318       linear_extrude(height = phone_height + phone_cnr_rad * 2)
319       minkowski(){
320         KeeperProfile();
321         rectfromto([ -keeper_gap_x,    -keeper_gap_z_bot ],
322                    [ keeper_gap_x_holes,    +keeper_gap_z_top ]);
323       }
324
325     // front camera
326     CaseBase_rhsflip([1])
327       mirror([0, 0, 1])
328       linear_extrude(height = 20)
329       mirror([0, 1, 0])
330       rectfromto(camera_pos_tl, camera_pos_br);
331
332     // struts (invisible, because they're buried in the case)
333     Struts(epp2i[0], epp2i[1] - case_th_bottom, case_th_bottom);
334
335     Buttons(){
336       mirror([1,0,0])
337         rotate([90,0,90]) {
338           translate([0,0,-10])
339             linear_extrude(height= 20)
340             ButtonPlan($button_l, 0,1);
341           translate([0,0, -bppR[0]])
342             linear_extrude(height= 20)
343             ButtonPlan($button_l, 1,1);
344         }
345     }
346   }
347 }
348
349 module Lid(){ ////toplevel
350   difference(){
351     union(){
352       AroundEdges(lpp10[1], lpp13[1] - lpp10[1], 0)
353         LidEdgeProfile();
354
355       // button covers
356       Buttons(){
357         intersection(){
358           rotate([90,0,90])
359             translate([0,0,-10])
360             linear_extrude(height= 20)
361             ButtonPlan($button_l, 1,0);
362           rotate([90,0,0])
363              translate([0,0,-100])
364             linear_extrude(height= 200)
365             ButtonCoverProfile();
366         }
367       }
368     }
369     Struts(lpp10[0] + strut_min_at_end, lpp13[1], -case_th_lid);
370   }
371 }
372
373 module TestLength(){ ////toplevel
374   intersection(){
375     Case();
376     translate([-30, -200, -20])
377     cube([30 + 15, 250, 40]);
378   }
379 }
380
381 module TestSelectWidth(){
382   translate([-30, -(phone_height - 25), -20])
383     mirror([0, 1, 0])
384     cube([200, 50, 40]);
385 }
386
387 module TestWidth(){ ////toplevel
388   intersection(){
389     Case();
390     TestSelectWidth();
391   }
392 }
393
394 module TestLidWidthPrint(){ ////toplevel
395   rotate([0,180.0]) TestLidWidth();
396 }
397
398 module TestSelectCamera(){
399   CaseBase_rhsflip(1)
400     translate([0,0,-25])
401     linear_extrude(height = 50)
402     mirror([0, 1, 0])
403     rectfromto([-20, -20],
404                camera_pos_br + [ 5, 5 ]);
405 }
406
407 module TestCamera(){ ////toplevel
408   intersection(){
409     Case();
410     TestSelectCamera();
411   }
412 }
413
414 module TestLidByCamera(){ ////toplevel
415   intersection(){
416     Lid();
417     TestSelectCamera();
418   }
419 }
420
421 module TestLidByCameraPrint(){ ////toplevel
422   rotate([180,0,0]) TestLidByCamera();
423 }
424
425 module DemoByCamera(){ ////toplevel
426   color("blue") TestLidByCamera();
427   color("red")  TestCamera();
428 }
429
430 module OneKeeper(){ ////toplevel
431   translate([0, -phone_cnr_rad, 0])
432     rotate([90, 0, 0])
433     linear_extrude(height = phone_height - phone_cnr_rad * 2)
434     KeeperProfile();
435 }
436
437 module OneKeeperPrint(){ ////toplevel
438   rotate([0,180,0])
439     OneKeeper();
440 }
441
442 module Keeper(){ ////toplevel
443   CaseBase_rhsflip()
444     OneKeeper();
445 }
446
447 module ButtonPlanForDemo(z, deep, cut){
448   translate([0,0,z])
449     ButtonPlan(8, deep, cut);
450 }
451
452 module DemoProfiles(){ ////toplevel
453   LidEdgeProfile();
454   %EdgeProfile();
455   KeeperProfile();
456   translate([0,0,-1]) color("black") KeeperProfile(1);
457
458   translate([20,0]) {
459     LidEdgeProfile();
460     %EdgeProfile();
461
462     demopoint_QR = [ bppS[0], bppQ[1] - 0.1];
463   
464     color("blue") ButtonCoverProfile();
465     color("red") {
466       rectfromto(bppQ, demopoint_QR);
467       rectfromto(bppR, demopoint_QR);
468     }
469   }
470
471   translate([-20,0]) {
472     color("black") ButtonPlanForDemo(-2, 0,1);
473     color("red" )  ButtonPlanForDemo(-4, 1,1);
474     color("blue")  ButtonPlanForDemo(-6, 1,0);
475   }
476 }
477
478 //EdgeProfile();
479 //KeeperProfile();
480 //CaseBase();
481 //%Case();
482 //Keeper();
483 //LidEdgeProfile();
484 //KeeperProfile();
485 //DemoProfiles();