New Cap for a Flask
Fleetingthe problem
I have a flask with a cap whose attach got broken.
I tried gluing the attach, but it won’t resist the day to day use of the flask.
I want to try printing the whole cap in PETG now.
analysis of the geometry
Taking a look at the cap,
We can see that it’s basically:
- a half-sphere shell
- with a small nose on one side
- and a clip on the other side
sphere + nose
By definition, the half sphere is created from a sphere cut with a cavity. Looking more closely at the nose, I can model it as a cylinder slightly offset from the sphere. I chose to model them together so that the cavity can cut in both together.
Let’s get started, the sphere-with-offset-nose part is simply this.
sphere_workplane = cq.Workplane("XY", origin=(0, 0, -nose_height))
sphere_with_nose = (
sphere_workplane.sphere(sphere_radius)
.composeunion(
cq.Workplane("XY", origin=(nose_length,0,0)).cylinder(nose_height*2, sphere_radius)
)
)
Then I cut it in half by subtracting a big box from it.
cap_plain = (
sphere_with_nose
.composecut(
cq.Workplane("XY")
.box(3*sphere_radius, 3*sphere_radius, sphere_radius, centered=(True, True, False))
)
)
Then I can simply “dig” the cavity inside it, like this.
cavity = sphere_workplane.sphere(sphere_radius-wall_width)
with_cavity = cap_plain.composecut(cavity)
add the attach
Let’s look closer at the attach.
It’s actually a simple holed cylinder with an opening at the top to clip with the flask.
And looking at the other side, we can see that it has a small bump that locks the cap open. This looks like an offset cylinder with a smaller radius.
First thing, let’s add the cylinder shape at the correct position relatively to the cap.
attach_workplane = cq.Workplane("XZ", origin=(- sphere_radius - cylinder_radius - cylinder_offset, 0, 0))
attach = (
attach_workplane
.cylinder(cylinder_height, cylinder_radius)
)
with_attach = with_cavity.composeunion(attach)
Then let’s add the bump and cut the hole and the opening (using a trapezoidal form) from it.
with_opening_and_hole = (
with_cavity
.union(attach)
.composeunion(
attach_workplane.move(-bump_depth, bump_z)
.cylinder(bump_height, bump_radius)
)
.composecut(
attach_workplane.circle(hole_radius).extrude(cylinder_height, both=True)
)
.composecut(
attach_workplane
.moveTo(-hole_radius / 2, 0)
.lineTo(hole_radius / 2, 0)
.lineTo(top_width / 2, 10)
.lineTo(-top_width / 2, 10)
.close()
.extrude(10, both=True)
)
)
add the neck
The cap and the attach look quite good, but they are actually separate parts. There is a neck that glue them together.
I chose to model it quite coarsely and cut again the cavity to get a clean interior.
with_neck = (
with_opening_and_hole
.composeunion(
attach_workplane
.moveTo(hole_radius * 1.4 ,0)
.lineTo(0,-cylinder_radius)
.lineTo(2*cylinder_radius, -2*cylinder_radius)
.lineTo(2*cylinder_radius, 0)
.close()
.extrude(cylinder_height/2,both=True)
)
.composecut(cavity)
)
and then, the snap holes
Looking closer at the interior of the cap and the neck of the flask, we can see the snap mechanism.
The small holes in the cap clip onto the flask to prevent it from opening while traveling.
I model them with small boxes that are cut into each side of the bottle.
def snap(direction=1):
return (
cq.Workplane("XY", origin=(0, direction * (sphere_radius - wall_width), - snap_z))
.box(snap_width, snap_depth, snap_height)
)
def sub_snap(direction=1):
return (
cq.Workplane("XY", origin=(0, direction * (sphere_radius - wall_width), 0))
.box(snap_width, sub_snap_depth, sub_snap_height)
)
with_snap_holes = with_neck.composecut(
snap(direction=1)
.union(snap(direction=-1))
.union(sub_snap(direction=1))
.union(sub_snap(direction=-1))
)
the printable model
The last think to do it to assemble the whole thing into a shinny STL file that I can feed to my printer.
printing
To test the snap, I cut the model into a fast printing circle.