{"id":51335779,"url":"https://github.com/brendanbank/relief-plate","last_synced_at":"2026-07-02T02:30:40.847Z","repository":{"id":364861666,"uuid":"1269290719","full_name":"brendanbank/relief-plate","owner":"brendanbank","description":"Claude Agent Skill: convert a black \u0026 white image into a 3D-printable relief printing plate (STL) for a rolling press / brayer — with auto-tiling (hidden dovetail joints) for plates larger than the printer bed. Tuned for the Bambu Lab X2D.","archived":false,"fork":false,"pushed_at":"2026-06-14T19:51:28.000Z","size":45,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-14T21:24:45.790Z","etag":null,"topics":["3d-printable","3d-printing","agent-skill","anthropic","bambu-lab","brayer","claude","claude-skill","image-to-stl","linocut","printing-plate","printmaking","python","relief-printing","stl"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brendanbank.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-14T14:31:43.000Z","updated_at":"2026-06-14T19:51:32.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/brendanbank/relief-plate","commit_stats":null,"previous_names":["brendanbank/relief-plate"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/brendanbank/relief-plate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brendanbank%2Frelief-plate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brendanbank%2Frelief-plate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brendanbank%2Frelief-plate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brendanbank%2Frelief-plate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brendanbank","download_url":"https://codeload.github.com/brendanbank/relief-plate/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brendanbank%2Frelief-plate/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35030920,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-02T02:00:06.368Z","response_time":173,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["3d-printable","3d-printing","agent-skill","anthropic","bambu-lab","brayer","claude","claude-skill","image-to-stl","linocut","printing-plate","printmaking","python","relief-printing","stl"],"created_at":"2026-07-02T02:30:40.416Z","updated_at":"2026-07-02T02:30:40.832Z","avatar_url":"https://github.com/brendanbank.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# relief-plate\n\nConvert a black-and-white image or painting into a **3D-printable relief printing\nplate** (STL) for a rolling press / brayer — with automatic tiling (hidden\ndovetail joints) for plates larger than the printer bed.\n\nDark areas of the image become a raised, uniform-height surface on a solid base;\nwhite areas stay recessed. Roll ink across the top with a brayer and the raised\nshapes transfer to paper under the press — like a 3D-printed linocut.\n\n`relief-plate` is a **Claude Agent Skill**: the [`SKILL.md`](SKILL.md) manifest\ntells the agent how to drive the scripts (asking the right sizing questions,\nsplitting oversized plates, building detail test tiles). The scripts also run\nstandalone — see [Usage](#usage).\n\n## Install\n\nIt's an Agent Skill, so add it to a Claude client:\n\n1. **Download or clone** this repo (or grab the `relief-plate.skill` bundle from\n   the [latest release](https://github.com/brendanbank/relief-plate/releases)):\n   ```bash\n   git clone https://github.com/brendanbank/relief-plate.git\n   ```\n2. **Register it as a skill** — either through the client's\n   **Settings → Capabilities** (point it at the folder / `.skill` bundle), or by\n   placing the skill under your skills directory, e.g.:\n   ```bash\n   mkdir -p ~/.claude/skills\n   cp -R relief-plate ~/.claude/skills/relief-plate   # must contain SKILL.md at its root\n   ```\n\nThe skill loader expects `SKILL.md` at the root of the skill folder (it is, here).\n\n## Requirements\n\n- Python 3\n- `numpy` and `Pillow`:\n  ```bash\n  pip install numpy pillow\n  ```\n\n## Usage\n\nInput must be a **PNG** (lossless — never JPEG; compression speckle ruins edge\ntracing), high resolution (≥2500 px on the long side), flattened, on a **white\nbackground**, with **black = the parts to be raised/inked**.\n\n**Build a plate:**\n```bash\npython3 scripts/build_relief.py INPUT.png OUT.stl \\\n    --size 230 --base 2 --relief 2 --mirror 1 --raised dark\n```\n\n**Plan a split** for a plate bigger than the bed (prints whether it fits, and if\nnot the tile grid + per-tile commands):\n```bash\npython3 scripts/split_plate.py PLAN INPUT.png --size 400 --bed 256\n```\n\n**Build one tile** (run once per tile from the plan):\n```bash\npython3 scripts/split_plate.py TILE INPUT.png OUTDIR \\\n    --size 400 --relief 2 --col 0 --row 0\n```\n\n**Detail test tiles** at production scale (print the trickiest regions first):\n```bash\npython3 scripts/build_tiles.py INPUT.png OUTDIR --size 230 --base 2 --relief 2 \\\n    --crop eye 560 1380 980 1700\n```\n\n### Splitting a large plate into tiles — and recombining them\n\nWhen a plate is larger than the printer bed (~256 mm on the Bambu X2D), you don't\nhave to shrink the artwork. Split it into a grid of tiles that **dovetail back\ntogether into one plate** after printing — the image stays full-size and the joints\nare hidden.\n\n1. **Plan** the grid. `PLAN` reports whether it fits and, if not, the grid plus one\n   `TILE` command per tile (and which tiles need support):\n   ```bash\n   python3 scripts/split_plate.py PLAN INPUT.png --size 400 --bed 256\n   # -\u003e plate 400.0 x 266.7 mm | grid 2 x 2 = 4 tiles\n   #      TILE --col 0 --row 0   (col 0 = left, row 0 = bottom)\n   #      TILE --col 1 --row 0\n   #      ...\n   ```\n2. **Build \u0026 print** each tile (`tile_c{C}_r{R}.stl`). Each tile is the relief slab\n   sitting on a 2 mm bottom layer that carries the dovetails. Print every tile\n   relief-up.\n3. **Recombine.** Each shared seam has **two hidden bottom-dovetails**: one tile\n   gets the **tabs**, the neighbour gets matching **pockets** (cut with clearance).\n   Slot the tabs into the pockets and the tiles lock together edge-to-edge,\n   reforming the full image as a single plate. Because the dovetails live entirely\n   in the bottom 2 mm, the relief **butts straight across the seam** — the joint is\n   invisible from the top (the inked/printing surface) and only shows underneath.\n   Add a little glue in the pockets if you want the assembly permanent.\n\n**Support:** tiles that carry a **pocket** edge must be printed **with support**\n(support filament on the X2D aux nozzle); tiles with only **tabs** print without.\nThe `TILE` command prints which case each tile is — by convention, `right`/`top`\nseams get tabs (no support) and `left`/`bottom` seams get pockets (support).\n\n### `build_relief.py` flags\n\n| Flag | Meaning | Default |\n|------|---------|---------|\n| `--size`   | Longest plate dimension in mm (detail scales with it) | `230` |\n| `--base`   | Solid base thickness in mm | `2` |\n| `--relief` | Raised relief height in mm (total height = base + relief) | `2` |\n| `--mirror` | `1` mirrors left-right so the *pressed print* matches the artwork | `1` |\n| `--raised` | `dark` = black raised (tonal art); `light` = engraved/negative look (line-art) | `dark` |\n\n## Key constraints\n\n- **0.4 mm nozzle is the detail floor.** Every raised line *and* every gap must\n  end up **≥ ~0.45 mm** on the plate, or it merges when sliced.\n- **Size the plate from its finest feature, not a target dimension.** The only\n  levers for more detail are scaling the plate up or simplifying the densest area.\n- **Raised vs. engraved:** use `--raised dark` for tonal portraits (black raised);\n  `--raised light` for an engraved/negative (scratchboard) look, better for\n  line-art.\n- **Oversized plates auto-split** into a grid joined by hidden bottom-dovetails\n  (the joint lives in the bottom 2 mm, so the relief butts straight across the\n  seam and the dovetail is invisible from the top). Tiles with pockets print with\n  support; tiles with only tabs print without.\n\nTuned for the **Bambu Lab X2D** (256 mm bed, 0.4 mm nozzle).\n\n## Repository layout\n\n```\nSKILL.md                  # Agent Skill manifest (frontmatter + instructions + changelog)\nLICENSE                   # MIT\nREADME.md\nrequirements.txt\nscripts/\n  build_relief.py         # image → watertight relief STL (+ preview)\n  split_plate.py          # cut oversized plates into dovetail-joined tiles (PLAN/TILE)\n  build_tiles.py          # detail-region test tiles at production scale\n  finalize_mesh.py        # dedupe + hole-fill + coherent outward normals (called by build)\n  earcut.py               # polygon triangulation (ear-clipping with holes)\n```\n\n## Changelog\n\n- **1.2.0** — correctness \u0026 watertightness fixes (detailed-art quality):\n  area-weighted region classification (a large background no longer mis-fills as\n  raised — fixes the solid-black-blob bug); removed the forced raised rim so the\n  plate edge sits flush at base height (a border now appears only if it's in the\n  artwork) while still guaranteeing a full solid base; `finalize_mesh` now runs\n  with the same interpreter (it was silently failing where the system `python3`\n  lacked numpy, shipping un-welded meshes) and uses robust hole-filling — output\n  is genuinely watertight (0 non-manifold edges) on detailed portraits.\n- **1.1.1** — correct author/copyright attribution to Brendan Bank; document how\n  split tiles recombine via hidden dovetails.\n- **1.1.0** — auto-split plates larger than the printer bed into dovetail-joined\n  tiles (`split_plate.py`): hidden bottom-dovetails, 2 per seam, automatic\n  tab/pocket assignment.\n- **1.0.0** — initial release: image → watertight relief STL (`build_relief.py`),\n  detail test tiles (`build_tiles.py`), watertight finalize, 0.4 mm nozzle sizing\n  rules.\n\n## License\n\nMIT © Brendan Bank — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrendanbank%2Frelief-plate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrendanbank%2Frelief-plate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrendanbank%2Frelief-plate/lists"}