Compare commits

...

38 Commits
v0.1 ... master

Author SHA1 Message Date
Dejvino
129f1890df Replaced platform locking with screws 2021-01-22 07:49:19 +01:00
Dejvino
5a274cdb03 Add screwhole to strap clip 2021-01-21 22:06:48 +01:00
Dejvino
3e5afb4ebd Fix clearances after first platform print 2021-01-21 21:14:44 +01:00
Dejvino
09336f1d0d Platform with a locking mechanism 2021-01-21 09:59:55 +01:00
Dejvino
58fc149bbe Extracted common code out 2021-01-20 15:55:17 +01:00
Dejvino
659c75c628 Platform with strap connectors 2021-01-20 15:24:25 +01:00
Dejvino
ebde72fab1 Refactor into specs scads 2021-01-20 12:56:26 +01:00
Dejvino
4d0931df73 Restructure files into src 2021-01-20 09:23:46 +01:00
Dejvino
f17455be4f Improved link strap interoperability 2021-01-19 14:47:16 +01:00
Dejvino
b85aee5fe1 Add closable fixed clip 2021-01-19 11:51:51 +01:00
Dejvino
a7c184e6cb Improved proportions and clearances 2021-01-18 21:04:14 +01:00
Dejvino
f1c4576519 Ratcheting strap clip 2021-01-18 19:03:27 +01:00
Dejvino
61dbc1e75a Improve linked strap parametrization 2021-01-17 22:33:57 +01:00
Dejvino
8b61f86eb6 Add terminal strap link 2021-01-17 17:04:12 +01:00
Dejvino
384480e824 Fixed linked strap parametrization 2021-01-17 16:49:08 +01:00
Dejvino
78e7a2b34a Add linked strap 2021-01-17 16:21:13 +01:00
Dejvino
eddd8f28e7 Deleting STLs 2021-01-15 12:51:31 +01:00
Dejvino
fd03cb0b30 Adjust shell size and back camera position after another print 2021-01-15 12:49:41 +01:00
Dejvino
bf2050ef08 Fix buttons. Update STLs 2021-01-12 16:54:41 +01:00
Dejvino
4f94f1d14c Add single-piece bottom model. Add dotted grill cutout for speaker and vents. 2021-01-12 10:00:07 +01:00
Dejvino
0295678c71 Update images and STLs 2021-01-11 22:46:08 +01:00
Dejvino
1c72dd1dbc Improved backplane. Adjusted rim thickness. 2021-01-11 22:00:51 +01:00
Dejvino
0c48f112a1 Add strap STLs 2021-01-11 20:10:41 +01:00
Dejvino
af395dbe35 Add harness STLs 2021-01-11 19:53:14 +01:00
Dejvino
ebf44a1045 Fix slider screw holes depth 2021-01-11 19:51:51 +01:00
Dejvino
7e5ecf72c2 Rounded screen cutout 2021-01-11 19:46:36 +01:00
Dejvino
9b15bc71dc Add buttons proxy. Moved bottom comps divider and changed rim rails. 2021-01-11 19:33:19 +01:00
Dejvino
b4ecb1c922 Fix readme image link 2021-01-11 18:03:37 +01:00
Dejvino
4d14bc667e Add back cover feature description 2021-01-11 18:02:25 +01:00
Dejvino
462a22943a Update image 2021-01-11 17:47:26 +01:00
Dejvino
d8329f5401 Add bottom component as a single sliding Print-in-Place model 2021-01-11 14:38:06 +01:00
Dejvino
118fe703b5 Adjust headphone cutout to slope in. Adjust USB-C cutout Z by 1mm. 2021-01-11 14:04:19 +01:00
Dejvino
383bcd084e Add secondary strap. Add preview model simplification. 2021-01-11 13:54:11 +01:00
Dejvino
6a6c5c57d2 Add strap 2021-01-02 10:43:54 +01:00
Dejvino
c259f393e3 Add backplane and rails with sliders 2020-12-31 23:51:48 +01:00
Dejvino
edb9f88fd9 Merge branch 'rugged' 2020-12-31 20:00:35 +01:00
Dejvino
0cee713849 Fix xray 2020-12-31 19:31:11 +01:00
Dejvino
85b27cd915 Rugged frame 2020-12-31 19:28:23 +01:00
17 changed files with 1052 additions and 218 deletions

View File

