diff --git a/HANDOFF.md b/HANDOFF.md index c02542a..8f88227 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -414,16 +414,11 @@ Known generator quirks, all still true: Oliver's notes on the art pass: the zombies are good, the runner "flickers very hard", and the player has three problems. The serve is now a drawn wind-up driven by the pull. -- **The pull IS the wind-up.** Four new frames (`p_pull1..4`, batch `player_windup`), chosen in - `drawChar` off `pullFrac(G.aim.mag)`. What separates the four is HOW FAR THE RACKET HAS FALLEN, and - that only came out once the prompt pinned the racket head to a different BODY LANDMARK per frame — - above his head, beside his ear, between his shoulder blades, down at the back of his thighs. - Described as "further into the wind-up each time" it drew four near-identical poses twice running - (Oliver: *"pull3 and pull4 are now basically the same image"*), which is the generator quirk the - pipeline notes already warned about, in a place I did not expect it. The stages are NOT spread evenly: **frame 1 owns the whole - first half of the pull and the three coiled frames are packed into the top half**, Oliver's call, - so the part of the gesture that costs power is the part that visibly costs effort. Release fires - `p_smash`. Verified: 30/74/95/125 px of drag -> pull1/2/3/4. +- **The serve is THREE poses, not an animation.** Waiting: the bounce stance. Drawing the + slingshot: `p_pull`, the racket dropping behind him. Release: `p_smash`, the drive. A four-stage + version keyed to `pullFrac` was built, wired late (frame 1 owning the first half) and played, and + Oliver cut it back to this — on a phone, at thumb speed, the middle stages are never read. The + intermediate frames are deleted, not commented out. - **The whole player is RIGHT-HANDED now, and that was the real bug.** `p_idle`, `p_bounce` and `p_toss` had been drawn holding the racket in his LEFT hand while `p_smash` and `p_swing` used his right, so any new frame copied whichever reference it was given and the racket changed hands @@ -445,25 +440,28 @@ shearing a limb, cutting a prop out — a wrong frame is wrong in more ways than (perspective, foreshortening, where the weight sits) and none of those survive a flip. There was an `assets/_derive.py` doing exactly that; it is deleted. Fix the PROMPT and re-run the batch. -**Three pipeline guards were added to `_regen.py`, each after a bad sheet installed silently:** +**THE WAY TO GET A POSE RIGHT IS A PHOTO, NOT A PARAGRAPH.** Describing a serve in prose and +re-rolling cost about fifteen generations and never landed — the racket changed hands, the four +stages came back near-identical twice, the coil came back as a fold. Oliver's fix: give the +generator the real thing. `scratchpad/pose_ref.png` is the recipe — his 10-frame photo sequence +cropped to five poses in a row, with two of our own sprites underneath as the character and style +anchor, and a prompt saying which strip means what. **One** generation returned the whole serve as +our man in our kit, and the frames were sliced straight out of it (anchored on the standing figure +it drew, which is what sets the scale). Note it also redraws the reference strip above the result — +harmless, crop to the magenta band before slicing. + +**Two pipeline guards were added to `_regen.py`, each after a bad sheet installed silently:** 1. **The reference's ASPECT is the answer's aspect.** One tall reference figure gave a portrait reference canvas and the generator answered with a 2x2 GRID, which `_fine.py` cannot cut (it segments columns). `ref_sheet` now widens to a landscape strip with one slot per frame asked for. 2. **A non-magenta background is a rejected roll.** "It sometimes ignores the magenta" was written off as harmless because the key uses the corner colour — until it returned a GREEN sheet and keyed away four grey-green racket heads with it, installing four men holding a grip and nothing else. -3. **`arm_left` rejects a MIRRORED frame.** The generator flips the later frames of a serve about - half the time, which puts the racket in the other hand halfway through the animation. Oliver - caught it twice by eye before the check existed. **It is a net, not a proof**: it asks whether - anything reaches the top of the LEFT half, and a mirrored frame whose racket head hangs over there - passes. A tighter discriminator was tried (ink mass in the top third, left vs right) and does NOT - separate the two — measured 0.61/0.57/0.41 on a mirrored sheet against 0.54/0.56/0.55 on a good - one. Every wind-up sheet still has to be looked at. -Still imperfect, and worth a look on the phone: the empty tossing arm WANDERS between the four -wind-up frames (up, out, bent, up) instead of staying thrown up at the ball. The roll that held the -arm still put the racket back in the wrong hand, and the racket is the load-bearing half of the -gesture, so this is the set that shipped. One more roll may fix both. +**And `_fine.py` takes `--defringe N` (default 2).** Each de-fringe pass eats a one-pixel line, and +a racket frame IS a one-pixel line: two passes were shredding every racket on the sheet. Detailed +art wants 1 or 0 — there was no magenta halo left at 0 on the serve sheet, because the palette +quantisation snaps to the sheet's own colours anyway. Also worth knowing: a batch of raised-arm poses has no honest scale to anchor on, so frame 0 is always a throwaway STANDING frame (`p_pull0`, deleted after installing). And a photo reference of a diff --git a/assets/_pose_ref_serve.png b/assets/_pose_ref_serve.png new file mode 100644 index 0000000..ad33daa Binary files /dev/null and b/assets/_pose_ref_serve.png differ diff --git a/assets/_regen.py b/assets/_regen.py index 54cc935..161039b 100644 --- a/assets/_regen.py +++ b/assets/_regen.py @@ -91,19 +91,6 @@ def touches_edge(sheet): return best return max(run(fg[0]), run(fg[-1]), run(fg[:, 0]), run(fg[:, -1])) >= 8 -def arm_not_left(outs): - """The generator MIRRORS the later frames of a serve about half the time — the coil is a strong - silhouette and it draws it whichever way it likes, which puts the racket in the other hand - halfway through the animation. Oliver spotted it twice. Every frame after the first must reach - its raised arm up the LEFT-hand side, so a frame whose left half has nothing near the top (the - arm went right instead) is a rejected roll, not something to notice later in the file browser.""" - for o in outs[1:]: - ink = np.array(Image.open(o).convert('RGBA'))[..., 3] > 128 - h, w = ink.shape - left = np.nonzero(ink[:, :w // 2].any(1))[0] - if not len(left) or left.min() > 0.18 * h: return o - return None - def colour_match(src, dst_name): """Fit the new frame onto the palette statistics of the sprite it replaces, so a redrawn body does not change colour halfway through a walk cycle.""" @@ -146,10 +133,6 @@ def run(name, spec, dry=False): print(r.stdout.strip() or r.stderr.strip()) if r.returncode or not all(os.path.exists(o) for o in outs): print(f'[{name}] REJECTED: slicing failed (frames merged?) — re-run or add "cut"'); return False - if spec.get('arm_left'): - bad = arm_not_left(outs) - if bad: - print(f'[{name}] REJECTED: {bad} was drawn MIRRORED — racket in the wrong hand'); return False if lead: outs = outs[1:] # the scale reference is not installed before = [density(os.path.join(HERE, s + '.png')) for s in sprites] after = [density(o) for o in outs] diff --git a/assets/_regen_batches.json b/assets/_regen_batches.json index 8627046..8057518 100644 --- a/assets/_regen_batches.json +++ b/assets/_regen_batches.json @@ -387,50 +387,5 @@ "anchor": 0, "check": "same", "subject": "The reference is ONE tennis player in a white shirt, blue shorts and a white headband, seen from behind, serving with the racket high in his right hand. Draw FOUR frames of that same man. Every figure is seen strictly FROM BEHIND \u2014 the back of his head and the back of his shirt towards you, NO face, NO eyes, NO nose, NO mouth. Every figure is drawn from exactly the same viewpoint: same camera height, same distance, straight on, standing on the same level ground line, with no perspective distortion, no foreshortening, no worm's-eye or bird's-eye angle and no three-quarter turn. Same size, same body, same style, same BRIGHT WHITE shirt and BRIGHT BLUE shorts as the reference \u2014 do not wash the colours out. He is RIGHT-HANDED and the racket NEVER changes hands: in every single frame the racket is in his RIGHT hand, which \u2014 because he is drawn from behind \u2014 is on the RIGHT-HAND SIDE OF THE IMAGE, exactly as in the reference. His empty left arm is on the LEFT-HAND SIDE OF THE IMAGE. Draw NO ball anywhere in any frame \u2014 the game draws the ball. Frame 1: he STANDS upright and relaxed at the baseline, arms down, the racket hanging at his right side. Frame 2: he leans forward a little and his empty LEFT hand reaches down and out to the LEFT-HAND SIDE OF THE IMAGE, palm down, as if patting a ball on the ground beside his left foot; the racket stays in his right hand at his right side. Frame 3 IS THE SAME DRAWING AS FRAME 2 \u2014 identical stance, identical legs, identical head, identical racket, identical everything \u2014 and the ONLY difference is that his left elbow is bent a little more so that left hand has risen about one hand's width higher, as if the ball had bounced back up into it. Frame 4: the ball toss \u2014 he leans back and throws that same empty LEFT arm up high above him on the LEFT-HAND SIDE OF THE IMAGE, having just released the ball, while the racket stays in his right hand down at his right side. Output exactly four figures in one row, evenly spaced and far apart." - }, - "player_windup": { - "sprites": [ - "p_pull0", - "p_pull1", - "p_pull2", - "p_pull3", - "p_pull4" - ], - "ref_only": [ - "p_idle1", - "p_toss" - ], - "anchor": 0, - "check": "same", - "scale": 1.6, - "arm_left": true, - "subject": "The reference shows the same tennis player twice \u2014 white shirt, blue shorts, white headband, seen from behind: standing with the racket in his right hand, and throwing the ball up to serve with his empty left arm high on the left-hand side of the image. Draw FIVE frames of that same man. Every figure is seen strictly FROM BEHIND \u2014 the back of his head towards you, NO face, NO eyes, NO nose \u2014 all from the same camera height and distance, on the same ground line, no perspective distortion and no three-quarter turn. Same size, same body, same style, same BRIGHT WHITE shirt and BRIGHT BLUE shorts, no coloured collar or trim. Draw NO ball anywhere \u2014 the game draws the ball. He is RIGHT-HANDED and the racket NEVER changes hands: his whole right arm stays on the RIGHT-HAND SIDE OF THE IMAGE and his empty LEFT arm stays thrown up high on the LEFT-HAND SIDE OF THE IMAGE. Never mirror him.\nTHE FOUR SERVE FRAMES MUST BE STRONGLY DIFFERENT FROM ONE ANOTHER, and what changes between them is HOW FAR THE RACKET HAS FALLEN: in each frame the racket head is at a clearly lower point on his body than in the frame before, and his body leans further back. Do not draw four similar poses. In ALL FOUR his empty left arm is held up high above him and never comes down \u2014 only the racket falls.\nFrame 1: he simply STANDS upright and relaxed, arms down \u2014 a plain standing frame.\nFrame 2: the racket is held STRAIGHT UP, its head HIGH ABOVE HIS HEAD with clear sky between the racket and his hair, his right arm almost straight, his body upright, and his empty LEFT arm still thrown up high above him on the left-hand side of the image.\nFrame 3: the racket has tipped over backwards so its head now points DOWN and sits BESIDE HIS EAR, level with his face, his right elbow raised to shoulder height, his body just beginning to lean back, and his empty LEFT arm still thrown up high above him on the left-hand side of the image.\nFrame 4: the racket head has fallen to the MIDDLE OF HIS BACK, between his shoulder blades, his right elbow high above his shoulder, his back arched and leaning clearly backwards, knees beginning to bend, and his empty LEFT arm still thrown up high above him on the left-hand side of the image.\nFrame 5: the racket head has fallen all the way down to the BACK OF HIS THIGHS, below his shorts, the racket hanging almost straight down behind him; his right elbow points at the sky beside his head, his body is leaned back as far as it goes with his hips pushed forward, his heels are lifted off the ground, and his empty LEFT arm is STILL thrown up high above him on the left-hand side of the image. This is maximum tension, the instant before he explodes upwards.\nOutput exactly five figures in one row, evenly spaced and far apart." - }, - "player_idle": { - "sprites": [ - "p_idle1", - "p_idle2" - ], - "ref_only": [ - "p_idle1" - ], - "anchor": 0, - "check": "same", - "scale": 1.6, - "subject": "The reference is ONE tennis player in a white shirt, blue shorts and a white headband, seen from behind, standing at the baseline with the racket in his right hand. Draw TWO frames of that same man waiting. Every figure is seen strictly FROM BEHIND \u2014 the back of his head and the back of his shirt towards you, NO face, NO eyes, NO nose, NO mouth. Every figure is drawn from exactly the same viewpoint: same camera height, same distance, straight on, standing on the same level ground line, with no perspective distortion, no foreshortening, no worm's-eye or bird's-eye angle and no three-quarter turn. Same size, same body, same style, same BRIGHT WHITE shirt and BRIGHT BLUE shorts as the reference \u2014 do not wash the colours out.He is RIGHT-HANDED and the racket NEVER changes hands: in every frame the racket is in his RIGHT hand, which \u2014 because he is drawn from behind \u2014 is on the RIGHT-HAND SIDE OF THE IMAGE, exactly as in the reference. His empty left arm hangs on the LEFT-HAND SIDE OF THE IMAGE. Draw NO ball anywhere. THE TWO FRAMES ARE THE SAME DRAWING: identical body, identical arms, identical racket, identical head \u2014 the ONLY difference is a small idle weight shift. Frame 1: his weight is on his left foot, that hip a little higher. Frame 2: the same man with his weight settled onto his right foot. Output exactly two figures in one row, far apart." - }, - "player_drive": { - "sprites": [ - "p_pull0", - "p_pull4" - ], - "ref_only": [ - "p_idle1", - "p_pull3" - ], - "anchor": 0, - "check": "same", - "scale": 1.6, - "subject": "The reference shows the same tennis player twice \u2014 white shirt, blue shorts, white headband, seen from behind: standing with the racket in his right hand, and part-way through a serve with the racket dropping behind his back. Draw TWO frames of that same man. Both are seen strictly FROM BEHIND \u2014 the back of his head towards you, NO face, NO eyes, NO nose \u2014 from the same camera height and distance, on the same ground line, no perspective distortion and no three-quarter turn. Same size, same body, same style, same BRIGHT WHITE shirt and BRIGHT BLUE shorts, no coloured collar or trim, a plain grey-strung wooden racket. Draw NO ball anywhere. He is RIGHT-HANDED and the racket stays in his RIGHT hand. Frame 1: he simply STANDS upright and relaxed, arms down \u2014 a plain standing frame. Frame 2 is the instant of MAXIMUM TENSION just before he uncoils. He stands tall and nearly straight, not crouched and not folded over: his legs are long, his heels lifted, and his upper body leans BACKWARDS so his shoulders are behind his hips and his hips are pushed forward. His RIGHT elbow is raised beside his head and the racket hangs STRAIGHT DOWN BEHIND HIS BACK from that hand, the racket head level with his hips. His LEFT arm is folded down in front of his chest. His head is UP and clearly visible from behind with the white headband on it \u2014 his arms must not cover his head, and he must not be bent over sideways or forwards. Output exactly two figures in one row, far apart." } } \ No newline at end of file diff --git a/assets/p_pull2.png b/assets/p_pull.png similarity index 100% rename from assets/p_pull2.png rename to assets/p_pull.png diff --git a/assets/p_pull1.png b/assets/p_pull1.png deleted file mode 100644 index 6f3c672..0000000 Binary files a/assets/p_pull1.png and /dev/null differ diff --git a/assets/p_pull3.png b/assets/p_pull3.png deleted file mode 100644 index 8469502..0000000 Binary files a/assets/p_pull3.png and /dev/null differ diff --git a/assets/p_pull4.png b/assets/p_pull4.png deleted file mode 100644 index bd3e0ac..0000000 Binary files a/assets/p_pull4.png and /dev/null differ diff --git a/assets/p_smash.png b/assets/p_smash.png index 53ef64e..bd3e0ac 100644 Binary files a/assets/p_smash.png and b/assets/p_smash.png differ diff --git a/index.html b/index.html index 8b61e18..b8f1f97 100644 --- a/index.html +++ b/index.html @@ -97,6 +97,6 @@
- +