Skip to content

Instantly share code, notes, and snippets.

@fsmv
Created August 6, 2024 01:10
Show Gist options
  • Save fsmv/f5c2ce1e5ae3e53e049b50609d4ef3e2 to your computer and use it in GitHub Desktop.
Save fsmv/f5c2ce1e5ae3e53e049b50609d4ef3e2 to your computer and use it in GitHub Desktop.
A gel cap pill filler. Designed for splitting a size #00 pill into two size #2 pills. Should be adjustable to any pill size if you find the correct size for half the volume you started with.
// Copyright 2024 Andy Kallmeyer <[email protected]>
// Model provided under the CC BY-SA 4.0 License
// https://creativecommons.org/licenses/by-sa/4.0/
//
// If you would like to re-use parts of this code instead of creating a
// variation of the whole model, I am providing the code snippets under the
// MIT License https://mit-license.org
//
// If you make a pill filler from this please use the CC BY-SA 4.0 License.
// A gel cap pill filler. Designed for splitting a size #00 pill into two size
// #2 pills. Should be adjustable to any pill size if you find the correct size
// for half the volume you started with.
//
// Use by inserting the halves of 2 empty gel caps with the cube together,
// dumping the larger gel cap contents on top and distributing evenly. Then
// remove the top of the cube and lift out the halves to put them together.
//
// Make sure to clean up the plastic strings left over from printing before use.
// I find that a wet paper towel then drying it off with another after works.
//
// All parameters are in millimeters
distance_between_parts = 10;
cube_size = 32; // cube side length (x and y)
cube_height = 24; // cube z height
corner_radius = 4;
chamfer_not_round = false;
marker_width = 4; // Width of the 3 indent markers on the body that orient the part
marker_depth = 1; // Depth of the 3 indent markers
bowl_bottom_radius = 10; // radius of the flat spot of the bowl
bowl_height = 6; // distance from top of the cap to bottom of the bowl
top_hole_depth = 5; // The distance between the bottom of the bowl and the bottom of the cube cap
bowl_margin = 1.5; // distance between the bowl edge and the cube edge
pill_tolerance = 0.3; // the distance between the pill caps and the edge of the hole (makes the holes a bit bigger)
hole_depth = 1; // Extra depth from the top of the pill caps to inset the caps
hole_margin = 0; // The distance from the edge of the pill hole to the edge of the bowl bottom
pip_tolerance = 0.15; // The extra size of the holes for the indexing pips
pip_radius = 2; // the size of the indexing pips
body_pip_margin = 3.5; // The distance from the edge of the cube to the edge of the two body-side pips
cap_pip_margin = 2.5; // The distance from the bottom edge of the cube to the one cap-side pipssss
// Size of a #2 veggie cap
// Source: https://www.lfacapsulefillers.com/capsule-size-chart
cap_height = 8.8;
cap_diameter = 6.39;
body_height = 15.15;
body_diameter = 6.12;
//pill_height = 17.6; // not used but here for reference
$fn = $preview ? 50 : 100; // Facet number (model resolution, unitless not mm)
zfight = 0.01; // small offset to counter z-fighting
// reduce power for a more straight curve, increase for a curve closer to right angle.
module bowl(height, bottom_radius, top_radius, power = 2) {
bowl_radius = top_radius-bottom_radius;
step = (bowl_radius) / $fn;
points = [
[0,0], [bottom_radius,0],
for (x=[bottom_radius:step:top_radius])
[x, (x-bottom_radius)^power * height / (bowl_radius)^power],
[0,height]];
rotate_extrude()
polygon(points);
}
// c = [x, y, z] outer boundary size of the cube
// r = corner radius
// chamfer_not_round = if true chamfer the corners (45 degree cutoff) instead of round
module rounded_cube(c, r, chamfer_not_round=chamfer_not_round) {
module offset_switcher(r)
if (chamfer_not_round)
offset(delta=r, chamfer=true) children();
else
offset(r=r) children();
translate([r,r,0])
linear_extrude(c.z) {
offset_switcher(r)
square([c.x - 2*r, c.y - 2*r]);
}
}
module bottom_pill(height, diameter) {
radius=diameter/2.0;
translate([0,0,radius]) {
cylinder(height - radius, radius, radius);
difference() {
sphere(radius);
translate([-radius, -radius, zfight])
cube([diameter, diameter, radius]);
}
}
}
module cap_hole() {
padded_diameter = cap_diameter + pill_tolerance*2;
padded_height = cap_height + hole_depth;
translate([0,0, -padded_height])
bottom_pill(padded_height, padded_diameter);
}
module body_hole() {
padded_diameter = body_diameter + pill_tolerance*2;
padded_height = body_height + hole_depth;
translate([0,0, -padded_height])
bottom_pill(padded_height, padded_diameter);
}
// A logistic curve based rounded corners indent.
// Like a cylinder cutout but if you rounded the sharp corners.
//
// size = [x, y, z] outer dimensions of the indent
// curve = higher for closer to 90 degree curves
// technically this is the width of the logistic function.
// Probably should be higher than 4.
module filleted_indent(size, curve = 6) {
step = (4*curve) / $fn;
points = [
[0,0],
for (x=[-curve:step:curve])
[(x+curve)/(2*curve), 1/(1+exp(-x))],
for (x=[-curve:step:curve])
[1 + ((x+curve)/(2*curve)), 1/(1+exp(x))],
[2, 0]];
translate([-size.x/2,0,0])
linear_extrude(size.z)
scale([size.x/2, size.y, 1])
polygon(points);
}
center = cube_size/2;
top_height = bowl_height + top_hole_depth;
bottom_height = cube_height - top_height;
// Distance from the center of the cube to the center of the two pill body pips
// Also the distance for the body side orientation marks
body_pip_distance = sqrt(2*(cube_size/2 - pip_radius - body_pip_margin)^2);
module marks() {
module mark()
filleted_indent([marker_width, marker_depth, cube_height + 2*zfight]);
translate([center, -zfight, -zfight])
mark();
translate([center - body_pip_distance*cos(45), cube_size +zfight, -zfight])
scale([1,-1,1]) mark();
translate([center + body_pip_distance*cos(45), cube_size +zfight, -zfight])
scale([1,-1,1]) mark();
}
module pips(tolerance=false) {
// Don't use the padded_pip_radius here because we want the two sizes
// to be in the same place.
cap_pip_distance = center - pip_radius - cap_pip_margin;
padded_pip_radius = tolerance ? pip_radius + pip_tolerance : pip_radius;
pip_z = bottom_height;
translate([center, center - cap_pip_distance, bottom_height])
sphere(padded_pip_radius);
translate([center - body_pip_distance*cos(45),
center + body_pip_distance*sin(45), bottom_height])
sphere(padded_pip_radius);
translate([center + body_pip_distance*cos(45),
center + body_pip_distance*sin(45), bottom_height])
sphere(padded_pip_radius);
}
module container()
difference() {
rounded_cube([cube_size, cube_size, cube_height], r=corner_radius);
bowl_z = cube_height-bowl_height + zfight;
translate([center, center, bowl_z])
bowl(bowl_height,
top_radius = cube_size/2 - bowl_margin,
bottom_radius = bowl_bottom_radius);
hole_z = bowl_z + zfight;
cap_hole_distance = bowl_bottom_radius -
cap_diameter/2-pill_tolerance - hole_margin;
body_hole_distance = bowl_bottom_radius -
body_diameter/2-pill_tolerance - hole_margin;
translate([center - cap_hole_distance*cos(45),
center - cap_hole_distance*sin(45), hole_z]) cap_hole();
translate([center + cap_hole_distance*cos(45),
center - cap_hole_distance*sin(45), hole_z]) cap_hole();
translate([center - body_hole_distance*cos(45),
center + body_hole_distance*sin(45), hole_z]) body_hole();
translate([center + body_hole_distance*cos(45),
center + body_hole_distance*sin(45), hole_z]) body_hole();
marks();
}
module top()
translate([cube_size, 0, -bottom_height])
difference() {
difference() {
container();
translate([-zfight, -zfight, -zfight])
cube([cube_size + 2*zfight, cube_size + 2*zfight, bottom_height]);
}
pips(true);
}
module bottom()
union() {
difference() {
container();
translate([-zfight, -zfight, bottom_height + zfight])
cube([cube_size + 2*zfight, cube_size + 2*zfight, top_height]);
}
pips(false);
}
//container();
translate([distance_between_parts,0,0])
top();
bottom();
@fsmv
Copy link
Author

fsmv commented Aug 6, 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment