Skip to content

Instantly share code, notes, and snippets.

@psmay
Last active January 28, 2021 19:04
Show Gist options
  • Save psmay/f8ecd3fa2f9ebc94dd2bb58d582c5960 to your computer and use it in GitHub Desktop.
Save psmay/f8ecd3fa2f9ebc94dd2bb58d582c5960 to your computer and use it in GitHub Desktop.
MPMD Magnetic Bed Clip plus way too many parameters
/*
MPMD Magnetic Bed Clip plus way too many parameters
Copyright © 2021 Peter S. May
Based on MPMD adjustable magnetic bed clip v6 by ksihota
https://www.thingiverse.com/thing:3412636
Licensed under Creative Commons - Attribution 4.0
https://creativecommons.org/licenses/by/4.0/
See this as a Gist:
https://gist.github.com/psmay/f8ecd3fa2f9ebc94dd2bb58d582c5960
- - - - -
This version is from 2021-01-28.
This is one of my first public outings with OpenSCAD, and I hope it's as
educational for you as it has been for me.
A few of the numbers are guessed, but this should be a reasonable facsimile
of the source material. The shape of the stub in the center where the screw
goes was particularly mystifying, but I think I've guessed it correctly.
* 2021-01-28:
* Several parameters have been reworked to make more sense to the
casual adjuster. Many smaller adjustments now just make sense
without having to make separate adjustments elsewhere. Some
values that should have been coupled together now are.
* In particular, the tube capacity can be defined directly
using inner height and inner diameter, and it (mostly) Just
Works. Previously, adjusting the inner diameters also
required adjustments to the outer diameters, the base size,
the tube center spacing, and so forth.
* If you need to increase the tube diameter past about
16.75, you'll probably need to raise the tube center
position as well; otherwise the tubes start to overtake
the tab on the front of the base. (NB: This size of
magnet may leave your print bed area a little crowded
anyway.)
* Manual formatting improvements.
* Light renaming.
* 2021-01-27: Initial.
*/
/* [Floor] */
floor_thickness_outside_tubes = 2;
floor_thickness_inside_tubes = 0.8;
/* [Front tab] */
tab_hole_diameter = 4.75;
// Distance from edge of tab to edge of tab hole
tab_hole_inset = 0;
tab_length = 6;
tab_width = 10;
tab_corner_radius = 4;
/* [Magnet tubes] */
// Y position of the centers of the magnet tubes
tube_center_position = 5;
tube_inner_height = 7.2;
tube_inner_diameter = 12.75;
tube_wall_thickness = 1.625;
// Distance between the facing edges of the magnet tubes
tube_edge_spacing = 10;
/* [Clip holes in magnet tubes] */
tube_clip_hole_diameter = 1.75;
tube_clip_hole_z = 6.5;
tube_clip_hole_max_depth = 3;
// Position of center of first clip hole, relative to tube center
tube_clip_hole_start_position = -1;
tube_clip_hole_count = 4;
tube_clip_hole_center_spacing = 2.5;
/* [Adjustment screw stub] */
stub_height = 3;
stub_width = 7;
// Front-to-back length
stub_outer_diameter = 10;
// Gap between front of stub and main base front
stub_inset = 0;
stub_cylinder_chamfer_height = 2;
stub_cylinder_chamfer_width = 1;
stub_box_chamfer_height = 1.25;
stub_box_chamfer_width = 1;
stub_front_slope = 0.333333333333;
// Z intercept of front slope, relative to the top of the stub
stub_front_z_intercept = 0.166666666667;
// Width of stub top projection
stub_proj_width = 3;
// Length of square part of stub top projection
stub_proj_length = 2;
// Length of pointed part of stub top projection
stub_proj_point_length = 6;
/* [Adjustment screw and nut] */
screw_hole_diameter = 3.25;
// Nut recess diameter, measured across opposite corners
nut_recess_diameter = 6.6;
nut_recess_depth = 3;
/* [Quality parameters] */
$fa = 6;
$fs = 0.07;
// Small amount added to certain dimensions to ensure that adjacent solids are treated as intersecting
q = 0.001;
/* Computed values */
tube_outer_diameter = tube_inner_diameter + (2 * tube_wall_thickness);
tube_center_spacing = tube_edge_spacing + tube_outer_diameter;
base_width = tube_center_spacing + tube_outer_diameter;
base_back_corner_radius = tube_outer_diameter / 2;
base_main_front_position = (-stub_outer_diameter / 2) - stub_inset;
base_front_corner_radius = tube_center_position - base_main_front_position;
stub_height_including_base = stub_height + floor_thickness_outside_tubes;
tube_clip_hole_start_y = tube_center_position + tube_clip_hole_start_position;
stub_proj_point = stub_proj_length + stub_proj_point_length;
tube_full_height = tube_inner_height + floor_thickness_inside_tubes;
color([1, 0, 0])
difference()
{
union()
{
// The base and tab, including the tab hole.
linear_extrude(floor_thickness_outside_tubes)
full_base_2d();
// The outer part of the magnet tubes.
translate([0, 0, q])
linear_extrude(tube_full_height - q)
tube_outer_outline_2d();
// The stub in the middle for supporting the screw.
stub();
}
union()
{
// The holes for the binder clip arms.
tube_clip_holes();
// The height of the hole for the nut.
translate([0, 0, -nut_recess_depth])
linear_extrude(nut_recess_depth * 2)
nut_recess_2d();
// The inner part of the magnet tubes.
translate([0, 0, floor_thickness_inside_tubes])
linear_extrude(tube_full_height)
tube_inner_outline_2d();
}
}
module stub()
{
// The stub in the middle is an odd shape; it is made in steps that
// involve differences of differences. It works approximately thus:
//
// 1. A chamfered cylinder is created.
// 2. A chamfered box is created.
// 3. #1 and #2 are intersected.
// 4. A sloped object is created to represent the negative of the front
// slope of the stub.
// 5. A projecting shape (an extruded "house-shaped" pentagon) is
// created to represent the part of the stub to be excluded from the
// slope cut.
// 6. #5 is subtracted from #4.
// 7. A cylinder is created to represent the negative of the screw hole.
// 8. #6 and #7 are subtracted from #3. This is the final stub shape.
ah = stub_height_including_base - q;
translate([0, 0, q])
difference()
{
group()
{
intersection()
{
rotate_extrude(angle = 360)
half_chamfer_topped_rect(
stub_outer_diameter / 2,
ah,
stub_cylinder_chamfer_width,
stub_cylinder_chamfer_height);
translate([0, stub_outer_diameter, 0])
rotate([90, 0, 0])
linear_extrude(stub_outer_diameter * 2)
chamfer_topped_rect(
stub_width,
ah,
stub_box_chamfer_width,
stub_box_chamfer_height);
}
}
group()
{
x1 = -stub_outer_diameter * 2;
x2 = stub_outer_diameter * 2;
z1 = (stub_front_slope * x1);
z2 = (stub_front_slope * x2);
ch = stub_height_including_base * 2;
cw = stub_width * 2;
difference()
{
group()
{
translate([
-cw / 2,
0,
stub_front_z_intercept + stub_height_including_base
])
rotate([90, 0, 90])
linear_extrude(cw)
trimming_quad(x1, z1, x2, z2, ch);
}
group()
{
linear_extrude(ch)
polygon([
[stub_proj_width / 2, 0],
[stub_proj_width / 2, -stub_proj_length],
[0, -stub_proj_point],
[-stub_proj_width / 2, -stub_proj_length],
[-stub_proj_width / 2, 0]
]);
}
}
translate([0, 0, -ch / 2])
cylinder(r = screw_hole_diameter / 2, h = ch * 2);
}
}
}
module tube_clip_holes()
{
for (xs = [1, -1])
{
scale([xs, 1, 1])
group()
{
translate([
-(tube_center_spacing / 2) + tube_outer_diameter / 2 + q,
tube_clip_hole_start_y,
tube_clip_hole_z
])
rotate([-90, 0, 90])
linear_extrude(tube_clip_hole_max_depth + q)
for (i = [0:tube_clip_hole_count - 1])
{
translate([i * tube_clip_hole_center_spacing, 0, 0])
circle(tube_clip_hole_diameter / 2);
}
}
}
}
module tube_inner_outline_2d()
{
for (xs = [1, -1])
{
scale([xs, 1, 1])
translate([-tube_center_spacing / 2, tube_center_position])
circle(tube_inner_diameter / 2);
}
}
module tube_outer_outline_2d()
{
for (xs = [1, -1])
{
scale([xs, 1, 1])
translate([-tube_center_spacing / 2, tube_center_position])
group()
{
circle(tube_outer_diameter / 2);
square(tube_outer_diameter / 2);
}
}
}
module full_base_2d()
{
group()
{
translate([0, base_main_front_position, 0])
rotate([0, 0, 180])
tab();
main_base_shape_2d();
}
}
module nut_recess_2d()
{
rotate([0, 0, 90])
circle(nut_recess_diameter / 2, $fn = 6);
}
module main_base_shape_2d()
{
translate([0, tube_center_position])
rotate([0, 0, 180])
ghost(base_width, base_front_corner_radius, base_front_corner_radius);
translate([0, tube_center_position])
capsule(base_width, base_back_corner_radius);
}
module tab()
{
difference()
{
// tab land
translate([0, -q, 0])
ghost(tab_width, tab_length + q, tab_corner_radius);
// tab hole
translate([0, tab_hole_inset + tab_hole_diameter / 2])
circle(tab_hole_diameter / 2);
}
}
// 2D shape similar to a line segment with round end caps.
// Drawn horizontally and centered.
module capsule(w, r)
{
_r = abs(r);
_w = abs(w);
width_without_caps = max(0, _w - (2 * _r));
for (xs = [1, -1])
{
scale([xs, 1, 1])
translate([width_without_caps / 2, 0])
circle(_r);
}
square([width_without_caps, 2 * r], center = true);
}
// 2D shape similar to a rectangle with rounded corners on the top but not the bottom.
// Drawn in +y centered on y-axis.
module ghost(w, h, r)
{
_r = abs(r);
_w = abs(w);
_h = abs(h);
theta = (h < 0) ? 180 : 0;
ah = _h - _r;
cut_w = _w + 1;
cut_h = 2 * _r + 1;
rotate([0, 0, theta])
difference()
{
group()
{
translate([0, ah])
capsule(_w, _r);
translate([-_w / 2, 0])
square([_w, ah]);
}
translate([-cut_w / 2, -cut_h])
square([cut_w, cut_h]);
}
}
// 2D shape similar to a rectangle with the upper right corner cut off.
// Drawn in first quadrant.
module half_chamfer_topped_rect(w, h, cw, ch)
{
polygon([
[w, 0],
[w, h - ch],
[w - cw, h],
[0, h],
[0, 0]
]);
}
// 2D shape similar to a rectangle with the upper corners cut off.
// Drawn in +y centered on y axis.
module chamfer_topped_rect(w, h, cw, ch)
{
polygon([
[w / 2, 0],
[w / 2, h - ch],
[w / 2 - cw, h],
[-(w / 2 - cw), h],
[-(w / 2), h - ch],
[-w / 2, 0]
]);
}
// 2D parallelogram made by stretching the line segment (x1,y1)-(x2,y2) upward by h.
module trimming_quad(x1, y1, x2, y2, h)
{
polygon([
[x1, y1],
[x1, y1 + h],
[x2, y2 + h],
[x2, y2]
]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment