Files
film-scanner/parts.scad

649 lines
20 KiB
OpenSCAD

include <constants.scad>
include <lib.scad>
include <stock_parts.scad>
include <units.inc>
frame_size = 20;
wall_thickness = 2;
corner_size = 40; // length of mating surface between corner and frame
// The length along each edge that the corner bracket takes. I.e., if
// you construct a frame of four corner brackets and 4 tubes of length
// $L-$corner_size, the distance between opposite tubes' centerlines
// will be $L.
corner_offset = frame_size / 2 + wall_thickness;
rail_diam = 8;
ysled_extralen = frame_size; // on each end
// Both of these are distance from frame centerline
belt_height1 = frame_size / 2 + wall_thickness + belt_width / 2 + 2;
belt_height2 = belt_height1 + belt_width + 2;
// calculated
ysled_rail_spacing = (sled_ysize);
motor_shaft_xoffset = 15;
motor_mount_length_adjustment = 8;
motor_mount_length = nema17_face + wall_thickness + motor_mount_length_adjustment;
// distance between centerline of rail and centerline of frame Y tube.
yrail_offset = motor_shaft_xoffset + pulley_sbend_offset;
// Generates bar of length $l, centered on origin extending in +Z
module tslot(l) {
bom_item(printable = false, label = str("tslot(", l, "mm)"));
colorize(stock_color) {
translate([ -frame_size / 2, -frame_size / 2, 0 ])
cube([ frame_size, frame_size, l ]);
}
}
// A corner, with tubes extending in +x and +y.
module corner_base(extension = wall_thickness) {
odim = frame_size / 2 + wall_thickness;
ldim = frame_size / 2 + corner_size + (extension - wall_thickness);
actual_corner_offset = frame_size / 2 + extension;
cap_points = [
[ -odim, -odim ], [ -odim, ldim ], [ odim, ldim ],
[odim, odim], [ ldim, odim ],
[ ldim, -odim ]
];
difference() {
translate([ 0, 0, -odim ]) linear_extrude(odim * 2)
polygon(points = cap_points);
// two bars...
translate([ -frame_size / 2, actual_corner_offset, -frame_size / 2 ])
cube([ frame_size, ldim, frame_size ]);
translate([ actual_corner_offset, -frame_size / 2, -frame_size / 2 ])
cube([ ldim, frame_size, frame_size ]);
// Screw holes
for (pos = [10, 30],
mirror_normal = [[0,1,0], [1,-1,0]]) {
mirror(mirror_normal)
translate([pos + actual_corner_offset, 0, -odim])
union() {
cylinder(d = 8, h=2, center=true);
cylinder(d = 5 * screw_hole_expansion, h = odim);
}
}
// Adjustable feet
cylinder(d=8, h=odim * 2 + 1, center=true, $fn = screw_num_sides);
}
}
rail_holder_width = (yrail_offset - frame_size / 2 - wall_thickness) * 2;
module rail_holder(in_x) {
odim = frame_size / 2 + wall_thickness;
box_size = (yrail_offset - odim) * 2;
assert(box_size == rail_holder_width);
ldim = frame_size / 2 + box_size;
tube_offset = (corner_size) / 2 + wall_thickness;
tube_rotation = in_x ? 90 : 0;
translated_rail_offset = yrail_offset - frame_size / 2 - wall_thickness;
translate([ odim, odim, -odim ]) {
difference() {
cube([
!in_x ? box_size - wall_thickness : 10,
!in_x ? 10 : box_size - wall_thickness,
wall_thickness * 2 + frame_size
]);
translate(
[ translated_rail_offset, translated_rail_offset, frame_size / 2 + wall_thickness ])
rotate([ tube_rotation - 90, tube_rotation, 0 ])
translate([ 0, 0, -corner_size ])
cylinder(d = rail_diam, corner_size * 4);
}
}
}
// centered on the rail.
module ysled_slider(associated_parts = $preview, is_left=true) {
spread = sled_ysize;
bom_item(label = str("ysled_slider(", spread, ")"));
sled_len = spread + frame_size;
sled_height = frame_size;
idler_spacing = spread / 6 + pulley_radius(idler_nteeth);
local_idler_xoffset = pulley_sbend_offset - yrail_offset + motor_shaft_xoffset;
echo("LIX:", local_idler_xoffset)
colorize("green") translate([ 0, -sled_len / 2, 0 ])
rotate([ -90, 0, 0 ]) {
difference() {
union() {
translate([ -frame_size / 2, -frame_size / 2, 0 ])
cube([ frame_size + 5, frame_size, sled_len ]);
translate([
pulley_sbend_offset - yrail_offset + motor_shaft_xoffset -
pulley_clearance_rad - 4,
-frame_size / 2, 0
]) cube([ frame_size + 5, frame_size, sled_len ]);
}
for (y = [idler_spacing, -idler_spacing]) {
rotate([90, 0, 0])
translate([local_idler_xoffset, sled_len/2 + y, 0])
cylinder(d=3, h = frame_size);
}
cylinder(d = rail_diam * 1.5, h = sled_len);
translate([ 0, 0, -1 ]) cylinder(d = bushing_od, h = bushing_len + 1);
translate([ 0, 0, sled_len - bushing_len ])
cylinder(d = bushing_od, h = bushing_len + 1);
for (zpos = [ frame_size / 2, sled_len - frame_size / 2 ]) {
translate([ sled_height / 2, 0, zpos ]) rotate([ 0, 90, 0 ]) {
cylinder(h = sled_height + 1, d = rail_diam);
}
}
}
}
// TODO: add posts for the idlers
// color("#f00")
translate(
[ local_idler_xoffset, 0, frame_size/2 - 1 ]) {
translate([ 0, idler_spacing, 0 ]) {
idler_post(belt_height = belt_height1 - frame_size/2 + 1, toothed = is_left);
}
translate([ 0, -idler_spacing, 0 ]) {
rotate([ 0, 0, -90 ]) { idler_post(belt_height = belt_height2 - frame_size/2 + 1, toothed = !is_left); }
}
}
if ($preview) {
translate([ 0, -sled_len / 2, 0 ]) rotate([ -90, 0, 0 ])
bushing();
translate([ 0, sled_len / 2, 0 ]) rotate([ 90, 0, 0 ])
bushing();
}
}
module nema17_housing() {
hwidth = corner_size + frame_size + wall_thickness;
hheight = nema17_face + wall_thickness * 2;
hdepth = frame_size + wall_thickness * 2;
vertical_extension = frame_size + belt_height2;
screw_dxy = 31 / 2;
slot_vector = [0, - motor_mount_length_adjustment, 0];
translate([ 0, -nema17_face / 2 - frame_size / 2 - wall_thickness, 0 ]) {
slotify(slot_vector) {
translate([ 0, 0, -belt_height2/2 - wall_thickness ])
cube([ nema17_face + 1, nema17_face + 1, hdepth + belt_height2 ], center = true);
cylinder(d = 23, frame_size);
}
for (x = [-1, 1], y = [-1,1]) {
slotify(slot_vector)
translate([
x * screw_dxy,
y * screw_dxy,
frame_size / 2 + wall_thickness + 0.5
])
rotate([ 180, 0, 0 ]) {
cylinder(d = 5 * screw_hole_expansion, h = wall_thickness + 1);
}
}
}
}
// belt height is the center of the belt.
// Roration is in Z axis, from +x/+y
module idler_post(belt_height, toothed = true) {
gap = 0.5;
post_radius = pulley_clearance_rad + 4;
top_shelf_thickness = 4;
total_height = idler_zmargin.y + belt_height + top_shelf_thickness + gap;
slot_radius = pulley_clearance_rad;
slot_height = idler_zmargin.x + idler_zmargin.y + gap * 2;
difference() {
union() {
// outer shell
cylinder(h = total_height, r = post_radius);
translate([ 0, -post_radius, 0 ])
cube([ post_radius, post_radius, total_height ]);
translate([ -post_radius, 0, 0 ])
cube([ post_radius, post_radius, total_height ]);
}
translate([ 0, 0, belt_height - idler_zmargin.x - gap ]) {
cylinder(r = slot_radius, slot_height);
translate([ -slot_radius, 0, 0 ])
cube([ post_radius * 2, post_radius * 2, slot_height ]);
translate([ 0, -slot_radius, 0 ])
cube([ post_radius * 2, post_radius * 2, slot_height ]);
}
cylinder(d = 3, h = total_height + 1);
translate([ 0, 0, total_height - 2 ]) cylinder(r = 3, h = 3);
}
if($preview) {
idler(height = belt_height, toothed=toothed);
}
}
module flcorner() {
bom_item();
motor_height = belt_height2 - belt_height1;
union() {
rotate([ 0, 0, 0 ]) {
corner_base();
rail_holder(false);
}
difference() {
translate([
-frame_size/2 - wall_thickness,
-frame_size/2 - wall_thickness - motor_mount_length,
-frame_size/2 - wall_thickness
])
cube([
frame_size + wall_thickness + corner_size,
motor_mount_length + wall_thickness,
frame_size + wall_thickness * 2 + motor_height
]);
translate([ motor_shaft_xoffset, 0, motor_height ]) nema17_housing();
}
// translate([motor_shaft_xoffset, 0, 0 ]) nema17_housing();
}
if ($preview)
translate([
motor_shaft_xoffset,
-nema17_face / 2 - frame_size / 2 - wall_thickness, frame_size / 2
]) {
translate([0,0,motor_height])
nema17();
translate([ 0, 0, -frame_size / 2 + belt_height2 ])
rotate([180,0,0])
{ active_pulley(); }
}
}
module frcorner() {
bom_item();
union() {
rotate([ 0, 0, 90 ]) {
corner_base();
rail_holder(true);
}
difference() {
translate([
-frame_size/2 - corner_size,
-frame_size/2 - wall_thickness - motor_mount_length,
-frame_size/2 - wall_thickness
])
cube([
frame_size + wall_thickness + corner_size,
motor_mount_length,
frame_size + wall_thickness * 2
]);
translate([ -motor_shaft_xoffset, 0, 0 ]) nema17_housing();
}
}
if ($preview)
translate([
-motor_shaft_xoffset,
-nema17_face / 2 - frame_size / 2 - wall_thickness, frame_size / 2
]) {
nema17();
translate([ 0, 0, belt_height1 - frame_size / 2 ]) {
rotate([ 180, 0, 0 ]) active_pulley();
}
}
}
module blcorner() {
bom_item();
difference() {
rotate([0, 0, 270]) {
corner_base(extension = rail_holder_width);
rail_holder(true);
}
for (pos = [
[motor_shaft_xoffset + pulley_sbend_offset, 0, 0],
[0, motor_shaft_xoffset + pulley_sbend_offset, 0],
]) {
translate(pos)
cylinder(d = 3, h = belt_height2, $fn=screw_num_sides);
}
}
translate([motor_shaft_xoffset + pulley_sbend_offset, 0, frame_size/2])
rotate([0,0,-90])
idler_post(belt_height = belt_height1 - frame_size/2, toothed=true);
translate([0, -(motor_shaft_xoffset + pulley_sbend_offset), frame_size/2])
rotate([0,0,-90])
idler_post(belt_height = belt_height2 - frame_size/2, toothed=true);
}
module brcorner() {
bom_item();
union()
rotate([ 0, 0, 180 ]) {
corner_base(extension = rail_holder_width);
rail_holder(false);
}
translate([-(motor_shaft_xoffset + pulley_sbend_offset), 0, frame_size/2])
rotate([0,0,180])
idler_post(belt_height = belt_height2 - frame_size/2, toothed=true);
translate([0, -(motor_shaft_xoffset + pulley_sbend_offset), frame_size/2])
rotate([0,0,180])
idler_post(belt_height = belt_height1 - frame_size/2, toothed=true);
}
module xsled_slider() {
slider_len = bushing_len + 10;
screw_tab_len = 20;
screw_tab_thickness = 10;
union()
// translate([screw_tab_len, 0, 0])
difference() {
union() {
rotate([ 0, 90, 0 ]) { cylinder(d = frame_size, h = slider_len); }
translate([ 0, -frame_size / 2, 0 ])
cube([ slider_len, frame_size, frame_size / 2 ]);
*translate([ -screw_tab_len, -frame_size / 2, frame_size / 2 ]) cube(
[ screw_tab_len * 2 + slider_len, frame_size, screw_tab_thickness ]);
}
rotate([ 0, 90, 0 ]) {
cylinder(d = bushing_od, h = bushing_len);
cylinder(d = rail_diam * 1.1, slider_len * 2);
}
}
if ($preview)
rotate([ 0, 90, 0 ]) bushing();
}
module sled_cornera() {
bom_item();
corner_base();
translate([ -frame_size / 2 - wall_thickness, 0, -frame_size ])
xsled_slider();
}
module sled_cornerb() {
bom_item();
corner_base();
rotate([ 0, 0, 90 ])
translate([ -frame_size / 2 - wall_thickness, 0, -frame_size ])
xsled_slider();
}
module xsled_frame() {
translate([ -sled_xsize / 2 + corner_offset, -ysled_rail_spacing / 2, 0 ])
rotate([ 0, 90, 0 ]) tslot(sled_xsize - corner_offset * 2);
translate([ -sled_xsize / 2 + corner_offset, ysled_rail_spacing / 2, 0 ])
rotate([ 0, 90, 0 ]) tslot(sled_xsize - corner_offset * 2);
translate([ -sled_xsize / 2, -ysled_rail_spacing / 2 + corner_offset, 0 ])
rotate([ -90, 0, 0 ]) tslot(sled_ysize - corner_offset * 2);
translate([ sled_xsize / 2, -ysled_rail_spacing / 2 + corner_offset, 0 ])
rotate([ -90, 0, 0 ]) tslot(sled_ysize - corner_offset * 2);
translate([ -sled_xsize / 2, -sled_ysize / 2, 0 ]) sled_cornera();
translate([ sled_xsize / 2, sled_ysize / 2, 0 ]) rotate([ 0, 0, 180 ])
sled_cornera();
translate([ sled_xsize / 2, -sled_ysize / 2, 0 ]) rotate([ 0, 0, 90 ])
sled_cornerb();
translate([ -sled_xsize / 2, sled_ysize / 2, 0 ]) rotate([ 0, 0, 270 ])
sled_cornerb();
// simulate a piece of film
if (false) {
color("#0008") cube([ 5 * inch, 4 * inch, 1 ], center = true);
}
}
gt2_clip_meshing_dist = belt_pitch * 6;
gt2_clip_peg_diam = 8; // min 6
gt2_clip_ease_in_diam = 6;
gt2_clip_narrowing_dist = 12; // distance from center of loop peg to meshing point
gt2_clip_gap_diam = gt2_clip_peg_diam + belt_thickness * 2 + 1;
gt2_clip_thickness = (gt2_clip_peg_diam + gt2_clip_gap_diam)/2;
gt2_clip_length = gt2_clip_narrowing_dist + gt2_clip_meshing_dist + gt2_clip_peg_diam / 2 + gt2_clip_gap_diam - belt_thickness;
module gt2_clip_cutout(teeth_inside) {
dual_width = belt_thickness * 2 - tooth_height;
loop_peg_diam = gt2_clip_peg_diam; // min 6mm
gap_diam = gt2_clip_gap_diam;
narrowing_ease_diam = 6;
narrowing_dist = gt2_clip_narrowing_dist;
meshing_dist = gt2_clip_meshing_dist;
ease_out_rad = gap_diam - belt_thickness ;
out_off = teeth_inside ? 0 : dual_width - belt_thickness;
translate([
-(narrowing_dist + meshing_dist + (ease_out_rad - out_off - belt_thickness))
-(teeth_inside ? tooth_height - belt_pld : belt_backing)
,
loop_peg_diam / 2,
-1])
union() {
difference() {
hull() {
circle(d = gap_diam);
polygon([
[0, 0],
[0, gap_diam / 2],
[narrowing_dist, gap_diam / 2],
[narrowing_dist, gap_diam / 2 - dual_width]
]);
}
circle(d = loop_peg_diam);
translate([narrowing_dist, gap_diam / 2 - dual_width - narrowing_ease_diam / 2])
circle(d = narrowing_ease_diam);
}
translate([narrowing_dist, gap_diam / 2])
polygon([
[0, 0],
[meshing_dist - 1, 0],
[meshing_dist, - out_off],
[meshing_dist, - belt_thickness - out_off],
[meshing_dist - 1, - dual_width],
[0, - dual_width]
]);
translate([narrowing_dist + meshing_dist, gap_diam / 2 - ease_out_rad]) {
intersection() {
translate([0, -2])
square(ease_out_rad + 2);
difference() {
circle(ease_out_rad - out_off);
circle(ease_out_rad - out_off - belt_thickness);
}
}
}
}
}
module gt2_clip(teeth_inside) {
out_off = teeth_inside ? 0 : belt_thickness - tooth_height;
actual_thickness = gt2_clip_thickness + wall_thickness;
translate([0,-actual_thickness, -belt_width/2 - 2])
difference() {
translate([-gt2_clip_length - belt_thickness + out_off/2, 0]) {
cube([gt2_clip_length + belt_thickness * 2, actual_thickness, belt_width + 4]);
mirror([0,0,1])
translate([0, actual_thickness])
linear_extrude(height=actual_thickness * 1.5, scale=[1,0])
translate([0, -actual_thickness])
square([
gt2_clip_length + belt_thickness * 2,
actual_thickness
]);
}
translate([0,0,0])
linear_extrude(belt_width + 6)
gt2_clip_cutout(teeth_inside);
}
}
module xsled2() {
bom_item();
tab_len = bushing_len * 2;
tab_thickness = bushing_od + wall_thickness * 2;
glass_thickness = 3; // TODO: Update when an actual size is available
inner_ysize = sled_ysize - tab_thickness;
film_height = belt_height2 + belt_width / 2;
ledge_width = 15;
inside_ledge_width = 8;
outside_ledge_width = ledge_width - inside_ledge_width;
film_thickness = 0.25;
finger_notch_diam = 25;
finger_notch_smdepth = 2;
glass_holder_points = [
[0, - tab_thickness / 2],
[0, film_height + glass_thickness],
[wall_thickness, film_height + glass_thickness],
[wall_thickness, film_height],
[outside_ledge_width, film_height],
[outside_ledge_width, film_height - glass_thickness],
[ledge_width, film_height - glass_thickness],
[wall_thickness * 2, - tab_thickness / 2]
];
echo("Ledge width: ", ledge_width - outside_ledge_width);
// rail attachment points
render() {
for (xsign = [1, - 1], ysign = [1, - 1]) {
translate([sled_xsize / 2 * xsign, sled_ysize / 2 * ysign, 0])
rotate([0, - 90 * xsign, 0]) {
difference() {
union() {
cylinder(d = tab_thickness, h = tab_len);
translate(
[- tab_thickness / 2, ysign > 0 ? - tab_thickness / 2 : 0, 0])
cube([tab_thickness, tab_thickness / 2 + 1, tab_len]);
}
translate([0, 0, - 0.5]) {
cylinder(d = bushing_od, h = bushing_len + 0.5);
cylinder(d = rail_diam * 1.1, h = tab_len + 1);
}
}
}
}
// glass holding frame
difference() {
union() {
translate([- sled_xsize / 2, - inner_ysize / 2, 0])
rotate([0, 90, 0]) rotate([0, 0, 90]) linear_extrude(sled_xsize)
polygon(glass_holder_points);
translate([sled_xsize / 2, inner_ysize / 2, 0])
rotate([0, 90, 180]) rotate([0, 0, 90])
linear_extrude(sled_xsize) polygon(glass_holder_points);
translate([sled_xsize / 2, - inner_ysize / 2, 0])
rotate([0, 90, 90]) rotate([0, 0, 90])
linear_extrude(inner_ysize) polygon(glass_holder_points);
translate([- sled_xsize / 2, inner_ysize / 2, 0])
rotate([0, 90, - 90]) rotate([0, 0, 90])
linear_extrude(inner_ysize) polygon(glass_holder_points);
}
// grub screw points for glass levelling
for (x = [- 1, 1],
y = [- 1, 1]) {
translate([x * (sled_xsize / 2 - outside_ledge_width - inside_ledge_width / 2),
y * (inner_ysize / 2 - outside_ledge_width - inside_ledge_width / 2),
film_height])
rotate([180, 0, 0]) {
cylinder(d = 4, h = 50);
cylinder(d = 5, h = 4 + glass_thickness);
}
}
// finger notch
translate([
- 10, - inner_ysize / 2,
film_height - glass_thickness + 0.5 - finger_notch_smdepth + finger_notch_diam / 2
]) rotate([90, 0, 0])
cylinder(d = finger_notch_diam, h = ledge_width * 3, center = true);
}
// belt attachment points
idler_spacing = sled_ysize / 6;
for (params = [
[true, [- sled_xsize / 2, idler_spacing, belt_height1]],
[false, [- sled_xsize / 2, - idler_spacing, belt_height2]],
[true, [sled_xsize / 2, idler_spacing, belt_height2]],
[false, [sled_xsize / 2, - idler_spacing, belt_height1]]
]) {
should_mirror = params[1][0] > 0 ? params[0] : !params[0];
translate(params[1])
rotate([0, 0, params[1][0] > 0 ? 90 : - 90]) {
if (should_mirror) {
mirror([1, 0, 0])
gt2_clip(params[0]);
} else {
gt2_clip(params[0]);
}
}
}
}
if ($preview) {
for (xsign = [1, - 1], ysign = [1, - 1]) {
translate([sled_xsize / 2 * xsign, sled_ysize / 2 * ysign, 0])
rotate([0, - 90 * xsign, 0]) {
bushing();
}
}
}
color("#fff3") if ($preview) {
// draw glass sheets
translate(
[0, 0, film_height - glass_thickness / 2 + 0.5])
cube(
[
sled_xsize - outside_ledge_width * 2,
inner_ysize - outside_ledge_width * 2,
glass_thickness
],
center = true);
*translate(
[0, 0, film_height + film_thickness * 2 + glass_thickness / 2 + 0.5])
cube(
[
sled_xsize - wall_thickness * 2,
inner_ysize - wall_thickness * 2,
glass_thickness
],
center = true);
}
if ($preview) {
color("#0003")
translate([0, 0, film_height + 0.5]) {
cube([5 * inch, 4 * inch, film_thickness], center = true);
}
}
}
// render()
// xsled2();