Created
August 6, 2024 01:10
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I posted this on Printables at https://www.printables.com/model/927778-gel-cap-splitter/files