Initial copy stand design
This commit is contained in:
+628
@@ -0,0 +1,628 @@
|
||||
/*
|
||||
* Parametric copy-stand base for a Bambu Lab X2D.
|
||||
* Service coordinates: X left-to-right, Y rear-to-front, Z bottom-to-top.
|
||||
* The grid origin is at plate Y=datum_y. The socket's straight front edge is
|
||||
* 0.5 inch forward of that origin and remains a convenient reference face.
|
||||
*/
|
||||
|
||||
/* [Output] */
|
||||
part = "assembly"; // [assembly,base,base_black,base_segment,base_black_segment,base_accent,socket,coupon,coupon_black,coupon_key,coupon_accent]
|
||||
segment_index = 0; // [0:1:5]
|
||||
show_references = true;
|
||||
|
||||
/* [Base] */
|
||||
plate_width = 203.2;
|
||||
plate_depth = 248;
|
||||
plate_height = 15;
|
||||
corner_radius = 12.7;
|
||||
datum_y = 26;
|
||||
top_skin = 3.2;
|
||||
perimeter_wall = 3.2;
|
||||
rib_width = 2.4;
|
||||
|
||||
/* [Grid inlay] */
|
||||
grid_pitch = 25.4;
|
||||
grid_columns = 8;
|
||||
grid_rows = 8;
|
||||
grid_line_width = 0.8;
|
||||
inlay_depth = 0.6;
|
||||
inlay_clearance = 0.08;
|
||||
|
||||
/* [McMaster 97171A240 inserts] */
|
||||
insert_pilot_diameter = 8.1;
|
||||
insert_body_length = 9.525;
|
||||
insert_flange_diameter = 12.9;
|
||||
insert_flange_depth = 1.35;
|
||||
screw_clearance_diameter = 6.8;
|
||||
insert_boss_diameter = 20;
|
||||
|
||||
/* [Feet] */
|
||||
foot_pad_diameter = 19;
|
||||
foot_pad_height = 8.5;
|
||||
foot_stud_length = 17.8;
|
||||
foot_nut_diameter = 24.4;
|
||||
foot_nut_height = 6;
|
||||
|
||||
/* [Removable 2020 socket] */
|
||||
rail_size = 20;
|
||||
rail_clearance = 0.05;
|
||||
socket_capture = 50;
|
||||
socket_front_y = grid_pitch / 2;
|
||||
socket_rear_y = -datum_y;
|
||||
socket_flange_size = [110, socket_front_y - socket_rear_y, 10];
|
||||
socket_key_size = [52, 22, 6];
|
||||
socket_key_clearance = 0.25;
|
||||
socket_side_wall = 6;
|
||||
socket_end_wall = 2.7;
|
||||
socket_mount_x = 45;
|
||||
socket_mount_ys = [-16, 3];
|
||||
socket_mount_tool_diameter = 19;
|
||||
socket_gusset_outer_x = 35;
|
||||
socket_gusset_y_span = [-23, 10];
|
||||
socket_gusset_height = 32;
|
||||
rail_screw_diameter = 5.5;
|
||||
rail_screw_head_diameter = 9.5;
|
||||
rail_screw_head_depth = 3;
|
||||
rail_tool_diameter = 14;
|
||||
rail_screw_heights = [15, 40];
|
||||
|
||||
/* [Calibration] */
|
||||
coupon_insert_diameters = [7.70, 7.85, 8.00, 8.15, 8.30];
|
||||
coupon_rail_gaps = [20.0, 20.1, 20.2, 20.3];
|
||||
|
||||
$fn = $preview ? 36 : 72;
|
||||
eps = 0.02;
|
||||
|
||||
assert(plate_width <= 256 && plate_depth <= 256,
|
||||
"Base exceeds the X2D main-nozzle build area");
|
||||
assert(plate_height >= 13.462,
|
||||
"Insert bosses violate McMaster's 0.530-inch minimum material thickness");
|
||||
assert(top_skin > inlay_depth,
|
||||
"Top skin must be thicker than the inlay");
|
||||
assert(grid_columns == 8 && grid_rows == 8,
|
||||
"This release's shared front-foot geometry expects an 8 x 8 grid");
|
||||
assert(socket_key_size[0] / 2 + socket_key_clearance
|
||||
< socket_mount_x - insert_boss_diameter / 2,
|
||||
"Socket key slot overlaps a full-depth mounting-insert boss");
|
||||
assert(socket_gusset_outer_x
|
||||
< socket_mount_x - socket_mount_tool_diameter / 2,
|
||||
"Socket gusset enters a flange-screw tool-access envelope");
|
||||
assert(socket_front_y == grid_pitch / 2,
|
||||
"Socket reference face must remain at the half-inch grid coordinate");
|
||||
assert(socket_rear_y == -datum_y,
|
||||
"Socket flange must reach the rear edge of the base");
|
||||
assert(socket_mount_x + socket_mount_tool_diameter / 2
|
||||
<= socket_flange_size[0] / 2,
|
||||
"Socket washer envelope exceeds the flange width");
|
||||
assert(socket_mount_ys[0] - socket_mount_tool_diameter / 2
|
||||
>= socket_rear_y
|
||||
&& socket_mount_ys[len(socket_mount_ys) - 1]
|
||||
+ socket_mount_tool_diameter / 2 <= socket_front_y,
|
||||
"Socket washer envelope exceeds the flange depth");
|
||||
assert(socket_rail_axis_y() + socket_key_size[1] / 2
|
||||
+ socket_key_clearance
|
||||
< grid_pitch - insert_boss_diameter / 2,
|
||||
"Socket key slot overlaps the first grid-row insert bosses");
|
||||
assert(socket_mount_ys[len(socket_mount_ys) - 1]
|
||||
+ insert_boss_diameter / 2
|
||||
< grid_pitch - insert_boss_diameter / 2,
|
||||
"Socket mounting bosses overlap the first grid-row bosses");
|
||||
assert(socket_key_size[2] < plate_height,
|
||||
"Socket tongue must stop above the base underside");
|
||||
|
||||
grid_xs = [for (i = [0 : grid_columns - 1])
|
||||
plate_width / 2 + (i - (grid_columns - 1) / 2) * grid_pitch];
|
||||
grid_ys = [for (i = [1 : grid_rows]) datum_y + i * grid_pitch];
|
||||
rear_foot_ys = corner_radius;
|
||||
socket_hole_xs = [plate_width / 2 - socket_mount_x,
|
||||
plate_width / 2 + socket_mount_x];
|
||||
socket_hole_ys = [for (y = socket_mount_ys) datum_y + y];
|
||||
all_insert_points = concat(
|
||||
[for (x = grid_xs, y = grid_ys) [x, y]],
|
||||
[for (x = [grid_xs[0], grid_xs[len(grid_xs) - 1]]) [x, rear_foot_ys]],
|
||||
[for (x = socket_hole_xs, y = socket_hole_ys) [x, y]]
|
||||
);
|
||||
|
||||
module rounded_rect_2d(size, radius) {
|
||||
offset(r = radius)
|
||||
square([size[0] - 2 * radius, size[1] - 2 * radius], center = true);
|
||||
}
|
||||
|
||||
module rounded_prism(size, radius) {
|
||||
linear_extrude(height = size[2])
|
||||
rounded_rect_2d([size[0], size[1]], radius);
|
||||
}
|
||||
|
||||
module plate_outline_2d(inset = 0) {
|
||||
translate([plate_width / 2, plate_depth / 2])
|
||||
offset(delta = -inset)
|
||||
rounded_rect_2d([plate_width, plate_depth], corner_radius);
|
||||
}
|
||||
|
||||
module insert_boss_at(x, y) {
|
||||
translate([x, y, 0]) cylinder(h = plate_height, d = insert_boss_diameter);
|
||||
}
|
||||
|
||||
module insert_positive_bosses() {
|
||||
for (p = all_insert_points) insert_boss_at(p[0], p[1]);
|
||||
}
|
||||
|
||||
module underside_structure_2d() {
|
||||
intersection() {
|
||||
plate_outline_2d();
|
||||
union() {
|
||||
difference() {
|
||||
plate_outline_2d();
|
||||
plate_outline_2d(perimeter_wall);
|
||||
}
|
||||
for (x = grid_xs)
|
||||
translate([x - rib_width / 2, corner_radius, 0])
|
||||
square([rib_width, plate_depth - 2 * corner_radius]);
|
||||
for (y = concat([datum_y], grid_ys))
|
||||
translate([corner_radius, y - rib_width / 2, 0])
|
||||
square([plate_width - 2 * corner_radius, rib_width]);
|
||||
|
||||
translate([corner_radius, rear_foot_ys - rib_width / 2, 0])
|
||||
square([plate_width - 2 * corner_radius, rib_width]);
|
||||
for (x = socket_hole_xs)
|
||||
translate([x - rib_width / 2, corner_radius, 0])
|
||||
square([rib_width, datum_y - corner_radius]);
|
||||
|
||||
for (p = all_insert_points)
|
||||
translate(p) circle(d = insert_boss_diameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module base_structure_positive() {
|
||||
union() {
|
||||
linear_extrude(height = plate_height - top_skin)
|
||||
underside_structure_2d();
|
||||
translate([0, 0, plate_height - top_skin])
|
||||
linear_extrude(height = top_skin) plate_outline_2d();
|
||||
}
|
||||
}
|
||||
|
||||
module one_insert_cutout(x, y, pilot = insert_pilot_diameter) {
|
||||
translate([x, y, -eps])
|
||||
cylinder(h = insert_flange_depth + eps, d = insert_flange_diameter);
|
||||
translate([x, y, insert_flange_depth - eps])
|
||||
cylinder(h = insert_body_length + 2 * eps, d = pilot);
|
||||
translate([x, y, insert_flange_depth + insert_body_length - eps])
|
||||
cylinder(
|
||||
h = plate_height - insert_flange_depth - insert_body_length + 2 * eps,
|
||||
d = screw_clearance_diameter
|
||||
);
|
||||
}
|
||||
|
||||
module all_insert_cutouts() {
|
||||
union() {
|
||||
for (p = all_insert_points)
|
||||
translate([p[0], p[1], -eps])
|
||||
cylinder(h = insert_flange_depth + eps,
|
||||
d = insert_flange_diameter);
|
||||
}
|
||||
union() {
|
||||
for (p = all_insert_points)
|
||||
translate([p[0], p[1], insert_flange_depth - eps])
|
||||
cylinder(h = insert_body_length + 2 * eps,
|
||||
d = insert_pilot_diameter);
|
||||
}
|
||||
union() {
|
||||
for (p = all_insert_points)
|
||||
translate([p[0], p[1],
|
||||
insert_flange_depth + insert_body_length - eps])
|
||||
cylinder(
|
||||
h = plate_height - insert_flange_depth
|
||||
- insert_body_length + 2 * eps,
|
||||
d = screw_clearance_diameter
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module insert_pattern_2d(diameter) {
|
||||
for (p = all_insert_points) translate(p) circle(d = diameter);
|
||||
}
|
||||
|
||||
module socket_key_2d(clearance = 0) {
|
||||
translate([plate_width / 2, datum_y + socket_rail_axis_y()])
|
||||
rounded_rect_2d(
|
||||
[socket_key_size[0] + 2 * clearance,
|
||||
socket_key_size[1] + 2 * clearance],
|
||||
2 + clearance
|
||||
);
|
||||
}
|
||||
|
||||
module grid_artwork_2d(clearance = 0) {
|
||||
intersection() {
|
||||
plate_outline_2d();
|
||||
offset(delta = clearance)
|
||||
union() {
|
||||
for (x = grid_xs)
|
||||
translate([x - grid_line_width / 2, datum_y])
|
||||
square([grid_line_width, plate_depth - datum_y]);
|
||||
for (y = concat([datum_y], grid_ys))
|
||||
translate([0, y - grid_line_width / 2])
|
||||
square([plate_width, grid_line_width]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module accent_service() {
|
||||
translate([0, 0, plate_height - inlay_depth])
|
||||
linear_extrude(height = inlay_depth)
|
||||
difference() {
|
||||
grid_artwork_2d();
|
||||
insert_pattern_2d(screw_clearance_diameter);
|
||||
socket_key_2d(socket_key_clearance);
|
||||
}
|
||||
}
|
||||
|
||||
module base_layer_2d(outline = false, hole_diameter, artwork = false) {
|
||||
difference() {
|
||||
if (outline) plate_outline_2d();
|
||||
else underside_structure_2d();
|
||||
insert_pattern_2d(hole_diameter);
|
||||
// The locator is a through-slot so no unsupported bridge is printed
|
||||
// against the textured build plate.
|
||||
socket_key_2d(socket_key_clearance);
|
||||
if (artwork) grid_artwork_2d(inlay_clearance);
|
||||
}
|
||||
}
|
||||
|
||||
module base_segment_service(index, with_artwork = false) {
|
||||
underside_top = plate_height - top_skin;
|
||||
body_top = insert_flange_depth + insert_body_length;
|
||||
artwork_bottom = plate_height - inlay_depth;
|
||||
top_split = (underside_top + artwork_bottom) / 2;
|
||||
|
||||
if (index == 0)
|
||||
linear_extrude(height = insert_flange_depth)
|
||||
base_layer_2d(false, insert_flange_diameter);
|
||||
else if (index == 1)
|
||||
translate([0, 0, insert_flange_depth])
|
||||
linear_extrude(height = insert_body_length)
|
||||
base_layer_2d(false, insert_pilot_diameter);
|
||||
else if (index == 2)
|
||||
translate([0, 0, body_top])
|
||||
linear_extrude(height = underside_top - body_top)
|
||||
base_layer_2d(false, screw_clearance_diameter);
|
||||
else if (index == 3)
|
||||
translate([0, 0, underside_top])
|
||||
linear_extrude(height = top_split - underside_top)
|
||||
base_layer_2d(true, screw_clearance_diameter);
|
||||
else if (index == 4)
|
||||
translate([0, 0, top_split])
|
||||
linear_extrude(height = artwork_bottom - top_split)
|
||||
base_layer_2d(true, screw_clearance_diameter);
|
||||
else if (index == 5)
|
||||
translate([0, 0, artwork_bottom])
|
||||
linear_extrude(height = inlay_depth)
|
||||
base_layer_2d(true, screw_clearance_diameter, with_artwork);
|
||||
else assert(false, str("Unknown base segment: ", index));
|
||||
}
|
||||
|
||||
module base_layered_service(with_artwork = false) {
|
||||
for (i = [0 : 5]) base_segment_service(i, with_artwork);
|
||||
}
|
||||
|
||||
module base_black_service() {
|
||||
base_layered_service(true);
|
||||
}
|
||||
|
||||
module base_complete_service() {
|
||||
base_layered_service(false);
|
||||
}
|
||||
|
||||
module print_orient_base() {
|
||||
translate([0, plate_depth, plate_height])
|
||||
rotate([180, 0, 0]) children();
|
||||
}
|
||||
|
||||
module socket_flange_2d() {
|
||||
translate([0, (socket_front_y + socket_rear_y) / 2])
|
||||
rounded_rect_2d([socket_flange_size[0], socket_flange_size[1]], 3);
|
||||
}
|
||||
|
||||
function socket_outer_y() = rail_size + 2 * rail_clearance
|
||||
+ 2 * socket_end_wall;
|
||||
function socket_rail_axis_y() = socket_front_y - socket_outer_y() / 2;
|
||||
|
||||
module socket_key_local_2d() {
|
||||
translate([0, socket_rail_axis_y()])
|
||||
rounded_rect_2d([socket_key_size[0], socket_key_size[1]], 2);
|
||||
}
|
||||
|
||||
module socket_gusset(side = 1) {
|
||||
cavity = rail_size + 2 * rail_clearance;
|
||||
collar_half_x = (cavity + 2 * socket_side_wall) / 2;
|
||||
near_x = side > 0 ? collar_half_x : -collar_half_x - 2;
|
||||
outer_x = side > 0 ? socket_gusset_outer_x - 2
|
||||
: -socket_gusset_outer_x;
|
||||
gusset_depth = socket_gusset_y_span[1] - socket_gusset_y_span[0];
|
||||
|
||||
// Broad buttress with a short outer reach. Tool-access bores below remove
|
||||
// any material that could obstruct either family of fasteners.
|
||||
hull() {
|
||||
translate([near_x, socket_gusset_y_span[0], socket_flange_size[2]])
|
||||
cube([2, gusset_depth, socket_gusset_height]);
|
||||
translate([outer_x, socket_gusset_y_span[0], socket_flange_size[2]])
|
||||
cube([2, gusset_depth, 2]);
|
||||
}
|
||||
}
|
||||
|
||||
module socket_positive() {
|
||||
cavity = rail_size + 2 * rail_clearance;
|
||||
outer_x = cavity + 2 * socket_side_wall;
|
||||
outer_y = socket_outer_y();
|
||||
|
||||
translate([0, 0, -socket_key_size[2]])
|
||||
linear_extrude(height = socket_key_size[2] + eps)
|
||||
socket_key_local_2d();
|
||||
linear_extrude(height = socket_flange_size[2]) socket_flange_2d();
|
||||
translate([-outer_x / 2, socket_front_y - outer_y,
|
||||
socket_flange_size[2]])
|
||||
cube([outer_x, outer_y, socket_capture]);
|
||||
socket_gusset(-1);
|
||||
socket_gusset(1);
|
||||
}
|
||||
|
||||
module socket_cutouts() {
|
||||
cavity = rail_size + 2 * rail_clearance;
|
||||
collar_half_x = (cavity + 2 * socket_side_wall) / 2;
|
||||
translate([-cavity / 2, socket_rail_axis_y() - cavity / 2,
|
||||
socket_flange_size[2]])
|
||||
cube([cavity, cavity, socket_capture + eps]);
|
||||
|
||||
// Four base mounting holes with clear vertical driver access.
|
||||
for (x = [-socket_mount_x, socket_mount_x], y = socket_mount_ys) {
|
||||
translate([x, y, -socket_key_size[2] - eps])
|
||||
cylinder(h = socket_flange_size[2] + socket_key_size[2] + 2 * eps,
|
||||
d = screw_clearance_diameter);
|
||||
translate([x, y, socket_flange_size[2]])
|
||||
cylinder(h = socket_gusset_height + eps,
|
||||
d = socket_mount_tool_diameter);
|
||||
}
|
||||
|
||||
// Two screw levels through both side walls create four M5 clamp points.
|
||||
for (h = rail_screw_heights) {
|
||||
translate([-socket_flange_size[0] / 2, socket_rail_axis_y(),
|
||||
socket_flange_size[2] + h])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(h = socket_flange_size[0], d = rail_screw_diameter);
|
||||
translate([-collar_half_x - eps, socket_rail_axis_y(),
|
||||
socket_flange_size[2] + h])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(h = rail_screw_head_depth + eps,
|
||||
d = rail_screw_head_diameter);
|
||||
translate([collar_half_x - rail_screw_head_depth,
|
||||
socket_rail_axis_y(),
|
||||
socket_flange_size[2] + h])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(h = rail_screw_head_depth + eps,
|
||||
d = rail_screw_head_diameter);
|
||||
|
||||
// Driver tunnels exist only outside the collar, preserving the side
|
||||
// wall while allowing a socket driver through each buttress.
|
||||
translate([-socket_gusset_outer_x - eps, socket_rail_axis_y(),
|
||||
socket_flange_size[2] + h])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(h = socket_gusset_outer_x - collar_half_x + 2 * eps,
|
||||
d = rail_tool_diameter);
|
||||
translate([collar_half_x - eps, socket_rail_axis_y(),
|
||||
socket_flange_size[2] + h])
|
||||
rotate([0, 90, 0])
|
||||
cylinder(h = socket_gusset_outer_x - collar_half_x + 2 * eps,
|
||||
d = rail_tool_diameter);
|
||||
}
|
||||
}
|
||||
|
||||
module socket_service() {
|
||||
difference() {
|
||||
socket_positive();
|
||||
socket_cutouts();
|
||||
}
|
||||
}
|
||||
|
||||
module socket_print() {
|
||||
translate([socket_flange_size[0] / 2, -socket_rear_y,
|
||||
socket_key_size[2]]) socket_service();
|
||||
}
|
||||
|
||||
module reference_rail() {
|
||||
color([0.08, 0.08, 0.09, 0.8])
|
||||
translate([plate_width / 2 - rail_size / 2,
|
||||
datum_y + socket_rail_axis_y() - rail_size / 2,
|
||||
plate_height + socket_flange_size[2]])
|
||||
cube([rail_size, rail_size, 400]);
|
||||
}
|
||||
|
||||
module reference_feet() {
|
||||
foot_points = concat(
|
||||
[[grid_xs[0], rear_foot_ys],
|
||||
[grid_xs[len(grid_xs)-1], rear_foot_ys]],
|
||||
[[grid_xs[0], grid_ys[len(grid_ys)-1]],
|
||||
[grid_xs[len(grid_xs)-1], grid_ys[len(grid_ys)-1]]]
|
||||
);
|
||||
for (p = foot_points) {
|
||||
color([0.05, 0.05, 0.05, 0.8]) {
|
||||
translate([p[0], p[1], -foot_nut_height])
|
||||
cylinder(h = foot_nut_height, d = foot_nut_diameter);
|
||||
translate([p[0], p[1], -foot_nut_height - foot_pad_height])
|
||||
cylinder(h = foot_pad_height, d = foot_pad_diameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Two-piece calibration coupon, modeled directly in print orientation.
|
||||
coupon_size = [165, 110, 3.2];
|
||||
coupon_boss_height = 15;
|
||||
coupon_rail_xs = [20, 60, 100, 140];
|
||||
coupon_rail_y = 23;
|
||||
coupon_fit_center = [82.5, 90];
|
||||
coupon_fit_outer = [64, 34];
|
||||
coupon_fit_height = 6;
|
||||
coupon_key_center = [200, 90];
|
||||
coupon_key_cap = [56, 26, 2];
|
||||
coupon_key_handle = [40, 6, 8];
|
||||
|
||||
assert(coupon_fit_height == socket_key_size[2],
|
||||
"Coupon socket depth must match the production tongue");
|
||||
assert(len(coupon_rail_gaps) == len(coupon_rail_xs),
|
||||
"Each rail gauge must have one layout position");
|
||||
assert(len([for (gap = coupon_rail_gaps)
|
||||
if (gap == rail_size + 2 * rail_clearance) gap]) == 1,
|
||||
"Production rail cavity must appear once in the coupon gauges");
|
||||
assert(coupon_rail_xs[0]
|
||||
- (coupon_rail_gaps[len(coupon_rail_gaps) - 1]
|
||||
+ 2 * socket_side_wall) / 2 >= 0
|
||||
&& coupon_rail_xs[len(coupon_rail_xs) - 1]
|
||||
+ (coupon_rail_gaps[len(coupon_rail_gaps) - 1]
|
||||
+ 2 * socket_side_wall) / 2 <= coupon_size[0],
|
||||
"Rail gauges exceed the main coupon width");
|
||||
assert(coupon_fit_center[0] - coupon_fit_outer[0] / 2 >= 0
|
||||
&& coupon_fit_center[0] + coupon_fit_outer[0] / 2 <= coupon_size[0]
|
||||
&& coupon_fit_center[1] - coupon_fit_outer[1] / 2 >= 0
|
||||
&& coupon_fit_center[1] + coupon_fit_outer[1] / 2 <= coupon_size[1],
|
||||
"Coupon fit socket exceeds the main coupon outline");
|
||||
assert(coupon_key_center[0] - coupon_key_cap[0] / 2 > coupon_size[0],
|
||||
"Coupon tongue must remain a physically separate print");
|
||||
assert(coupon_key_center[0] + coupon_key_cap[0] / 2 <= 256
|
||||
&& coupon_key_center[1] + coupon_key_cap[1] / 2 <= 256,
|
||||
"Coupon and separate tongue exceed the X2D build area");
|
||||
|
||||
module coupon_outline_2d() {
|
||||
translate([coupon_size[0] / 2, coupon_size[1] / 2])
|
||||
rounded_rect_2d([coupon_size[0], coupon_size[1]], 4);
|
||||
}
|
||||
|
||||
module coupon_accent_2d(clearance = 0) {
|
||||
offset(delta = clearance)
|
||||
translate([8, 6]) square([75, grid_line_width]);
|
||||
}
|
||||
|
||||
module coupon_positive() {
|
||||
linear_extrude(height = coupon_size[2]) coupon_outline_2d();
|
||||
for (i = [0 : len(coupon_insert_diameters) - 1])
|
||||
translate([14 + i * 24, 56, coupon_size[2]])
|
||||
cylinder(h = coupon_boss_height - coupon_size[2], d = insert_boss_diameter);
|
||||
|
||||
// Four production-style closed collars, left-to-right 20.0-20.3 mm.
|
||||
for (i = [0 : len(coupon_rail_gaps) - 1]) {
|
||||
gap = coupon_rail_gaps[i];
|
||||
outer_x = gap + 2 * socket_side_wall;
|
||||
outer_y = gap + 2 * socket_end_wall;
|
||||
translate([coupon_rail_xs[i], coupon_rail_y,
|
||||
coupon_size[2] - eps])
|
||||
difference() {
|
||||
translate([-outer_x / 2, -outer_y / 2, 0])
|
||||
cube([outer_x, outer_y, socket_capture + eps]);
|
||||
translate([-gap / 2, -gap / 2, -eps])
|
||||
cube([gap, gap, socket_capture + 3 * eps]);
|
||||
}
|
||||
}
|
||||
|
||||
// Full-depth test socket surrounding the production-size through-slot.
|
||||
translate([coupon_fit_center[0], coupon_fit_center[1],
|
||||
coupon_size[2] - eps])
|
||||
rounded_prism(
|
||||
[coupon_fit_outer[0], coupon_fit_outer[1],
|
||||
coupon_fit_height - coupon_size[2] + eps],
|
||||
3
|
||||
);
|
||||
}
|
||||
|
||||
module coupon_common_cutouts() {
|
||||
for (i = [0 : len(coupon_insert_diameters) - 1]) {
|
||||
x = 14 + i * 24;
|
||||
pilot = coupon_insert_diameters[i];
|
||||
translate([x, 56, coupon_boss_height - insert_flange_depth])
|
||||
cylinder(h = insert_flange_depth + eps, d = insert_flange_diameter);
|
||||
translate([x, 56, coupon_boss_height - insert_flange_depth - insert_body_length])
|
||||
cylinder(h = insert_body_length + eps, d = pilot);
|
||||
translate([x, 56, -eps])
|
||||
cylinder(h = coupon_boss_height - insert_flange_depth
|
||||
- insert_body_length + 2 * eps,
|
||||
d = screw_clearance_diameter);
|
||||
}
|
||||
translate([coupon_fit_center[0], coupon_fit_center[1], -eps])
|
||||
rounded_prism(
|
||||
[socket_key_size[0] + 2 * socket_key_clearance,
|
||||
socket_key_size[1] + 2 * socket_key_clearance,
|
||||
coupon_fit_height + 2 * eps],
|
||||
2 + socket_key_clearance
|
||||
);
|
||||
}
|
||||
|
||||
module coupon_key() {
|
||||
// Separate full-size tongue. A 45-degree stop shoulder remains
|
||||
// support-free, and the vertical rib provides a grip for removal.
|
||||
translate([coupon_key_center[0], coupon_key_center[1], 0]) {
|
||||
rounded_prism(socket_key_size, 2);
|
||||
hull() {
|
||||
translate([0, 0, socket_key_size[2] - eps])
|
||||
rounded_prism(
|
||||
[socket_key_size[0], socket_key_size[1], eps], 2);
|
||||
translate([0, 0,
|
||||
socket_key_size[2] + coupon_key_cap[2] - eps])
|
||||
rounded_prism(
|
||||
[coupon_key_cap[0], coupon_key_cap[1], eps], 3);
|
||||
}
|
||||
translate([0, 0, socket_key_size[2] + coupon_key_cap[2] - eps])
|
||||
rounded_prism(
|
||||
[coupon_key_handle[0], coupon_key_handle[1],
|
||||
coupon_key_handle[2] + eps],
|
||||
2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module coupon_black() {
|
||||
difference() {
|
||||
coupon_positive();
|
||||
coupon_common_cutouts();
|
||||
translate([0, 0, -eps])
|
||||
linear_extrude(height = inlay_depth + 2 * eps)
|
||||
coupon_accent_2d(inlay_clearance);
|
||||
}
|
||||
}
|
||||
|
||||
module coupon_accent() {
|
||||
linear_extrude(height = inlay_depth) coupon_accent_2d();
|
||||
}
|
||||
|
||||
module coupon_complete() {
|
||||
union() {
|
||||
difference() {
|
||||
coupon_positive();
|
||||
coupon_common_cutouts();
|
||||
}
|
||||
coupon_key();
|
||||
}
|
||||
}
|
||||
|
||||
module assembly() {
|
||||
color([0.035, 0.035, 0.04]) base_black_service();
|
||||
color([0.05, 0.25, 0.8]) accent_service();
|
||||
color([0.035, 0.035, 0.04])
|
||||
translate([plate_width / 2, datum_y, plate_height]) socket_service();
|
||||
if (show_references) {
|
||||
reference_rail();
|
||||
reference_feet();
|
||||
}
|
||||
}
|
||||
|
||||
if (part == "assembly") assembly();
|
||||
else if (part == "base") print_orient_base() base_complete_service();
|
||||
else if (part == "base_black") print_orient_base() base_black_service();
|
||||
else if (part == "base_segment")
|
||||
print_orient_base() base_segment_service(segment_index, false);
|
||||
else if (part == "base_black_segment")
|
||||
print_orient_base() base_segment_service(segment_index, true);
|
||||
else if (part == "base_accent") print_orient_base() accent_service();
|
||||
else if (part == "socket") socket_print();
|
||||
else if (part == "coupon") coupon_complete();
|
||||
else if (part == "coupon_black") coupon_black();
|
||||
else if (part == "coupon_key") coupon_key();
|
||||
else if (part == "coupon_accent") coupon_accent();
|
||||
else assert(false, str("Unknown part: ", part));
|
||||
Reference in New Issue
Block a user