Compare commits

..

No commits in common. "master" and "v0.1" have entirely different histories.
master ... v0.1

17 changed files with 218 additions and 1052 deletions

View File

@ -8,14 +8,6 @@ Pip-Boy inspired phone holder built around the PINE64 PinePhone.
- OpenScad - OpenScad
- [BOSL library](https://github.com/revarbat/BOSL) - [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 ## Build Instructions
See [Instructions Readme](instructions/README.md). See [Instructions Readme](instructions/README.md).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 20 KiB

218
main.scad Normal file
View File

@ -0,0 +1,218 @@
// 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();
}
}
}

View File

@ -1,22 +0,0 @@
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];

View File

@ -1,32 +0,0 @@
/*
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;

View File

@ -1,425 +0,0 @@
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();
}
}
}

View File

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

View File

@ -1,134 +0,0 @@
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);
}

View File

@ -1,21 +0,0 @@
// 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();
}
}

View File

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

View File

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

View File

@ -1,22 +0,0 @@
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;

View File

@ -1,11 +0,0 @@
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;

View File

@ -1,292 +0,0 @@
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);
}
}

View File

@ -1,68 +0,0 @@
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();
}