unease 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# unease
|
|
2
|
+
|
|
3
|
+
Infer an easing curve or spring config from an animation video (`.mp4`).
|
|
4
|
+
|
|
5
|
+
`unease` is a Rust CLI **and MCP server**. A model identifies the item *once*; `unease`
|
|
6
|
+
then tracks it across every frame algorithmically and fits the curve — deterministic,
|
|
7
|
+
precise, reproducible. The model does perception (naming/pointing at the item); the
|
|
8
|
+
algorithm does inference (tracking + fitting).
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
open_video → [model marks item with a bbox] → track_item → emit
|
|
12
|
+
│
|
|
13
|
+
color-mask · connected-blob · fit
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Why this shape
|
|
17
|
+
|
|
18
|
+
The naive approach — hand every frame to a vision model and have it read the item's size
|
|
19
|
+
by eye — is non-deterministic, imprecise (can't read sub-pixel scale), slow, and breaks
|
|
20
|
+
when scale, translation, and camera-zoom combine. So perception is minimized to a single
|
|
21
|
+
seed; everything measurable is a plain pixel algorithm:
|
|
22
|
+
|
|
23
|
+
- **seed** — model marks the item with one bbox; unease samples its hue
|
|
24
|
+
- **track** — per frame: hue-mask (survives the brightness/glow ramp UI elements undergo),
|
|
25
|
+
label connected components, follow the item's blob by centroid continuity (so a
|
|
26
|
+
same-colored label elsewhere can't steal the track)
|
|
27
|
+
- **signal** — the blob's geometry over time, normalized `(metric−start)/(settle−start)`
|
|
28
|
+
so start=0, settle=1, overshoot>1
|
|
29
|
+
- **fit** — cubic-bezier and damped-spring models, dependency-free Nelder–Mead
|
|
30
|
+
- **classify** — spring only on real overshoot *and* a clearly better fit
|
|
31
|
+
- **emit** — CSS, framer-motion, reanimated, SwiftUI
|
|
32
|
+
- **verify** — optional `--demo`: a blue pill the item's size replays the *inferred* curve
|
|
33
|
+
on white; if it matches the source, the fit is good
|
|
34
|
+
|
|
35
|
+
A manual `sample_frames`/`observe` loop remains as a fallback for items color-masking
|
|
36
|
+
can't isolate.
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
Needs `ffmpeg` on `PATH`.
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
cargo build --release
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## CLI
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
unease probe anim.mp4 # fps, duration, dims (+ low-fps warning)
|
|
50
|
+
unease frames anim.mp4 --count 12 # dump evenly spaced frames
|
|
51
|
+
unease track anim.mp4 --bbox x,y,w,h # algorithmic track + fit (seed color from a bbox)
|
|
52
|
+
unease track anim.mp4 --color R,G,B # …or seed the color directly
|
|
53
|
+
unease fit observations.json # fit from a samples file (pure math, no video)
|
|
54
|
+
unease mcp # run the MCP server on stdio
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### `track` — the main path
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
# seed the item with a bbox on frame 0, track its scale, render a verification demo
|
|
61
|
+
unease track anim.mp4 --bbox 590,530,55,55 --property height --demo
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
- `--bbox x,y,w,h` (on `--ref-frame N`) or `--color R,G,B` — seed the target
|
|
65
|
+
- `--property height|width|area|cx|cy` — what to track; omit for **auto** (largest change)
|
|
66
|
+
- `--hue-tol`, `--min-value`, `--min-sat` — mask tuning (defaults handle glow)
|
|
67
|
+
- `--demo [--demo-out path]` — write `<video>.demo.mp4`: a blue pill the item's size
|
|
68
|
+
replaying the inferred curve on white
|
|
69
|
+
- `--dump-metrics` — per-frame bbox metrics to stderr
|
|
70
|
+
|
|
71
|
+
`observations.json`:
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{ "duration": 0.5, "samples": [ {"t":0.0,"v":0.0}, {"t":0.5,"v":1.18}, {"t":1.0,"v":1.0} ] }
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
- `t` — normalized time `[0,1]` over the animation window
|
|
78
|
+
- `v` — normalized value of the tracked property (`0` start, `1` settle, `>1` overshoot)
|
|
79
|
+
|
|
80
|
+
## MCP tools
|
|
81
|
+
|
|
82
|
+
| tool | purpose |
|
|
83
|
+
|------|---------|
|
|
84
|
+
| `open_video(path)` | metadata + a session handle; warns on low fps |
|
|
85
|
+
| `sample_frames(handle, count?\|timestamps?)` | returns frames as images (to *see* the item before marking it) |
|
|
86
|
+
| **`track_item(handle, bbox, property?, demo?)`** | **mark item once → algorithmic track + fit in one call** |
|
|
87
|
+
| `observe(handle, samples, window_seconds?)` | fallback: model submits tracked `{t,v}` per frame |
|
|
88
|
+
| `fit(handle)` | fallback: fit observed samples + classify |
|
|
89
|
+
| `emit(handle)` | render the fitted curve into framework snippets |
|
|
90
|
+
|
|
91
|
+
Primary flow: `open_video` → `sample_frames` (see the item) → `track_item(bbox)` → done.
|
|
92
|
+
|
|
93
|
+
### Register with Claude Code
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
claude mcp add unease -- /path/to/unease mcp
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Then: *"infer the easing of the blue button in demo.mp4"* — the model walks the loop.
|
|
100
|
+
|
|
101
|
+
## Output example (underdamped spring)
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"kind": "spring",
|
|
106
|
+
"confidence": 0.77,
|
|
107
|
+
"framer_motion": "transition={{ type: 'spring', stiffness: 681.3, damping: 24.52, mass: 1.00 }}",
|
|
108
|
+
"swiftui": ".spring(response: 0.241, dampingFraction: 0.470)",
|
|
109
|
+
"notes": ["Overshoot peak 18.0% — underdamped spring."]
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Limits
|
|
114
|
+
|
|
115
|
+
- **Frame rate** — 30 fps undersamples fast springs; `unease` warns. 60 fps preferred.
|
|
116
|
+
- **Single property** — fits one value channel (position *or* scale *or* opacity) at a time.
|
|
117
|
+
- **Window** — spring stiffness/damping scale with the real animation duration; report it
|
|
118
|
+
via `window_seconds` for physical params.
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "unease",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Infer easing/spring curves from animation video via algorithmic tracking + MCP",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"easing",
|
|
7
|
+
"spring",
|
|
8
|
+
"animation",
|
|
9
|
+
"cubic-bezier",
|
|
10
|
+
"mcp",
|
|
11
|
+
"reverse-engineering",
|
|
12
|
+
"motion"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "Enzo Manuel Mangano",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/enzomanuelmangano/unease.git"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/enzomanuelmangano/unease#readme",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/enzomanuelmangano/unease/issues"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"README.md"
|
|
26
|
+
]
|
|
27
|
+
}
|