Skip to content

Instantly share code, notes, and snippets.

@dov
Created February 10, 2024 22:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dov/f840e12f24804501540dac1794331cb9 to your computer and use it in GitHub Desktop.
Save dov/f840e12f24804501540dac1794331cb9 to your computer and use it in GitHub Desktop.
A shampoo holder created with CadQuery
#!/usr/bin/env python
######################################################################
# A cup holder
# 2024-02-10 Sat
# Dov Grobgeld <dov.grobgeld@gmail.com>
#
# This code is in the public domain.
#
# The shower pole shampoo holder is built as follows
#
# - The cyl part for attachements to the shower 25 mm diam pole)
# - The cup part (for holding the soap)
#
# The cup is built as follows:
# - Make a general cup by extruction and cut
# - Remove a hole at the bottom of the cup for inserting the bottom
# part
# - The bottom part is a cylinder with a hexagonal grid of holes
# - The bottom part is unioned into the cup
#
######################################################################
import cadquery as cq
from cadquery import exporters
import math
def make_cup(inner_radius,
inner_height,
thickness,
bottom_fillet_radius,
rim_fillet_radius,
):
outer_radius = inner_radius + thickness
outer_height = inner_height +thickness
return (cq
.Workplane("XY")
.circle(outer_radius)
.extrude(outer_height)
.faces(">Z")
.workplane()
.circle(inner_radius)
# To do a pocket need cut
.extrude(until=-inner_height,
combine='cut')
.faces("<Z[-1] or <Z[-2]").fillet(bottom_fillet_radius) # Smooth inner and outer cup bottom
.faces(">Z").fillet(rim_fillet_radius) # Smooth the rim
)
def make_hollow_cylinder(inner_radius,
height,
thickness,
rim_fillet_radius):
outer_radius = inner_radius + thickness
return (cq
.Workplane("XY")
.circle(outer_radius)
.circle(inner_radius)
.extrude(until=height)
.faces(">Z or <Z").fillet(rim_fillet_radius) # Smooth the rims
)
# Generate all the points lying on a hexagonal (honycomb) grid
# inside a circle.
def make_hex_points_in_circle(big_circle_radius,
hex_grid_step):
dy = hex_grid_step * math.sin(math.radians(60))
num_rows = math.floor(big_circle_radius / dy)
num_x = math.floor(big_circle_radius/ hex_grid_step)
for y_idx in range(num_rows*2+1):
centered_y = y_idx-num_rows
y = centered_y * dy
for x_idx in range(num_x*2+2):
x_offset = 0 if centered_y%2==0 else -hex_grid_step/2
x = x_offset + (x_idx-num_x) * hex_grid_step
if x**2 + y**2 < big_circle_radius**2:
yield (x,y)
# Parameters
thickness=3
cup_inner_radius = 40
cup_height = 35
bottom_fillet_radius=2
rim_fillet_radius = 1
hole_radius = 3
cyl_inner_radius = 12.6
cyl_height = 25
holes_grid_spacing = 13
hole_fillet_radius = 0.5
cup = make_cup(inner_radius = cup_inner_radius,
inner_height = cup_height-thickness,
thickness = thickness,
bottom_fillet_radius = bottom_fillet_radius,
rim_fillet_radius = rim_fillet_radius)
# remove a cylinder from the cup to make space for the bottom
bottom_radius = cup_inner_radius - bottom_fillet_radius*2
bottom_mask = (cq.Workplane('XY')
.circle(bottom_radius)
.extrude(thickness))
cup = cup - bottom_mask
# Create the bottom by a cylinder with holes
hole_points = make_hex_points_in_circle(cup_inner_radius-thickness-hole_radius*2,
holes_grid_spacing)
#print(f'{hole_points=}')
bottom = (cq.Workplane('XY')
.circle(bottom_radius+bottom_fillet_radius) # Make this bigger to fix fillet at edges
.pushPoints(hole_points)
.circle(hole_radius)
.extrude(thickness)
.edges('>Z or <Z').fillet(hole_fillet_radius)
).intersect(bottom_mask)
cup += bottom
# The pole connector cylinder
cyl = make_hollow_cylinder(inner_radius = cyl_inner_radius,
height = cyl_height,
thickness = thickness,
rim_fillet_radius = rim_fillet_radius)
result = cyl + cup.translate(cq.Vector(cup_inner_radius+cyl_inner_radius+thickness,0,0))
exporters.export(result, 'shampoo-holder.stl')
print('ok')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment