diff --git a/README.md b/README.md index c39d319..8f25669 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,22 @@ -# microfinity +# Microfinity [![PyPI](https://img.shields.io/pypi/v/microfinity.svg)](https://pypi.org/project/microfinity/) ![python version](https://img.shields.io/static/v1?label=python&message=3.9%2B&color=blue&style=flat&logo=python) [![CadQuery](https://img.shields.io/static/v1?label=dependencies&message=CadQuery%202.0%2B&color=blue&style=flat)](https://github.com/CadQuery/cadquery) -[![cq-kit](https://img.shields.io/badge/CQ--kit-blue)](https://github.com/michaelgale/cq-kit) ![license](https://img.shields.io/badge/license-MIT-blue.svg) -[![code style: black](https://img.shields.io/badge/code%20style-black-black.svg)](http://github.com/psf/black) -A Python library to make [Gridfinity](https://gridfinity.xyz) compatible objects with [CadQuery](https://github.com/CadQuery/cadquery). +**Gridfinity-compatible storage system with optional sub-grid sizing for more granular bin dimensions.** -The Gridfinity system was created by [Zach Freedman](https://www.youtube.com/c/ZackFreedman) as a versatile system of modular organization and storage modules. This library provides Python classes to create parameterized Gridfinity components including boxes, baseplates, drawer spacers, and rugged storage boxes. +Microfinity extends the [Gridfinity](https://gridfinity.xyz) modular storage system with support for fractional grid units (0.25U and 0.5U increments), allowing you to create bins that better fit your specific items while remaining fully compatible with standard Gridfinity baseplates and bins. -> **Note:** This is a fork of [cq-gridfinity](https://github.com/michaelgale/cq-gridfinity) by Michael Gale. +## Key Features + +- **100% Gridfinity Compatible** - All bins work on standard Gridfinity baseplates +- **Fractional Sizing** - Create bins in 0.25U (10.5mm) or 0.5U (21mm) increments +- **Automatic Drawer Layouts** - Generate optimally-segmented baseplates for any drawer size +- **Multi-Piece Baseplates** - Connection clips join pieces that exceed your build plate +- **Parametric Design** - Full control over holes, scoops, labels, dividers, and more +- **Multiple Export Formats** - STEP, STL, and SVG output ## Installation @@ -29,183 +34,131 @@ pip install -e . ### Dependencies -- [CadQuery](https://github.com/CadQuery/cadquery) -- [cq-kit](https://github.com/michaelgale/cq-kit) +- [CadQuery](https://github.com/CadQuery/cadquery) (>= 2.0) +- [cq-kit](https://github.com/michaelgale/cq-kit) (>= 0.5.6) ## Quick Start -```python -import microfinity -print(microfinity.__version__) -``` +### Create a Standard Box ```python -from microfinity import * +from microfinity import GridfinityBox -# Make a simple box -box = GridfinityBox(3, 2, 5, holes=True, scoops=True, labels=True) -box.save_stl_file() -# Output: gf_box_3x2x5_holes_scoops_labels.stl +# 2x3x4 box with magnets, scoops, and labels +box = GridfinityBox(2, 3, 4, holes=True, scoops=True, labels=True) +box.save_stl_file() # Saves: gf_box_2x3x4_holes_scoops_labels.stl ``` -## CLI Commands +### Create a Fractional Box -### `microfinity-box` +```python +from microfinity import GridfinityBox -```bash -microfinity-box 2 3 5 -m -f stl +# 1.25U x 0.5U x 3U box - fits in tighter spaces +box = GridfinityBox(1.25, 0.5, 3, micro_divisions=4) +box.save_stl_file() # Saves: gf_box_1.25x0.50x3_micro4.stl ``` -### `microfinity-base` - -```bash -microfinity-base 7 4 -s -f stl -``` +### Create a Baseplate -### `microfinity-rugged` +```python +from microfinity import GridfinityBaseplate -```bash -microfinity-rugged 5 4 6 --box --lid -f stl +# 4x3 baseplate with corner mounting screws +baseplate = GridfinityBaseplate(4, 3, corner_screws=True) +baseplate.save_stl_file() ``` -## Classes - -### Geometry Classes - -- `GridfinityBox` - Boxes with dividers, scoops, labels, magnet holes -- `GridfinitySolidBox` - Solid boxes without interior cutout -- `GridfinityBaseplate` - Baseplates with optional mounting tabs and notches -- `GridfinityBaseplateLayout` - Tiled baseplate layouts with automatic partitioning for large prints -- `GridfinityConnectionClip` - Connection clips for joining baseplate pieces -- `GridfinityDrawerSpacer` - Spacers for fitting baseplates in drawers -- `GridfinityRuggedBox` - Rugged storage boxes with lids and handles - -### Utility Classes - -- `GridfinityExporter` - Export utility for STEP, STL, SVG formats -- `SVGView` - Enum for SVG view directions - -### Enums - -- `EdgeMode` - Edge treatment options for baseplate layouts -- `SegmentationMode` - Partitioning strategies for large baseplates -- `ToleranceMode` - Tolerance handling for baseplate fitting - -## Export - -The `GridfinityExporter` class provides a unified interface for exporting Gridfinity objects to various file formats. - -### Basic Usage +### Generate a Drawer Layout ```python -from microfinity import GridfinityBox, GridfinityExporter, SVGView - -# Create a box -box = GridfinityBox(2, 2, 4, holes=True) - -# Export using the exporter -exporter = GridfinityExporter(box) -exporter.save_step_file("box.step") -exporter.save_stl_file("box.stl") -exporter.save_svg_file("box.svg", view=SVGView.ISOMETRIC) -``` - -### Direct Class Methods (Backward Compatible) +from microfinity import GridfinityBaseplateLayout -All geometry classes also have export methods directly available: +# Automatically segment a drawer into printable pieces +layout = GridfinityBaseplateLayout( + drawer_x_mm=450, + drawer_y_mm=380, + build_plate_x_mm=220, + build_plate_y_mm=220, +) -```python -box = GridfinityBox(2, 2, 4) -box.save_stl_file() # Auto-generates filename based on parameters -box.save_step_file("custom_name.step") -box.save_svg_file("preview.svg") +layout.print_summary() # Shows piece breakdown +layout.export_all("./drawer_plates", file_format="stl") ``` -### SVG Views +## CLI Commands -The `SVGView` enum provides the following view options: +Generate models directly from the command line: -| View | Description | -|------|-------------| -| `SVGView.FRONT` | Front view (+Y direction) | -| `SVGView.BACK` | Back view (-Y direction) | -| `SVGView.LEFT` | Left view (-X direction) | -| `SVGView.RIGHT` | Right view (+X direction) | -| `SVGView.TOP` | Top view (+Z direction) | -| `SVGView.BOTTOM` | Bottom view (-Z direction) | -| `SVGView.ISOMETRIC` | Isometric view (default) | +```bash +# Standard 2x3x5 box with magnet holes +microfinity-box 2 3 5 -m -f stl -### Export Options +# Fractional 1.25x2x3 box (quarter-grid) +microfinity-box 1.25 2 3 -f stl -```python -exporter = GridfinityExporter(obj) +# 6x4 baseplate with corner screws +microfinity-base 6 4 -s -f stl -# STL with custom tolerance -exporter.save_stl_file("output.stl", tolerance=0.01, angular_tolerance=0.1) +# Generate drawer layout +microfinity-baseplate-layout -d 450 380 -b 220 220 -o ./drawer -f stl -# SVG with custom styling -exporter.save_svg_file( - "output.svg", - view=SVGView.TOP, - line_width=0.5, - show_hidden=False -) +# Generate calibration test prints +microfinity-calibrate -o ./calibration -f stl ``` -### Baseplate Layout Export +## The Microgrid System -`GridfinityBaseplateLayout` has additional export methods for batch exporting all pieces: +Standard Gridfinity uses a 42mm grid (1U). Microfinity adds support for smaller increments: -```python -from microfinity import GridfinityBaseplateLayout - -layout = GridfinityBaseplateLayout( - length_mm=300, - width_mm=200, - max_piece_u=4 -) +| micro_divisions | Pitch | Increment | Use Case | +|-----------------|---------|-----------|----------| +| 1 | 42.0mm | 1U | Standard Gridfinity | +| 2 | 21.0mm | 0.5U | Half-grid sizing | +| 4 | 10.5mm | 0.25U | Quarter-grid sizing | -# Export all pieces and clips -results = layout.export_all( - output_dir="./output", - formats=["stl", "step"], - prefix="my_baseplate" -) +Fractional bins use smaller "micro-feet" that still index properly on standard baseplates: -# Export preview SVGs only -layout.export_preview(output_dir="./previews") ``` - -## Development - -```bash -python -m venv .venv -source .venv/bin/activate -pip install -e ".[dev]" -pytest +Standard 1U bin: Fractional 0.5U bin: +┌────────────────┐ ┌───────┐ +│ │ │ │ +│ Single 42mm │ │ 21mm │ +│ foot │ │ foot │ +│ │ │ │ +└────────────────┘ └───────┘ ``` -### Running Tests +See [The Microgrid System](docs/microgrid-system.md) for details. -```bash -# Run all tests -pytest +## Documentation -# Run with coverage -pytest --cov=microfinity +- [Getting Started](docs/getting-started.md) - Installation and first steps +- [The Microgrid System](docs/microgrid-system.md) - How fractional sizing works +- **Components:** + - [Boxes](docs/components/boxes.md) - GridfinityBox parameters and features + - [Baseplates](docs/components/baseplates.md) - GridfinityBaseplate options + - [Layouts](docs/components/layouts.md) - Automatic drawer tiling system + - [Spacers](docs/components/spacers.md) - Drawer spacer generation +- [CLI Reference](docs/cli-reference.md) - Command line tools +- [Calibration](docs/calibration.md) - Test prints for tuning fit +- [Export Options](docs/export.md) - File formats and batch export +- [Python API](docs/python-api.md) - Programmatic usage -# Run specific test file -pytest tests/test_box.py +## Examples -# Update golden test baselines -UPDATE_GOLDEN=1 pytest tests/test_golden.py -``` +See the [examples/](examples/) directory for complete scripts: + +- `basic_box.py` - Simple box with common features +- `fractional_bins.py` - Various fractional sizes +- `drawer_layout.py` - Complete drawer layout workflow +- `custom_baseplate.py` - Baseplate with specific options +- `batch_export.py` - Generate families of parts -## References +## Credits -- [Gridfinity wiki](https://gridfinity.xyz) -- [Original cq-gridfinity](https://github.com/michaelgale/cq-gridfinity) by Michael Gale +Microfinity is a fork of [cq-gridfinity](https://github.com/michaelgale/cq-gridfinity) by Michael Gale. The Gridfinity system was created by [Zack Freedman](https://www.youtube.com/c/ZackFreedman). ## License -MIT License. Originally created by [Michael Gale](https://github.com/michaelgale). +MIT License - See [LICENSE](LICENSE) for details. diff --git a/docs/calibration.md b/docs/calibration.md new file mode 100644 index 0000000..9d37a79 --- /dev/null +++ b/docs/calibration.md @@ -0,0 +1,240 @@ +# Calibration + +This guide explains how to calibrate Microfinity for your specific 3D printer to ensure optimal fit. + +## Why Calibrate? + +Every 3D printer has slightly different dimensional accuracy. What fits perfectly on one printer might be too tight or too loose on another. Calibration helps you: + +1. **Verify fractional pocket fit** - Ensure bins fit properly in 0.25U, 0.5U, and 0.75U pockets +2. **Tune clip clearance** - Find the optimal clearance for connection clips + +## Test Print Types + +### 1. Fractional Pocket Tests + +Small baseplate sections with fractional pockets: + +- **0.25U test** - 0.25U pocket + 1U reference pocket +- **0.5U test** - 0.5U pocket + 1U reference pocket +- **0.75U test** - 0.75U pocket + 1U reference pocket + +Each test plate includes: +- The fractional pocket for testing bin fit +- A 1U reference pocket for comparison +- Female connector slots for testing clip fit + +### 2. Clip Clearance Sweep + +A set of 15 separate clips with varying clearances: + +| Clip | Clearance | Fit | +|------|-----------|-----| +| 1 | -0.10mm | Tightest | +| 2 | -0.05mm | Very tight | +| 3 | 0.00mm | Nominal | +| 4 | +0.05mm | Slightly loose | +| 5 | +0.10mm | Loose | +| ... | ... | ... | +| 15 | +0.60mm | Loosest | + +The clips are arranged in order from tightest (left) to loosest (right). + +## Generating Test Prints + +### Using CLI + +```bash +# Generate all test prints +microfinity-calibrate -o ./calibration -f stl + +# Only fractional pocket tests +microfinity-calibrate --fractional -o ./calibration -f stl + +# Only clip clearance sweep +microfinity-calibrate --clips -o ./calibration -f stl +``` + +### Using Python + +```python +from microfinity.calibration import export_test_prints + +# Export all test prints +export_test_prints("./calibration", file_format="stl") +``` + +Or generate individual pieces: + +```python +from microfinity.calibration import ( + generate_fractional_pocket_test, + generate_fractional_pocket_test_set, + generate_clip_clearance_sweep, +) +from microfinity.core.export import GridfinityExporter + +# Generate single fractional test +test_025 = generate_fractional_pocket_test(0.25, include_slots=True) +GridfinityExporter.to_stl(test_025, "test_025.stl") + +# Generate all fractional tests +tests = generate_fractional_pocket_test_set(include_slots=True) +for name, geom in tests.items(): + GridfinityExporter.to_stl(geom, f"test_{name}.stl") + +# Generate clip sweep +clips, clearances = generate_clip_clearance_sweep() +GridfinityExporter.to_stl(clips, "clip_sweep.stl") +``` + +## Calibration Workflow + +### Step 1: Print Test Pieces + +Print the following: +1. All three fractional pocket test plates +2. The clip clearance sweep + +Use your normal print settings for functional parts. + +### Step 2: Test Fractional Pockets + +1. Take a standard 1U Gridfinity bin +2. Test fit in each pocket on the test plates +3. The bin should: + - Sit flat without rocking + - Drop in with minimal force + - Not be so loose it slides around + +If the reference 1U pocket fits well but fractional pockets don't, there may be an issue with your printer's dimensional accuracy at small feature sizes. + +### Step 3: Test Clips + +1. Take each clip from the clearance sweep (left to right) +2. Test it in the female slots on the fractional test plates +3. Find the clip that: + - Snaps in with satisfying feedback + - Holds securely + - Can be removed without excessive force + +### Step 4: Note Your Optimal Clearance + +The clip that fits best corresponds to a clearance value: + +| Clip Position | Clearance | +|---------------|-----------| +| 1 (leftmost) | -0.10mm | +| 2 | -0.05mm | +| 3 | 0.00mm | +| 4 | +0.05mm | +| 5 | +0.10mm | +| 6 | +0.15mm | +| 7 | +0.20mm | +| 8 | +0.25mm | +| 9 | +0.30mm | +| 10 | +0.35mm | +| 11 | +0.40mm | +| 12 | +0.45mm | +| 13 | +0.50mm | +| 14 | +0.55mm | +| 15 (rightmost) | +0.60mm | + +A text file `clip_clearance_values.txt` is included with the test prints for reference. + +### Step 5: Use Your Calibrated Value + +When creating clips for your projects: + +```python +from microfinity import GridfinityConnectionClip + +# Use your calibrated clearance +clip = GridfinityConnectionClip(clip_clearance_mm=0.15) # Example: +0.15mm was best +``` + +Or via CLI: + +```bash +microfinity-baseplate-layout -d 450 380 -b 220 220 --clip-clearance 0.15 -o ./drawer -f stl +``` + +## Typical Values + +Most printers work well with clearances between **+0.10mm and +0.25mm**. + +- **FDM printers** typically need +0.15mm to +0.25mm +- **Resin printers** may need 0.00mm to +0.10mm +- **Well-tuned printers** often work at +0.10mm to +0.15mm + +## Tips + +### Print Quality Matters + +- Use normal layer height (0.2mm is fine) +- Ensure good first layer adhesion +- Let prints cool before testing + +### Material Considerations + +Different materials have different shrinkage: +- **PLA** - Minimal shrinkage, often needs less clearance +- **PETG** - Some shrinkage, moderate clearance +- **ABS/ASA** - More shrinkage, may need more clearance + +Test with the same material you'll use for production parts. + +### Multiple Printers + +If you have multiple printers, run calibration on each. Keep track of optimal clearances per printer. + +### Re-Calibration + +Consider re-calibrating if: +- You change materials +- You adjust printer settings significantly +- You notice fit issues with printed parts + +## Troubleshooting + +### All Clips Too Tight + +Your printer may be over-extruding or your holes may be undersized. Consider: +- Calibrating extrusion multiplier +- Checking for elephant's foot on first layer +- Using even larger clearance values + +### All Clips Too Loose + +Your printer may be under-extruding or your holes may be oversized. Consider: +- Calibrating extrusion multiplier +- Checking for dimensional accuracy +- Using smaller (or negative) clearance values + +### Bins Don't Fit in Fractional Pockets + +Check: +- Is the reference 1U pocket correct? +- Is your printer's XY dimensional accuracy calibrated? +- Are there any warping issues with the test plate? + +## Quick Reference Card + +``` +CALIBRATION WORKFLOW +==================== + +1. Generate: microfinity-calibrate -o ./cal -f stl + +2. Print: - test_fractional_*.stl (3 plates) + - test_clips_clearance_sweep.stl + +3. Test: - Fit 1U bin in all pockets + - Test each clip in slots + - Note best clip number + +4. Calculate: Clearance = -0.10 + (clip_number - 1) * 0.05 + +5. Use: GridfinityConnectionClip(clip_clearance_mm=X) + or --clip-clearance X on CLI +``` diff --git a/docs/cli-reference.md b/docs/cli-reference.md new file mode 100644 index 0000000..a2edaa1 --- /dev/null +++ b/docs/cli-reference.md @@ -0,0 +1,328 @@ +# CLI Reference + +Microfinity provides command-line tools for generating Gridfinity components without writing Python code. + +## Available Commands + +| Command | Description | +|---------|-------------| +| `microfinity-box` | Generate Gridfinity boxes | +| `microfinity-base` | Generate Gridfinity baseplates | +| `microfinity-baseplate-layout` | Generate segmented drawer layouts | +| `microfinity-calibrate` | Generate calibration test prints | + +--- + +## microfinity-box + +Generate customized Gridfinity boxes. + +### Usage + +```bash +microfinity-box LENGTH WIDTH HEIGHT [OPTIONS] +``` + +### Required Arguments + +| Argument | Type | Description | +|----------|------|-------------| +| `LENGTH` | float | Box length in U (1U = 42mm) | +| `WIDTH` | float | Box width in U (1U = 42mm) | +| `HEIGHT` | int | Box height in U (1U = 7mm) | + +### Options + +| Option | Short | Default | Description | +|--------|-------|---------|-------------| +| `--micro` | `-M` | 4 | Micro-grid divisions (1, 2, or 4) | +| `--magnetholes` | `-m` | off | Add magnet/mounting holes | +| `--unsupported` | `-u` | off | Printer-friendly holes (no supports) | +| `--nolip` | `-n` | off | Remove top stacking lip | +| `--scoops` | `-s` | off | Add finger scoops | +| `--labels` | `-l` | off | Add label strips | +| `--ecolite` | `-e` | off | Economy/lite style (no floor) | +| `--solid` | `-d` | off | Solid filled box | +| `--ratio` | `-r` | 1.0 | Solid fill ratio (0.0-1.0) | +| `--lengthdiv` | `-ld` | 0 | Number of lengthwise dividers | +| `--widthdiv` | `-wd` | 0 | Number of widthwise dividers | +| `--wall` | `-wt` | 1.0 | Wall thickness (mm) | +| `--format` | `-f` | step | Output format (step, stl, svg) | +| `--output` | `-o` | auto | Output filename | + +### Examples + +```bash +# Basic 2x3x4 box +microfinity-box 2 3 4 -f stl + +# Box with all features +microfinity-box 2 3 4 -m -s -l -f stl + +# Fractional box (quarter-grid) +microfinity-box 1.25 0.5 3 -f stl + +# Standard grid only (no micro) +microfinity-box 2 3 4 --micro 1 -f stl + +# Box with dividers +microfinity-box 4 2 3 -ld 3 -wd 1 -f stl + +# Economy/lite style +microfinity-box 3 2 3 -e -s -f stl + +# Solid box with 50% fill +microfinity-box 2 2 3 -d -r 0.5 -f stl + +# Custom wall thickness +microfinity-box 2 2 3 -wt 1.5 -f stl + +# Custom output filename +microfinity-box 2 3 4 -m -o MyBox.stl +``` + +--- + +## microfinity-base + +Generate Gridfinity baseplates. + +### Usage + +```bash +microfinity-base LENGTH WIDTH [OPTIONS] +``` + +### Required Arguments + +| Argument | Type | Description | +|----------|------|-------------| +| `LENGTH` | float | Baseplate length in U (1U = 42mm) | +| `WIDTH` | float | Baseplate width in U (1U = 42mm) | + +### Options + +| Option | Short | Default | Description | +|--------|-------|---------|-------------| +| `--micro` | `-M` | 4 | Micro-grid divisions (1, 2, or 4) | +| `--screws` | `-s` | off | Add corner screw tabs | +| `--depth` | `-d` | 0 | Extended bottom depth (mm) | +| `--holediam` | `-hd` | 5.0 | Screw hole diameter (mm) | +| `--cskdiam` | `-hc` | 10.0 | Countersink diameter (mm) | +| `--cskangle` | `-ca` | 82 | Countersink angle (degrees) | +| `--format` | `-f` | step | Output format (step, stl, svg) | +| `--output` | `-o` | auto | Output filename | + +### Examples + +```bash +# Basic 6x4 baseplate +microfinity-base 6 4 -f stl + +# With corner screws +microfinity-base 6 4 -s -f stl + +# Fractional baseplate +microfinity-base 5.5 3.25 -f stl + +# Extended depth +microfinity-base 6 4 -d 10 -f stl + +# Custom screw holes +microfinity-base 6 4 -s -hd 4.0 -hc 8.0 -f stl + +# Custom output filename +microfinity-base 6 4 -o MyBaseplate.stl +``` + +--- + +## microfinity-baseplate-layout + +Generate segmented baseplate layouts for drawers larger than your build plate. + +### Usage + +```bash +microfinity-baseplate-layout --drawer X Y --buildplate X Y [OPTIONS] +``` + +### Required Options + +| Option | Short | Description | +|--------|-------|-------------| +| `--drawer X Y` | `-d` | Drawer dimensions in mm | +| `--buildplate X Y` | `-b` | Build plate dimensions in mm | + +### Options + +| Option | Short | Default | Description | +|--------|-------|---------|-------------| +| `--micro` | `-M` | 4 | Micro-grid divisions (1, 2, or 4) | +| `--tolerance` | | 0.5 | Drawer clearance tolerance (mm) | +| `--min-segment` | | 1.0 | Minimum segment size (U) | +| `--clip-pitch` | | 1.0 | Clip spacing (U) | +| `--clip-clearance` | | 0.2 | Clip fit clearance (mm) | +| `--no-clips` | | off | Skip clip export | +| `--clips-only` | | off | Export only clips | +| `--fit-strips` | | off | Also export fit test strips | +| `--fit-strips-only` | | off | Export only fit test strips | +| `--preview` | | off | Export preview assembly | +| `--summary` | | off | Print summary only (no export) | +| `--format` | `-f` | step | Output format (step, stl) | +| `--output` | `-o` | ./output | Output directory | + +### Examples + +```bash +# Print layout summary (no export) +microfinity-baseplate-layout -d 450 380 -b 220 220 --summary + +# Export all pieces and clips +microfinity-baseplate-layout -d 450 380 -b 220 220 -o ./drawer -f stl + +# Export fit test strips first +microfinity-baseplate-layout -d 450 380 -b 220 220 --fit-strips-only -o ./fit -f stl + +# Export without clips +microfinity-baseplate-layout -d 450 380 -b 220 220 --no-clips -o ./drawer -f stl + +# Export only clips +microfinity-baseplate-layout -d 450 380 -b 220 220 --clips-only -o ./clips -f stl + +# Export preview assembly +microfinity-baseplate-layout -d 450 380 -b 220 220 --preview -o ./preview -f step + +# Custom tolerances +microfinity-baseplate-layout -d 450 380 -b 220 220 --tolerance 0.75 --clip-clearance 0.15 -o ./drawer -f stl +``` + +### Output Files + +The command generates: + +1. **Baseplate pieces** - One file per unique piece type, named like: + - `baseplate_5.00x4.00U_joinR_x2.stl` (5x4U piece with right seam, quantity 2) + - `baseplate_2.50x4.00U_fx5.2_x1.stl` (2.5x4U piece with 5.2mm fill, quantity 1) + +2. **Clips** - `clips_x24.stl` (24 clips needed) + +3. **Fit strips** (if requested) - `fit_strip_x_piece_0_0.stl`, etc. + +4. **Preview** (if requested) - Full assembly for visualization + +--- + +## microfinity-calibrate + +Generate calibration test prints for tuning fit. + +### Usage + +```bash +microfinity-calibrate [OPTIONS] +``` + +### Options + +| Option | Short | Default | Description | +|--------|-------|---------|-------------| +| `--output` | `-o` | ./calibration_prints | Output directory | +| `--format` | `-f` | step | Output format (step, stl) | +| `--fractional` | | off | Generate only fractional pocket tests | +| `--clips` | | off | Generate only clip clearance sweep | +| `--quiet` | `-q` | off | Suppress output messages | + +### Examples + +```bash +# Generate all test prints +microfinity-calibrate -o ./calibration -f stl + +# Only fractional pocket tests +microfinity-calibrate --fractional -o ./test_prints -f stl + +# Only clip clearance sweep +microfinity-calibrate --clips -o ./test_prints -f stl +``` + +### Output Files + +1. **Fractional pocket tests** - Small baseplates with fractional pockets: + - `test_fractional_0_25U.stl` - 0.25U pocket + 1U reference + - `test_fractional_0_50U.stl` - 0.5U pocket + 1U reference + - `test_fractional_0_75U.stl` - 0.75U pocket + 1U reference + + Each includes female connector slots for testing clips. + +2. **Clip clearance sweep** - Multiple clips with varying clearances: + - `test_clips_clearance_sweep.stl` - 15 clips from -0.10mm to +0.60mm + - `clip_clearance_values.txt` - Reference guide + +### Calibration Workflow + +1. Print the fractional test plates +2. Print the clip clearance sweep +3. Test each clip in the female slots +4. Find the best snap-fit clip +5. Note the clearance value (e.g., +0.15mm) +6. Use that value: `GridfinityConnectionClip(clip_clearance_mm=0.15)` + +See [Calibration](calibration.md) for detailed instructions. + +--- + +## Common Patterns + +### Generate a Family of Boxes + +```bash +# Create boxes of different heights +for h in 2 3 4 5; do + microfinity-box 2 2 $h -m -s -f stl +done +``` + +### Drawer Workflow + +```bash +# 1. Check layout +microfinity-baseplate-layout -d 450 380 -b 220 220 --summary + +# 2. Print fit strips +microfinity-baseplate-layout -d 450 380 -b 220 220 --fit-strips-only -o ./fit -f stl +# (print and test fit) + +# 3. Export final pieces +microfinity-baseplate-layout -d 450 380 -b 220 220 -o ./drawer -f stl +``` + +### Calibration Workflow + +```bash +# 1. Generate calibration prints +microfinity-calibrate -o ./calibration -f stl + +# 2. Print and test +# 3. Note best clearance (e.g., 0.15mm) + +# 4. Use in future layouts +microfinity-baseplate-layout -d 450 380 -b 220 220 --clip-clearance 0.15 -o ./drawer -f stl +``` + +--- + +## Environment + +All commands display version information: + +``` + _____ _ _ __ _ _ _ ____ + / ____| (_) | |/ _(_) (_) | | _ \ +| | __ _ __ _ __| | |_ _ _ __ _| |_ _ _ | |_) | _____ __ +... + +Version: 0.6.0 +``` + +Output files are saved to the current directory unless `--output` is specified. diff --git a/docs/components/baseplates.md b/docs/components/baseplates.md new file mode 100644 index 0000000..d25bc94 --- /dev/null +++ b/docs/components/baseplates.md @@ -0,0 +1,281 @@ +# Baseplates + +GridfinityBaseplate creates the foundation that bins sit on. This document covers all baseplate parameters and options. + +## Basic Usage + +```python +from microfinity import GridfinityBaseplate + +# Simple 4x3 baseplate +baseplate = GridfinityBaseplate(4, 3) +baseplate.save_stl_file() +``` + +## Constructor + +```python +GridfinityBaseplate(length_u, width_u, **kwargs) +``` + +### Required Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `length_u` | float | Length in U (1U = 42mm). Can be fractional with micro_divisions > 1 | +| `width_u` | float | Width in U (1U = 42mm). Can be fractional with micro_divisions > 1 | + +### Standard Parameters + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `ext_depth` | float | 0 | Extend bottom by this amount (mm) | +| `straight_bottom` | bool | False | Remove bottom chamfer (flat bottom) | + +### Mounting Screws + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `corner_screws` | bool | False | Add countersink screw holes in corners | +| `corner_tab_size` | float | 21 | Size of mounting tab (mm) | +| `csk_hole` | float | 5.0 | Screw hole diameter (mm) | +| `csk_diam` | float | 10.0 | Countersink diameter (mm) | +| `csk_angle` | float | 82 | Countersink angle (degrees) | + +### Micro-Grid Parameters + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `micro_divisions` | int | auto | Grid subdivision (1, 2, or 4). Auto-detects from size | +| `origin_mx` | int | 0 | Global X microcell offset (for multi-piece alignment) | +| `origin_my` | int | 0 | Global Y microcell offset (for multi-piece alignment) | +| `outer_fillet_radius` | float | 4.0 | Corner fillet radius (0 for sharp corners) | + +### Edge System Parameters + +For multi-piece baseplates (see [Layouts](layouts.md)): + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `edge_roles` | dict | all OUTER | Edge roles: OUTER, SEAM, or FILL_OUTER | +| `solid_fill` | dict | all 0.0 | Fill amounts per edge (mm) | +| `notch_positions` | dict | all empty | Notch positions per SEAM edge | + +## Features in Detail + +### Extended Depth + +Add thickness to the bottom for structural strength or mounting: + +```python +# 5mm extra depth +baseplate = GridfinityBaseplate(4, 3, ext_depth=5) +``` + +### Straight Bottom + +Remove the bottom chamfer for a flat bottom surface: + +```python +baseplate = GridfinityBaseplate(4, 3, straight_bottom=True) +``` + +### Corner Screws + +Add countersink mounting holes for permanent installation: + +```python +baseplate = GridfinityBaseplate(4, 3, corner_screws=True) + +# Custom screw dimensions +baseplate = GridfinityBaseplate( + 4, 3, + corner_screws=True, + csk_hole=4.0, # Smaller screw + csk_diam=8.0, # Smaller countersink + csk_angle=90, # 90-degree countersink +) +``` + +Note: `corner_screws=True` automatically sets `ext_depth` to at least 5mm. + +### Fractional Sizing + +Create baseplates with fractional dimensions: + +```python +# Quarter-grid sizing (auto-detects micro_divisions=4) +baseplate = GridfinityBaseplate(3.5, 2.25) + +# Explicit micro_divisions +baseplate = GridfinityBaseplate(3.5, 2.25, micro_divisions=4) + +# Half-grid +baseplate = GridfinityBaseplate(2.5, 3.5, micro_divisions=2) +``` + +The `micro_divisions` is automatically detected from the size: +- Integer sizes: `micro_divisions=1` (backward compatible) +- Fractional sizes: Smallest compatible value (2 or 4) + +## Properties + +Access computed dimensions: + +```python +baseplate = GridfinityBaseplate(4, 3) + +print(baseplate.length) # Length in mm: 168.0 +print(baseplate.width) # Width in mm: 126.0 +print(baseplate.total_height) # Total height including ext_depth +print(baseplate.total_length) # Length including solid fill +print(baseplate.total_width) # Width including solid fill +``` + +## Edge System + +For multi-piece baseplates, edges can have different roles: + +### Edge Roles + +| Role | Description | +|------|-------------| +| `OUTER` | Touches drawer wall - full frame, rounded corners | +| `SEAM` | Joins adjacent piece - has notches for clips | +| `FILL_OUTER` | Outer boundary of fill strip - flat wall | + +```python +from microfinity.parts.baseplate import EdgeRole + +baseplate = GridfinityBaseplate( + 4, 3, + edge_roles={ + "left": EdgeRole.OUTER, + "right": EdgeRole.SEAM, # Will join another piece + "front": EdgeRole.OUTER, + "back": EdgeRole.OUTER, + } +) +``` + +### Solid Fill + +Add solid strips to fill sub-grid gaps in drawers: + +```python +baseplate = GridfinityBaseplate( + 4, 3, + solid_fill={ + "left": 0.0, + "right": 5.2, # 5.2mm fill on right edge + "front": 0.0, + "back": 3.1, # 3.1mm fill on back edge + } +) +``` + +### Notch Positions + +Specify where connector notches are placed on SEAM edges: + +```python +baseplate = GridfinityBaseplate( + 4, 3, + edge_roles={"right": EdgeRole.SEAM, ...}, + notch_positions={ + "right": [2, 6, 10], # Positions in micro-cells + } +) +``` + +**Note:** For most use cases, use `GridfinityBaseplateLayout` which handles all edge configuration automatically. See [Layouts](layouts.md). + +## Rendering and Export + +```python +baseplate = GridfinityBaseplate(4, 3, corner_screws=True) + +# Render geometry +geometry = baseplate.render() + +# Export +baseplate.save_stl_file() # Auto-generated filename +baseplate.save_stl_file("my_baseplate.stl") # Custom filename +baseplate.save_step_file("my_baseplate.step") +baseplate.save_svg_file("my_baseplate.svg") + +# Auto-generated filename +print(baseplate.filename()) # gf_baseplate_4x3_screwtabs +``` + +## Common Configurations + +### Basic Baseplate + +```python +baseplate = GridfinityBaseplate(6, 4) +``` + +### Mountable Baseplate + +```python +baseplate = GridfinityBaseplate(6, 4, corner_screws=True) +``` + +### Thick Baseplate + +```python +baseplate = GridfinityBaseplate(6, 4, ext_depth=10, straight_bottom=True) +``` + +### Fractional Baseplate + +```python +baseplate = GridfinityBaseplate(5.5, 3.25, micro_divisions=4) +``` + +## CLI Usage + +Generate baseplates from the command line: + +```bash +# Basic baseplate +microfinity-base 6 4 -f stl + +# With corner screws +microfinity-base 6 4 -s -f stl + +# Fractional +microfinity-base 5.5 3.25 -f stl + +# With extended depth +microfinity-base 6 4 -d 10 -f stl +``` + +See [CLI Reference](../cli-reference.md) for all options. + +## Multi-Piece Baseplates + +For baseplates larger than your build plate, use `GridfinityBaseplateLayout`: + +```python +from microfinity import GridfinityBaseplateLayout + +layout = GridfinityBaseplateLayout( + drawer_x_mm=450, + drawer_y_mm=380, + build_plate_x_mm=220, + build_plate_y_mm=220, +) + +layout.export_all("./output", file_format="stl") +``` + +This automatically: +- Segments the baseplate into printable pieces +- Configures edge roles (SEAM where pieces join) +- Places connection notches +- Generates clips for assembly +- Adds fill strips for sub-grid gaps + +See [Layouts](layouts.md) for complete documentation. diff --git a/docs/components/boxes.md b/docs/components/boxes.md new file mode 100644 index 0000000..e05a28c --- /dev/null +++ b/docs/components/boxes.md @@ -0,0 +1,349 @@ +# Boxes + +GridfinityBox is the main class for creating storage bins. This document covers all parameters and features. + +## Basic Usage + +```python +from microfinity import GridfinityBox + +# Minimal box: 2U x 3U x 4U +box = GridfinityBox(2, 3, 4) +box.save_stl_file() +``` + +## Constructor + +```python +GridfinityBox(length_u, width_u, height_u, **kwargs) +``` + +### Required Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `length_u` | float | Length in U (1U = 42mm). Can be fractional with micro_divisions > 1 | +| `width_u` | float | Width in U (1U = 42mm). Can be fractional with micro_divisions > 1 | +| `height_u` | int | Height in U (1U = 7mm). Must be integer | + +### Optional Parameters + +#### Micro-Grid + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `micro_divisions` | int | 1 | Grid subdivision: 1 (standard), 2 (half-grid), or 4 (quarter-grid) | + +#### Bottom Features + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `holes` | bool | False | Add magnet/screw mounting holes | +| `unsupported_holes` | bool | False | Printer-friendly holes (no supports needed) | +| `hole_diam` | float | 6.5 | Magnet/bolt hole diameter (mm) | + +#### Interior Features + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `scoops` | bool | False | Add finger scoop cutouts on front wall | +| `scoop_rad` | float | 14 | Scoop radius (mm) | +| `labels` | bool | False | Add label ledge on back wall | +| `label_width` | float | 12 | Label ledge width (mm) | +| `label_height` | float | 10 | Label overhang depth (mm) | + +#### Dividers + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `length_div` | int | 0 | Number of lengthwise divider walls | +| `width_div` | int | 0 | Number of widthwise divider walls | + +#### Solid Fill + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `solid` | bool | False | Solid interior (no cavity) | +| `solid_ratio` | float | 1.0 | Fill ratio: 0.0 (minimal) to 1.0 (full height) | + +#### Top Lip + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `no_lip` | bool | False | Remove the stacking lip | + +#### Wall Thickness + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `wall_th` | float | 1.0 | Wall thickness in mm (range: 0.5 - 2.5) | + +#### Economy Style + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `lite_style` | bool | False | Economical shell (no elevated floor) | + +## Features in Detail + +### Magnet/Screw Holes + +Add mounting holes for magnets or screws to secure bins to baseplates: + +```python +# Standard holes +box = GridfinityBox(2, 2, 3, holes=True) + +# Printer-friendly holes (no support needed) +box = GridfinityBox(2, 2, 3, holes=True, unsupported_holes=True) + +# Custom hole diameter (for different magnet sizes) +box = GridfinityBox(2, 2, 3, holes=True, hole_diam=6.0) +``` + +The holes are placed at the four corners of each grid cell, matching the standard Gridfinity hole pattern. + +### Finger Scoops + +Scoops make it easier to retrieve items from the bin: + +```python +box = GridfinityBox(2, 2, 3, scoops=True) + +# Custom scoop radius +box = GridfinityBox(2, 2, 3, scoops=True, scoop_rad=10) +``` + +Scoops are added along the front edge of each compartment. + +### Label Strips + +Label ledges provide a surface for labels or tags: + +```python +box = GridfinityBox(2, 2, 3, labels=True) + +# Custom label dimensions +box = GridfinityBox(2, 2, 3, labels=True, label_width=15, label_height=12) +``` + +Label strips are added along the back wall of each compartment. + +### Dividers + +Divide the bin into compartments: + +```python +# 3 lengthwise dividers = 4 columns +box = GridfinityBox(4, 2, 3, length_div=3) + +# 2 widthwise dividers = 3 rows +box = GridfinityBox(2, 3, 3, width_div=2) + +# Grid of compartments +box = GridfinityBox(4, 3, 3, length_div=3, width_div=2) # 4x3 grid +``` + +### Solid Boxes + +Solid boxes have no interior cavity - useful for custom modifications: + +```python +# Fully solid +box = GridfinityBox(2, 2, 3, solid=True) + +# Partially solid (50% fill) +box = GridfinityBox(2, 2, 3, solid=True, solid_ratio=0.5) +``` + +Or use the convenience class: + +```python +from microfinity import GridfinitySolidBox + +box = GridfinitySolidBox(2, 2, 3) +``` + +### No Stacking Lip + +Remove the top lip if you don't need stacking: + +```python +box = GridfinityBox(2, 2, 3, no_lip=True) +``` + +### Wall Thickness + +Adjust wall thickness for strength or material savings: + +```python +# Thicker walls (stronger) +box = GridfinityBox(2, 2, 3, wall_th=1.5) + +# Thinner walls (less material) +box = GridfinityBox(2, 2, 3, wall_th=0.8) +``` + +**Constraints:** +- Minimum: 0.5mm +- Maximum: 2.5mm (1.5mm for lite_style) + +### Lite Style + +Economy shell with no elevated floor - uses less material but has restrictions: + +```python +box = GridfinityBox(2, 2, 3, lite_style=True) +``` + +**Constraints:** +- Not compatible with `micro_divisions > 1` +- Not compatible with `solid` +- Not compatible with `holes` +- Maximum wall thickness: 1.5mm + +## Fractional Sizing + +Create bins with fractional dimensions using the micro-grid system: + +```python +# Quarter-grid sizing (0.25U increments) +box = GridfinityBox(1.25, 0.5, 3, micro_divisions=4) + +# Half-grid sizing (0.5U increments) +box = GridfinityBox(1.5, 2.5, 4, micro_divisions=2) +``` + +See [The Microgrid System](../microgrid-system.md) for details. + +## Common Combinations + +### Parts Bin + +```python +box = GridfinityBox(2, 2, 3, holes=True, scoops=True, labels=True) +``` + +### Divided Organizer + +```python +box = GridfinityBox(4, 2, 2, holes=True, length_div=3, width_div=1) +``` + +### Deep Storage + +```python +box = GridfinityBox(2, 2, 6, holes=True, scoops=True) +``` + +### Economy Bin + +```python +box = GridfinityBox(3, 2, 3, lite_style=True, scoops=True) +``` + +### Small Item Tray (Fractional) + +```python +box = GridfinityBox(0.5, 0.5, 2, micro_divisions=4) +``` + +## Properties + +After creating a box, you can access computed properties: + +```python +box = GridfinityBox(2, 3, 4) + +print(box.length) # Length in mm: 84.0 +print(box.width) # Width in mm: 126.0 +print(box.height) # Total height in mm: ~31.8 +print(box.outer_l) # Outer envelope length: 83.5 +print(box.outer_w) # Outer envelope width: 125.5 +print(box.inner_l) # Inner cavity length +print(box.inner_w) # Inner cavity width +print(box.int_height) # Interior height +print(box.floor_h) # Floor height above base +``` + +## Rendering and Export + +```python +box = GridfinityBox(2, 2, 3, holes=True) + +# Render the geometry (usually automatic) +geometry = box.render() + +# Export to files +box.save_stl_file() # Auto-generated filename +box.save_stl_file("my_box.stl") # Custom filename +box.save_step_file("my_box.step") # STEP format +box.save_svg_file("my_box.svg") # SVG projection + +# Get auto-generated filename +print(box.filename()) # gf_box_2x2x3_holes +``` + +## Error Handling + +Common errors and their causes: + +```python +# Invalid micro_divisions +GridfinityBox(2, 2, 3, micro_divisions=3) +# ValueError: micro_divisions must be 1, 2, or 4 + +# Size doesn't align with micro_divisions +GridfinityBox(1.3, 2, 3, micro_divisions=4) +# ValueError: length_u=1.3 must be a multiple of 0.25 when micro_divisions=4 + +# lite_style with micro_divisions +GridfinityBox(1.25, 2, 3, micro_divisions=4, lite_style=True) +# ValueError: lite_style is not supported with micro_divisions > 1 + +# lite_style with solid +GridfinityBox(2, 2, 3, lite_style=True, solid=True) +# ValueError: Cannot select both solid and lite box styles together + +# lite_style with holes +GridfinityBox(2, 2, 3, lite_style=True, holes=True) +# ValueError: Cannot select both holes and lite box styles together + +# Wall too thick +GridfinityBox(2, 2, 3, wall_th=3.0) +# ValueError: Wall thickness cannot exceed 2.5 mm +``` + +## GridfinitySolidBox + +A convenience wrapper for solid boxes: + +```python +from microfinity import GridfinitySolidBox + +# Equivalent to GridfinityBox(..., solid=True) +box = GridfinitySolidBox(2, 2, 3) + +# Can still use other options +box = GridfinitySolidBox(2, 2, 3, holes=True, solid_ratio=0.75) +``` + +## CLI Usage + +Generate boxes from the command line: + +```bash +# Basic box +microfinity-box 2 3 4 -f stl + +# With features +microfinity-box 2 3 4 -m -s -l -f stl + +# Fractional +microfinity-box 1.25 2 3 -f stl + +# With dividers +microfinity-box 4 2 3 -ld 3 -wd 1 -f stl +``` + +See [CLI Reference](../cli-reference.md) for all options. diff --git a/docs/components/layouts.md b/docs/components/layouts.md new file mode 100644 index 0000000..744e8eb --- /dev/null +++ b/docs/components/layouts.md @@ -0,0 +1,437 @@ +# Layouts + +GridfinityBaseplateLayout automatically segments large baseplates into printable pieces and generates connection hardware. + +## Overview + +When a drawer is larger than your 3D printer's build plate, you need to: + +1. Divide the baseplate into smaller pieces +2. Connect the pieces together +3. Fill any sub-grid gaps at the edges + +`GridfinityBaseplateLayout` handles all of this automatically. + +## Basic Usage + +```python +from microfinity import GridfinityBaseplateLayout + +# Create a layout for a 450mm x 380mm drawer +# with a 220mm x 220mm build plate +layout = GridfinityBaseplateLayout( + drawer_x_mm=450, + drawer_y_mm=380, + build_plate_x_mm=220, + build_plate_y_mm=220, +) + +# Print what will be generated +layout.print_summary() + +# Export all pieces +layout.export_all("./drawer_plates", file_format="stl") +``` + +## Constructor + +```python +GridfinityBaseplateLayout( + drawer_x_mm, + drawer_y_mm, + build_plate_x_mm, + build_plate_y_mm, + **kwargs +) +``` + +### Required Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `drawer_x_mm` | float | Drawer interior X dimension (mm) | +| `drawer_y_mm` | float | Drawer interior Y dimension (mm) | +| `build_plate_x_mm` | float | 3D printer build plate X dimension (mm) | +| `build_plate_y_mm` | float | 3D printer build plate Y dimension (mm) | + +### Optional Parameters + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `micro_divisions` | int | 4 | Grid subdivision (1, 2, or 4) | +| `tolerance_mm` | float | 0.5 | Total drawer clearance (mm) | +| `tolerance_mode` | ToleranceMode | CENTERED | How tolerance is applied | +| `min_segment_u` | float | 1.0 | Minimum segment size in U | +| `segmentation_mode` | SegmentationMode | ALIGNED | Partitioning strategy | +| `print_margin_mm` | float | 2.0 | Build plate safety margin (mm) | +| `clip_pitch_u` | float | 1.0 | Spacing between clips (U) | +| `clip_end_margin_u` | float | 0.25 | Keep-out from corners (U) | +| `fill_edges` | tuple | all 4 edges | Which edges get fill strips | + +## How It Works + +### Step 1: Calculate Grid Size + +The layout calculates how many grid cells fit in the drawer: + +``` +Drawer: 450mm x 380mm +Tolerance: 0.5mm +Usable: 449.5mm x 379.5mm + +Grid cells: 449.5 / 42 = 10.70 → 10 full units +Remainder: 449.5 - (10 * 42) = 29.5mm → becomes fill strip +``` + +### Step 2: Segment into Printable Pieces + +The grid is divided into pieces that fit on the build plate: + +``` +Build plate: 220mm +Max segment: 220 / 42 ≈ 5U per piece + +10U total → 2 pieces of 5U each +``` + +### Step 3: Configure Edges + +Each piece gets appropriate edge configuration: + +- **Outer edges**: Touch drawer wall (rounded corners) +- **Seam edges**: Join adjacent pieces (notches for clips) +- **Fill edges**: Have solid fill strips for sub-grid gaps + +### Step 4: Generate Clips + +Clips are generated to connect pieces at seams. + +## Segmentation Modes + +### ALIGNED (Default) + +Internal seams fall on full-U boundaries. Fractional sizes only appear at the perimeter. + +```python +layout = GridfinityBaseplateLayout( + ..., + segmentation_mode=SegmentationMode.ALIGNED +) +``` + +**Advantages:** +- Clean internal seams +- Easier to reason about piece sizes +- Better for most use cases + +### EVEN + +Distributes size evenly across all pieces to minimize tiny end pieces. + +```python +from microfinity import SegmentationMode + +layout = GridfinityBaseplateLayout( + ..., + segmentation_mode=SegmentationMode.EVEN +) +``` + +### MAX_THEN_REMAINDER + +Uses maximum size for all pieces except the last, which gets the remainder. + +```python +layout = GridfinityBaseplateLayout( + ..., + segmentation_mode=SegmentationMode.MAX_THEN_REMAINDER +) +``` + +## Tolerance Modes + +### CENTERED (Default) + +Tolerance is split evenly on all sides: + +```python +layout = GridfinityBaseplateLayout( + ..., + tolerance_mode=ToleranceMode.CENTERED +) +# 0.5mm total → 0.25mm on each side +``` + +### CORNER + +Tolerance is applied to one corner: + +```python +from microfinity import ToleranceMode + +layout = GridfinityBaseplateLayout( + ..., + tolerance_mode=ToleranceMode.CORNER +) +# Grid starts at origin corner +``` + +## Connection System + +### Notches + +Notches are female slots cut into SEAM edges. They accept clips. + + + +### Clips + +`GridfinityConnectionClip` creates the male clips that span seams: + +```python +from microfinity import GridfinityConnectionClip + +# Default clip +clip = GridfinityConnectionClip() + +# With clearance adjustment (for your printer) +clip = GridfinityConnectionClip(clip_clearance_mm=0.15) + +# With lead-in chamfers (easier insertion) +clip = GridfinityConnectionClip(clip_clearance_mm=0.15, lead_in_mm=0.3) +``` + +#### Clip Parameters + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `clip_clearance_mm` | float | 0.0 | Clearance (+ = looser fit) | +| `seam_gap_mm` | float | 0.0 | Gap between pieces | +| `lead_in_mm` | float | 0.0 | Edge chamfer for insertion | +| `axial_tolerance_mm` | float | auto | Length clearance | +| `clip_ct` | int | 1 | Number of clips in render_flat() | + +#### Tuning Clip Fit + +Run calibration to find the best clearance for your printer: + +```bash +microfinity-calibrate -o ./calibration -f stl +``` + +Then use the clearance value: + +```python +clip = GridfinityConnectionClip(clip_clearance_mm=0.15) +``` + +See [Calibration](../calibration.md) for the full workflow. + +## Methods + +### get_layout() + +Returns the calculated layout result: + +```python +result = layout.get_layout() + +print(result.num_pieces) # Total pieces +print(result.grid_size) # (pieces_x, pieces_y) +print(result.clip_count) # Number of clips needed +print(result.total_u) # Total grid size in U +print(result.fill_x_mm) # X-axis fill amount +print(result.fill_y_mm) # Y-axis fill amount +``` + +### print_summary() + +Prints a human-readable layout summary: + +```python +layout.print_summary() +``` + +Output: +``` +============================================================ +GRIDFINITY BASEPLATE LAYOUT +============================================================ + +INPUT: + Drawer: 450.0mm x 380.0mm + Build plate: 220.0mm x 220.0mm + Micro-div: 4 (10.50mm = 0.25U) + Tolerance: 0.5mm (centered) + +GRID: + Total grid: 40mx x 36my + (10.00U x 9.00U) + Fill: X=29.50mm, Y=1.50mm + +SEGMENTATION: + X segments: [20, 20] (micro-cells) + Y segments: [20, 16] (micro-cells) + Grid: 2 x 2 pieces + +... +``` + +### render_piece() + +Render a specific piece: + +```python +geom = layout.render_piece("piece_0_0") + +# Or by grid position +geom = layout.render_piece_at(0, 1) +``` + +### render_preview() + +Render all pieces positioned in the drawer: + +```python +preview = layout.render_preview() + +# Include clips at seams +preview = layout.render_preview(include_clips=True) +``` + +### render_clip_sheet() + +Render clips arranged for batch printing: + +```python +clips = layout.render_clip_sheet() + +# Custom count +clips = layout.render_clip_sheet(count=20) +``` + +### export_all() + +Export all unique pieces: + +```python +files = layout.export_all("./output", file_format="stl") +print(files) # List of exported file paths +``` + +Options: +- `path`: Output directory +- `file_format`: "step" or "stl" +- `include_clips`: Export clip sheet (default: True) + +### export_preview() + +Export the full preview assembly: + +```python +layout.export_preview("./preview.stl", file_format="stl") +``` + +### export_fit_strips() + +Export thin test strips for verifying drawer fit: + +```python +layout.export_fit_strips("./fit_test", file_format="stl") +``` + +## Fit Test Strips + +Before printing all pieces, test the fit with thin edge strips: + +```python +# Get X-dimension test strips (front edge) +x_strips = layout.render_fit_strip_x(strip_width_mm=10) + +# Get Y-dimension test strips (left edge) +y_strips = layout.render_fit_strip_y(strip_width_mm=10) + +# Export all fit strips +layout.export_fit_strips("./fit_test", file_format="stl") +``` + +Print these thin strips, assemble them, and check if they fit the drawer before committing to full pieces. + +## Complete Workflow + +```python +from microfinity import GridfinityBaseplateLayout + +# 1. Create layout +layout = GridfinityBaseplateLayout( + drawer_x_mm=450, + drawer_y_mm=380, + build_plate_x_mm=220, + build_plate_y_mm=220, +) + +# 2. Review the plan +layout.print_summary() + +# 3. Export fit test strips (optional) +layout.export_fit_strips("./fit_test", file_format="stl") +# Print these, test fit in drawer + +# 4. Export all pieces +layout.export_all("./drawer_plates", file_format="stl") + +# 5. Export preview for visualization (optional) +layout.export_preview("./preview.step", include_clips=True) +``` + +## CLI Usage + +```bash +# Print summary +microfinity-baseplate-layout -d 450 380 -b 220 220 --summary + +# Export all pieces +microfinity-baseplate-layout -d 450 380 -b 220 220 -o ./drawer -f stl + +# Export fit strips only +microfinity-baseplate-layout -d 450 380 -b 220 220 --fit-strips-only -o ./fit + +# Export clips only +microfinity-baseplate-layout -d 450 380 -b 220 220 --clips-only -o ./clips + +# Export preview +microfinity-baseplate-layout -d 450 380 -b 220 220 --preview -o ./preview +``` + +See [CLI Reference](../cli-reference.md) for all options. + +## Advanced Configuration + +### Custom Clip Pitch + +```python +layout = GridfinityBaseplateLayout( + ..., + clip_pitch_u=2.0, # Clip every 2U instead of 1U +) +``` + +### Selective Fill Edges + +```python +layout = GridfinityBaseplateLayout( + ..., + fill_edges=("left", "back"), # Only fill left and back edges +) +``` + +### Minimum Segment Size + +Prevent tiny pieces with "flooring logic": + +```python +layout = GridfinityBaseplateLayout( + ..., + min_segment_u=2.0, # No pieces smaller than 2U +) +``` diff --git a/docs/components/spacers.md b/docs/components/spacers.md new file mode 100644 index 0000000..3f890ef --- /dev/null +++ b/docs/components/spacers.md @@ -0,0 +1,140 @@ +# Spacers + +GridfinityDrawerSpacer creates spacer elements that fill gaps around baseplates in drawers. + +## Overview + +When baseplates don't perfectly fill a drawer, spacers can fill the remaining space to keep baseplates from sliding around. + +**Note:** For most use cases, the [Layout System](layouts.md) with integrated fill strips is recommended instead of separate spacers. The layout system calculates optimal piece sizes and integrates fill strips directly into the baseplate pieces. + +## Basic Usage + +```python +from microfinity import GridfinityDrawerSpacer + +# Create a spacer for a specific drawer size +spacer = GridfinityDrawerSpacer(dr_width=450, dr_depth=380) + +# Render the spacer set +geometry = spacer.render_half_set() +geometry.save("spacers.stl") +``` + +## Constructor + +```python +GridfinityDrawerSpacer(dr_width=None, dr_depth=None, **kwargs) +``` + +### Parameters + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `dr_width` | float | None | Drawer width (mm) | +| `dr_depth` | float | None | Drawer depth (mm) | +| `thickness` | float | 4.75 | Spacer thickness (mm) | +| `show_arrows` | bool | True | Add orientation arrows | +| `align_features` | bool | True | Add alignment pegs/holes | +| `front_and_back` | bool | True | Generate front/back spacers | +| `tolerance` | float | 0.5 | Fit tolerance (mm) | + +## Methods + +### best_fit_to_dim() + +Calculate optimal baseplate fit for drawer dimensions: + +```python +spacer = GridfinityDrawerSpacer(dr_width=450, dr_depth=380) +fit_info = spacer.best_fit_to_dim(450, 380) +print(fit_info) # Shows how many U fit and remaining gap +``` + +### render() + +Render a corner spacer: + +```python +corner = spacer.render() +``` + +### render_length_filler() + +Render the front/back filler strip: + +```python +length_filler = spacer.render_length_filler() +``` + +### render_width_filler() + +Render the left/right filler strip: + +```python +width_filler = spacer.render_width_filler() +``` + +### render_full_set() + +Render all spacers positioned for the drawer: + +```python +full_set = spacer.render_full_set() +``` + +### render_half_set() + +Render a minimal set of spacers for printing (symmetric): + +```python +half_set = spacer.render_half_set() +``` + +## When to Use Spacers vs Layout Fill + +### Use Spacers When: + +- You have existing baseplates that don't fill the drawer +- You want removable/adjustable fill pieces +- The drawer dimensions change frequently + +### Use Layout Fill When: + +- Creating a new drawer layout from scratch +- You want integrated, permanent fill +- You need optimal use of drawer space + +```python +# Layout with integrated fill (recommended for new drawers) +from microfinity import GridfinityBaseplateLayout + +layout = GridfinityBaseplateLayout( + drawer_x_mm=450, + drawer_y_mm=380, + build_plate_x_mm=220, + build_plate_y_mm=220, +) +# Fill strips are automatically integrated into pieces +layout.export_all("./output", file_format="stl") +``` + +## Example + +```python +from microfinity import GridfinityDrawerSpacer + +# Create spacer for a specific drawer +spacer = GridfinityDrawerSpacer( + dr_width=450, + dr_depth=380, + thickness=4.75, + tolerance=0.5, +) + +# Export half set for printing +half_set = spacer.render_half_set() + +# If using CadQuery directly: +# cq.exporters.export(half_set, "spacers.stl") +``` diff --git a/docs/export.md b/docs/export.md new file mode 100644 index 0000000..0c35e44 --- /dev/null +++ b/docs/export.md @@ -0,0 +1,278 @@ +# Export Options + +Microfinity supports multiple export formats for different use cases. + +## Supported Formats + +| Format | Extension | Use Case | +|--------|-----------|----------| +| STEP | `.step` | CAD software, further editing | +| STL | `.stl` | 3D printing / slicing | +| SVG | `.svg` | Documentation, previews | + +## Basic Export + +All Gridfinity objects have built-in export methods: + +```python +from microfinity import GridfinityBox + +box = GridfinityBox(2, 2, 3, holes=True) + +# Export to different formats +box.save_stl_file("my_box.stl") +box.save_step_file("my_box.step") +box.save_svg_file("my_box.svg") +``` + +## Auto-Generated Filenames + +If you don't specify a filename, descriptive names are generated automatically: + +```python +box = GridfinityBox(2, 3, 4, holes=True, scoops=True) +box.save_stl_file() +# Saves as: gf_box_2x3x4_holes_scoops.stl + +baseplate = GridfinityBaseplate(4, 3, corner_screws=True) +baseplate.save_stl_file() +# Saves as: gf_baseplate_4x3_screwtabs.stl +``` + +Fractional sizes include micro_divisions: +```python +box = GridfinityBox(1.25, 0.5, 3, micro_divisions=4) +box.save_stl_file() +# Saves as: gf_box_1.25x0.50x3_micro4.stl +``` + +## GridfinityExporter + +For more control, use the `GridfinityExporter` class directly: + +```python +from microfinity import GridfinityBox, GridfinityExporter, SVGView + +box = GridfinityBox(2, 2, 3) + +# Export using static methods +GridfinityExporter.to_step(box.cq_obj, "output.step") +GridfinityExporter.to_stl(box.cq_obj, "output.stl") +GridfinityExporter.to_svg(box.cq_obj, "output.svg", view=SVGView.ISOMETRIC) +``` + +### STL Options + +Control mesh quality for STL export: + +```python +GridfinityExporter.to_stl( + box.cq_obj, + "output.stl", + linear_tolerance=0.01, # Mesh accuracy (mm) + angular_tolerance=0.1, # Angular accuracy (radians) +) +``` + +Lower tolerance values = higher quality but larger file size. + +| Use Case | linear_tolerance | angular_tolerance | +|----------|-----------------|-------------------| +| Quick preview | 0.1 | 0.5 | +| Normal (default) | 0.01 | 0.1 | +| High quality | 0.001 | 0.05 | + +### SVG Views + +SVG projections support multiple view angles: + +```python +from microfinity import SVGView + +# Available views +GridfinityExporter.to_svg(obj, "iso.svg", view=SVGView.ISOMETRIC) +GridfinityExporter.to_svg(obj, "front.svg", view=SVGView.FRONT) +GridfinityExporter.to_svg(obj, "top.svg", view=SVGView.TOP) +GridfinityExporter.to_svg(obj, "right.svg", view=SVGView.RIGHT) +``` + +| View | Description | +|------|-------------| +| `ISOMETRIC` | 3/4 isometric view (default) | +| `FRONT` | Front elevation (XZ plane) | +| `TOP` | Plan view (XY plane) | +| `RIGHT` | Right elevation (YZ plane) | +| `BACK` | Back view | +| `LEFT` | Left view | +| `BOTTOM` | Bottom view | +| `ISOMETRIC_FLAT` | Isometric without pre-rotation | + +## Batch Export + +Export multiple objects efficiently: + +```python +from microfinity import GridfinityBox, GridfinityExporter + +# Create multiple boxes +boxes = [ + (GridfinityBox(1, 1, 3).cq_obj, "box_1x1x3.stl"), + (GridfinityBox(2, 1, 3).cq_obj, "box_2x1x3.stl"), + (GridfinityBox(2, 2, 4).cq_obj, "box_2x2x4.stl"), +] + +# Batch export (sequential - OCCT is not thread-safe) +exported = GridfinityExporter.batch_export( + boxes, + file_format="stl", + strict=False, # Continue on errors +) + +print(f"Exported {len(exported)} files") +``` + +### batch_export() Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `items` | list | List of (geometry, filepath) tuples | +| `file_format` | str | "step" or "stl" | +| `strict` | bool | If True, raise on first error; if False, warn and continue | + +## Layout Export + +`GridfinityBaseplateLayout` has specialized export methods: + +### export_all() + +Export all unique pieces with automatic deduplication: + +```python +from microfinity import GridfinityBaseplateLayout + +layout = GridfinityBaseplateLayout( + drawer_x_mm=450, + drawer_y_mm=380, + build_plate_x_mm=220, + build_plate_y_mm=220, +) + +# Export all pieces +files = layout.export_all( + path="./output", + file_format="stl", + include_clips=True, +) +``` + +Output includes: +- One file per unique piece type (with count in filename) +- Clip sheet for connecting pieces + +### export_preview() + +Export full assembly for visualization: + +```python +layout.export_preview( + filepath="./preview.step", + file_format="step", + include_clips=True, +) +``` + +### export_fit_strips() + +Export thin test strips for verifying fit: + +```python +layout.export_fit_strips( + path="./fit_test", + strip_width_mm=10, + file_format="stl", +) +``` + +## File Paths + +Export methods return the absolute path to the exported file: + +```python +path = box.save_stl_file("my_box.stl") +print(path) # /full/path/to/my_box.stl +``` + +You can also specify a directory: + +```python +box.save_stl_file(path="./output", prefix="custom_") +# Saves as: ./output/custom_2x2x3.stl +``` + +## Working with CadQuery + +If you need the raw CadQuery geometry for further manipulation: + +```python +from microfinity import GridfinityBox +import cadquery as cq + +box = GridfinityBox(2, 2, 3) + +# Get the CadQuery Workplane +geometry = box.cq_obj + +# Or explicitly render +geometry = box.render() + +# Now use CadQuery's export methods +cq.exporters.export(geometry, "output.step") +cq.exporters.export(geometry, "output.stl") +``` + +## CLI Export + +All CLI commands support format selection: + +```bash +# STEP format (default) +microfinity-box 2 2 3 -f step + +# STL format +microfinity-box 2 2 3 -f stl + +# SVG format +microfinity-box 2 2 3 -f svg +``` + +Custom output path: + +```bash +microfinity-box 2 2 3 -f stl -o ./output/my_box.stl +``` + +## Best Practices + +### For 3D Printing + +- Use STL format +- Default tolerance (0.01) is usually sufficient +- Higher tolerance reduces file size if needed + +### For CAD Work + +- Use STEP format +- Preserves precise geometry +- Importable into most CAD software + +### For Documentation + +- Use SVG for clean vector graphics +- ISOMETRIC view shows 3D shape clearly +- TOP view useful for layout diagrams + +### Large Projects + +- Use batch_export() for multiple files +- Use export_all() for layout pieces +- Consider exporting to a dedicated directory diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..9edaa33 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,175 @@ +# Getting Started + +This guide will help you install Microfinity and create your first Gridfinity-compatible parts. + +## Installation + +### From PyPI (Recommended) + +```bash +pip install microfinity +``` + +### From Source + +```bash +git clone https://github.com/nullstack65/microfinity.git +cd microfinity +pip install -e . +``` + +### Dependencies + +Microfinity requires: + +- **Python 3.9+** +- **[CadQuery](https://github.com/CadQuery/cadquery)** (>= 2.0) - The CAD kernel +- **[cq-kit](https://github.com/michaelgale/cq-kit)** (>= 0.5.6) - CadQuery helper utilities + +These are installed automatically when you install microfinity. + +#### Installing CadQuery + +If you have trouble with CadQuery installation, the recommended approach is: + +```bash +# Using conda (most reliable) +conda install -c conda-forge cadquery + +# Or using pip +pip install cadquery +``` + +### Verify Installation + +```python +import microfinity +print(microfinity.__version__) +``` + +## Your First Box + +Let's create a simple Gridfinity box: + +```python +from microfinity import GridfinityBox + +# Create a 2x3x4 box (2U x 3U x 4U) +# That's 84mm x 126mm x ~32mm +box = GridfinityBox(2, 3, 4) + +# Save it as an STL file for 3D printing +box.save_stl_file() +# Output: gf_box_2x3x4.stl +``` + +### Adding Features + +Most bins benefit from additional features: + +```python +from microfinity import GridfinityBox + +box = GridfinityBox( + 2, 3, 4, + holes=True, # Magnet/screw holes in the bottom + scoops=True, # Finger scoops for easy retrieval + labels=True, # Label ledge on the front +) + +box.save_stl_file() +# Output: gf_box_2x3x4_holes_scoops_labels.stl +``` + +## Your First Baseplate + +Baseplates provide the foundation that bins sit on: + +```python +from microfinity import GridfinityBaseplate + +# Create a 4x3 baseplate (168mm x 126mm) +baseplate = GridfinityBaseplate(4, 3) +baseplate.save_stl_file() +# Output: gf_baseplate_4x3.stl +``` + +### Adding Mounting Screws + +For permanent installation: + +```python +from microfinity import GridfinityBaseplate + +baseplate = GridfinityBaseplate(4, 3, corner_screws=True) +baseplate.save_stl_file() +# Output: gf_baseplate_4x3_screwtabs.stl +``` + +## Using the CLI + +You can also generate parts from the command line without writing Python: + +```bash +# Create a 2x3x4 box with magnet holes +microfinity-box 2 3 4 -m -f stl + +# Create a 4x3 baseplate with screw tabs +microfinity-base 4 3 -s -f stl +``` + +See [CLI Reference](cli-reference.md) for all available commands. + +## Exporting Files + +Microfinity supports multiple export formats: + +```python +from microfinity import GridfinityBox + +box = GridfinityBox(2, 2, 3) + +# STL for 3D printing +box.save_stl_file("my_box.stl") + +# STEP for CAD software +box.save_step_file("my_box.step") + +# SVG for documentation +box.save_svg_file("my_box.svg") +``` + +### Auto-Generated Filenames + +If you don't specify a filename, Microfinity generates a descriptive one: + +```python +box = GridfinityBox(2, 3, 4, holes=True, scoops=True) +box.save_stl_file() +# Saves as: gf_box_2x3x4_holes_scoops.stl +``` + +## Understanding Dimensions + +Gridfinity uses a unit system: + +| Dimension | Unit Size | Example | +|-----------|-----------|---------| +| Length/Width | 1U = 42mm | 2U = 84mm | +| Height | 1U = 7mm | 4U = ~32mm (includes base) | + +A "2x3x4" box is: +- 2 units long (84mm) +- 3 units wide (126mm) +- 4 height units (~32mm total height) + +## Next Steps + +Now that you have the basics, explore: + +- **[The Microgrid System](microgrid-system.md)** - Learn about fractional sizing (0.25U, 0.5U) +- **[Boxes](components/boxes.md)** - All box parameters and features +- **[Baseplates](components/baseplates.md)** - Baseplate options +- **[Layouts](components/layouts.md)** - Automatic drawer tiling for large baseplates +- **[CLI Reference](cli-reference.md)** - Command line tools +- **[Examples](../examples/)** - Complete example scripts diff --git a/docs/microgrid-system.md b/docs/microgrid-system.md new file mode 100644 index 0000000..fef5a19 --- /dev/null +++ b/docs/microgrid-system.md @@ -0,0 +1,258 @@ +# The Microgrid System + +The Microgrid System is Microfinity's key extension to standard Gridfinity, enabling fractional bin sizes for more precise organization. + +## Standard Gridfinity Recap + +Standard Gridfinity uses a 42mm grid: + +- **1U (Unit) = 42mm** in the X and Y directions +- Bins have a single foot per unit that indexes into the baseplate +- Bins must be integer multiples: 1x1, 2x1, 2x2, etc. + +``` +Standard 2x2 Baseplate (84mm x 84mm): +┌─────────────────────┬─────────────────────┐ +│ │ │ +│ Cell 0,0 │ Cell 1,0 │ +│ 42mm │ 42mm │ +│ │ │ +├─────────────────────┼─────────────────────┤ +│ │ │ +│ Cell 0,1 │ Cell 1,1 │ +│ 42mm │ 42mm │ +│ │ │ +└─────────────────────┴─────────────────────┘ +``` + +## The Problem + +Sometimes 42mm is too coarse: + +- A tool that's 50mm wide wastes space in a 2U (84mm) bin +- Small items like SD cards don't need a full 42mm cell +- Odd-sized drawers leave awkward gaps + +## The Microgrid Solution + +Microfinity subdivides the standard 42mm grid into smaller increments: + +| micro_divisions | Pitch | Size Name | Description | +|-----------------|-------|-----------|-------------| +| 1 | 42.0mm | 1U | Standard Gridfinity (default) | +| 2 | 21.0mm | 0.5U | Half-grid | +| 4 | 10.5mm | 0.25U | Quarter-grid | + +With `micro_divisions=4`, you can create bins sized in 0.25U (10.5mm) increments: + +- 0.25U, 0.5U, 0.75U, 1U, 1.25U, 1.5U, 1.75U, 2U, ... + +## How It Works + +### Micro-Feet + +Instead of one large foot per unit, fractional bins have multiple smaller "micro-feet": + +``` +Standard 1U Bin (42mm): +┌──────────────────────────────────────┐ +│ │ +│ Single 41.5mm foot │ +│ (42mm - 0.5mm tol) │ +│ │ +└──────────────────────────────────────┘ + +Quarter-Grid 1U Bin (42mm, micro_divisions=4): +┌────────┬────────┬────────┬────────┐ +│ 10mm │ 10mm │ 10mm │ 10mm │ +│ foot │ foot │ foot │ foot │ +└────────┴────────┴────────┴────────┘ + ↑ 0.5mm gaps form the divider ridges +``` + +The 0.5mm gaps between micro-feet create the same profile as the ridges between cells on a standard baseplate. + +### Phase-Locking + +Fractional bins remain **phase-locked** to the 42mm macro grid: + +- The outer envelope is always `size_u * 42mm - 0.5mm` +- A 1.25U bin is `1.25 * 42 - 0.5 = 52.0mm` wide +- This ensures bins align properly regardless of position + +``` +1.25U Bin on Standard Baseplate: +┌────────────────────────────────────────────┬─────────────────────┐ +│ │ │ +│ 1.25U Bin (52mm) │ Empty 0.75U │ +│ │ │ +│ ┌────┬────┬────┬────┬────┐ │ │ +│ │foot│foot│foot│foot│foot│ │ │ +│ └────┴────┴────┴────┴────┘ │ │ +│ │ │ +└────────────────────────────────────────────┴─────────────────────┘ + Cell 0 Cell 1 +``` + +## Creating Fractional Bins + +### Python API + +```python +from microfinity import GridfinityBox + +# Quarter-grid bin (0.25U increments) +box = GridfinityBox(1.25, 0.5, 3, micro_divisions=4) +box.save_stl_file() + +# Half-grid bin (0.5U increments) +box = GridfinityBox(1.5, 2.5, 4, micro_divisions=2) +box.save_stl_file() +``` + +### CLI + +```bash +# Default is micro_divisions=4 (quarter-grid) +microfinity-box 1.25 0.5 3 -f stl + +# Explicit quarter-grid +microfinity-box 1.25 2 3 --micro 4 -f stl + +# Half-grid +microfinity-box 1.5 2.5 4 --micro 2 -f stl + +# Standard (disable micro-grid) +microfinity-box 2 3 4 --micro 1 -f stl +``` + +## Valid Fractional Sizes + +Sizes must be multiples of the micro-pitch: + +| micro_divisions | Valid Increments | Example Sizes | +|-----------------|------------------|---------------| +| 4 | 0.25U | 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, ... | +| 2 | 0.5U | 0.5, 1, 1.5, 2, 2.5, 3, ... | +| 1 | 1U | 1, 2, 3, 4, ... | + +Invalid sizes will raise an error: + +```python +# This will fail - 0.3 is not a multiple of 0.25 +box = GridfinityBox(0.3, 1, 3, micro_divisions=4) +# ValueError: length_u=0.3 must be a multiple of 0.25 when micro_divisions=4 +``` + +## Compatibility + +### Fractional Bins on Standard Baseplates + +**Yes, they work!** Fractional bins index correctly on any standard Gridfinity baseplate because: + +1. Micro-feet are sized to fit in the standard cell profile +2. The gaps between micro-feet align with the baseplate ridges +3. Phase-locking keeps the outer envelope grid-aligned + +### Standard Bins on Fractional Baseplates + +Standard bins also work on fractional baseplates. The baseplate's microcell pockets are designed to accept both standard and fractional feet. + +### Mixing Sizes + +You can freely mix standard and fractional bins on the same baseplate: + +``` +┌─────────────┬─────────┬───────────────────────┐ +│ │ │ │ +│ 1.5U x 1U │ 0.5U x 1│ 2U x 1U │ +│ │ │ │ +├─────────────┴─────────┼───────────────────────┤ +│ │ │ +│ 2U x 1U │ 2U x 1U │ +│ │ │ +└───────────────────────┴───────────────────────┘ +``` + +## Fractional Baseplates + +Baseplates can also be fractional: + +```python +from microfinity import GridfinityBaseplate + +# 3.5U x 2.25U baseplate +bp = GridfinityBaseplate(3.5, 2.25, micro_divisions=4) +bp.save_stl_file() +``` + +This is especially useful with the [Layout System](components/layouts.md) for filling oddly-sized drawers. + +## Constraints + +### Lite Style Not Supported + +The economical "lite style" box (`lite_style=True`) is not compatible with fractional sizing: + +```python +# This will fail +box = GridfinityBox(1.25, 2, 3, micro_divisions=4, lite_style=True) +# ValueError: lite_style is not supported with micro_divisions > 1 +``` + +Use standard box style for fractional bins. + +### Height is Always Integer + +While length and width can be fractional, height must be an integer: + +```python +# Length and width: fractional OK +# Height: must be integer +box = GridfinityBox(1.25, 0.5, 3, micro_divisions=4) # OK +box = GridfinityBox(1.25, 0.5, 3.5, micro_divisions=4) # Error +``` + +## Real-World Use Cases + +### Small Items Storage + +```python +# SD card organizer - 0.5U cells fit cards perfectly +box = GridfinityBox(0.5, 0.5, 2, micro_divisions=4) +``` + +### Tool Drawer Optimization + +```python +# Screwdriver that's 35mm wide - 0.75U (31.5mm) + wall ≈ perfect fit +box = GridfinityBox(0.75, 3, 4, micro_divisions=4) +``` + +### Odd Drawer Dimensions + +```python +from microfinity import GridfinityBaseplateLayout + +# 347mm drawer - standard grid leaves 11mm gap +# Microgrid can fill it precisely +layout = GridfinityBaseplateLayout( + drawer_x_mm=347, + drawer_y_mm=280, + build_plate_x_mm=220, + build_plate_y_mm=220, + micro_divisions=4, +) +``` + +## Summary + +| Feature | Standard Gridfinity | Microfinity | +|---------|---------------------|-------------| +| Grid pitch | 42mm only | 42mm, 21mm, or 10.5mm | +| Bin sizes | Integer U only | 0.25U increments | +| Baseplate sizes | Integer U only | 0.25U increments | +| Compatibility | - | 100% compatible with standard | +| Drawer fill | Gaps possible | Precise fill with fractional sizes | + +The Microgrid System gives you finer control over bin dimensions while maintaining full compatibility with the Gridfinity ecosystem. diff --git a/docs/python-api.md b/docs/python-api.md new file mode 100644 index 0000000..8b98df9 --- /dev/null +++ b/docs/python-api.md @@ -0,0 +1,392 @@ +# Python API + +This guide covers programmatic usage of Microfinity for scripting and automation. + +## Imports + +```python +# Import main classes +from microfinity import ( + GridfinityBox, + GridfinitySolidBox, + GridfinityBaseplate, + GridfinityBaseplateLayout, + GridfinityConnectionClip, + GridfinityDrawerSpacer, + GridfinityExporter, + SVGView, +) + +# Import enums for layout configuration +from microfinity import ( + SegmentationMode, + ToleranceMode, +) + +# Import constants +from microfinity import ( + GRU, # Grid unit: 42mm + GRHU, # Height unit: 7mm + GR_TOL, # Tolerance: 0.5mm +) +``` + +## Creating Objects + +### Boxes + +```python +from microfinity import GridfinityBox, GridfinitySolidBox + +# Basic box +box = GridfinityBox(2, 3, 4) + +# Box with features +box = GridfinityBox( + 2, 3, 4, + holes=True, + scoops=True, + labels=True, + length_div=1, +) + +# Fractional box +box = GridfinityBox(1.25, 0.5, 3, micro_divisions=4) + +# Solid box +solid = GridfinitySolidBox(2, 2, 3) +# or +solid = GridfinityBox(2, 2, 3, solid=True) +``` + +### Baseplates + +```python +from microfinity import GridfinityBaseplate + +# Basic baseplate +bp = GridfinityBaseplate(4, 3) + +# With mounting screws +bp = GridfinityBaseplate(4, 3, corner_screws=True) + +# Fractional +bp = GridfinityBaseplate(3.5, 2.25) +``` + +### Layouts + +```python +from microfinity import GridfinityBaseplateLayout + +layout = GridfinityBaseplateLayout( + drawer_x_mm=450, + drawer_y_mm=380, + build_plate_x_mm=220, + build_plate_y_mm=220, +) +``` + +### Clips + +```python +from microfinity import GridfinityConnectionClip + +# Default clip +clip = GridfinityConnectionClip() + +# With tuned clearance +clip = GridfinityConnectionClip(clip_clearance_mm=0.15) +``` + +## Rendering + +Objects have a `render()` method that generates CadQuery geometry: + +```python +box = GridfinityBox(2, 2, 3) + +# Render explicitly +geometry = box.render() + +# Or access via property (auto-renders if needed) +geometry = box.cq_obj +``` + +The geometry is a CadQuery `Workplane` object that can be manipulated further. + +## Properties + +Access computed dimensions and information: + +```python +box = GridfinityBox(2, 3, 4, holes=True) + +# Dimensions +print(box.length) # 84.0 (2 * 42mm) +print(box.width) # 126.0 (3 * 42mm) +print(box.height) # ~31.8mm total +print(box.outer_l) # 83.5mm (length - tolerance) +print(box.outer_w) # 125.5mm +print(box.inner_l) # Interior length +print(box.inner_w) # Interior width +print(box.int_height) # Interior height + +# Auto-generated filename +print(box.filename()) # gf_box_2x3x4_holes +``` + +## Exporting + +### Simple Export + +```python +box = GridfinityBox(2, 2, 3) + +# Auto-generated filename +box.save_stl_file() + +# Custom filename +box.save_stl_file("my_box.stl") +box.save_step_file("my_box.step") +box.save_svg_file("my_box.svg") +``` + +### Using GridfinityExporter + +```python +from microfinity import GridfinityExporter, SVGView + +geometry = box.render() + +GridfinityExporter.to_stl(geometry, "output.stl") +GridfinityExporter.to_step(geometry, "output.step") +GridfinityExporter.to_svg(geometry, "output.svg", view=SVGView.ISOMETRIC) +``` + +### Batch Export + +```python +items = [] +for size in [(1, 1), (2, 1), (2, 2)]: + box = GridfinityBox(size[0], size[1], 3) + items.append((box.cq_obj, f"box_{size[0]}x{size[1]}.stl")) + +GridfinityExporter.batch_export(items, file_format="stl") +``` + +## Working with Layouts + +### Get Layout Information + +```python +from microfinity import GridfinityBaseplateLayout + +layout = GridfinityBaseplateLayout( + drawer_x_mm=450, + drawer_y_mm=380, + build_plate_x_mm=220, + build_plate_y_mm=220, +) + +# Get calculated layout +result = layout.get_layout() + +print(result.num_pieces) # Total pieces +print(result.grid_size) # (pieces_x, pieces_y) +print(result.clip_count) # Clips needed +print(result.total_u) # Grid size in U +print(result.fill_x_mm) # X fill amount +print(result.fill_y_mm) # Y fill amount + +# Print human-readable summary +layout.print_summary() +``` + +### Access Individual Pieces + +```python +# Get piece by ID +piece = layout.get_piece("piece_0_0") + +# Get piece by grid position +piece = layout.get_piece_at(1, 0) + +# Piece properties +print(piece.size_mx) # Size in micro-cells +print(piece.size_my) +print(piece.fill_left) # Fill amounts +print(piece.edge_roles) # Edge configuration +``` + +### Render Pieces + +```python +# Render single piece +geom = layout.render_piece("piece_0_0") + +# Render by position +geom = layout.render_piece_at(0, 1) + +# Render full preview +preview = layout.render_preview(include_clips=True) + +# Render clip sheet +clips = layout.render_clip_sheet() +``` + +### Export Layout + +```python +# Export all unique pieces +layout.export_all("./output", file_format="stl") + +# Export preview +layout.export_preview("./preview.step") + +# Export fit test strips +layout.export_fit_strips("./fit_test", file_format="stl") +``` + +## Iteration Patterns + +### Generate Multiple Sizes + +```python +from microfinity import GridfinityBox +import os + +os.makedirs("./boxes", exist_ok=True) + +# Standard sizes +for l in [1, 2, 3]: + for w in [1, 2]: + for h in [2, 3, 4]: + box = GridfinityBox(l, w, h, holes=True) + box.save_stl_file(path="./boxes") +``` + +### Generate Feature Variations + +```python +features = [ + {"holes": True}, + {"holes": True, "scoops": True}, + {"holes": True, "scoops": True, "labels": True}, +] + +for i, feat in enumerate(features): + box = GridfinityBox(2, 2, 3, **feat) + box.save_stl_file(f"variant_{i}.stl") +``` + +### Fractional Size Generation + +```python +# Quarter-grid sizes +sizes = [0.25, 0.5, 0.75, 1.0, 1.25, 1.5] + +for size in sizes: + box = GridfinityBox(size, 1, 3, micro_divisions=4) + box.save_stl_file(f"box_{size}x1x3.stl") +``` + +## Combining Objects + +Use CadQuery operations to combine objects: + +```python +import cadquery as cq +from microfinity import GridfinityBox + +# Create two boxes +box1 = GridfinityBox(2, 2, 3).render() +box2 = GridfinityBox(1, 1, 3).render().translate((100, 0, 0)) + +# Combine +combined = box1.union(box2) + +# Export +cq.exporters.export(combined, "combined.stl") +``` + +## Custom Modifications + +Modify geometry after rendering: + +```python +from microfinity import GridfinitySolidBox + +# Start with solid box +box = GridfinitySolidBox(2, 2, 3) +geometry = box.render() + +# Add custom cutout +cutout = cq.Workplane("XY").box(20, 20, 50) +modified = geometry.cut(cutout.translate((0, 0, 20))) + +# Export +cq.exporters.export(modified, "custom_box.stl") +``` + +## Error Handling + +```python +from microfinity import GridfinityBox + +try: + # Invalid micro_divisions + box = GridfinityBox(2, 2, 3, micro_divisions=3) +except ValueError as e: + print(f"Error: {e}") + +try: + # Size doesn't match micro_divisions + box = GridfinityBox(1.3, 2, 3, micro_divisions=4) +except ValueError as e: + print(f"Error: {e}") +``` + +## Type Hints + +Microfinity includes type hints for IDE support: + +```python +from microfinity import GridfinityBox + +def create_box( + length: float, + width: float, + height: int, + with_holes: bool = False, +) -> GridfinityBox: + return GridfinityBox( + length, width, height, + holes=with_holes, + ) +``` + +## Constants Reference + +```python +from microfinity.core.constants import ( + GRU, # 42mm - Grid unit + GRU2, # 21mm - Half grid unit + GRHU, # 7mm - Height unit + GR_TOL, # 0.5mm - Nominal tolerance + GR_RAD, # 4mm - Exterior fillet radius + GR_WALL, # 1.0mm - Nominal wall thickness + GR_BASE_HEIGHT,# 4.75mm - Base height + GR_HOLE_D, # 6.5mm - Magnet hole diameter +) +``` + +## Example Scripts + +See the [examples/](../examples/) directory for complete scripts: + +- `basic_box.py` - Simple box creation +- `fractional_bins.py` - Fractional size examples +- `drawer_layout.py` - Complete drawer layout +- `custom_baseplate.py` - Baseplate options +- `batch_export.py` - Generating families of parts diff --git a/examples/basic_box.py b/examples/basic_box.py new file mode 100644 index 0000000..0088ed0 --- /dev/null +++ b/examples/basic_box.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +""" +Basic Box Example + +Creates a simple Gridfinity box with common features. +""" + +from microfinity import GridfinityBox + +# Create a 2x3x4 box with typical features +box = GridfinityBox( + 2, + 3, + 4, # 2U x 3U x 4U (84mm x 126mm x ~32mm) + holes=True, # Magnet/screw holes in base + scoops=True, # Finger scoops for easy retrieval + labels=True, # Label ledge on back wall +) + +# Print dimensions +print(f"Box dimensions: {box.length}mm x {box.width}mm x {box.height}mm") +print(f"Interior height: {box.int_height}mm") + +# Export to STL (auto-generated filename) +filepath = box.save_stl_file() +print(f"Saved to: {filepath}") + +# Or specify a custom filename +# box.save_stl_file("my_custom_box.stl") diff --git a/examples/drawer_layout.py b/examples/drawer_layout.py new file mode 100644 index 0000000..4aa36c8 --- /dev/null +++ b/examples/drawer_layout.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +""" +Drawer Layout Example + +Demonstrates the complete workflow for creating a multi-piece baseplate +layout for a drawer larger than the 3D printer build plate. +""" + +from microfinity import GridfinityBaseplateLayout +import os + +# Define your drawer and printer dimensions +DRAWER_X = 450 # Drawer width in mm +DRAWER_Y = 380 # Drawer depth in mm +BUILD_PLATE_X = 220 # Printer bed X in mm +BUILD_PLATE_Y = 220 # Printer bed Y in mm + +# Create the layout calculator +layout = GridfinityBaseplateLayout( + drawer_x_mm=DRAWER_X, + drawer_y_mm=DRAWER_Y, + build_plate_x_mm=BUILD_PLATE_X, + build_plate_y_mm=BUILD_PLATE_Y, + micro_divisions=4, # Quarter-grid for precise fill + tolerance_mm=0.5, # Clearance for drawer fit + clip_pitch_u=1.0, # Clips every 1U along seams +) + +# Print the layout summary +print("=" * 60) +print("DRAWER LAYOUT SUMMARY") +print("=" * 60) +layout.print_summary() + +# Get detailed layout information +result = layout.get_layout() + +print(f"\nPieces to print: {result.num_pieces}") +print(f"Grid arrangement: {result.grid_size[0]} x {result.grid_size[1]}") +print(f"Clips needed: {result.clip_count}") +print(f"Fill strips: X={result.fill_x_mm:.2f}mm, Y={result.fill_y_mm:.2f}mm") + +# Create output directory +output_dir = "./drawer_layout_output" +os.makedirs(output_dir, exist_ok=True) + +# Option 1: Export fit test strips first (recommended!) +print("\n" + "=" * 60) +print("STEP 1: Exporting fit test strips...") +print("=" * 60) +fit_dir = os.path.join(output_dir, "fit_test") +fit_files = layout.export_fit_strips(fit_dir, file_format="stl") +print(f"Fit strips saved to {fit_dir}/") +print("Print these first to verify drawer fit before printing all pieces!") + +# Option 2: Export all pieces +print("\n" + "=" * 60) +print("STEP 2: Exporting all pieces...") +print("=" * 60) +pieces_dir = os.path.join(output_dir, "pieces") +piece_files = layout.export_all( + pieces_dir, + file_format="stl", + include_clips=True, +) +print(f"Exported {len(piece_files)} files to {pieces_dir}/") + +# Option 3: Export preview (optional - for visualization) +print("\n" + "=" * 60) +print("STEP 3: Exporting preview assembly...") +print("=" * 60) +preview_file = os.path.join(output_dir, "preview.step") +layout.export_preview(preview_file, include_clips=True) +print(f"Preview saved to {preview_file}") + +print("\n" + "=" * 60) +print("WORKFLOW COMPLETE") +print("=" * 60) +print( + """ +Next steps: +1. Print the fit test strips from {}/ +2. Test fit in your drawer +3. If fit is good, print all pieces from {}/ +4. Assemble pieces using the printed clips +""".format( + fit_dir, pieces_dir + ) +) diff --git a/examples/fractional_bins.py b/examples/fractional_bins.py new file mode 100644 index 0000000..f764ffc --- /dev/null +++ b/examples/fractional_bins.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +""" +Fractional Bins Example + +Demonstrates creating boxes with fractional sizes using the micro-grid system. +Fractional bins are fully compatible with standard Gridfinity baseplates. +""" + +from microfinity import GridfinityBox +import os + +# Create output directory +os.makedirs("./fractional_output", exist_ok=True) + +# Quarter-grid (0.25U increments) - most granular +print("Creating quarter-grid boxes (micro_divisions=4)...") + +# Narrow bin for small items +narrow = GridfinityBox(0.5, 1, 3, micro_divisions=4) +narrow.save_stl_file("./fractional_output/narrow_0.5x1x3.stl") +print(f" Narrow: {narrow.outer_l}mm x {narrow.outer_w}mm") + +# Medium bin +medium = GridfinityBox(1.25, 1, 3, micro_divisions=4) +medium.save_stl_file("./fractional_output/medium_1.25x1x3.stl") +print(f" Medium: {medium.outer_l}mm x {medium.outer_w}mm") + +# Wide bin +wide = GridfinityBox(1.75, 2, 4, micro_divisions=4, holes=True) +wide.save_stl_file("./fractional_output/wide_1.75x2x4.stl") +print(f" Wide: {wide.outer_l}mm x {wide.outer_w}mm") + + +# Half-grid (0.5U increments) +print("\nCreating half-grid boxes (micro_divisions=2)...") + +half_grid = GridfinityBox(1.5, 2.5, 3, micro_divisions=2) +half_grid.save_stl_file("./fractional_output/half_1.5x2.5x3.stl") +print(f" Half-grid: {half_grid.outer_l}mm x {half_grid.outer_w}mm") + + +# Generate a set of SD card organizer sizes +print("\nCreating SD card organizer set...") + +sd_sizes = [ + (0.5, 0.5, 2), # Single SD slot + (0.5, 1, 2), # Two SD slots + (1, 0.5, 2), # Wide single row +] + +for l, w, h in sd_sizes: + box = GridfinityBox(l, w, h, micro_divisions=4) + filename = f"./fractional_output/sd_organizer_{l}x{w}x{h}.stl" + box.save_stl_file(filename) + print(f" SD {l}x{w}x{h}: {box.outer_l}mm x {box.outer_w}mm") + +print("\nAll fractional bins saved to ./fractional_output/") diff --git a/todo.md b/todo.md deleted file mode 100644 index 9564fcc..0000000 --- a/todo.md +++ /dev/null @@ -1,245 +0,0 @@ -# Microfinity Improvement Plan - -## Completed Items - -### Bug Fixes -- [x] **Through-Slot Clip Fix** - Notch cuts now go through the wall entirely, clips sized correctly for through-slot mounting - -### Performance Improvements -- [x] **1.1 Replace Sequential Unions** - Created `union_all()` helper in `gf_helpers.py` and refactored 7 locations to use it -- [x] **1.2 Cache Notch Base Geometry** - `_create_all_notch_cutters()` now creates base notch once and reuses with transforms -- [x] **4.2 Union Loop Helper** - `union_all()` function added to `gf_helpers.py` - -### Architecture Improvements -- [x] **3.1 Extract Export Logic** - Created `gf_export.py` with `GridfinityExporter` class and `SVGView` enum. Export methods in `gf_obj.py` and `gf_baseplate_layout.py` now delegate to the exporter. -- [x] **3.3 Fix Star Imports** - Replaced `from microfinity import *` with explicit imports in `gf_box.py` and `gf_obj.py` - -### Test Coverage -- [x] **Priority 1: Tests for `clip_ct` feature** - Added 9 tests to `TestConnectionClip` class -- [x] **Priority 3: Tests for `test_prints.py`** - Created `tests/test_test_prints.py` with 15 tests -- [x] **Priority 4: Tests for export methods** - Created `tests/test_export.py` with 10 tests -- [x] **Tests for `gf_helpers.py`** - Created `tests/test_helpers.py` with 14 tests -- [x] **Priority 6: Golden Test Infrastructure** - Created `tests/golden_test_utils.py` and `tests/test_golden.py` with 14 golden tests - -### New Test Files Created -| File | Tests | Coverage | -|------|-------|----------| -| `tests/test_helpers.py` | 14 | `union_all()`, `quarter_circle()`, `chamf_cyl()`, `chamf_rect()` | -| `tests/test_export.py` | 10 | `save_step_file()`, `save_stl_file()`, `save_svg_file()`, `GridfinityExporter` | -| `tests/test_test_prints.py` | 15 | `generate_fractional_pocket_test()`, `generate_clip_clearance_sweep()`, etc. | -| `tests/test_golden.py` | 14 | Golden/regression tests for boxes, baseplates, clips, spacers | -| `tests/golden_test_utils.py` | - | Utility functions for golden test infrastructure | - -### Golden Test Data -Golden test baselines stored in `tests/golden_data/`: -- `box_*.json` - Box geometry signatures -- `baseplate_*.json` - Baseplate geometry signatures -- `clip_*.json` - Clip geometry signatures -- `solidbox_*.json` - Solid box geometry signatures -- `spacer_*.json` - Spacer geometry signatures - -To update golden baselines: `UPDATE_GOLDEN=1 pytest tests/test_golden.py` - ---- - -## 1. Performance Improvements (Remaining) - -### 1.3 Add Render Caching for Layout -**Priority: MEDIUM | Effort: MEDIUM** - -Add `_render_cache` dict to `GridfinityBaseplateLayout` for repeated piece renders. - ---- - -## 2. Test Coverage (Remaining Gaps) - -### 2.1 Missing Test Types - -| Test Type | Status | Recommendation | -|-----------|--------|----------------| -| Golden/Snapshot Tests | DONE | Added geometry signature comparison | -| Performance Benchmarks | MISSING | Add timing tests for render operations | -| Property-based Tests | MISSING | Consider hypothesis for edge cases | - -### 2.2 Untested Modules (Remaining) - -| Module | Risk | -|--------|------| -| `shims/` directory | CQGI integration - completely untested | - -### 2.3 Partially Tested Methods - -| Method | Missing Coverage | -|--------|-----------------| -| `GridfinityBaseplate.crop_to_strip()` | Never tested directly | - ---- - -## 3. Architecture / Separation of Concerns (Remaining) - -### 3.2 Consolidate Validation -**Priority: LOW | Effort: MEDIUM** - -Validation is scattered: -- `gf_box.py:156-178` - validation in `render()` instead of `__init__` -- `gf_baseplate.py:506` - corner screw logic inline in `__init__` - -Consolidate into dedicated `validate()` methods. - ---- - -## 4. Code Duplication (Remaining) - -### 4.1 Edge Processing Duplication -**Priority: LOW | Effort: MEDIUM** - -`gf_baseplate_layout.py:925-1021` repeats edge processing 4 times. Extract to: -```python -def _process_edge(self, edge_name: str, is_left_or_front: bool, ...) -> EdgeConfig -``` - -### 4.3 Tolerance Mode Position Adjustment -**Priority: LOW | Effort: LOW** - -Repeated pattern at: -- `gf_baseplate_layout.py:1027-1029` -- `gf_baseplate_layout.py:1308-1315` -- `gf_baseplate_layout.py:1334-1341` - ---- - -## 5. Parameterization Opportunities - -### 5.1 Hardcoded Values to Expose - -| File | Line | Value | Suggested Param | -|------|------|-------|-----------------| -| `gf_box.py` | 173 | `1.5` | `max_lite_wall_th` | -| `gf_box.py` | 175 | `2.5` | `max_wall_th` | -| `gf_box.py` | 177 | `0.5` | `min_wall_th` | -| `gf_obj.py` | 394-406 | SVG export options | `svg_export_config` dict | -| `gf_baseplate.py` | 691 | `0.001` empty cutter | Constant `EMPTY_CUTTER_SIZE` | -| `gf_baseplate.py` | 721 | `0.1` min radius | Constant `MIN_FILLET_RADIUS` | - ---- - -## 6. Long Functions to Refactor - -### 6.1 `_calculate_layout()` - 287 lines -**Priority: LOW | Effort: MEDIUM** - -**Location:** `gf_baseplate_layout.py:838-1125` - -Break into: -- `_compute_grid_dimensions()` (lines 842-884) -- `_segment_axes()` (lines 886-911) -- `_generate_pieces()` (lines 913-1072) -- `_count_clips()` (lines 1074-1099) - -### 6.2 `GridfinityBox.render()` - 67 lines -**Location:** `gf_box.py:152-219` - -Break into: -- `_validate_render_params()` (lines 156-178) -- `_apply_interior_fillets()` (lines 186-212) - ---- - -## 7. New Feature Opportunities - -### 7.1 VTK PNG Rendering -Generate 3D rendered PNGs at multiple angles for MakerWorld listings. -- Full camera control (isometric, front, top, etc.) -- Professional-looking images -- Requires writing a helper script using VTK -- Already have VTK installed (comes with CadQuery) - -### 7.3 Partial Render for Fit Strips -Add `render_edge_strip()` to avoid rendering full piece then cropping. - -**Location:** `gf_baseplate_layout.py:1571-1576, 1613-1618` - currently renders full piece then crops. - ---- - -## 8. Updated Priority Order (Remaining Items) - -| Priority | Task | Impact | Effort | Status | -|----------|------|--------|--------|--------| -| 1 | Add tests for `clip_ct` feature | HIGH | LOW | DONE | -| 2 | Replace sequential unions | HIGH | LOW | DONE | -| 3 | Add tests for `test_prints.py` | MEDIUM | MEDIUM | DONE | -| 4 | Add tests for export methods | MEDIUM | LOW | DONE | -| 5 | Cache notch geometry | MEDIUM | LOW | DONE | -| 6 | Add golden test infrastructure | MEDIUM | MEDIUM | DONE | -| 7 | Extract union_all helper | LOW | LOW | DONE | -| 8 | Extract export logic to class | MEDIUM | MEDIUM | DONE | -| 9 | Refactor `_calculate_layout()` | LOW | MEDIUM | TODO | - ---- - -## 9. Current Test Coverage Summary - -### Test Files - -| Test File | Tests | Coverage Target | -|-----------|-------|----------------| -| `test_baseplate.py` | ~20 | `GridfinityBaseplate` - dimensions, edge modes, notches, fill, fractional sizes | -| `test_baseplate_layout.py` | 51 | `GridfinityBaseplateLayout`, `GridfinityConnectionClip`, partition algorithms | -| `test_box.py` | ~15 | `GridfinityBox`, `GridfinitySolidBox` - basic, lite, empty, solid, divided | -| `test_microgrid.py` | ~10 | Micro-grid support for boxes | -| `test_rbox.py` | ~15 | `GridfinityRuggedBox` - box, lid, accessories | -| `test_spacer.py` | ~8 | `GridfinityDrawerSpacer` | -| `test_helpers.py` | 14 | `gf_helpers.py` functions | -| `test_export.py` | 10 | Export functionality | -| `test_test_prints.py` | 15 | Test print generators | -| `test_golden.py` | 14 | Golden/regression tests | - -### Test Types Present - -| Test Type | Present? | -|-----------|----------| -| Unit Tests | YES | -| Integration Tests | YES (TestIntegration class) | -| Smoke/Render Tests | YES | -| Dimension Validation | YES | -| Face/Edge Count Tests | YES | -| Input Validation Tests | YES | -| Export Tests | YES | -| Golden/Snapshot Tests | YES | -| Performance Benchmarks | NO | -| Property-based Tests | NO | - ---- - -## 10. Notes - -### `clip_ct` Parameter -- Added to `GridfinityConnectionClip` in `gf_baseplate_layout.py` -- `render()` always returns single clip (safe for layout multiplication) -- `render_flat()` respects `clip_ct` and arranges multiple clips in a grid -- Internal constant `_CLIP_GAP_MM = 2.0` controls spacing between clips - -### `union_all()` Helper -- Added to `gf_helpers.py` -- Currently uses sequential union (not Compound) for compatibility -- All 7 union loop locations refactored to use it -- Note: `Compound.makeCompound()` produces different geometry structure that breaks some downstream operations - -### Star Import Fixes -- `gf_box.py`: Replaced with explicit imports from `microfinity.constants` and `microfinity.gf_obj` -- `gf_obj.py`: Replaced with explicit imports from `microfinity.constants` - -### Export Refactor -- `gf_export.py`: New module with `GridfinityExporter` class and `SVGView` enum -- `GridfinityExporter`: Handles STEP, STL, SVG export with configurable options -- `SVGView`: Enum for SVG view directions (FRONT, BACK, LEFT, RIGHT, TOP, BOTTOM, ISOMETRIC) -- Export methods in `gf_obj.py` and `gf_baseplate_layout.py` now delegate to exporter -- Parallel export removed (CAD operations not thread-safe) - -### Golden Test Infrastructure -- `tests/golden_test_utils.py`: Utility functions for computing and comparing geometry signatures -- `tests/test_golden.py`: 14 golden tests covering boxes, baseplates, clips, spacers -- Signatures include: `xlen`, `ylen`, `zlen`, `volume` -- Tolerance: 0.01mm for dimensions, 1% for volume -- Update baselines: `UPDATE_GOLDEN=1 pytest tests/test_golden.py` -- Golden data stored in `tests/golden_data/*.json`