Konubinix' opinionated web of thoughts

New Cap for a Flask

Fleeting

the 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:

  1. a half-sphere shell
  2. with a small nose on one side
  3. 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.