From 1e9057c4647f1e524d48f8310804722c65c7c003 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Fri, 14 Jan 2022 19:10:46 +0000 Subject: [PATCH] fairphone4-case: clone, prep for hack Signed-off-by: Ian Jackson --- Makefile | 2 +- fairphone4-case.scad | 1755 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1756 insertions(+), 1 deletion(-) create mode 100644 fairphone4-case.scad diff --git a/Makefile b/Makefile index 384be3b..c55b91c 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 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-straphook powerbank-bike-clamp lipo-flat-mount $(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 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-straphook powerbank-bike-clamp lipo-flat-mount $(foreach x,500 1000,adafruit-powerboost-$x) AUTO_INCS += sealing-box.scad sewing-table.scad nutbox.scad \ powerbank-anker-10000.dxf \ diff --git a/fairphone4-case.scad b/fairphone4-case.scad new file mode 100644 index 0000000..2998b54 --- /dev/null +++ b/fairphone4-case.scad @@ -0,0 +1,1755 @@ +// -*- 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. Decide about the notification LED aperture. See the variable +// led_window_style, below. The default here is "ad-hoc +// multi-colour", which can produces a translucent (clear-ish) +// window set into the lid, even on a single-nozzle printer. +// See "Ad-hoc multi-colour", below. +// +// 3. use "make" to generate the necessary files: +// +// make -j8 fairphone-case.auto.scads `for f in \ +// HingePrint \ +// LidWindowPrint \ +// LidPrint \ +// OneKeeperPrint \ +// Case \ +// ; do echo fairphone-case,$f.auto.stl; done` +// +// 4. Print them. Case and OneKeeperPrint should probably be +// the same colour. +// +// For Lid and LidWindowPrint, if you are doing ad-hoc +// multi-colour: +// i. Set up for clear filament +// ii. Print LidWindowPrint. Wait for it to finish. +// It won't take long. As soon as it finishes, tell +// your printer to warm up (so that in fact it does +// not cool down). +// iii. Leaving the output so far on the printbed, reload +// your printer with the main lid colour. +// iv. Print LidPrint. You can let this go unattended. +// +// 5. Assemble the hinge. You will need 4x M2 12mm machine screws +// and 8x M2 full nuts. +// +// Make sure you get the hinge the right way round. If you're not +// sure, run +// openscad fairphone-case,DemoHinge.auto.scad +// to see an assembly diagram. +// +// The nuts recess into the hinge. You will want very fine +// pliers. As you screw each screw in, add the second nut when +// the screw thread emerges from the first - this will be a +// locknut. Screw each screw to an appropriate tightness for the +// hinge stiffness. You want the lid-side hinge to be stiffer as +// that makes closing the case work better. +// +// When you have the stiffness right, tighten the locknuts onto +// each first nut. +// +// 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 +// +// Ad-hoc multi-colour +// +// This file is set up to let you make a translucent window using a +// single-extruder printer, using a "two print run" technique. This +// works well with our Lulzbot TAZ 5 and Aleph Objects' version of +// Cura. If you are using a different printer, you may need to +// adjust the parameters or try a different technique. In particular, +// initial_layer_thick +// set so that the window is one layer thick +// initial_layer_width +// set so that the slicer draws a rectangle around the whole +// object, rather than putting a "skirt" or anything inside +// +// If you have a dual-extruder printer, you can set led_window_style +// to 2 and do a single print of LidPrint and LidWindowPrint. +// +// Alternatively you can set it to 1 (just a hole) or 0 (no hole). +// +// Thanks to Clare Boothby for the ad-hoc multi-colour technique (and +// the parameters for our Lulzbot TAZ 5 and Aleph Objects's Cura). +// +// 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 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.0, 145.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 = 3; +// 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 = 6.0; +phone_rim_depth = 0.80; // includes allowance for a screen protector + +button_cutout_depth = 9; + +phone_edge_thick = 9.0; +phone_total_thick = 12.0; +phone_backside_slope_inner = 1.5; // larger means shallower +phone_backside_slope_outer = 1.0; // larger means shallower + +camera_pos_tl = [ 6.450, 12.750 ]; // measured from tl corner +camera_pos_br = [ 22.300, 37.600 ]; // tl/br as seen from back + +jack_pos = [ 13.83, 8.485 ]; +jack_dia = 10.64 + .5; // some jack I had lying around + +led_pos = [ 13.98, 10.00 ]; +led_aperture = 9; +led_window_ledge = 0.75; // each side + +noisecancelmic_pos = [ 19.54, 7.37 ]; // from rhs +noisecancelmic_dia = 4.00; + +//fingerpushhole_dias = [ 15, 18 ]; +fingerpushhole_dias = []; + +lanyard_half_dia = 1.15; +lanyard_entry_rel_breadth = 2; +lanyard_channel_len = 15; +rearspeaker_pos_bl = [ 12.64, 18.72 ]; +rearspeaker_size = [ 3.76, 7.36 ]; + +microusb_above = 3.27 - 0.25; +microusb_below = 0.0; +microusb_width = 16.12 + 1.25; + +case_th_bottom = 2.5; +case_th_lid = 3.0; +case_th_side = 2.3; +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_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.65; + +foldover_gap = 0.50; +foldover_lever_gap = 0.50; + +// properties of the hinge fasteners +hingescrew_shaft_dia = 2.0 + 0.25; // M2 x 12mm machine screw +hingescrew_shaft_len = 12; +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; + +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; + +// ---------- calculated ---------- + +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; + +phone_backside_slope_thick = phone_total_thick - phone_edge_thick; + +//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; + + +//lid_lip_overlap_width xxx bad name = ; +//lid_lip_inner_slope = [ 5, 5 ]; // xxx + +epp0 = [0,0]; +epp1 = [0, -phone_edge_thick]; +epp2i = epp1 + phone_backside_slope_thick * [ phone_backside_slope_inner, -1 ]; +epp2o = epp1 + phone_backside_slope_thick * [ phone_backside_slope_outer, -1 ]; +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 = -atan2vector(epp2i - epp1); +ly_o = epp2i + 3 * ly_r * unitvector2d(epp1 - epp2i); + +max_case_bottom_edge_thickness = + case_th_bottom + + sin(ly_theta) * (epp2i-epp2o)[0]; + +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(slant=0){ + use_e = kppe + [0,-1] * slant * keeper_inner_width / keeper_slant_slope; + polygon([use_e, kppd, kppc, kppb, kppa, kppf]); +} + +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, + epp2i, + 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) { + theta = 180/$fn; + translate([ pos[0] + bumper[0], + -epp2i[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(15.580, +1, 8.9 ) children(); // power + Flip_rhs(1) SideButton(48.700, -1, 8.920 ) children(); // camera + Flip_rhs(0) SideButton(30.800, +1, 21.96, 1) children(); // volume + Flip_rhs( ) LidButtonishLeg(14, -1) children(); +// Flip_rhs(0) LidButtonishLeg(20, +1, 20) 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 MicroUSB(){ + 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 OrdinaryRearApertures(){ + // rear speaker + OrdinaryRearAperture(1,1, rearspeaker_pos_bl) + rectfromto(-rearspeaker_gap, + rearspeaker_size + rearspeaker_gap); + + // finger hole to remove phone + if (len(fingerpushhole_dias)) + OrdinaryRearAperture(1,0, [ fingerpushhole_dias[0]/2 + epp2i[0], + phone[1]/2 ]) + scale(fingerpushhole_dias) + circle(r= 0.5 ); +} + +module RearCameraAperture(){ + Flip_rhs(1) + mirror([0, 0, 1]) + linear_extrude(height = 20) + mirror([0, 1, 0]) + translate(bumper) + rectfromto(camera_pos_tl, camera_pos_br); +} + +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, epp2i, 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]) + 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(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 + 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(); + 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); + } + + OrdinaryRearApertures(); + + MicroUSB(); + + // gaps for the lid's hinge arms + 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 + MicroUSB(); + Flip_hinge() MicroUSB(); + } +} + +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(); +} + +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); + } +} + +module TestSelectLidFrame(){ + TestSelectFrame(); + 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") TestFrameCase(); + color("blue") intersection(){ Lid(); TestSelectLidFrame(); } + color("black") HingeScrews(); + %HingeLever(); +} + +module DemoLanyardCutout(){ ////toplevel + LanyardCutout(25); +} + +module DemoHingedFrame(){ ///toplevel + color("red") TestFrameCase(); + translate([0,0, -2*hp_k]) + color("blue") intersection(){ Lid(); TestSelectLidFrame(); } + + Flip_hinge(){ + color("orange") HingeLever(); + color("black") 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(); -- 2.30.2