diff --git a/README.md b/README.md index 074397a..3e80129 100644 --- a/README.md +++ b/README.md @@ -28,3 +28,10 @@ To generate the box and lid STL file: ```bash python parametric_box.py ``` + +### pill_container +Parameterized pill containers with a sliding lid. +To generate the box and lid STL file: +```bash +python pill_container.py +``` diff --git a/pill_container.py b/pill_container.py new file mode 100644 index 0000000..f706813 --- /dev/null +++ b/pill_container.py @@ -0,0 +1,118 @@ +#!/bin/python3 +from __future__ import annotations + +import json +import subprocess +import sys +from pathlib import Path + +from utils import ask_float, ask_int, ask_yes_no + + +BASE_DIR = Path(__file__).resolve().parent +SCAD_DIR = BASE_DIR / "pill_container" +OUT_DIR = BASE_DIR / "stl" + +DAYS_OF_WEEK = [ + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday", +] + + +def render_scad( + scad_file: Path, + out_file: Path, + inner_dimension: float, + cases: int, + engraving: str, +) -> None: + """Render one pill container to an STL file.""" + + cmd = [ + "openscad", + "-D", + f"X={inner_dimension}", + "-D", + f"cases={cases}", + "-D", + f"engraving={json.dumps(engraving)}", + "-D", + 'part="plate"', + "-o", + str(out_file), + str(scad_file), + ] + + result = subprocess.run( + cmd, + cwd=BASE_DIR, + text=True, + capture_output=True, + ) + + if result.returncode != 0: + print(f"\nFailed to render {out_file.name}") + + if result.stdout: + print(result.stdout) + + if result.stderr: + print(result.stderr, file=sys.stderr) + + raise SystemExit(result.returncode) + + print(f"Generated {out_file}") + + +def main() -> None: + if not SCAD_DIR.exists(): + raise SystemExit(f"Missing SCAD directory: {SCAD_DIR}") + + scad_file = SCAD_DIR / "pill_container.scad" + + if not scad_file.exists(): + raise SystemExit(f"Missing OpenSCAD file: {scad_file}") + + print("Pill container generator\n") + print("Dimensions less than 20 mm may result in unusable STL files.\n") + + use_day_engraving = ask_yes_no("Generate engraved containers for each day of the week? [y/n]: ") + + inner_dimension = ask_float("Inner compartment dimension in mm (recommended 20mm): ") + + cases = ask_int("Number of compartments (cases): ") + + OUT_DIR.mkdir(parents=True, exist_ok=True) + + if use_day_engraving: + for day in DAYS_OF_WEEK: + output_file = OUT_DIR / f"pill-container-{day.lower()}.stl" + + render_scad( + scad_file=scad_file, + out_file=output_file, + inner_dimension=inner_dimension, + cases=cases, + engraving=day, + ) + else: + output_file = OUT_DIR / "pill-container.stl" + + render_scad( + scad_file=scad_file, + out_file=output_file, + inner_dimension=inner_dimension, + cases=cases, + engraving="", + ) + + print("\nDone.") + + +if __name__ == "__main__": + main() diff --git a/utils.py b/utils.py index 306ef10..c5b832d 100644 --- a/utils.py +++ b/utils.py @@ -26,3 +26,16 @@ def ask_float(prompt: str) -> float: return value except ValueError: print("Please enter a valid number.") + +def ask_yes_no(prompt: str) -> bool: + """Prompt the user for a yes/no answer.""" + while True: + answer = input(prompt).strip().lower() + + if answer in {"y", "yes"}: + return True + + if answer in {"n", "no"}: + return False + + print("Please enter yes or no. [y/n]")