@ -8,6 +8,14 @@ Pip-Boy inspired phone holder built around the PINE64 PinePhone.
- OpenScad
- [BOSL library](https://github.com/revarbat/BOSL)
## Features
### PIP slideout back cover
The back cover is composed of two interconnected components, printed in place (PIP). This allows the cover to expand and retract again, automatically connecting any connectors fixed to the cover.
![Back model](feature_bottom_pip.png)
The inner construction makes sure the back cover remains sturdy and the two pieces are inseparable. See the "xray":
![Back model xray](feature_bottom_pip_xray.png)
## Build Instructions
See [Instructions Readme](instructions/README.md).

BIN
feature_bottom_pip.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
feature_bottom_pip_xray.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 92 KiB

218
main.scad
View File

@ -1,218 +0,0 @@
// dependency: https://github.com/revarbat/BOSL
include <BOSL/constants.scad>
use <BOSL/transforms.scad>
use <BOSL/shapes.scad>
use <BOSL/masks.scad>
// DEBUG:
//validation = 1;
//xray = 1;
// EXPORT:
MODEL_HARNESS_LEFT=1;
MODEL_HARNESS_RIGHT=2;
MODEL_HARNESS_TOP=3;
export=2; // [0:5]
function is_not_export() = is_undef(export) || export == 0;
function is_export() = !is_not_export();
function is_model_strict(m) = is_export() && export == m;
function is_model(m) = is_not_export() || export == m;
function get_phone_size() = [160, 76.7, 10];
rim_edge_thickness=0.8;
rim_edge_tolerance=0.6;
module phone() {
s=get_phone_size();
color("gray")
cuboid(s, fillet=5);
}
module arm() {
$fn=6;
s=[210, 55, 50];
color("SaddleBrown")
down(s.z/2 + get_phone_size().z/2) cuboid(s, fillet=20);
}
module phone_harness() {
ops=get_phone_size();
ps=[ops.x+0.2, ops.y+0.2, ops.z+0.2];
w=1;
f=2;
rf=5;
hs=[ps.x+w*2, ps.y+w*2, ps.z+w*2];
module shell() {
$fn = $preview ? 6 : 30;
difference() {
cuboid(hs, fillet=f);
cuboid(ps, fillet=f);
}
shell_rim();
}
module shell_rim() {
$fn = $preview ? 6 : 30;
w=2;
s=[hs.x+2*w, hs.y+2*w, 6];
intersection() {
difference() {
cuboid(s, fillet=rf, edges=EDGES_Z_ALL);
cuboid(ps, fillet=f, edges=EDGES_Z_ALL);
}
cuboid(s, fillet=1);
}
}
module shell_rim_edge(thickness, groove, tolerance=rim_edge_tolerance) {
$fn = $preview ? 6 : 30;
w=thickness + (groove ? tolerance : 0);
offset=(groove ? 0 : tolerance/2);
res=[hs.x+2*(w+offset), hs.y+2*(w+offset), 4.5];
resi=[hs.x+2*offset, hs.y+2*offset, res.z+1];
up(0.5) difference() {
intersection() {
cuboid(res, fillet=rf, edges=EDGES_Z_ALL);
cuboid(res, fillet=(groove?1:2), edges=EDGES_BOTTOM);
}
cuboid(resi, fillet=rf, edges=EDGES_Z_ALL);
}
}
module screen_cutout() {
s=[138, 69, 10];
up(s.z/2) cuboid(s, fillet=3, edges=EDGES_Z_ALL);
}
top_offset=6.5;
module speaker_cutout() {
s=[3, 12, 10];
right(ps.x/2 - top_offset) up(ps.z/2) cuboid(s, fillet=1);
}
module front_camera_cutout() {
$fn = $preview ? 6 : 30;
d=5;
s=[d, d, 10];
right(ps.x/2 - top_offset) fwd(11) cylinder(d=s.x, h=s.z);
}
module leds_cutout() {
s=[4, 14, 10];
right(ps.x/2 - top_offset) up(ps.z/2) back(23) cuboid(s, fillet=1);
}
module top_cutout() {
screen_cutout();
speaker_cutout();
front_camera_cutout();
leds_cutout();
}
module back_camera_cutout() {
s=[13, 25, 10];
right(ps.x/2 - 8) fwd(ps.y/2 - 23) down(ps.z/2) cuboid(s, fillet=6, edges=EDGES_Z_ALL);
}
module bottom_cutout() {
back_camera_cutout();
}
module headphones_cutout() {
$fn = $preview ? 10 : 30;
d=9;
p=[ps.x/2, ps.y/2-24, -1];
h=10;
translate(p) yrot(90) down(h/2) cylinder(h=h, d=d);
}
module right_cutout() {
headphones_cutout();
}
module usbc_cutout() {
s=[10, 20, 6];
left(ps.x/2) down(2) cuboid(s, fillet=1);
}
module left_cutout() {
usbc_cutout();
}
module buttons_cutout() {
s=[42, 10, 5];
fwd(ps.y/2) right(ps.x/2 - 44.5) cuboid(s, fillet=1);
}
module front_cutout() {
buttons_cutout();
}
module shell_cutouts() {
top_cutout();
bottom_cutout();
left_cutout();
right_cutout();
front_cutout();
}
divider=[30, 0, 2.3];
module model_harness_left() {
intersection() {
intersection() {
translate(divider) downcube([1000, 1000, 1000]);
translate(divider) leftcube([1000, 1000, 1000]);
}
difference() {
shell();
shell_rim_edge(rim_edge_thickness, groove=true, tolerance=rim_edge_tolerance);
shell_cutouts();
}
}
}
module model_harness_right() {
intersection() {
intersection() {
translate(divider) downcube([1000, 1000, 1000]);
translate(divider) rightcube([1000, 1000, 1000]);
}
difference() {
shell();
shell_rim_edge(rim_edge_thickness, groove=true, tolerance=rim_edge_tolerance);
shell_cutouts();
}
}
}
module model_harness_top() {
intersection() {
translate(divider) upcube([1000, 1000, 1000]);
difference() {
shell();
shell_cutouts();
}
}
difference() {
shell_rim_edge(rim_edge_thickness, groove=false);
shell_cutouts();
}
}
if (is_model(MODEL_HARNESS_LEFT)) model_harness_left();
if (is_model(MODEL_HARNESS_RIGHT)) model_harness_right();
if (is_model(MODEL_HARNESS_TOP)) model_harness_top();
}
module model() {
phone_harness();
}
if (!is_undef(validation)) {
intersection() {
phone();
model();
}
} else {
intersection() {
if (!is_undef(xray)) {
span_cube([0,1000*xray], [0,1000*xray], [-1000,1000]);
}
union() {
if (is_not_export()) {
//phone();
//arm();
}
model();
}
}
}

22
src/common.scad Normal file
View File

@ -0,0 +1,22 @@
use <BOSL/shapes.scad>
include <BOSL/constants.scad>
module cubi(s, fillet=0, edges=EDGES_ALL) {
if ($preview) {
cube(s, center=true);
} else {
cuboid(s, fillet=fillet, edges=edges);
}
}
module screwhole(d, h, head=true) {
$fn=$preview ? 5 : 20;
translate([0, 0, h]) cylinder(d=d*1.5, h=d*0.5);
split=h*0.5;
translate([0, 0, split]) cylinder(d=d, h=(h-split));
cylinder(d2=d, d1=d*0.1, h=split);
cylinder(d2=d*0.6, d1=d*0.6, h=split/2);
}
function vec_sum(v, w) = [v.x + w.x, v.y + w.y, v.z + w.z];

32
src/export.scad Normal file
View File

@ -0,0 +1,32 @@
/*
export=0; // <-- prepend to the main file to activate the export
*/
function is_not_export() = is_undef(export) || export == 0;
function is_export() = !is_not_export();
function is_model_strict(m) = is_export() && export == m;
function is_model(m) = is_not_export() || export == m;
MODEL_DEMO=0;
// Harness
MODEL_HARNESS_LEFT=101;
MODEL_HARNESS_RIGHT=102;
MODEL_HARNESS_BOTTOM=103;
MODEL_HARNESS_BOTTOM_PIP=104;
MODEL_HARNESS_TOP=105;
MODEL_HARNESS_SLIDER_FRONT=106;
MODEL_HARNESS_SLIDER_BACK=107;
MODEL_HARNESS_BUTTONS=108;
// Platform
MODEL_PLATFORM_HARNESS=201;
MODEL_PLATFORM_STRAP=202;
// Strap
MODEL_LINKS=301;
MODEL_CLIP_A=302;
MODEL_CLIP_B=303;
MODEL_CLIP_RATCHETING=304;
MODEL_CLIP_RATCHETING_A=305;
MODEL_CLIP_RATCHETING_B=306;

425
src/harness.scad Normal file
View File

@ -0,0 +1,425 @@
include <export.scad>
// dependency: https://github.com/revarbat/BOSL
include <BOSL/constants.scad>
use <BOSL/transforms.scad>
use <BOSL/shapes.scad>
use <BOSL/masks.scad>
use <BOSL/sliders.scad>
use <specs_phone.scad>
use <specs_harness.scad>
use <specs_platform.scad>
// DEBUG:
//validation = 1;
XRAY_POSITIVE=1;
XRAY_BACKPLANE=2;
XRAY_BOTTOM=3;
//xray = 2;
function use_slideout_backplane() = is_model_strict(MODEL_HARNESS_BOTTOM_PIP) || is_model_strict(MODEL_HARNESS_LEFT) || is_model_strict(MODEL_HARNESS_RIGHT);
backplane_pip_slideout=10;
use <common.scad>
module dotted_grill_cutout(vents_area, vents_x=10, vents_y=10, mesh=1.5, cut_corners=false) {
function is_corner(x, y, xm, ym) = (x == 0 || x == xm) && (y == 0 || y == ym);
vent_size=[vents_area.x/vents_x-mesh, vents_area.y/vents_y-mesh, vents_area.z];
translate([-vents_area.x/2 - vent_size.x/2, vents_area.y/2 - vent_size.y/2, 0])
for (x=[0:vents_x-1]) {
for (y=[0:vents_y-1]) {
if (!cut_corners || !is_corner(x, y, vents_x-1, vents_y-1)) {
right(x*(vent_size.x+mesh))
fwd(y*(vent_size.y+mesh))
cubi(vent_size, fillet=min(vent_size.x/2, vent_size.y/2));
}
}
}
}
module phone() {
s=get_phone_size();
color("gray")
cubi(s, fillet=5);
}
module arm() {
$fn=6;
s=[210, 55, 50];
color("SaddleBrown")
down(s.z/2 + get_phone_size().z/2) cubi(s, fillet=20);
}
module phone_harness() {
ops=get_phone_size();
ps=[ops.x+0.2, ops.y+0.2, ops.z+0.2];
shell_wall=3;
shell_rim_wall=3;
rim_edge_thickness=1.5;
rim_edge_tolerance=0.4;
expose_back_camera=true;
screwmount_screw=3;
harness_divider=[1, 0, 1.3];
harness_size=[ps.x+shell_wall*2, ps.y+shell_wall*2, ps.z+shell_wall*2];
rim_size=[harness_size.x+2*shell_rim_wall, harness_size.y+2*shell_rim_wall, 8];
w=shell_wall;
f=2;
rf=5;
hs=harness_size;
backplane_rim_size=[40, ps.y*0.95, shell_wall/2];
backplane_teeth=6;
// ==== SHELL ====
module shell() {
$fn = $preview ? 6 : 30;
difference() {
cubi(hs, fillet=f);
cubi(ps, fillet=f);
}
shell_rim();
shell_screwmounts();
}
module shell_rim() {
$fn = $preview ? 6 : 30;
w=shell_rim_wall;
s=rim_size;
intersection() {
difference() {
cubi(s, fillet=rf, edges=EDGES_Z_ALL);
cubi(ps, fillet=f, edges=EDGES_Z_ALL);
}
cubi(s, fillet=1);
}
rail_extension=10;
fwd(hs.y/2) xrot(90) harness_rail(slider_len_front+rail_extension);
back(hs.y/2) xrot(-90) harness_rail(slider_len_back+rail_extension);
}
module shell_rim_edge(thickness, groove, tolerance=rim_edge_tolerance) {
$fn = $preview ? 6 : 30;
w=thickness + (groove ? tolerance : 0);
offset=(groove ? 0 : tolerance/2);
res=[hs.x+2*(w+offset), hs.y+2*(w+offset), 3];
resi=[hs.x+2*offset, hs.y+2*offset, res.z+1];
up(0.0) difference() {
intersection() {
cubi(res, fillet=rf, edges=EDGES_Z_ALL);
cubi(res, fillet=(groove?1:1.5), edges=EDGES_BOTTOM);
}
cubi(resi, fillet=rf, edges=EDGES_Z_ALL);
}
}
module foreach_screwmount() {
for(x = [0:1]) for (y = [0:1]) {
translate([(-0.5+x)*ps.x, (-0.5+y)*ps.y, -hs.z/2])
children();
}
}
module shell_screwmounts() {
$fn = $preview ? 6 : 30;
r=3;
foreach_screwmount() {
cylinder(r=r, h=hs.z);
}
}
module shell_screwmounts_holes() {
$fn = $preview ? 6 : 30;
foreach_screwmount() {
up(w) cylinder(d2=screwmount_screw, d1=screwmount_screw*0.5, h=hs.z+1);
up(hs.z-1) cylinder(d=screwmount_screw+1.5, h=5);
}
}
module shell_backplane(groove) {
module groove_scale() { scale([groove?1.05:1, groove?1.05:1, groove?1.2:1]) children(); }
if (use_slideout_backplane()) {
down(hs.z/2 - shell_wall/3) up(backplane_rim_size.z/2) right(harness_divider.x) {
back(backplane_rim_size.y/2) {
teeth=backplane_teeth;
segment=backplane_rim_size.y/teeth;
tooth_size=[backplane_rim_size.x, segment/2, backplane_rim_size.z];
joiner_base=5;
joiner_size=[joiner_base + (groove?backplane_pip_slideout:0), segment*1.5, backplane_rim_size.z];
for (i = [0:teeth-1]) {
fwd(i*segment) fwd(tooth_size.y) {
groove_scale() cubi(tooth_size, fillet=0.5, edges=EDGES_X_ALL);
if (i%2 == 0) {
left(backplane_rim_size.x/2 + joiner_base/2) fwd(joiner_size.y/3) left(-joiner_size.x/2) groove_scale() cubi(joiner_size, fillet=2, edges=EDGES_Z_ALL);
}
}
}
}
}
}
}
// ==== SHELL SLIDER AND RAIL ====
rail_angle=36;
rail_wall=1;
slider_slop=0.1;
slider_len_back=hs.x*0.3;
slider_len_front=hs.x*0.15;
module harness_slider(l) {
base=2;
ss=[l, shell_rim_wall, rim_size.z+rail_wall*2];
up(ss.y + base) xrot(180) difference() {
slider(l=ss.x, w=ss.z, h=ss.y, base=base, chamfer=0.9, wall=2, ang=rail_angle, slop=slider_slop, orient=ORIENT_X, align=V_UP);
screws=2;
screws_area_l=0.7*l;
for (i=[0:screws-1]) {
$fn=$preview ? 6 : 16;
right(-screws_area_l/2 + screws_area_l/(screws-1) * i) cylinder(d2=screwmount_screw*0.5, d1=screwmount_screw, h=ss.y*0.7);
}
}
}
module harness_rail(l) {
sw=2;
ss=[l, shell_rim_wall, rim_size.z+rail_wall*2];
rail(l=ss.x, w=ss.z, h=ss.y, chamfer=0.5, ang=rail_angle, orient=ORIENT_X, align=V_UP);
}
// ==== SHELL CUTOUTS ====
module shell_cutouts() {
top_cutout();
bottom_cutout();
left_cutout();
right_cutout();
front_cutout();
shell_screwmounts_holes();
}
// -- TOP --
module screen_cutout() {
$fn=$preview ? 4 : 12;
s=[138, 69, 12];
b = 10;
s1=[s.x, s.y];
s2=[s.x+b, s.y+b];
h=s.z;
up(5) rounded_prismoid(size1=s1, size2=s2, h=h, r1=1, r2=15);
}
top_offset=6.5;
module top_speaker_cutout() {
s=[3, 12, 100];
right(ps.x/2 - top_offset) up(s.z/2 - ps.z/3) cubi(s, fillet=1);
}
module front_camera_cutout() {
$fn = $preview ? 6 : 30;
d=5;
s=[d, d, 100];
right(ps.x/2 - top_offset) fwd(11.5) cylinder(d=s.x, h=s.z);
}
module leds_cutout() {
s=[4, 14, 100];
right(ps.x/2 - top_offset) up(s.z/2 - ps.z/3) back(23) cubi(s, fillet=1);
}
module top_cutout() {
screen_cutout();
top_speaker_cutout();
front_camera_cutout();
leds_cutout();
}
// -- BOTTOM --
module back_camera_cutout() {
s=[13, 25, expose_back_camera ? 100 : 11];
right(ps.x/2 - 7.5) fwd(ps.y/2 - 24.5) down(s.z/2 - ps.z/3) cubi(s, fillet=6, edges=EDGES_Z_ALL);
}
module bottom_speaker_cutout() {
s=[8, 49, 100];
left(ps.x/2 - 13)
down(s.z/2 - ps.z/3)
dotted_grill_cutout(s, vents_x=4, vents_y=8, cut_corners=true, mesh=1);
}
module bottom_vents_cutout() {
vents_x=10;
vents_y=6;
vents_area=[35, 50, 10];
vents_pos=[45, 0, -ps.z/2];
mesh=1.5;
translate(vents_pos)
dotted_grill_cutout(vents_area, vents_x=vents_x, vents_y=vents_y, mesh=mesh, cut_corners=true);
}
module bottom_cutout() {
back_camera_cutout();
bottom_speaker_cutout();
bottom_vents_cutout();
}
// -- RIGHT --
module headphones_cutout() {
$fn = $preview ? 10 : 30;
d1=8.8;
d2=14;
p=[ps.x/2, ps.y/2-24, -0.88];
h=22;
translate(p) yrot(90) down(10) cylinder(h=h, d1=d1, d2=d2);
}
module right_cutout() {
headphones_cutout();
}
// -- LEFT --
module usbc_cutout() {
s=[20, 20, 7];
left(ps.x/2) down(2) cubi(s, fillet=1);
}
module left_cutout() {
usbc_cutout();
}
// -- FRONT --
buttons_cutout_size=[42, 100, 4.5];
buttons_cutout_pos=[ps.x/2 - 44.5, -ps.y/2 - 3, 0.25];
module buttons_cutout() {
s=buttons_cutout_size;
p=buttons_cutout_pos;
translate(p) cubi(s, fillet=1);
}
module front_cutout() {
buttons_cutout();
}
module front_buttons(socket) {
s=[buttons_cutout_size.x-2, 4, buttons_cutout_size.z-0.5];
p=buttons_cutout_pos;
t=socket ? 0.2 : 0;
pin_size=[3 + t, 3 + t, s.z+2 + t/2];
touch_size=[2, s.y, 2];
touch_power_size=[3, touch_size.y, touch_size.z];
translate(p) {
cs=[4, s.y, s.z];
left(5) {
bs=[13, s.y, s.z];
left(bs.x/6) cubi(pin_size);
left(bs.x/2) {
difference() {
cubi(bs);
back(1) cubi(cs);
}
}
fwd(1) left(bs.x*0.8) cubi(touch_power_size, fillet=1);
}
right(8) {
bs=[18, s.y, s.z];
cubi(pin_size);
difference() {
cubi(bs);
back(1) {
left(bs.x/4) cubi(cs);
right(bs.x/4) cubi(cs);
}
}
fwd(1) {
left(bs.x*0.4) cubi(touch_size, fillet=1);
right(bs.x*0.4) cubi(touch_size, fillet=1);
}
}
}
}
// ==== MODEL ====
divider=harness_divider;
module model_harness_left() {
intersection() {
intersection() {
translate(divider) downcube([1000, 1000, 1000]);
translate(divider) leftcube([1000, 1000, 1000]);
}
difference() {
shell();
shell_rim_edge(rim_edge_thickness, groove=true, tolerance=rim_edge_tolerance);
shell_cutouts();
shell_backplane(groove=true);
}
}
}
module model_harness_right() {
intersection() {
intersection() {
translate(divider) downcube([1000, 1000, 1000]);
translate(divider) rightcube([1000, 1000, 1000]);
}
difference() {
shell();
shell_rim_edge(rim_edge_thickness, groove=true, tolerance=rim_edge_tolerance);
shell_cutouts();
front_buttons(socket=true);
}
}
shell_backplane(groove=false);
}
module model_harness_top() {
intersection() {
translate(divider) upcube([1000, 1000, 1000]);
difference() {
shell();
shell_cutouts();
front_buttons(socket=true);
}
}
difference() {
shell_rim_edge(rim_edge_thickness, groove=false);
shell_cutouts();
front_buttons(socket=true);
}
}
module model_harness_slider_front() {
difference() {
fwd(hs.y/2) xrot(90) harness_slider(slider_len_front);
front_cutout();
}
}
module model_harness_slider_back() {
back(hs.y/2) xrot(-90) harness_slider(slider_len_back);
}
module model_harness_buttons() {
front_buttons(socket=false);
}
if (is_model(MODEL_HARNESS_LEFT)) model_harness_left();
if (is_model(MODEL_HARNESS_RIGHT)) model_harness_right();
if (is_model_strict(MODEL_HARNESS_BOTTOM)) {
model_harness_left();
model_harness_right();
}
if (is_model_strict(MODEL_HARNESS_BOTTOM_PIP)) {
model_harness_left();
right(backplane_pip_slideout) model_harness_right();
}
if (is_model(MODEL_HARNESS_TOP)) model_harness_top();
if (is_model(MODEL_HARNESS_SLIDER_FRONT)) model_harness_slider_front();
if (is_model(MODEL_HARNESS_SLIDER_BACK)) model_harness_slider_back();
if (is_model(MODEL_HARNESS_BUTTONS)) model_harness_buttons();
}
module model() {
up(get_harness_size().z/2) phone_harness();
}
if (!is_undef(validation)) {
intersection() {
phone();
model();
}
} else {
intersection() {
if (!is_undef(xray)) {
if (xray == XRAY_POSITIVE) {
span_cube([0,1000], [0,1000], [-1000,1000]);
} else if (abs(xray) == XRAY_BACKPLANE) {
span_cube([-1000,1000], [-1000,1000], [-1000*sign(xray),-6]);
} else if (xray == XRAY_BOTTOM) {
span_cube([-1000,1000], [-1000,1000], [-1000,0]);
}
}
union() {
if (is_not_export()) {
//phone();
//arm();
}
model();
}
}
}

6
src/main.scad Normal file
View File

@ -0,0 +1,6 @@
export=0;
include <export.scad>
include <harness.scad>
include <platform.scad>
include <strap.scad>

134
src/platform.scad Normal file
View File

@ -0,0 +1,134 @@
include <export.scad>
//export=MODEL_PLATFORM_HARNESS;
//export=MODEL_PLATFORM_STRAP;
// dependency: https://github.com/revarbat/BOSL
include <BOSL/constants.scad>
use <BOSL/transforms.scad>
use <BOSL/shapes.scad>
use <BOSL/masks.scad>
use <BOSL/sliders.scad>
use <specs_platform.scad>
use <specs_strap.scad>
use <common.scad>
use <strap_common.scad>
use <platform_common.scad>
// DEBUG:
//validation = 1;
//xray = 1;
// PROP >>
module prop_arm() {
$fn=$preview ? 10 : 30;
arm_size=[200, 55, 40];
color("gray")
down(arm_size.z/2) {
cuboid(arm_size, fillet=16, edges=EDGES_X_ALL);
left(arm_size.x*0.6) scale([1, 1, 0.8]) yrot(90) cylinder(d1=arm_size.y*1.4, d2=arm_size.y*0.95, h=arm_size.x/2);
}
}
module model_prop_arm() {
if (is_not_export()) down(3) prop_arm();
}
// <<<
// Blocks:
module platform(base) {
size=get_platform_size();
base_size=size;
wall=get_platform_wall();
top_size=get_platform_top_size();
top=!base;
module platform_screwholes() {
module screwhole_one() {
fwd(base_size.y/2) xrot(90) down(get_platform_screwhole_h()) screwhole(d=get_platform_screwhole_d(), h=get_platform_screwhole_h());
}
module screwhole_sided() {
offset=16;
left(offset) screwhole_one();
right(offset) screwhole_one();
}
up(top_size.z/5) {
screwhole_sided();
scale([1,-1,1]) screwhole_sided();
}
}
module base_platform_cutout_for_top() {
base_top_diff=[base_size.x-top_size.x, base_size.y-top_size.y];
guiding=1;
top_slack=[0.2, 0.2, 0];
translate(get_platform_top_pos()) {
cube(vec_sum(top_size, top_slack), center=true);
down(top_size.z/2 - 2)
prismoid(size1=[top_size.x,top_size.y], size2=[top_size.x+base_top_diff.x*guiding,top_size.y+base_top_diff.y*guiding], h=top_size.z);
}
}
module base_platform() {
module base_platform_core() {
braced_thinning_wall(h=size.y, l=size.x, thick=size.z, wall=0, strut=wall, orient=ORIENT_X_90);
}
difference() {
base_platform_core();
base_platform_cutout_for_top();
platform_screwholes();
}
}
module top_platform() {
difference() {
up((base_size.z-top_size.z)/2)
up(top_size.z/2) intersection() {
scale([1,1,4]) braced_thinning_wall(h=top_size.y, l=top_size.x, thick=top_size.z, wall=0, strut=wall/4, orient=ORIENT_X_90);
down(top_size.z/2) scale([2,2,1]) cube(top_size, center=true);
}
platform_screwholes();
}
}
module strap_connectors() {
//down(size.z/2)
difference() {
foreach_platform_strap_connector() {
link_connector_pin();
}
base_platform_cutout_for_top();
}
}
color(top?"Olive":"YellowGreen")
if (base) {
base_platform();
strap_connectors();
} else {
top_platform();
}
}
// Models:
module model_platform_harness() {
platform(base=false);
}
module model_platform_strap() {
platform(base=true);
}
// Validation:
/*intersection() {
model_platform_harness();
model_platform_strap();
}*/
// Export:
difference() {
union() {
if (is_model(MODEL_PLATFORM_HARNESS)) model_platform_harness();
if (is_model(MODEL_PLATFORM_STRAP)) model_platform_strap();
//if (is_model(MODEL_DEMO)) model_prop_arm();
}
// xray:
//left(500+5) cube([1000, 1000, 1000], center=true);
}

21
src/platform_common.scad Normal file
View File

@ -0,0 +1,21 @@
// dependency: https://github.com/revarbat/BOSL
include <BOSL/constants.scad>
use <BOSL/transforms.scad>
use <BOSL/shapes.scad>
use <BOSL/masks.scad>
use <BOSL/sliders.scad>
use <specs_platform.scad>
use <specs_strap.scad>
use <strap_common.scad>
module foreach_platform_strap_connector() {
offset=get_platform_strap_connector_offset();
for (i=[0:3]) {
translate([((i%2*(-2)+1) * offset.x), -((round(i/2)%2*(-2)+1) * offset.y), offset.z])
zrot(90 + (round(i/2)%2*180))
children();
}
}

9
src/specs_harness.scad Normal file
View File

@ -0,0 +1,9 @@
use <specs_phone.scad>
// TODO
function get_harness_size() = [
get_phone_size().x + 10,
get_phone_size().y + 10,
get_phone_size().z + 10,
];

2
src/specs_phone.scad Normal file
View File

@ -0,0 +1,2 @@
function get_phone_size() = [161.2, 77.2, 10];

22
src/specs_platform.scad Normal file
View File

@ -0,0 +1,22 @@
use <specs_phone.scad>
use <specs_strap.scad>
function get_platform_size() = [
get_phone_size().x - 30,
get_phone_size().y - 5,
get_platform_wall()*3
];
function get_platform_top_size() = [
get_platform_size().x - 2*get_platform_wall(),
get_platform_size().y - 2*get_platform_wall(),
get_platform_size().z - get_platform_wall()];
function get_platform_top_pos() = [0, 0, (get_platform_size().z-get_platform_top_size().z)/2];
function get_platform_wall() = 3;
function get_platform_strap_connector_offset() = [
(get_platform_size().x/2 - get_link_segment_size().y/2) - 3,
get_platform_size().y/2 + 4,
-get_link_segment_size().z/2 + 0.5
];
function get_platform_screwhole_d() = 3.2;
function get_platform_screwhole_h() = 10;

11
src/specs_strap.scad Normal file
View File

@ -0,0 +1,11 @@
function get_link_segment_size() = [15, 30, 5];
function get_link_pin_diameter() = 2;
function get_link_socket_slack() = [1, 4, 1];
function get_link_joiner_arm_size() = [get_link_segment_size().x - 2, 3, get_link_segment_size().z];
function get_link_socket_size() = [get_link_segment_size().z,
get_link_segment_size().y - get_link_joiner_arm_size().y*2,
get_link_segment_size().z];
function get_link_clip_size() = [get_link_pin_diameter()*5, get_link_segment_size().y - 5, get_link_segment_size().z];
function get_link_socket_roundgap() = 2;

292
src/strap.scad Normal file
View File

@ -0,0 +1,292 @@
links_count=2; // [1..20]
include_terminal=true; // [true,false]
include_screwhole=true; // [true,false]
clip_screwhole=2;
include <export.scad>
//export=MODEL_LINKS;
//export=MODEL_CLIP_A;
//export=MODEL_CLIP_B;
//export=MODEL_CLIP_RATCHETING_A;
//export=MODEL_CLIP_RATCHETING_B;
use <BOSL/transforms.scad>
use <BOSL/shapes.scad>
include <BOSL/constants.scad>
use <specs_strap.scad>
use <specs_platform.scad>
use <common.scad>
use <strap_common.scad>
use <platform_common.scad>
if (is_model_strict(MODEL_LINKS)) {
echo("===============================");
echo(str("Strap length: ", (links_count * get_link_segment_size().x), " mm"));
echo("===============================");
}
assert(get_link_segment_size().x >= 10, "Link segment is too short.");
assert(get_link_segment_size().z >= (get_link_pin_diameter() + get_link_socket_slack().z + 1), "Link segment is too thin.");
module link(terminal=false) {
$fn=$preview?4:20;
module link_joiner() {
gap=get_link_socket_roundgap();
segment_size=get_link_segment_size();
arm_size=get_link_joiner_arm_size();
module pin_to_socket_joiner() {
joiner_size=[segment_size.x-get_link_socket_size().x+get_link_socket_size().x*0.2,
segment_size.y-4,
segment_size.z];
difference() {
right(joiner_size.x/2 + 4.5) cuboid(joiner_size, fillet=1, edges=EDGES_Z_ALL + EDGES_BOTTOM);
right(segment_size.x) scale([1, 2, 1]) pin_socket_area();
}
}
pin_to_socket_joiner();
}
link_connector_pin();
if (terminal) {
right(get_link_segment_size().x + 2) zrot(180) link_connector_pin();
} else {
link_joiner();
right(get_link_segment_size().x) link_connector_socket();
}
}
module link_chain(links_count, include_terminal=true) {
for (i = [0:links_count-1]) {
terminal=(i==links_count-1) && include_terminal;
right(i*get_link_segment_size().x) link(terminal=terminal);
}
}
module clip() {
$fn=$preview?4:20;
socket_size=get_link_socket_size();
clip_size=get_link_clip_size();
pin_diam=get_link_pin_diameter();
slot_slack=get_link_socket_slack();
size=[clip_size.x+socket_size.x, clip_size.y, clip_size.z];
slot_size=[clip_size.x+slot_slack.x, size.y+1, pin_diam+slot_slack.z];
entry_size=[slot_size.x, clip_size.y+1, clip_size.z];
entry_pos=[0, 0, 1.5];
module socket_area() {
$fn=$preview? 10 : 20;
xrot(90) down(size.y/2) cylinder(d=size.z, h=size.y);
}
module socket_cutout() {
$fn=$preview? 10 : 20;
xrot(90) down(slot_size.y/2) cylinder(d=slot_size.z, h=slot_size.y);
}
module cover() {
translate(entry_pos) cube(entry_size, center=true);
}
module joiner(groove) {
slack=groove?0.2:0;
joiner_size=[entry_size.x-6+slack, entry_size.y-6+slack, entry_size.z-3+slack];
joiner_pos=[entry_pos.x, entry_pos.y, entry_pos.z - entry_size.z/2 + joiner_size.z/2];
difference() {
translate(joiner_pos) cube(joiner_size, center=true);
screw_cutout();
}
}
module screw_cutout() {
if (include_screwhole) down(clip_size.z/2) screwhole(d=clip_screwhole, h=clip_size.z);
}
module model() {
difference() {
union() {
right(clip_size.x/2) socket_area();
left(clip_size.x/2) socket_area();
cube(clip_size, center=true);
}
right(clip_size.x/2) socket_cutout();
left(clip_size.x/2) socket_cutout();
screw_cutout();
}
}
left(socket_size.x/2)
{
if (is_model(MODEL_CLIP_A)) {
difference() {
model();
cover();
}
joiner(groove=false);
}
if (is_model(MODEL_CLIP_B)) {
difference() {
intersection() {
model();
cover();
}
joiner(groove=true);
}
}
}
}
module clip_ratcheting(ratchet_length=28) {
$fn=$preview?4:20;
socket_size=get_link_socket_size();
clip_size=get_link_clip_size();
pin_diam=get_link_pin_diameter();
slot_slack=get_link_socket_slack();
size=[clip_size.x+socket_size.x, clip_size.y, clip_size.z];
slot_size=[clip_size.x+slot_slack.x + 0.2, size.y+1, pin_diam+slot_slack.z];
entry_size=[slot_size.x-5, clip_size.y+1, clip_size.z];
connector_size=[25, 0, 0];
module hookRight() {
right(connector_size.x/2) zrot(180) link_connector_pin();
}
module hookLeft() {
left(connector_size.x/2) link_connector_pin();
}
wall=2;
tooth_size=[4, 3, 3];
module tooth() {
s=[tooth_size.x, tooth_size.z, tooth_size.y];
up(tooth_size.z/2) xrot(-90) right_triangle(s);
}
module teethFwd() {
tooth_count = ratchet_length/tooth_size.x;
fwd((clip_size.y-wall*2)/2)
right(ratchet_length/2) {
for (i=[1:tooth_count]) {
left(i*tooth_size.x) tooth();
}
cap_size=[ratchet_length, tooth_size.y, 1];
up(tooth_size.z/2 + cap_size.z/2) left(cap_size.x/2) back(tooth_size.y/2) {
difference() {
cube(cap_size, center=true);
translate([0.1, 0.1, 0]) back(tooth_size.y/2) right(ratchet_length/2) zrot(180) tooth();
}
}
}
}
module teethBack() {
scale([1, -1, 1]) teethFwd();
}
module ratchetLeft() {
right(ratchet_length/2) {
difference() {
cube([ratchet_length, clip_size.y, clip_size.z], center=true);
up(wall/2) cube([ratchet_length, clip_size.y-wall*2, clip_size.z], center=true);
right(ratchet_length/2+1) {
slideRailGuide(groove=true);
scale([1,-1,1]) slideRailGuide(groove=true);
}
}
teethFwd();
teethBack();
}
}
ratchet_tooth_count=2;
ratchet_wall=2.5;
ratchet_slack=0.3;
rail_slack=0.2;
ratchet_tooth_scale_z=0.8;
module ratchetTeethFront() {
module ratchetButton() {
size=[8,wall*4,4];
up(size.z+1) fwd(wall*2 - ratchet_wall) cuboid(size, fillet=1, edges=EDGES_FRONT);
up(2) back(ratchet_wall/2) cube([size.x, ratchet_wall, tooth_size.z*ratchet_tooth_scale_z + size.z], center=true);
}
fwd((clip_size.y-wall*2)/2 - tooth_size.y - ratchet_slack)
left(ratchet_length) {
for (i=[0:ratchet_tooth_count-1]) {
right(i*tooth_size.x) scale([-1,-1*(1-i/5),ratchet_tooth_scale_z]) tooth();
}
ratchetButton();
}
module ratchetWall() {
up(tooth_size.z/2) left(ratchet_length/2 + wall - wall/2) fwd(clip_size.y/2-wall-tooth_size.y-ratchet_wall/2 - ratchet_slack)
down(wall/2+0.5) cube([ratchet_length + wall, ratchet_wall, tooth_size.z*ratchet_tooth_scale_z], center=true);
}
ratchetWall();
}
module ratchetTeethBack() {
scale([1,-1,1]) ratchetTeethFront();
}
module ratchetRight() {
left(wall) cube([wall*2, clip_size.y + rail_slack*2, clip_size.z], center=true);
ratchetTeethFront();
ratchetTeethBack();
}
module slideRailGuide(groove) {
size=[ratchet_length, wall, wall];
fwd(clip_size.y/2 + rail_slack)
left(size.x/2) scale(groove?1.5:1) cuboid(size, fillet=0.5);
}
module slideRailLeft() {
size=[ratchet_length, clip_size.y/2 - ratchet_wall*2 - wall/2 - ratchet_slack - tooth_size.y, clip_size.z];
right(size.x/2) cube(size, center=true);
}
module slideRailRight() {
size=[ratchet_length + 3, wall*1.5, clip_size.z];
module railFront() {
fwd(clip_size.y/2 + size.y/2 + rail_slack) left(size.x/2)
cuboid(size, fillet=1, edges=EDGES_FRONT);
slideRailGuide(groove=false);
}
module railBack() {
scale([1, -1, 1]) railFront();
}
railFront();
railBack();
}
right(size.x + connector_size.x - 2) {
left(2)
if (is_model(MODEL_CLIP_RATCHETING_A) || is_model(MODEL_CLIP_RATCHETING)) {
left(ratchet_length + wall + 1) {
hookLeft();
left(0.1) {
ratchetLeft();
slideRailLeft();
}
}
}
right(is_model(MODEL_DEMO) ? ratchet_length + 5 : 0)
if (is_model(MODEL_CLIP_RATCHETING_B) || is_model(MODEL_CLIP_RATCHETING)) {
ratchetRight();
hookRight();
slideRailRight();
}
}
}
// === Export ===
if (is_model(MODEL_DEMO)) {
foreach_platform_strap_connector() {
zrot(180) down(get_link_segment_size().z/2) {
link_chain(links_count, include_terminal=include_terminal);
/*right(links_count * get_link_segment_size().x + get_link_clip_size().x) {
clip();
right(get_link_clip_size().x) clip_ratcheting();
}*/
}
}
} else {
if (is_model(MODEL_CLIP_A) || is_model(MODEL_CLIP_B)) {
left(get_link_clip_size().x) clip();
}
if (is_model(MODEL_CLIP_RATCHETING) || is_model(MODEL_CLIP_RATCHETING_A) || is_model(MODEL_CLIP_RATCHETING_B)) {
left(get_link_clip_size().x * 2 + 3) clip_ratcheting();
}
if (is_model(MODEL_LINKS)) for (i = [0:links_count-1]) {
terminal=(i==links_count-1) && include_terminal;
right(i*get_link_segment_size().x) link(terminal=terminal);
}
}

68
src/strap_common.scad Normal file
View File

@ -0,0 +1,68 @@
include <export.scad>
use <BOSL/transforms.scad>
use <BOSL/shapes.scad>
include <BOSL/constants.scad>
use <specs_strap.scad>
use <common.scad>
module pin() {
segment_size=get_link_segment_size();
h=segment_size.y;
d=get_link_pin_diameter();
back(h/2) xrot(90) cylinder(h=h, d=d, $fn=$preview ? 10 : 30);
}
module pin_socket_area() {
segment_size=get_link_segment_size();
h=segment_size.y-get_link_socket_slack().y;
d=segment_size.z;
back(h/2) xrot(90) cylinder(h=h, d=d, $fn=$preview ? 10 : 30);
}
module pin_socket() {
difference() {
pin_socket_area();
scaleup = [
((get_link_pin_diameter() + get_link_socket_slack().x) / get_link_pin_diameter()),
((get_link_pin_diameter() + get_link_socket_slack().y) / get_link_pin_diameter()),
((get_link_pin_diameter() + get_link_socket_slack().z) / get_link_pin_diameter())
];
scale(scaleup) pin();
}
}
module arm() {
segment_size=get_link_segment_size();
arm_size=get_link_joiner_arm_size();
fwd(segment_size.y/2 + arm_size.y/2 - 1) right(arm_size.x/2 - 1) cubi(arm_size, fillet=1, edges=EDGES_FRONT + EDGES_Y_ALL);
}
module armFront() {
arm();
}
module armBack() {
scale([1, -1, 1]) arm();
}
module armJoiner() {
gap=get_link_socket_roundgap();
arm_size=get_link_joiner_arm_size();
segment_size=get_link_segment_size();
joiner_size=[arm_size.x-get_link_socket_size().x, segment_size.y, segment_size.z];
right(joiner_size.x/2 + get_link_socket_size().x/2 + gap) cubi(joiner_size, fillet=1, edges=EDGES_Z_ALL + EDGES_BOTTOM);
}
module link_arms() {
$fn=$preview?4:20;
armFront();
armBack();
armJoiner();
}
module link_connector_pin() {
pin();
link_arms();
}
module link_connector_socket() {
pin_socket();
}