chiark / gitweb /
401f9fcdca599543f387a745b833a3bb0c9245e4
[reprap-play.git] / fairphone-case.scad
1 // -*- C -*-
2
3 phone_height = 146.5 - .80;
4 phone_width = 76.75;
5
6 phone_cnr_rad = 6.0;
7
8 button_cutout_depth = 9;
9
10 phone_edge_thick = 9.0;
11 phone_total_thick = 12.0;
12 phone_backside_slope_inner = 1.5; // larger means shallower
13 phone_backside_slope_outer = 1.0; // larger means shallower
14
15 camera_pos_tl = [  7.0, 13.0 ]; // measured from tl corner
16 camera_pos_br = [ 24.0, 39.5 ]; // tl/br as seen from back
17
18 case_th_bottom = 2.5;
19 case_th_lid = 2.5;
20 case_th_side = 2;
21 case_th_lip = 1.2;
22
23 case_struts_count = 6;
24 case_struts_solid_below = 0.75;
25 case_struts_solid_above = 1.00;
26 case_struts_width = 0.10;
27
28 keeper_th_z = 0.75;
29 keeper_th_x = 0.75;
30 keeper_inner_width = 1.75;
31 keeper_inner_height = 1.75;
32
33 keeper_gap_z_top = 0.25;
34 keeper_gap_z_bot = 0.75;
35 keeper_gap_x     = 0.25;
36 keeper_gap_x_holes = 0.75;
37
38 case_lip = 1.25;
39
40 lid_gap_x = 0.25;
41 lid_gap_z = 0.25;
42 lid_lip = 1.75;
43
44 $fa = 5;
45 $fs = 0.1;
46
47 // calculated
48
49 // could be changed
50 lid_buttoncover_gap = lid_gap_x;
51 lid_buttoncover_overlap = case_th_lip + keeper_gap_z_top;
52
53 phone_backside_slope_thick = phone_total_thick - phone_edge_thick;
54
55 //lid_lip_overlap_width xxx bad name = ;
56 //lid_lip_inner_slope = [ 5, 5 ]; // xxx
57
58 epp0 = [0,0];
59 epp1 = [0, -phone_edge_thick];
60 epp2i = epp1 + phone_backside_slope_thick * [ phone_backside_slope_inner, -1 ];
61 epp2o = epp1 + phone_backside_slope_thick * [ phone_backside_slope_outer, -1 ];
62 epp3 = epp2i + [10, 0];
63 epp5 = epp0 + [0,1] * (keeper_th_z + keeper_gap_z_top + case_lip);
64 epp4 = epp5 + [-1,0] * case_th_side;
65
66 kppe = [0,0];
67 kppd = kppe + [1,0] * keeper_inner_width;
68 kppc = kppd + [0,1] * keeper_th_z;
69 kppb = [ kppe[0] - keeper_th_x, kppc[1] ];
70 kppf = kppe - [0,1] * keeper_inner_height;
71 kppa = [ kppb[0], kppf[1] ];
72
73 lpp10 = [ epp5[0] + lid_gap_x, kppc[1] + lid_gap_z ];
74 lpp11 = [ lpp10[0],            epp5[1] + lid_gap_z ];
75 lpp12 = [ epp4[0] + lid_lip,   lpp11[1] ];
76 lpp13 = [ lpp12[0],            lpp12[1] + lid_lip ];
77
78 bppM = epp4 + [0,5];
79 bppN = [ 0.5 * (epp0[0] + epp4[0]), bppM[1] ];
80 bppR = [ bppN[0] + lid_buttoncover_gap, -button_cutout_depth ];
81 bppS = [ epp1[0], bppR[1] ];
82 bppQ = [ bppM[0], bppR[1] - lid_buttoncover_overlap ];
83 bppP = bppQ + [0,1] * lid_buttoncover_gap;
84 bppO = [ bppN[0], bppP[1] ];
85 bppL = lpp10 + [5,0];
86 bppK = [ bppL[0], bppN[1] ];
87 bppJ = [ bppN[0], bppL[1] ];
88
89 module rectfromto(a,b) {
90   ab = b - a;
91   translate([min(a[0], b[0]), min(a[1], b[1])])
92     square([abs(ab[0]), abs(ab[1])]);
93 }
94 module circleat(c,r) { translate(c) circle(r); }
95
96 module KeeperProfile(){
97   polygon([kppe, kppd, kppc, kppb, kppa, kppf]);
98 }
99
100 module EdgeProfile(){
101   difference(){
102     hull(){
103       translate(epp3) square(case_th_bottom*2, center=true);
104       circleat(epp2o, r=case_th_bottom);
105       circleat(epp1, r=case_th_side);
106       rectfromto(epp0, epp4);
107     }
108     polygon([ epp5 + [0,10],
109               epp1,
110               epp2i,
111               epp3 + [10,0] ]);
112   }
113 }
114
115 module LidEdgeProfile(){
116   polygon([ lpp10,
117             lpp11,
118             lpp12,
119             lpp13,
120             lpp13 + [10, 0],
121             lpp10 + [10, 0]
122             ]);
123   intersection(){
124     circleat(lpp12, r=lid_lip);
125     rectfromto( lpp12 + [-10,   0],
126                 lpp12 + [+10, +10] );
127   }
128 }
129
130 module ButtonCoverProfile(){
131   intersection(){
132     polygon([ bppM, bppP, bppO, bppJ, bppL, bppK ]);
133     hull(){
134       EdgeProfile();
135       LidEdgeProfile();
136     }
137   }
138 }
139
140 module ButtonPlan(){
141 }
142
143 module CaseBase_rhsflip(yn=[0,1]) {
144   for (rhs=yn) {
145     translate([phone_width/2, 0, 0])
146       mirror([rhs,0,0])
147       translate([-phone_width/2, 0, 0])
148       children();
149   }
150 }
151
152 module CaseBase_botflip() {
153   for (bot=[0,1]) {
154     translate([0, -phone_height/2, 0])
155       mirror([0, bot, 0])
156       translate([0, phone_height/2, 0])
157       children();
158   }
159 }  
160
161 module AroundEdges(fill_zstart, fill_th, fill_downwards=0){
162   // sides
163   CaseBase_rhsflip(){
164     translate([0, -phone_cnr_rad, 0])
165       rotate([90,0,0])
166       linear_extrude(height = phone_height - phone_cnr_rad*2)
167       children(0);
168   }
169   // corners
170   CaseBase_rhsflip() CaseBase_botflip() {
171     translate([+1,-1] * phone_cnr_rad)
172       intersection(){
173         rotate_extrude()
174           intersection(){
175             mirror([1,0,0])
176               translate([-1,0] * phone_cnr_rad)
177               children(0);
178             rectfromto([0,-20],[10,20]);
179           }
180         translate([-10, 0, -20] + 0.01 * [+1,-1, 0] )
181           cube([10,10,40]);
182       }
183   }
184   // top and bottom
185   CaseBase_botflip(){
186     translate([ phone_width - phone_cnr_rad, 0,0 ])
187       rotate([90,0,-90])
188       linear_extrude(height = phone_width - phone_cnr_rad*2)
189       children(0);
190   }
191   // fill
192   translate([0,0, fill_zstart])
193     mirror([0,0, fill_downwards])
194     linear_extrude(height = fill_th)
195     rectfromto([+1,-1] * phone_cnr_rad,
196                [phone_width, -phone_height] + [-1,+1] * phone_cnr_rad);
197 }
198
199 module CaseBase(){
200   AroundEdges(epp3[1], case_th_bottom, 1)
201     EdgeProfile();
202 }
203
204 module Case(){ ////toplevel
205   difference(){
206     union(){
207       CaseBase();
208
209       // ledge (fixed keeper)
210       intersection(){
211         rotate([90, 0, 0])
212           linear_extrude(height = phone_height + phone_cnr_rad * 2)
213           KeeperProfile();
214
215         // outline of the whole case, to stop it protruding
216         translate([0,0, -25])
217           linear_extrude(height = 50)
218           hull()
219           // CaseBase_rhsflip() // actually, we only care about the LH
220           CaseBase_botflip()
221           circleat([+1,-1] * phone_cnr_rad, phone_cnr_rad + case_th_side/2);
222       }
223     }
224
225     // slot for keeper
226     CaseBase_rhsflip(1)
227       translate([0, -phone_cnr_rad, 0])
228       rotate([90, 0, 0])
229       linear_extrude(height = phone_height + phone_cnr_rad * 2)
230       minkowski(){
231         KeeperProfile();
232         rectfromto([ -keeper_gap_x,    -keeper_gap_z_bot ],
233                    [ keeper_gap_x_holes,    +keeper_gap_z_top ]);
234       }
235
236     // front camera
237     CaseBase_rhsflip([1])
238       mirror([0, 0, 1])
239       linear_extrude(height = 20)
240       mirror([0, 1, 0])
241       rectfromto(camera_pos_tl, camera_pos_br);
242
243     // struts (invisible, because they're buried in the case)
244     for (i= [1 : 1 : case_struts_count]) {
245       translate([0,
246                  -phone_height * i / (case_struts_count+1),
247                  epp2i[1] - case_th_bottom + case_struts_solid_below])
248         linear_extrude(height= case_th_bottom
249                        -(case_struts_solid_below+case_struts_solid_above))
250         rectfromto([               epp2i[0], -0.5 * case_struts_width ],
251                    [ phone_width - epp2i[0], +0.5 * case_struts_width ]);
252     }
253   }
254 }
255
256 module Lid(){ ////toplevel
257   AroundEdges(lpp10[1], lpp13[1] - lpp10[1], 0)
258     LidEdgeProfile();
259 }
260
261 module TestLength(){ ////toplevel
262   intersection(){
263     Case();
264     translate([-30, -200, -20])
265     cube([30 + 15, 250, 40]);
266   }
267 }
268
269 module TestWidth(){ ////toplevel
270   intersection(){
271     Case();
272     translate([-30, -(phone_height - 25), -20])
273       mirror([0, 1, 0])
274       cube([200, 50, 40]);
275   }
276 }
277
278 module TestLidWidthPrint(){ ////toplevel
279   rotate([0,180.0])
280     intersection(){
281       Lid();
282       translate([-30, -(phone_height - 25), -20])
283         mirror([0, 1, 0])
284         cube([200, 50, 40]);
285     }
286 }
287
288 module TestCamera(){ ////toplevel
289   intersection(){
290     Case();
291     CaseBase_rhsflip(1)
292       translate([0,0,-25])
293       linear_extrude(height = 50)
294       mirror([0, 1, 0])
295       rectfromto([-20, -20],
296                  camera_pos_br + [ 5, 5 ]);
297   }
298 }
299
300 module OneKeeper(){ ////toplevel
301   translate([0, -phone_cnr_rad, 0])
302     rotate([90, 0, 0])
303     linear_extrude(height = phone_height - phone_cnr_rad * 2)
304     KeeperProfile();
305 }
306
307 module OneKeeperPrint(){ ////toplevel
308   rotate([0,180,0])
309     OneKeeper();
310 }
311
312 module Keeper(){ ////toplevel
313   CaseBase_rhsflip()
314     OneKeeper();
315 }
316
317 module DemoProfiles(){ ////toplevel
318   LidEdgeProfile();
319   %EdgeProfile();
320   KeeperProfile();
321
322   translate([20,0]) {
323     LidEdgeProfile();
324     %EdgeProfile();
325
326     demopoint_QR = [ bppS[0], bppQ[1] - 0.1];
327   
328     color("blue") ButtonCoverProfile();
329     color("red") {
330       rectfromto(bppQ, demopoint_QR);
331       rectfromto(bppR, demopoint_QR);
332     }
333   }
334 }
335
336 //EdgeProfile();
337 //KeeperProfile();
338 //CaseBase();
339 //%Case();
340 //Keeper();
341 //LidEdgeProfile();
342 //KeeperProfile();
343 //DemoProfiles();