Metadata-Version: 2.5
Name: cutan
Version: 0.0.49
Summary: Cut-out animation for an: rigged characters, faces, lip-sync and styles, as a genre package on the an core.
Project-URL: Homepage, https://github.com/thorwhalen/cutan
Project-URL: Repository, https://github.com/thorwhalen/cutan
Project-URL: Documentation, https://thorwhalen.github.io/cutan
Author: Thor Whalen
License-Expression: MIT
License-File: LICENSE
Keywords: an,animation,cut-out,cutout,lip-sync
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Multimedia :: Graphics
Requires-Python: >=3.10
Requires-Dist: an
Requires-Dist: numpy
Requires-Dist: pillow
Requires-Dist: pyyaml
Provides-Extra: carve
Requires-Dist: opencv-python-headless; extra == 'carve'
Provides-Extra: dev
Requires-Dist: an[stage]; extra == 'dev'
Requires-Dist: nw; extra == 'dev'
Requires-Dist: opencv-python-headless; extra == 'dev'
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Provides-Extra: docs
Requires-Dist: sphinx-rtd-theme>=1.0; extra == 'docs'
Requires-Dist: sphinx>=6.0; extra == 'docs'
Provides-Extra: faces
Requires-Dist: insightface; extra == 'faces'
Requires-Dist: onnxruntime; extra == 'faces'
Requires-Dist: opencv-python-headless; extra == 'faces'
Provides-Extra: nw
Requires-Dist: nw; extra == 'nw'
Provides-Extra: rembg
Requires-Dist: opencv-python-headless; extra == 'rembg'
Requires-Dist: rembg; extra == 'rembg'
Description-Content-Type: text/markdown

# cutan

Cut-out animation for [`an`](https://github.com/thorwhalen/an): rigged characters, faces and expressions, lip-sync, swap sets and views, impacts, and cut-out styles, as a genre package on the `an` core.

```bash
pip install "an[cutout]"      # an + the stage + cutan
an character new maya --offline
```

`an` finds `cutan` through the `an.genres` entry point and loads it with `an.genres.load()` (the CLI and `an.load(project)` do). The scene format, rendering and the asset library are `an`'s; see its README.

## What is here

`cutan` registers with the core:

- the `character` entity kind and the `play` and `expression` actions, the `[emotion]` dialogue sugar and the semantic checks that go with them (`cutan.genre`, `cutan.characters`, `cutan.expression`);
- the rig, face, viseme, blink, gaze and swap-pose compile passes over the `an.stage` compiler (`cutan.compile`), and the mouth and eye visuals of the stage runtime (`cutan/runtime/visuals.js`);
- the lip-sync providers `offline`, `rhubarb` and `whisper` (`cutan.audio`);
- locomotion, speech, blink and turn methods with their requirements and defaults, and the cut-out vocabulary;
- the `an character …` and `an impacts …` command namespaces;
- carving parts out of photos and frames (`cutan.carve`, below);
- the style lint (`python -m cutan.verify.style VIDEO <style>`) and the named style specs it measures against, shipped as package data: `cutan.style_spec("south_park")`, `python -m cutan.styles` to list them (the `cutan-style` skill applies one).

## Carving parts from a photo or a frame

`cutan.carve` turns a photo or a video frame into a matted, cleaned, provenance-carrying cut-out part, and writes it as a library-ready prop folder. It needs OpenCV: `pip install "cutan[carve]"` (it installs `opencv-python-headless`; an environment that already has `opencv-python` or `opencv-contrib-python` works as it is, since all three provide the same `cv2`). Add `cutan[rembg]` for the neural matte and `cutan[faces]` for the face locator; `cutan.carve.check_requirements()` lists what is installed.

```python
from cutan.carve import carve, carve_head, frame_source, write_prop, grab_frame, Polygon, FlatColour

src = frame_source("https://www.youtube.com/watch?v=VIDEO_ID", t=34.5, license="all-rights-reserved")
frame = grab_frame("clip.mp4", 34.5)
lamp = carve(frame, point=(1060, 130), source=src)               # flat_colour: the cartoon-frame default
print(lamp.quality.warnings())                                    # backdrop left on the rim, glyphs attached, ...
write_prop(lamp, "assets/props/lamp")                             # prop.json + parts/body.png, source included
head = carve_head(frame, face=(830, 170, 930, 305), matte="rembg")  # neck cut, 512² canvas, face centre as origin
```

The matte is a strategy: `flat_colour` (the default), `chroma`, `grabcut`, `polygon`, `focus`, `rembg`, or any `(rgb, hint) -> alpha` callable. Two combine with `&` and `|`: `Polygon(outline) & FlatColour()` is a hand outline cleaned by a colour key. `split_parts` lifts moving parts off a carving, each with a pivot (clock hands off the face), and `write_prop` gives each part its own bone. Publishing the folder (`an library publish <folder> prop.<name> --package cutan --origin carved`) records the rights from the descriptor's own `source`; `write_prop` refuses a carving with no provenance (unless the pixels are yours: `ours=True`) and a `source=` that would loosen its licence without `relicense=`. Every coordinate is in the source's pixels, and a carving's `recipe` is its arguments by name, so `carve(frame, **part.recipe)` replays it: a batch of carves is a list of recipes.

The cut-out bench corpus (`misc/bench/`), `examples/` and the demo gallery (`misc/demos/`) live here too; `cutan.bench.run_bench()` runs the corpus through `an`'s bench runner.

## Names that never change

Moving the code renamed nothing that is stored. Scenes keep `renderer: cutout`, the genre keeps its slug `cutout_animation`, character descriptors keep their document kind and version, and asset ids keep their `character.` prefix. These names are in `cutan/__init__.py`. The old `an.characters`, `an.expression`, `an.impacts`, `an.audio.offline_lipsync`-style import paths still work in `an`, with a `MovedModuleWarning`.

## Where data lives

The genre's asset library and agent-made projects live under `~/.local/share/cutan` by default. Set `CUTAN_HOME` to move them. The core's own library (voices, sounds, fonts) stays under `an`'s root, and a project reads both as one search path.

## Working on cutan

Read `CLAUDE.md`. In short: `pip install -e . --no-deps` next to an editable `an`, then `pytest`. The tests that render need a headless browser (`playwright install chromium`) and `ffmpeg`.
