From: Ian Jackson Date: Mon, 30 Jun 2025 14:56:39 +0000 (+0100) Subject: fairphone5-case: fork from fairphone4-case X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=191eb1aa9269efd80486804b187ca7e10a63a883;p=reprap-play.git fairphone5-case: fork from fairphone4-case Signed-off-by: Ian Jackson --- diff --git a/Makefile b/Makefile index 5399986..7d936e0 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ FILAMENTSPOOL_AUTOS += filamentspool-storarm3 QUACKSES = $(addprefix quacks-ingredients-L, 1 2 3 4 5) QUACKS_SCADS = $(addsuffix .scad, $(QUACKSES)) -USING_AUTOS ?= $(FILAMENTSPOOL_AUTOS) xeno-drivebay-bracket dungeonquest-cone anke-gps-bracket cable-hole-trunking-cover anglepoise-neck crossbar-computer-led-mount wardrobe-hook knifeblock pandemic-counter pattress-boxes-3-cover bike-lipo-box earring-stand bike-stalk-led-mount sewing-table sewing-table-test sewing-table-jig maglite-holder poster-tube-lid poster-tube-lid-coarse fairphone-case fairphone-battery-case fairphone4-case fairphone4-case-coarse lock-inframe-bracket ksafe-base $(QUACKSES) quacks-ingredients-demos mic-table-clamp nook-case nook-case-test scaffold-clamp-common scaffold-clamp-tensioner scaffold-clamp-linear-bracket scaffold-clamp-straphook powerbank-bike-clamp topeak-mtx-tortec-expeditionrack-adapter lipo-flat-mount laptop-sound-cable-hooks digispark-with-cable chimney-cable-retainer mudguard-bracket waring-blender-motor-coupler toolbox-inserts toolbox-inserts-coarse toolbox-inserts-magnet-embed-test $(foreach x,500 1000,adafruit-powerboost-$x) +USING_AUTOS ?= $(FILAMENTSPOOL_AUTOS) xeno-drivebay-bracket dungeonquest-cone anke-gps-bracket cable-hole-trunking-cover anglepoise-neck crossbar-computer-led-mount wardrobe-hook knifeblock pandemic-counter pattress-boxes-3-cover bike-lipo-box earring-stand bike-stalk-led-mount sewing-table sewing-table-test sewing-table-jig maglite-holder poster-tube-lid poster-tube-lid-coarse fairphone-case fairphone-battery-case fairphone4-case fairphone4-case-coarse fairphone5-case fairphone5-case-coarse lock-inframe-bracket ksafe-base $(QUACKSES) quacks-ingredients-demos mic-table-clamp nook-case nook-case-test scaffold-clamp-common scaffold-clamp-tensioner scaffold-clamp-linear-bracket scaffold-clamp-straphook powerbank-bike-clamp topeak-mtx-tortec-expeditionrack-adapter lipo-flat-mount laptop-sound-cable-hooks digispark-with-cable chimney-cable-retainer mudguard-bracket waring-blender-motor-coupler toolbox-inserts toolbox-inserts-coarse toolbox-inserts-magnet-embed-test $(foreach x,500 1000,adafruit-powerboost-$x) AUTO_INCS += sealing-box.scad sewing-table.scad nutbox.scad \ powerbank-anker-10000.dxf \ diff --git a/fairphone5-case-coarse.scad b/fairphone5-case-coarse.scad new file mode 100644 index 0000000..ac67a3e --- /dev/null +++ b/fairphone5-case-coarse.scad @@ -0,0 +1,7 @@ +// -*- C -*- + +//// toplevels-from: +include + +$fa = 20; +$fs = 2; diff --git a/fairphone5-case-mounted.scad b/fairphone5-case-mounted.scad new file mode 100644 index 0000000..f95bb4e --- /dev/null +++ b/fairphone5-case-mounted.scad @@ -0,0 +1,15 @@ +// -*- C -*- + +include + +module CaseMounted(){ ////toplevel + Case(); + translate([ phone_width/2, + -phone_height/2, epp3[1] - case_th_bottom ]) + Mount(); +} + +//// toplevels-from: +include +$suppress_forward_holes = true; +$suppress_hinge = true; diff --git a/fairphone5-case-tripod.scad b/fairphone5-case-tripod.scad new file mode 100644 index 0000000..00e44cd --- /dev/null +++ b/fairphone5-case-tripod.scad @@ -0,0 +1,37 @@ +// -*- C -*- + +include + +tr_cube_offset = 20; +tr_cube_sz = [20, 20, 15]; +tr_around = 10; + +module Mount(){ + translate([0, - tr_cube_sz[1], 0]) + difference(){ + translate([0, tr_cube_sz[1]/2 - tr_cube_offset/2, tr_cube_sz[2]/2]) + cube(tr_cube_sz + [0, tr_cube_offset, 0], center=true); + translate([0, tr_cube_sz[1]/2 - tr_cube_offset, 0]) + rotate([180,0,0]) + render() CameraMountThread(tr_cube_sz[2] + 1); + } +} + +module CaseMounted(){ ////toplevel + difference(){ + render() Case(); + translate([ phone_width/2, -phone_height/2 ]) + linextr(-50, 50) + square([phone_width, phone_height] - tr_around * 2 * [1,1], + center=true); + } + translate([ phone_width, + -phone_height + tr_cube_sz[0] * 0.7, + epp3[1] - case_th_bottom ]) + rotate([0,0,90]) + Mount(); +} + +//// toplevels-from: +include +$suppress_hinge = true; diff --git a/fairphone5-case.scad b/fairphone5-case.scad new file mode 100644 index 0000000..0a4b63d --- /dev/null +++ b/fairphone5-case.scad @@ -0,0 +1,1801 @@ +// -*- C -*- + +// Hard case for Fairphone 2 +// +// Copyright 2018 Ian Jackson. There is NO WARRANTY. +// See below for full licensing and disclaimer. +// +// Instructions +// +// 1. You will want to git clone this repository. +// +// 2. +// +// 3. use "make" to generate the necessary files: +// +// make -j8 fairphone-case.auto.scads `for f in \ +// HingeLeverPrint \ +// LidPrint \ +// OneKeeperPrint \ +// Case \ +// ; do echo fairphone-case,$f.auto.stl; done` +// +// 4. Print them. Case and OneKeeperPrint should probably be +// the same colour. +// +// 5. Assemble the hinge. After placing the parts in the appropirate +// relative placement: +// +// Use long bit of wire to ensure holes are lined up and proper +// Cut four short bits of wire, using above as a guage +// +// Push two short bits into two holes on same side +// Use long bit of wire to ensure properly in holes +// Keep that side up so they don't fall out! +// +// For each of the two holes +// Use 20-30cm hunk of 2.85mm PLA +// Use gas flame to melt end until it catches fire (!) +// Remove from flame, wave to extinguish, and quickly: +// Dab end onto where hole is +// As it congeals, use sidecutters to cut off by hole +// +// Repeat for two holes on other side +// When cool, file down rough edges +// +// 6. In use: +// +// - To put the phone in, drop its RH side into the RH side of +// the case. Then feed the keeper through the small hole. +// Feed it right through. +// +// - The optional prop can be used to prop the phone up (in +// portrait orientation only right now). See +// openscad fairphone-case,DemoPropAngles.auto.scad +// +// Other phones +// +// It might well be possible to adapt this file for other phones. +// If you do, let me know how you get on. +// +// +// AUTHORSHIP, COPYRIGHT, LICENCE, AND LACK OF WARRANTY +// +// Copyright (C) 2018-2022 Ian Jackson. +// +// This program for generating a 3D model is free software: you can +// redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public +// License along with this program. If not, see +// . +// +// In particular DO NOT BLAME ME IF THIS CASE DOES NOT ADEQUATELY +// PROTECT YOUR PHONE ! It is your responsibility to decide whether +// this case will meet your needs. + +include +include + +phone = [ 75.86, 162.0 ]; + +prop_buildout_less = 3; + +prop_angles = [ 15, 30, 45, 60 ]; + +bumper = [ 0.250, -0.025 ]; +// ^ One side. Overall size is increased by twice this. +// If no bumpers, is the gap around the phone. + +enable_support = 1; + +led_window_style = 0; +// 0: no window +// 1: simply an opening +// 2: opening with separate cover model, for printing in clear (two colour) +// 3: like 2 but one-layer window for ad-hoc multi-colour + +initial_layer_thick = 0.400; // ^ needed for mode 3 only +initial_layer_width = 0.750; // ^ needed for mode 3 only +multicolour_gap = 0.15; // each side + +phone_cnr_rad = 7.0; // actuall 8.mumble, but smaller is fine +phone_rim_depth = 0.01; // includes allowance for a screen protector + +button_cutout_depth = 9; + +phone_edge_thick = 11.25; + +camera_pos_tl = [ 5.600, 5.750 ]; // from tl corner (as seen from back) +camera_edge_rad = 9.750 + 0.700; +camera_sz = 32.920 + .750 + 1.000; + +// this is disabled, FP4 doesn't have one +jack_pos = [ 13.83, 8.485 ]; +jack_dia = 10.64 + .5; // some jack I had lying around + +// this led stuff, is irrelevant, we have disabled it as it doesn't have one +led_pos = []; // [ 13.98, 10.00 ]; +led_aperture = 9; +led_window_ledge = 0.75; // each side + +noisecancelmic_pos = [ 15.08 + .720, 4.35 ]; // from rhs, from top edge +noisecancelmic_dia = 4.00; + +mainmic_pos = [ 21.0, 4.65 ]; // from lhs, from top edge +mainmic_dia = 4.00; + +lhshole_pos = [ phone[1]/2 + 0.40, 4.35 ]; + +fingerpushhole_dias = []; +//fingerpushhole_dias = [ 15, 18 ]; // this is for testing + +lanyard_half_dia = 1.15; +lanyard_entry_rel_breadth = 2; +lanyard_channel_len = 8; +//rearspeaker_pos_bl = [ 12.64, 18.72 ]; +//rearspeaker_size = [ 3.76, 7.36 ]; + +bottomspeaker_size = [ 11.35, 1.90 ] + [1,1] * 0.5; +bottomspeaker_pos = [ 17.55, 5.17 ]; // from rhs, from top + +microusb_above = 1.64 - 0.25; +microusb_below = 2.42; +microusb_width = 12.16 + 2.0 + 1.25; + +case_th_bottom = 2.5; +case_th_lid = 3.0; +case_th_side = 2.6; +case_th_lip = 1.2; + +lid_screen_gap_extra = .66; + +case_struts_count = 6; +case_struts_solid_below = 1.00; +case_struts_solid_above = 0.75; +case_struts_width = 0.10; + +keeper_th_z = 0.75; +keeper_th_x = 0.75; +keeper_inner_width = 2.75; +keeper_inner_height = 2.75; +keeper_slant_slope = 2; // larger means steeper + +keeper_gap_z_top = 0.25; +keeper_gap_z_bot = 0.75; +keeper_gap_x = 0.25; +keeper_gap_x_holes = 0.75; +keeper_fatter = 0.45; +keeper_fatter_hole = 1.20; +keeper_stubbier = 0.0; + +keeper_side = 0; // 0 = lhs; 1 = rhs + +case_lip = 1.25; + +lid_gap_x = 0.25; +lid_gap_z = 0.25; +lid_lip = 1.75; +lid_edgepart_width = 5.0; +lid_buttoncover_thick = 1.3; +lid_buttoncover_reinf = 0.95; + +foldover_gap = 0.50; +foldover_lever_gap = 0.50; + +// properties of the hinge fasteners +hingescrew_shaft_dia = 1.600 + 0.45; // beading wire +hingescrew_shaft_len = 10; +hingescrew_fasteners_extra_thick = 0.40; +// ^ amount of thread protruding if everything was completely nominal +// and we are using two nuts +hingescrew_nut_access_dia = 4.72 + 0.50; +// ^ washer is 4.72 dia +// also, want to get pliers or tiny spanner in to do up locknut +hingescrew_nut_across = 3.92 + 0.25; // incl. slop around recess slop +hingescrew_nut_thick = 1.93; +hingescrew_head_th = 1.38 + 0.75; +hingescrew_head_dia = 3.92; + +hingescrew_nut_recess_portion = 2/3; // portion of nut in recess + +lever_cover_th = 0.75; +hingemount_th = 2.5; +hingemount_wd = 4.8725; + +$fa = 5; +$fs = 0.1; + +button_l_fudge = 4.4; +buttonishleg_default_l_is_fudge = 10; + +hinge_base_slope = 1.5; // bigger is steeper + +strut_min_at_end = 1.5; + +hinge_x_gap = 0.125; +hinge_x_postscrew_gap = 0.75; +hinge_x_arms_gap = 0.35; +hinge_r_arms_gap = 0.55; +hinge_over_nut_plate = -0.50; // bodge apropos slope + +// there isn't one of these, speaker is by hinge +// rearspeaker_gap = [ 2.0, 2.0 ]; // each side + +thumbrecess_depth = 1.3; +thumbrecess_width = 16.5; +thumbrecess_topcurve_r = 5.0; + +prop_recess_under = 0.50; +prop_recess_slop = 0.200; // each side +prop_end_dia = 0.5; +prop_main_th = 3; +prop_taper_len = 6; +prop_main_width = 4; +prop_side_gap = 0.75; // each side +prop_lidrecess_behind = 0.75; +prop_caserecess_behind = 0.75; +prop_caserecess_taper = 0.45; // one side only +prop_prop_gap = 0.5; +prop_prong_heel_slope = 0.5; + +lid_fold_clearance_antislop = 0.5; + +$button_leg_only = false; +$suppress_forward_holes = false; +$suppress_hinge = false; + +// ---------- calculated ---------- + +phone_total_thick = phone_edge_thick; + +phone_width = (phone + bumper*2)[0]; +phone_height = (phone + bumper*2)[1]; + +inside_br = [phone_width, -phone_height]; + +prop_prong_h = prop_main_th; + +//echo(camera_pos_tl + bumper, +// camera_pos_br + bumper); + +// ----- could be changed ----- +lid_buttoncover_gap = lid_gap_x; +lid_buttoncover_overlap = case_th_lip + keeper_gap_z_top; + +//prop_lidrecess_depth = case_th_lid - prop_recess_under; + +//prop_nose_len = case_th_lid - prop_recess_under; +//prop_recess_slope = tan(prop_max_angle); // bigger means steeper +//prop_recess_width = prop_main_th / cos(prop_max_angle) + prop_backfwd_gap; + + +epp0 = [0,0]; +epp1 = [0, -phone_edge_thick]; +epp2i = epp1; // conflated for FP4 +epp2o = epp2i; +epp3 = epp2i + [10, 0]; +epp5 = epp0 + [0,1] * (keeper_th_z + keeper_gap_z_top + case_lip); +epp4 = epp5 + [-1,0] * case_th_side; + +kppe = [0,0]; +kppd = kppe + [1,0] * keeper_inner_width; +kppc = kppd + [0,1] * keeper_th_z; +kppb = [ kppe[0] - keeper_th_x, kppc[1] ]; +kppf = kppe - [0,1] * keeper_inner_height; +kppa = [ kppb[0], kppf[1] ]; + +lpp10 = [ epp5[0] + lid_gap_x, kppc[1] + lid_gap_z ]; +lpp11 = [ lpp10[0], epp5[1] + lid_gap_z ]; + +lpp14 = lpp10 + [1,0] * max(keeper_inner_width, lid_edgepart_width); +// exact x posn not very important; must extend past end of keeper + +lpp15 = [ lpp14[0], + epp0[1] - phone_rim_depth + 1/2.5 * case_th_lid + + lid_screen_gap_extra ]; +// ^ beam theory says to maximise force before contact, +// the gap below the `beam' (the lid) must be 1/3 +// the thickness (ie the lid thickness) if the beam +// is solid, or 1/2 if it has a top and bottom only. +// ours is mostly solid. + +lp_r12 = max(case_th_lid - (lpp11[1] - lpp15[1]), + case_th_lip); + +lpp12 = [ epp4[0] + lp_r12, lpp11[1] ]; +lpp13 = [ lpp12[0], lpp12[1] + lp_r12 ]; + +case_bottom_z = epp2o[1] - case_th_bottom; + +// button profile +bppM = epp4 + [0,5]; +bppN = [ bppM[0] + lid_buttoncover_thick, bppM[1] ]; +bppR = [ bppN[0] + lid_buttoncover_gap, -button_cutout_depth ]; +bppS = [ epp1[0], bppR[1] ]; +bppQ = [ bppM[0], bppR[1] - lid_buttoncover_overlap ]; +bppP = bppQ + [0,1] * lid_buttoncover_gap; +bppO = [ bppN[0], bppP[1] ]; +bppL = lpp10 + [5,0]; +bppK = [ bppL[0], bppN[1] ]; +bppJ = [ bppN[0], bppL[1] ]; +bppU = [ bppJ[0], lpp12[1] ]; +bppV = lpp11; +bppW = lpp10; + +echo("BUTTON COVER TH", bppO[0] - bppP[0]); + +// notification led aperture + +nla_r0 = led_aperture/2; +nla_r1 = nla_r0 + led_window_ledge; +nla_r2 = nla_r1 + multicolour_gap; +nla_t = + led_window_style >= 3 ? initial_layer_thick : + led_window_style >= 2 ? led_window_ledge : 0; + + +// hinge plan +hp_rn = hingescrew_nut_access_dia/2; +hp_r2_min = hp_rn + lever_cover_th; +hp_rs = hingescrew_shaft_dia/2; +hp_r1_min = hp_rs + hingemount_th; + +hp_r1 = max(hp_r1_min, hp_r2_min); +hp_r2 = hp_r1; + +hppU = lpp13; +hppS = epp2o + [0,-1] * case_th_bottom; +hp_k = 0.5 * (hppU[1] - hppS[1] + foldover_gap); + +hppM = [ epp4[0] - foldover_lever_gap - hp_r2, + 0.5 * (hppU + hppS)[1] ]; +hppT = [ hppM[0], hppU[1] - hp_r1 ]; +hppB = hppT + [0,-1] * hp_k; + +hppE_y = epp2o[1] - case_th_bottom + hp_r1; +hppE_x = hppB[0] + (hppB[1] - hppE_y) * hinge_base_slope; +hppE = [ hppE_x, hppE_y ]; + +// hinge elevation x coords + +hex20 = max(epp2o[0], + phone_cnr_rad, + kppd[0] + hingescrew_head_th + keeper_gap_x_holes); +hex21 = hex20 + hingemount_wd; +hex22 = hex21 + hinge_x_gap; +hex27 = hex20 + hingescrew_shaft_len; +hex24 = hex27 + hinge_x_postscrew_gap; +hex23 = hex27 - (hingescrew_nut_thick*2 + + hingescrew_fasteners_extra_thick); +hex26 = hex23 + hingescrew_nut_thick * 2/3; + +//echo(hex20, hex21, hex22, hex23, hex24); +//// 6, 10.8725, 10.9975, 13.74, 18.75 +//module chk(act,exp) { +// if (abs(act-exp) > 1e-9) echo("WRONG", act, exp); +// else echo("ok", act); +//} +//chk(hex20, 6); +//chk(hex21, 10.8725); +//chk(hex22, 10.9975); +//chk(hex23, 13.74); +//chk(hex24, 18.75); + +lid_fold_clearance_skew = + (lpp10[1] - hppB[1]) / + (lpp10[0] - hppB[0]); + +echo("SK",lid_fold_clearance_skew); + +// thumb recess (used to be "catch" hence cpp* + +cppA = epp4 + [thumbrecess_depth, 0]; +cppB = [ cppA[0], epp1[1] ]; + +// lanyard + +ly_r = lanyard_half_dia / 2; +ly_rc = ly_r * 2; + +ly_theta = 90; +ly_o = epp2i + 3 * ly_r * [0,1]; + +max_case_bottom_edge_thickness = + case_th_bottom; + +ly_q_z = -(ly_rc + ly_r); +ly_re = max_case_bottom_edge_thickness - (-ly_q_z); + +ly_oec_y = lanyard_entry_rel_breadth * ly_r; + +// prop recess in case + +prop_x_pos = phone_width/2; + +prop_recess_hw = 0.5 * prop_main_width + prop_side_gap; + +prc_r1 = prop_end_dia/2; +prc_r3 = prc_r1 + prop_recess_slop; + +prcp2 = [ epp4[0] + prop_buildout_less, + case_bottom_z ]; + +prop_caserecess_buildout_r = -1; // prcp2[0] - epp2o[0]; + +prcp1 = [ epp2o[0] + prc_r3 + prop_caserecess_behind, + epp2i[1] - prc_r3 - prop_recess_under]; + +// prop recess in lid + +prl_r10 = prop_end_dia/2; +prl_r10o = prl_r10 + prop_recess_slop; + +prlp10 = lpp10 + [1,1] * prl_r10o + + [1,0] * prop_lidrecess_behind + + [0,1] * prop_recess_under; + +// prop + +$prpp10 = [0,0]; +$prpp11 = [0, prop_taper_len]; + +$prp_r10 = prl_r10; + +// ---------- modules ---------- + +module AdhocMultiprintFrame(phase, z0, zs) { + // from z0 to z0 + zs*layer + extra = phase * (initial_layer_width + multicolour_gap) + 5; + xextra = extra + -epp4[0]; + xrange = [ 0, phone_width ] + [-1,+1] * xextra; + yextra = extra + -epp4[0]; + yrange = [ -phone_height + +hppB[0] - hp_r2, 0 ] + [-1,+1] * yextra; + p0 = [ xrange[0], yrange[0] ]; + p1 = [ xrange[1], yrange[1] ]; + echo(p0, p1); + translate([0,0, z0]) + mirror([0,0, zs<0 ? 1 : 0]) + linear_extrude(height= initial_layer_thick) + difference(){ + rectfromto(p0 - [1,1] * initial_layer_width, + p1 + [1,1] * initial_layer_width); + rectfromto(p0, p1); + } +} + +module KeeperProfile(fatter=0, slant=0, stubbier=0){ + use_e = kppe + [0,-1] * slant * keeper_inner_width / keeper_slant_slope; + polygon([use_e + [+1,-1] * fatter, + kppd + [ 0,-1] * fatter - stubbier * [1,0], + kppc - stubbier * [1,0], + kppb, + kppa + stubbier * [0,1], + kppf + [+1, 0] * fatter + stubbier * [0,1] + ]); +} + +module EdgeProfile(){ + difference(){ + hull(){ + translate(epp3) square(case_th_bottom*2, center=true); + circleat(epp2o, r=case_th_bottom); + circleat(epp1, r=case_th_side); + rectfromto(epp0, epp4); + } + polygon([ epp5 + [0,10], + epp1, + epp3 + [10,0] ]); + } +} + +module LanyardLanyardProfile(entry=false){ + hull(){ + for (xs=[-1,+1] * (entry ? lanyard_entry_rel_breadth : 1)) + translate(xs * 0.5 * lanyard_half_dia * [1,0]) + circle(r= lanyard_half_dia/2); + } +} + +module LanyardCurveChannelProfile(){ + translate([0, -ly_r]) + LanyardLanyardProfile(); +} + +module LanyardEntryChannelProfile(){ + LanyardLanyardProfile(true); +} + +module LanyardMainChannelProfile(){ + LanyardCurveChannelProfile(); + difference(){ + square(center=true, ly_r * [6, 2]); + for (xs=[-1,+1]) + translate(ly_r * [3 * xs, -1]) + circle(r = ly_r); + } +} + +module LanyardEntryOuterProfile(){ + circleat([ly_re + ly_r, 0], ly_re); +} + +module LanyardEntry(){ + q_z = ly_q_z; + oec_y = ly_oec_y; + + d_x = -ly_rc; + + translate([d_x, 0, q_z]) { + intersection(){ + rotate([90,0,0]) + rotate_extrude(convexity=10) + rotate(90) + translate([0, -q_z]) + LanyardCurveChannelProfile(); + translate([0,-10,0]) + cube([20,20,20]); + } + } + + mirror([0,0,1]) + translate([0,0,-1]) + linear_extrude(height=20) + rotate(-90) + LanyardEntryChannelProfile(); + + translate([0, ly_r*2, 0]) + rotate([90,0,0]) + linear_extrude(height = ly_r*4){ + difference(){ + rectfromto([d_x, q_z], [ly_r, 0]); + circleat([d_x, q_z], ly_rc); + } + } + + translate([0,0,q_z]){ + for (my=[0,1]) + mirror([0,my,0]){ + translate([0, oec_y, 0]){ + difference(){ + translate(ly_re * [-1,0,-2]) + cube(ly_re * [2,1,2]); + rotate_extrude(convexity=10) + LanyardEntryOuterProfile(); + } + } + } + difference(){ + translate([-ly_re, -(oec_y + 0.01), -2*ly_re]) + cube([ly_re*2, 2*(oec_y + 0.01), 2*ly_re]); + for (mx=[0,1]) + mirror([mx,0,0]) + rotate([90,0,0]) + translate([0,0,-10]) + linear_extrude(height=20) + LanyardEntryOuterProfile(); + } + } +} + +module LanyardCutout(l){ + rotate([0,-90,0]) + linear_extrude(height=l) + rotate(-90) + LanyardMainChannelProfile(); + + for (ee=[0,1]){ + translate(ee * l * [-1,0]) + mirror([ee,0,0]) + LanyardEntry(); + } +} + +module LidEdgeProfile(){ + polygon([ lpp10, + lpp11, + lpp12, + lpp13, + lpp13 + [10, 0], + lpp15 + [10, 0], + lpp15, + lpp14, + ]); + intersection(){ + circleat(lpp12, r=lp_r12); + rectfromto( lpp12 + [-10, 0], + lpp12 + [+10, +10] ); + } +} + +module LidEdgeFoldClearanceProfile(){ + translate([-lid_fold_clearance_antislop, 0]) + polygon([ lpp10, + lpp11, + lpp11 + [-20, 0], + lpp11 + [-20, 20], + lpp11 + [+20, 20], + lpp10 + [+20, 0] ]); +} + +module ButtonCoverProfile(){ + intersection(){ + polygon(concat([ bppM, bppP, bppO, bppJ ], + (enable_support && !$button_suppress_over_keeper + ? [ bppU, bppV, bppW ] : []), + [ bppL, bppK ])); + hull(){ + EdgeProfile(); + LidEdgeProfile(); + } + } +} + +module ButtonPlan(l, deep, cut){ + epsilon = + (cut ? 0 : lid_buttoncover_gap); + + delta = + (deep ? lid_buttoncover_overlap : 0); + + C = [0,0]; // by definition + T = [ 0, epp4[1] ]; + G = T + [0,10]; + + B0 = C + [0,-1] * button_cutout_depth; + B1 = B0 + [0,1] * epsilon; + + r0 = 0.5 * (T[1] - B0[1]); + A = [ -(l + button_l_fudge)/2 + r0, 0.5 * (T[1] + B0[1]) ]; + H = A + [0,-1] * delta; + + D = A + [-2,0] * r0; + F = D + [0,10]; + + E0 = 0.5 * (D + A); + E1 = E0 + [1,0] * epsilon; + + I0 = [ E0[0], H[1] ]; + I1 = [ E1[0], H[1] ]; + + hull(){ + for (m=[0,1]) mirror([m,0]) + circleat(H, r0 - epsilon); + } + for (m=[0,1]) mirror([m,0]) { + difference(){ + polygon([ E1, + I1, + H, + B1, + G, + F, + D + ]); + circleat(D, r0 + epsilon); + } + } +} + +module ButtonCoverReinf(){ ////toplevel + minkowski(){ + rotate([90,0,0]) + linear_extrude(height=0.01) + intersection(){ + ButtonCoverProfile(); + translate([bppJ[0] + 0.1, -50]) mirror([1,0]) + square([100,100]); + } + mirror([0,0,1]) linear_extrude(height=0.01) intersection(){ + circle(r= lid_buttoncover_reinf); + translate([-20,0]) square(40, center=true); + } + } +} + +module ThumbRecessCutProfile(){ + difference(){ + polygon([ cppA + [-10,0], + cppB + [-10,0], + cppB, + cppA ]); + circleat(epp1, r=case_th_side); + } +} + +module Flip_rhs(yn=[0,1]) { + for ($rhsflip=yn) { + translate([phone_width/2, 0, 0]) + mirror([$rhsflip,0,0]) + translate([-phone_width/2, 0, 0]) + children(); + } +} + +module Flip_bot(yn=[0,1]) { + for ($botflip=yn) { + translate([0, -phone_height/2, 0]) + mirror([0, $botflip, 0]) + translate([0, phone_height/2, 0]) + children(); + } +} + +module AroundEdges(fill_zstart, fill_th, fill_downwards=0){ + // sides + Flip_rhs(){ + translate([0, -phone_cnr_rad, 0]) + rotate([90,0,0]) + linear_extrude(height = phone_height - phone_cnr_rad*2) + children(0); + } + // corners + Flip_rhs() Flip_bot() { + translate([+1,-1] * phone_cnr_rad) + intersection(){ + rotate_extrude() + intersection(){ + mirror([1,0,0]) + translate([-1,0] * phone_cnr_rad) + children(0); + rectfromto([0,-20],[10,20]); + } + translate([-10, 0, -20] + 0.01 * [+1,-1, 0] ) + cube([10,10,40]); + } + } + // top and bottom + Flip_bot(){ + translate([ phone_width - phone_cnr_rad, 0,0 ]) + rotate([90,0,-90]) + linear_extrude(height = phone_width - phone_cnr_rad*2) + children(0); + } + // fill + translate([0,0, fill_zstart]) + mirror([0,0, fill_downwards]) + linear_extrude(height = fill_th) + rectfromto([+1,-1] * phone_cnr_rad, + [phone_width, -phone_height] + [-1,+1] * phone_cnr_rad); +} + +module CaseAperture(pos, dia, $fn, topbottom=0) { + theta = 180/$fn; + translate([ bumper[0], + -epp2i[0], + 0 ]) + rotate([0,0, 90*topbottom]) + translate([ pos[0] * (topbottom>0 ? -1 : +1), 0, -pos[1] ]) + rotate([-90, theta, 0]) + cylinder(r = dia/2 / cos(theta), + h = 60); +} + +module SideButton(y, y_ref_sign, l, suppress_over_keeper=0){ + // y_ref_sign: + // +1 measured from top of actual phone to top of button + // -1 measured from bottom of actual phone to bottom of button + // 0 y is centre of button in coordinate system + $button_l= l; + $button_suppress_over_keeper= suppress_over_keeper; + eff_y = y_ref_sign > 0 ? -bumper [1] -y -l/2 : + y_ref_sign < 0 ? (-phone -bumper)[1] +y +l/2 : + y; + //echo(eff_y); + translate([0, eff_y, 0]) + children(); +} + +module LidButtonishLeg(y, y_ref_sign, l=buttonishleg_default_l_is_fudge) { + $button_leg_only = true; + SideButton(y, y_ref_sign, l) children(); +} + +module Buttons(){ + Flip_rhs(1) SideButton(30.320, +1, 22.960 ) children(); // volume + Flip_rhs(1) SideButton(64.220, +1, 14.500 ) children(); // power + Flip_rhs(1) LidButtonishLeg(14, -1) children(); + Flip_rhs(0) LidButtonishLeg(21, -1) children(); + Flip_rhs(0) LidButtonishLeg(38, +1) children(); + Flip_rhs(0) LidButtonishLeg(14, +1) children(); +} + +module Struts(x_start, z_min, th){ + // if th is negative, starts at z_min and works towards -ve z + // and object should then be printed other way up + for (i= [1 : 1 : case_struts_count]) { + translate([0, + 0, + z_min]) + mirror([0,0, th<0 ? 1 : 0]) + translate([0, + -phone_height * i / (case_struts_count+1), + case_struts_solid_below]) + linear_extrude(height= abs(th) + -(case_struts_solid_below+case_struts_solid_above)) + rectfromto([ x_start, -0.5 * case_struts_width ], + [ phone_width - x_start, +0.5 * case_struts_width ]); + } +} + +module OrdinaryRearAperture(rhs,bot, pos){ + Flip_rhs(rhs) Flip_bot(bot) + linextr(-20, 20) + mirror([0,1]) + translate(pos + bumper) + children(); +} + +module MicroUSBEtc(){ + Flip_bot(1){ + rotate([90,0,0]) + mirror([0,0,1]) + linextr(-epp2i[0], 60) + translate([0.5 * phone_width, 0, 0]) + rectfromto([-microusb_width/2, epp2i[1] + microusb_below], + [+microusb_width/2, epp0[1] + -microusb_above]); + } +} + +module OrdinaryBottomEdgeApertures(){ + Flip_bot(1) + CaseAperture(mainmic_pos, mainmic_dia, 8); + + Flip_bot(1) Flip_rhs(1) { + linextr_y_xz(-epp2i[0], 60) + hull() + for (x= [-1,+1]) { + translate([ -bottomspeaker_pos[0], -bottomspeaker_pos[1] ] + + [ 0.5 * x * bottomspeaker_size[0] - bottomspeaker_size[1], + 0 ]) + rotate(360/16) + circle(r = bottomspeaker_size[1], $fn = 8); + } + } +} + +module OrdinaryRearApertures(){ + // rear speaker + // OrdinaryRearAperture(1,1, rearspeaker_pos_bl) + // rectfromto(-rearspeaker_gap, + // rearspeaker_size + rearspeaker_gap); +} + +module NotInTestFrameRearApertures(){ + // finger hole to remove phone + if (len(fingerpushhole_dias)) + OrdinaryRearAperture(0,0, [ fingerpushhole_dias[0] + epp2i[0], + phone[1]/2 ]) + scale(fingerpushhole_dias) + circle(r= 0.5 ); +} + +module RearCameraAperture(){ + Flip_rhs(1) + mirror([0, 0, 1]) + translate([0,0,0]) + hull() // there is some kind of bug if hull() is done in 2D here! + linear_extrude(height = 20) + mirror([0, 1, 0]) + translate(bumper) + translate(camera_pos_tl) + for (xy = [ [0,0], [0,1], [1,0] ]) { + translate( + camera_edge_rad * [1,1] + + xy * (camera_sz - camera_edge_rad * 2) + ) + circle(r = camera_edge_rad); + } +} + +module HingeLidProfile(){ + hull(){ + circleat(hppT, hp_r1); + circleat(lpp12, lp_r12); + polygon([lpp10, + lpp13 + [2,0], + lpp12, + hppT]); + } +} + +module HingeBaseProfile(){ + difference(){ + hull(){ + circleat(hppB, hp_r1); + circleat(hppE, hp_r1); + circleat(epp2o, case_th_bottom); + circleat(hppB + [10,0], hp_r1); + } + polygon([epp5, epp1, epp3, bppL]); + } +} + +module HingeLeverOuterProfile(){ + hull(){ + circleat(hppT, hp_r2); + circleat(hppB, hp_r2); + } +} + +module HingeLeverInnerProfile(){ + for (s = [-1,+1]) { + c = s > 0 ? hppT : hppB; + translate(c) + mirror([0,0, s>0 ? 1 : 0]) + rotate(s<0 ? -40 : 0) + hull() + for (x=[-20,20]) + for (y=[0, s * 10]) + translate([x,y]) + circle(hp_rn); + } +} + +module HingeLeverNutProfile(){ + for (c= [hppB, hppT]) { + translate(c) + circle($fn=6, r= 0.5 * hingescrew_nut_across / cos(30)); + } +} + +module Flip_hinge(doflip=1){ + hinge_origin = [0, -(phone_height - hppB[0]), hppB[1]]; + translate(hinge_origin) + rotate([doflip*180,0,0]) + translate(-hinge_origin) + children(); +} + +module HingePortion(x0,x1){ + Flip_rhs() Flip_bot(1) + translate([x0,0,0]) + mirror([1,0,0]) + rotate([90,0,-90]) + linear_extrude(height=x1-x0) + children(); +} + +module ThumbRecessApply(ztop){ + width = thumbrecess_width; + w = width + thumbrecess_topcurve_r*2 + 1; + translate([phone_width/2, 0,0]){ + difference(){ + rotate([90,0,-90]) + linextr(-w/2, w/2) + children(0); + translate([0, 50, 0]) + rotate([90,0,0]) + linear_extrude(height=100){ + for (m=[0,1]) mirror([m,0,0]) { + hull(){ + translate([w/2, ztop - thumbrecess_topcurve_r]) + circle(thumbrecess_topcurve_r); + translate([w/2, -50]) + square(thumbrecess_topcurve_r*2, center=true); + } + } + } + } + } +} + +module CaseBase(){ + AroundEdges(epp3[1], case_th_bottom, 1) + EdgeProfile(); +} + +function prop_x(gamma) = hp_k / (2 * sin(gamma/2)) - hppT[0]; + +module PropProfileAssignments(gamma){ + // https://en.wikipedia.org/wiki/Solution_of_triangles#Two_sides_and_the_included_angle_given_(SAS) + x = prop_x(gamma); + p = phone_height + prlp10[0] - hppB[0]; + b = p + x; + + q = phone_height - hppT[0] - prcp1[0]; // $prpp7[0] is 0 by definition + a = q + x; + c = sqrt(a*a + b*b - 2*a*b*cos(gamma)); + $prp_alpha = acos( (b*b + c*c - a*a) / (2*b*c) ); + + $prp_theta = 90 - $prp_alpha; + beta = 180 - $prp_alpha - gamma; + psi = 90 - beta; + + //echo("abc", a,b,c); + + v1 = [ [ cos(psi), -sin(psi) ], // x + [ sin(psi), cos(psi) ] ]; // y + + $prpp7 = [0, c + (lpp13[1] - $prpp10[1] - hp_k) ]; + + $prp_r1 = prc_r1; + $prp_r11 = prop_main_th/2; + + $prpp1 = $prpp7 + [1,0] * + // this is approximate, but will do + (prop_main_th/2 + prop_prop_gap + prcp1[0] - cppA[0]); + $prpp3 = $prpp1 + + v1[0] * -$prp_r1 + + v1[1] * ((prcp2[1] - prcp1[1]) - prop_prop_gap); + $prpp12 = $prpp3 + v1[0] * + (prop_end_dia + prop_caserecess_taper * ($prpp1[1] - $prpp3[1])); + $prp_r8 = prop_main_th; + $prpp4 = [ prop_main_th/2, $prpp3[1] ]; + $prp_r5 = $prp_r8; + $prpp5 = [ $prpp12[0] - $prp_r5, + $prpp3[1] - prop_prong_h + $prp_r5 ]; + $prpp6 = $prpp4 + [0,-1] * (prop_prong_h + + prop_prong_heel_slope * ($prpp5[0] - $prpp4[0])); + $prpp8 = $prpp4 + [0,-1] * $prp_r8; + $prpp9 = $prpp8 + [-1,0] * $prp_r8; + + children(); +} + +module PropProfile(gamma, cut=0, rot=0){ + PropProfileAssignments(gamma){ + + //#circleat($prpp3,1); + //#circleat($prpp12,1); + + if (!cut) { + hull(){ + translate($prpp8) + intersection(){ + circle($prp_r8); + polygon([[-20,-0], [20,20], [0,0]]); + } + rectfromto($prpp6, $prpp9); + translate($prpp5) intersection(){ + circle($prp_r5); + polygon([[-10,-10], [0,0], [10,0]]); + } + rectfromto($prpp12 + [0,-0.1], $prpp3); + } + hull(){ + circleat($prpp1, $prp_r1); + rectfromto($prpp12 + [0,-0.1], $prpp3); + } + } + // main shaft + rotate([0,0, rot*-$prp_theta]){ + hull(){ + extra = cut ? prop_recess_slop : 0; + rectfromto($prpp6, $prpp9); + circleat($prpp11, $prp_r11 + extra); + circleat($prpp10, $prp_r10 + extra); + } + } + } +} + +module PropAggregateProfile(){ + for (angle = prop_angles) + PropProfile(angle, 0,0); +} + +module Prop(){ ////toplevel + hw = prop_main_width/2; + linextr(-hw, +hw) + PropAggregateProfile(); +} + +module Case(){ ////toplevel + difference(){ + union(){ + CaseBase(); + + // ledge (fixed keeper) + Flip_rhs(1-keeper_side) intersection(){ + rotate([90, 0, 0]) + linear_extrude(height = phone_height + phone_cnr_rad * 2) + KeeperProfile(fatter=0, slant=1); + + // outline of the whole case, to stop it protruding + translate([0,0, -25]) + linear_extrude(height = 50) + hull() + Flip_bot() + circleat([+1,-1] * phone_cnr_rad, phone_cnr_rad + case_th_side/2); + } + + // hinge + if (!$suppress_hinge) + HingePortion(hex20, hex21) HingeBaseProfile(); + + // buildout for prop recess + if (prop_caserecess_buildout_r > 0) Flip_rhs(1) + linextr(case_bottom_z, epp2i[1]) + hull() { + for (dxs = [-1,+1]) + circleat([ prop_x_pos + dxs * prop_caserecess_buildout_r, + -epp2o[0] ], + r = epp2o[0] - prcp2[0]); + } + } + + // slot for keeper + Flip_rhs(keeper_side) + translate([0, -phone_cnr_rad, 0]) + rotate([90, 0, 0]) + linear_extrude(height = phone_height + phone_cnr_rad * 2) + minkowski(){ + KeeperProfile(fatter=keeper_fatter_hole); + rectfromto([ -keeper_gap_x, -keeper_gap_z_bot ], + [ keeper_gap_x_holes, +keeper_gap_z_top ]); + } + + // front camera + RearCameraAperture(); + + // struts (invisible, because they're buried in the case) + Struts(epp2i[0], epp2i[1] - case_th_bottom, case_th_bottom); + + Buttons(){ + mirror([1,0,0]) + rotate([90,0,90]) { + if (!($button_leg_only && enable_support)) + intersection(){ + translate([0,0,-10]) + linear_extrude(height= 20) + ButtonPlan($button_l, 0,1); + if ($button_leg_only) + rotate([-90,90,0]) + translate([phone_width/2, -400, kppe[1]]) + mirror([1-abs($rhsflip - keeper_side),0,0]) + cube([400, 800, 50]); + if (enable_support && !$button_suppress_over_keeper) + rotate([-90,90,0]) + translate([-400, -400, kppd[1]]) + mirror([0,0,1]) + cube([800,800,100]); + } + translate([0,0, -bppR[0]]) + linear_extrude(height= 20) + ButtonPlan($button_l, 1,1); + } + + } + + // apertures along top edge + if (!$suppress_forward_holes) { + // CaseAperture(jack_pos, jack_dia, 8); + Flip_rhs(1) + CaseAperture(noisecancelmic_pos, noisecancelmic_dia, 8); + } + CaseAperture(lhshole_pos, noisecancelmic_dia, 8, 1); + + OrdinaryBottomEdgeApertures(); + + OrdinaryRearApertures(); + NotInTestFrameRearApertures(); + + MicroUSBEtc(); + + // gaps for the lid's hinge arms + if (!$suppress_hinge) { + HingePortion(hex20 - hinge_x_arms_gap, + hex21 + hinge_x_arms_gap) + minkowski(){ + HingeLidProfile(); + circle(r= hinge_r_arms_gap, $fn= 8); + } + + // screw holes in the hinge arms + HingeScrews(); + } + + // thumb recess + ThumbRecessApply(epp4[1]) + ThumbRecessCutProfile(); + + // lanyard + Flip_bot(1) + translate([ly_o[0], -(phone_cnr_rad + ly_re), ly_o[1]]) + rotate([0, ly_theta, 0]) + rotate([0,0,90]) + LanyardCutout(lanyard_channel_len); + + // prop recess + Flip_rhs(1) + translate([prop_x_pos,0,0]) + mirror([0,1,0]) + rotate([90,0,90]) + linextr(-prop_recess_hw, +prop_recess_hw) + hull(){ + for (d=[ [0,0], [0,-1], [+1,-1/prop_caserecess_taper] ]) + circleat(prcp1 + 20*d, + prc_r3); + } + } +} + +module LidAdhocMultiprintFrame(phase){ + if (led_window_style >= 3) { + AdhocMultiprintFrame(phase, lpp13[1], -1); + } +} + +module LidAroundEdges(){ + AroundEdges(lpp15[1], lpp13[1] - lpp15[1], 0) + children(); +} + +module Lid(){ ////toplevel + skew_centre = [0, lpp11[0], lpp11[1]]; + difference(){ + union(){ + intersection(){ + LidAroundEdges() + LidEdgeProfile(); + + translate(skew_centre) + multmatrix([[ 1, 0, 0, 0 ], + [ 0, 1, -lid_fold_clearance_skew, 0 ], + [ 0, 0, 1, 0 ], + [ 0, 0, 0, 1 ]]) + translate(-skew_centre) + LidAroundEdges() + LidEdgeFoldClearanceProfile(); + } + + // button covers + Buttons(){ + intersection(){ + rotate([90,0,90]) + translate([0,0,-10]) + linear_extrude(height= 20) + ButtonPlan($button_l, 1,0); + union(){ + rotate([90,0,0]) + translate([0,0,-100]) + linear_extrude(height= 200) + ButtonCoverProfile(); + hull() + for (y= [-1,+1] * (($button_l + button_l_fudge)/2 + - lid_buttoncover_reinf)) + translate([0,y,0]) + ButtonCoverReinf(); + } + } + } + + // hinge arms + HingePortion(hex20, hex21) { + LidEdgeProfile(); + HingeLidProfile(); + } + } + Struts(lpp10[0] + strut_min_at_end, lpp13[1], -case_th_lid); + + // screw holes in the hinge arms + HingeScrews(); + + // prop recess + translate([prop_x_pos, -prlp10[0], prlp10[1]]) + mirror([0,1,0]) + rotate([90,0,90]) + linextr(-prop_recess_hw, +prop_recess_hw) + hull() + for (pa = prop_angles) + PropProfile(pa, 1,1); + + // notification led aperture + if (led_window_style) + translate([led_pos[0], -led_pos[1], lpp13[1]]) { + translate([0,0,-10]) + cylinder(r=nla_r0, h=20); + if (led_window_style >= 2) + translate([0,0, -nla_t]) + cylinder(r=nla_r2, height=20); + } + + } + + LidAdhocMultiprintFrame(1); +} + +module HingeLever(){ ////toplevel + difference() { + // outer body, positive + HingePortion(hex22, hex22 + phone_width/2) + HingeLeverOuterProfile(); + + // space for the screws +// HingePortion(hex26, hex24) +// HingeLeverInnerProfile(); + + // recesses for the nuts +// HingePortion(hex23, hex26+1) +// HingeLeverNutProfile(); + + // bores for the screws + HingeScrews(); + + // space for the charging cable and speaker and micc apertures + hull() { + for (x = [-1,+1]) { + multmatrix([[ 1,0, + + x + * ( (hex24 + hinge_over_nut_plate) - + (phone_width/2 - microusb_width/2) + ) + / ( (epp0[1] - microusb_above) + - + (hppB[1] - hp_r2) ), + + x * (epp0[1] - microusb_above) + + ], + [ 0,1,0, 0 ], + [ 0,0,1, 0 ]]) { + union(){ + MicroUSBEtc(); + Flip_hinge() MicroUSBEtc(); + } + } + } + } + } +} + +module LidWindow(){ ////toplevel + translate([led_pos[0], -led_pos[1], lpp13[1]]) + mirror([0,0,1]) + cylinder(r= nla_r1, h=nla_t); + LidAdhocMultiprintFrame(0); +} + +module LidWindowPrint(){ ////toplevel + rotate([0,180,0]) + LidWindow(); +} + +module DemoLidWindowSelect(){ + translate([led_pos[0], led_pos[1], -100]) { + translate([0, -30, 0]) cube([400, 400, 200]); + } +} + +module DemoLidWindow(){ ////toplevel + %Lid(); + LidWindow(); + translate([0,40,0]){ + color("blue") intersection(){ Lid(); DemoLidWindowSelect(); } + color("red") intersection(){ LidWindow(); DemoLidWindowSelect(); } + } +} + +module HingeLeverPrint(){ ////toplevel + rotate([-90,0,0]) + translate([-phone_width/2, phone_height, 0]) + HingeLever(); +} + +module TestSelectLength(){ + translate([-30, -200, -20]) + cube([30 + 15, 250, 40]); +} + +module TestLength(){ ////toplevel + intersection(){ + Case(); + TestSelectLength(); + } +} + +module TestLengthRight(){ ////toplevel + intersection(){ + Case(); + Flip_rhs(1) + TestSelectLength(); + } +} + +module TestSelectWidth(){ + translate([-30, -(phone_height - 25), -20]) + mirror([0, 1, 0]) + cube([200, 50, 40]); +} + +module TestWidth(){ ////toplevel + intersection(){ + Case(); + TestSelectWidth(); + } +} + +module TestLidWidthPrint(){ ////toplevel + rotate([0,180.0]) intersection(){ + Lid(); + TestSelectWidth(); + } +} + +module TestSelectRearAperture(){ + minkowski(){ + union() children(); + translate([20, 0,0]) + cube([42, 2, 1], center=true); + } +} + +module TestSelectCamera(){ + minkowski(){ + TestSelectRearAperture() + RearCameraAperture(); + cube([0.1, 50, 0.1]); + } +} + +module TestSelectOrdinaryRearApertures(){ + TestSelectRearAperture() + OrdinaryRearApertures(); +} + +module TestCamera(){ ////toplevel + intersection(){ + Case(); + TestSelectCamera(); + } +} + +module TestLidByCamera(){ ////toplevel + intersection(){ + Lid(); + TestSelectCamera(); + } +} + +module TestLidByCameraPrint(){ ////toplevel + rotate([180,0,0]) TestLidByCamera(); +} + +module DemoByCamera(){ ////toplevel + color("blue") TestLidByCamera(); + color("red") TestCamera(); +} + +module OneKeeper(){ ////toplevel + translate([0, -phone_cnr_rad, 0]) + rotate([90, 0, 0]) + linear_extrude(height = phone_height - phone_cnr_rad * 2) + KeeperProfile(fatter=keeper_fatter, stubbier=keeper_stubbier); +} + +module OneKeeperPrint(){ ////toplevel + rotate([0,180,0]) + OneKeeper(); +} + +module LidPrint(){ ////toplevel + rotate([0,180,0]) + Lid(); +} + +module TestSelectFrame(){ + include = [1,-1] * (epp2i[0] + 4); + + difference(){ + cube(1000, center=true); + translate([0,0, -100]) + linear_extrude(height=200) + rectfromto(include, inside_br - include); + } + + for (i= [1,2]) { + translate([ 0, -phone[1] * i/3, 0 ]) + cube(center=true, [1000, 4, 100]); + } +} + +module TestSelectLidFrame(){ + TestSelectFrame(); + if (len(led_pos)) + translate([led_pos[0], -led_pos[1], -50]) + cylinder(r= nla_r2+3, h=100); +} + +module TestFrameCase(){ ////toplevel + intersection(){ + Case(); + union(){ + TestSelectFrame(); + TestSelectCamera(); + TestSelectOrdinaryRearApertures(); + } + } +} + +module TestSelectTopApertures(){ + translate([-100, -35, -100]) + cube([400, 100, 200]); + LidAdhocMultiprintFrame(0); + LidAdhocMultiprintFrame(1); +} + +module TestTopApertures(){ ////toplevel + intersection(){ + Case(); + TestSelectFrame(); + TestSelectTopApertures(); + } +} + +module TestLidTopAperturesPrint(){ ////toplevel + rotate([0,180,0]) intersection(){ + Lid(); + TestSelectLidFrame(); + TestSelectTopApertures(); + } +} + +module TestLidWindowTopAperturesPrint(){ ////toplevel + rotate([0,180,0]) intersection(){ + LidWindow(); + TestSelectTopApertures(); + } +} + +module TestFrameLidPrint(){ ////toplevel + rotate([0,180,0]) intersection(){ + Lid(); + TestSelectLidFrame(); + } +} + +module ButtonPlanForDemo(z, deep, cut){ + translate([0,0,z]) + ButtonPlan(8, deep, cut); +} + +module HingeScrews(){ + Flip_rhs() Flip_bot(1){ + for (c= [ hppT, hppB ]) + translate([ hex20, + -c[0], + c[1] ]){ + rotate([0,90,0]) + translate([0,0,-.2]) + cylinder( r= hingescrew_shaft_dia/2, + h = hingescrew_shaft_len+0.2 ); + rotate([0,-90,0]) + translate([0,0,+.1]) + cylinder( r= hingescrew_head_dia/2, h = hingescrew_head_th ); + } + } +} + +module DemoPropAngleSelect(c){ + color(c) difference(){ + union(){ children(); } + translate([ prop_x_pos, -400, -200 ]) + cube([ 400,800,400 ]); + } +} + +module DemoPropAngle(ang){ + hL = [0, -(phone_height - hppT[0]), hppT[1] - hp_k*2]; + hC = [0, -(phone_height - hppB[0]), hppB[1]]; + + translate(hL) + rotate([ang/2,0,0]) + translate(-hL) + translate(hC) + rotate([ang/2,0,0]) + translate(-hC) { + DemoPropAngleSelect("red") Case(); + + color("orange") + translate([prop_x_pos, -prcp1[0], prcp1[1]]) + PropProfileAssignments(ang) { + echo($prpp1); + rotate([-$prp_theta, 0, 0]) + translate([0, $prpp1[0], -$prpp1[1]]) + rotate([90,0,-90]) + Prop(); + } + } + + translate([0,0, -hp_k*2]) + DemoPropAngleSelect("blue") + Lid(); +} + +module DemoPropAngles(){ ////toplevel + for (i=[0 : len(prop_angles)-1]) + translate(i * [0, -100, 100]) + DemoPropAngle(prop_angles[i]); +} + +module DemoHingeAngle(ang1,ang2){ + hL = [0, -(phone_height - hppT[0]), hppT[1]]; + hC = [0, -(phone_height - hppB[0]), hppB[1]]; + + translate(hL) + rotate([ang2,0,0]) + translate(-hL) + translate(hC) + rotate([ang1,0,0]) + translate(-hC) { + color("red") Lid(); + } + + color("blue") intersection(){ + Case(); + union(){ + translate([bppJ[0], -400, -200]) + mirror([1,0,0]) + cube([400, 800, 400]); + translate([10, -400, -200]) + cube([10, 800, 400]); + } + } +} + +module DemoHingeAngles(){ ////toplevel + angles = [ 0, 4, 8, 12 ]; + echo("angles",angles); + for (i=[0 : len(angles)-1]) { + translate(i * [0, 0, 30]) { + DemoHingeAngle(0,angles[i]); + translate([0, 200, 0]) + DemoHingeAngle(angles[i],0); + } + } +} + +module DemoSelectAdhocLeftRight(right=0) { + translate([phone_width/2, -400, -100]) // , -15, -100 to cross-section + mirror([1-right, 0,0]) + cube([400, 800, 200]); +} + +module DemoLeft(){ ////toplevel + color("red") intersection(){ Case(); DemoSelectAdhocLeftRight(); } + color("blue") intersection(){ Lid(); DemoSelectAdhocLeftRight(); } +} + +module DemoFrame(){ ////toplevel + color("red") render() TestFrameCase(); + color("blue") render() intersection(){ Lid(); TestSelectLidFrame(); } + color("black") render() HingeScrews(); + %render() HingeLever(); +} + +module DemoLanyardCutout(){ ////toplevel + LanyardCutout(25); +} + +module DemoHingedFrame(){ ///toplevel + color("red") render() TestFrameCase(); + translate([0,0, -2*hp_k]) + color("blue") render() intersection(){ Lid(); TestSelectLidFrame(); } + + Flip_hinge(){ + color("orange") render() HingeLever(); + color("black") render() HingeScrews(); + } +} + +module DemoHinge(){ ////toplevel + translate([ -0.5*phone_width, phone_height, hp_k*3 ]) { + DemoFrame(); + translate([0,0, -hp_k*3]) + DemoHingedFrame(); + } +} + +module DemoProfiles(){ ////toplevel + LidEdgeProfile(); + %EdgeProfile(); + KeeperProfile(); + translate([0,0,-1]) color("black") KeeperProfile(1); + translate(ly_o){ + rotate(-ly_theta){ + translate([0,0,+1]) color("purple") LanyardMainChannelProfile(); + translate([0,0,+2]) color("red") LanyardCurveChannelProfile(); + translate([0, ly_q_z]){ + translate([0,0,-1]) color("blue") LanyardEntryChannelProfile(); + translate([ly_oec_y,0,-2]) color("black") LanyardEntryOuterProfile(); + } + } + } + translate([0,0,-5]) color("white") translate(epp2i) + rotate(-ly_theta) + rectfromto([-15, 0], + [+15, -max_case_bottom_edge_thickness]); + + translate([0,20]) { + LanyardMainChannelProfile(); + translate([0,0,1]) color("purple") LanyardCurveChannelProfile(); + translate([0,0,-1]) color("red") LanyardEntryChannelProfile(); + } + + translate([20,0]) { + LidEdgeProfile(); + %EdgeProfile(); + + demopoint_QR = [ bppS[0], bppQ[1] - 0.1]; + + color("blue") ButtonCoverProfile(); + color("red") { + rectfromto(bppQ, demopoint_QR); + rectfromto(bppR, demopoint_QR); + } + } + + translate([-20,0]) { + color("black") ButtonPlanForDemo(-2, 0,1); + color("red" ) ButtonPlanForDemo(-4, 1,1); + color("blue") ButtonPlanForDemo(-6, 1,0); + } + + translate([0, -30]) { + %LidEdgeProfile(); + %EdgeProfile(); + color("blue") HingeLidProfile(); + color("red") HingeBaseProfile(); + color("black") translate([0,0,-2]) HingeLeverOuterProfile(); + } + + for (f=[0,1]) { + translate([-30, -60 + 30*f]) { + translate([0,0,-4]) EdgeProfile(); + %translate([0,0,-10]) HingeBaseProfile(); + translate([0,-2] * f * hp_k) { + translate([0,0,-4]) LidEdgeProfile(); + %translate([0,0,-10]) %HingeLidProfile(); + } + translate(+hppB) rotate([0,0,180*f]) translate(-hppB) { + translate([0,0,-2]) color("black") HingeLeverOuterProfile(); + translate([0,0,0]) color("red") difference(){ + HingeLeverOuterProfile(); + HingeLeverInnerProfile(); + } + translate([0,0,3]) color("yellow") HingeLeverNutProfile(); + } + } + } + + translate([20,-30]) { + %EdgeProfile(); + %LidEdgeProfile(); + //translate([0,0,1]) ThumbRecessCutProfile(); + translate([0,0,+1]) color("red") + difference(){ EdgeProfile(); ThumbRecessCutProfile(); } + } + + translate([40,-30]) { + difference(){ + LidEdgeProfile(); + translate(prlp10) + PropProfile(10, 1, 0); + } + translate(prlp10) + PropProfile(15, 0); + } + translate([60,-30]) { + PropAggregateProfile(); + } +} + +//EdgeProfile(); +//KeeperProfile(); +//CaseBase(); +//%Case(); +//Keeper(); +//LidEdgeProfile(); +//KeeperProfile(); +//DemoProfiles(); +//PropRecess();