diff --git a/assets/__pycache__/_regen.cpython-314.pyc b/assets/__pycache__/_regen.cpython-314.pyc
index c4a3f2e..1cd98d0 100644
Binary files a/assets/__pycache__/_regen.cpython-314.pyc and b/assets/__pycache__/_regen.cpython-314.pyc differ
diff --git a/assets/_regen.py b/assets/_regen.py
index 3d3ec93..4c95f9c 100644
--- a/assets/_regen.py
+++ b/assets/_regen.py
@@ -55,6 +55,9 @@ CANVAS = (
"The canvas is a WIDE LANDSCAPE STRIP and every figure stands side by side in ONE SINGLE HORIZONTAL "
"ROW on the same ground line — never a grid, never two rows, never stacked."
)
+BG_FIRST = ("THE BACKGROUND OF THE OUTPUT IMAGE MUST BE ONE FLAT SOLID MAGENTA #FF00FF. No court, "
+ "no grass, no lines, no scenery, no sky, no gradient — nothing behind the figures but flat "
+ "magenta, edge to edge. ")
STYLE = RES + FIDELITY + CANVAS
NEW_STYLE = RES + (
"Match the reference's DRAWING STYLE, its overall figure height and its facing — but the "
@@ -177,16 +180,40 @@ def kit_off(outs, ref_name, tol=(12, 10, 1.5, 4)):
# opt-in per batch (`stride: [[i, j]]`) rather than applied to everything with two frames in it.
STRIDE_H, STRIDE_TOP, STRIDE_LEG, STRIDE_HIP = 0.04, 0.20, 0.30, 0.62
-def stride_stats(pa, pb):
+def _masks(pa, pb):
a, b = Image.open(pa).convert('RGBA'), Image.open(pb).convert('RGBA')
w, h = max(a.width, b.width), max(a.height, b.height)
def m(im):
c = Image.new('RGBA', (w, h)); c.paste(im, ((w - im.width) // 2, h - im.height))
return np.array(c)[..., 3] > 110
- ma, mb = m(a), m(b)
+ return a, b, m(a), m(b), h
+
+def _d(x, y):
+ return round(float((x ^ y).sum() / max(1, (x | y).sum())), 3)
+
+def stride_stats(pa, pb):
+ a, b, ma, mb, h = _masks(pa, pb)
hip = int(h * STRIDE_HIP)
- d = lambda x, y: round(float((x ^ y).sum() / max(1, (x | y).sum())), 3)
- return round(a.height / max(1, b.height), 3), d(ma[:hip], mb[:hip]), d(ma[hip:], mb[hip:])
+ return round(a.height / max(1, b.height), 3), _d(ma[:hip], mb[:hip]), _d(ma[hip:], mb[hip:])
+
+# ---- DID THE CHANGE ACTUALLY HAPPEN? A chained restyle asks for one sheet redrawn with ONE THING
+# different, and "copy this exactly" reliably beats "…but change that": removing a HEAD works every
+# time (it is dramatic), removing an ARM mostly did not — five of seven wound frames came back as
+# the reference redrawn, differing by 0.02-0.05 of their own silhouette, and the ladder silently
+# healed the body it had just maimed. So a batch can declare how much it must differ from the sheet
+# it was drawn FROM. Measured against the known numbers: a copy scores 0.02-0.05, a real amputation
+# 0.12-0.38, a decapitation 0.27-0.75. Nothing else in the pipeline can see this — the art is
+# beautiful, correctly coloured, correctly posed and completely wrong.
+def changed_off(outs, refs, want):
+ for o, r in zip(outs, refs):
+ rp = os.path.join(HERE, r + '.png')
+ if not os.path.exists(rp): continue
+ _, _, ma, mb, _ = _masks(o, rp)
+ d = _d(ma, mb)
+ if d < want:
+ return (f'{os.path.basename(o)} is {r} redrawn — only {d:.3f} of the silhouette differs '
+ f'(want >= {want}). The thing that was supposed to change did not change.')
+ return None
def stride_off(outs, pairs):
for i, j in pairs:
@@ -245,7 +272,14 @@ def run(name, spec, dry=False):
ref = ref_sheet(shown, f'{SCRATCH}/{name}_ref.png', spec.get('scale', 2.0),
frames=len(sprites) + (1 if lead else 0))
sheet = f'{SCRATCH}/{name}_hi.png'
- prompt = f"{spec['subject']} {STATE_STYLE if spec.get('state') else POSE_STYLE if spec.get('pose') else NEW_STYLE if spec.get('newart') else STYLE}"
+ # THE BACKGROUND RULE GOES FIRST, not only at the end of the style block. The chained restyle
+ # subjects are long, and with the magenta instruction sitting behind three hundred words of
+ # "keep everything exactly as the reference" the generator started painting the figures on a
+ # COURT — grey-blue, grey-green, dark green backgrounds, six rejections in eleven rolls. Same
+ # sentence, said before anything else has had a chance to bury it.
+ prompt = (BG_FIRST + spec['subject'] + ' '
+ + (STATE_STYLE if spec.get('state') else POSE_STYLE if spec.get('pose')
+ else NEW_STYLE if spec.get('newart') else STYLE))
if dry:
print(f'[{name}] would generate {len(sprites)} frames: {", ".join(sprites)}'); return True
print(f'[{name}] generating {len(sprites)} frames...')
@@ -287,6 +321,10 @@ def run(name, spec, dry=False):
if spec.get('stride'):
bad = stride_off(outs, spec['stride'])
if bad: print(f'[{name}] REJECTED: {bad}'); return False
+ # a chained restyle declares how far it must move from the sheet it was drawn from
+ if spec.get('changed') and len(spec.get('ref_only', [])) == len(sprites):
+ bad = changed_off(outs, spec['ref_only'], spec['changed'])
+ if bad: print(f'[{name}] REJECTED: {bad}'); return False
for o, s in zip(outs, sprites):
if spec.get('check', 'finer') == 'finer': colour_match(o, s)
Image.open(o).save(os.path.join(HERE, s + '.png'))
diff --git a/assets/_regen_batches.json b/assets/_regen_batches.json
index 3a0a48b..ca44270 100644
--- a/assets/_regen_batches.json
+++ b/assets/_regen_batches.json
@@ -1302,12 +1302,12 @@
"anchor": 0,
"palette_ref": "z_r1",
"palette_tol": [
- 12,
- 10,
+ 20,
+ 14,
99,
- 4
+ 5
],
- "subject": "Three frames of the zombie SERVING. The FIRST reference image is the ZOMBIE TENNIS PLAYER this sheet is of — the same dead man, and he must be IDENTICAL to it in every frame: dirty yellow-blond hair, a RED CLOTH HEADBAND, a torn red short-sleeved tennis shirt, WARM CHARCOAL GREY tennis shorts above the knee (a slightly brownish dark grey, never blue-grey, never pale), bare grey-green dead legs, white socks and white tennis shoes, grey-green corpse skin, sunken eyes, a wooden racket with a pale strung head. Same build, same height, same colours, same drawing style. The REMAINING reference images are there ONLY to show the POSES to copy — never the character, never the clothes, never the skin. Copy each pose exactly. Frame 1: he stands at the baseline about to serve, bouncing the ball on the ground in front of him — copy reference image 2: upright, racket held low across the body, the free hand down with a tennis ball just under it. Frame 2: the TOSS — copy reference image 3: the free arm thrown STRAIGHT UP above the head with the ball leaving the fingertips, the racket dropping back behind the shoulder, weight rocking onto the back foot, the head tipped up watching the ball. Frame 3: the STRIKE — copy reference image 4: the racket at FULL STRETCH overhead at the moment of contact, the body extended upward on the toes, the free arm coming down across the chest. Output exactly 3 figures side by side in ONE horizontal row, all standing on the same ground line, with empty magenta between them. "
+ "subject": "Three frames of the zombie SERVING. The FIRST reference image is the ZOMBIE TENNIS PLAYER this sheet is of — the same dead man, and he must be IDENTICAL to it in every frame: dirty yellow-blond hair, a RED CLOTH HEADBAND, a torn red short-sleeved tennis shirt, WARM CHARCOAL GREY tennis shorts above the knee (a slightly brownish dark grey, never blue-grey, never pale), bare grey-green dead legs, white socks and white tennis shoes, grey-green corpse skin, sunken eyes, a wooden racket with a pale strung head. Same build, same height, same colours, same drawing style. The REMAINING reference images are there ONLY to show the POSES to copy — never the character, never the clothes, never the skin. Copy each pose exactly. Frame 1: he stands at the baseline about to serve, bouncing the ball on the ground in front of him — copy reference image 2: upright, racket held low across the body, the free hand down and EMPTY, palm open, as if he had just let a ball go. Frame 2: the TOSS — copy reference image 3: the free arm thrown STRAIGHT UP above the head the hand OPEN AND EMPTY at the top of the throw, the racket dropping back behind the shoulder, weight rocking onto the back foot, the head tipped up watching the ball. Frame 3: the STRIKE — copy reference image 4: the racket at FULL STRETCH overhead at the top of the swing, the body extended upward on the toes, the free arm coming down across the chest. Output exactly 3 figures side by side in ONE horizontal row, all standing on the same ground line, with empty magenta between them. THERE IS NO TENNIS BALL ANYWHERE IN ANY FRAME. Do not draw a ball in his hand, in the air, on the ground or on the racket — the ball is drawn separately by the game. His hands and the air around him are EMPTY, and the poses are the same as if a ball were there. "
},
"racket_fall": {
"sprites": [
@@ -1333,5 +1333,105 @@
4
],
"subject": "Four frames of the zombie COLLAPSING AND GETTING BACK UP. The FIRST reference image is the ZOMBIE TENNIS PLAYER this sheet is of — the same dead man, and he must be IDENTICAL to it in every frame: dirty yellow-blond hair, a RED CLOTH HEADBAND, a torn red short-sleeved tennis shirt, WARM CHARCOAL GREY tennis shorts above the knee (a slightly brownish dark grey, never blue-grey, never pale), bare grey-green dead legs, white socks and white tennis shoes, grey-green corpse skin, sunken eyes, a wooden racket with a pale strung head. Same build, same height, same colours, same drawing style. The REMAINING reference images are there ONLY to show the POSES to copy — never the character, never the clothes, never the skin. Copy each pose exactly. Frame 1: the knees buckle — copy reference image 2: still upright but folding, one knee giving way, the racket arm flailing out for balance. Frame 2: DOWN ON HIS KNEES, doubled over, head low, one hand on the ground, still gripping the racket in the other — copy the crumpled posture of reference image 3. Frame 3: FLAT ON THE GROUND, face down, sprawled, the racket loose beside the outstretched hand — copy the collapsed posture of reference image 4. Draw him lying down: this figure is WIDE AND LOW, not standing. Frame 4: DRAGGING HIMSELF BACK UP — up on one knee with the other foot planted, one hand pushing off the ground, head coming up, the racket gripped again, halfway to standing. Output exactly 4 figures side by side in ONE horizontal row, all standing on the same ground line, with empty magenta between them. "
+ },
+ "racket_serve_w1": {
+ "sprites": [
+ "z_rw1bounce",
+ "z_rw1toss",
+ "z_rw1serve"
+ ],
+ "ref_only": [
+ "z_rbounce",
+ "z_rtoss",
+ "z_rserve"
+ ],
+ "state": true,
+ "check": "loose",
+ "anchor": 0,
+ "palette_ref": "z_rbounce",
+ "palette_tol": [
+ 26,
+ 14,
+ 99,
+ 5
+ ],
+ "subject": "The reference image is a SPRITE SHEET of ONE zombie tennis player, 3 frames. REDRAW THE WHOLE SHEET with ONE THING CHANGED: his LEFT ARM — the one WITHOUT the racket — IS COMPLETELY GONE. He has ONLY ONE ARM in every frame: no left arm, no left hand, no left sleeve anywhere, just a ragged torn stump at the left shoulder gushing blood down his side, and his shirt slashed open across the ribs. Where the reference uses that arm, the stump is thrown out in its place instead. COUNT THE ARMS: there must be EXACTLY ONE ARM in every single frame — the one holding the racket. The other shoulder ends in a stump and nothing hangs from it. A frame with two arms in it is wrong. This is the whole change being asked for; do not copy the reference's second arm across. EVERYTHING ELSE STAYS EXACTLY AS IT IS IN THE REFERENCE — the same character, the same red shirt, the same WARM CHARCOAL shorts and their exact shade, the same grey-green skin, the same white socks and shoes, the same wooden racket, the same 3 poses in the same order, the same body positions, the same facing, the same heights. Output exactly 3 figures side by side in ONE horizontal row. Because he has only one arm he can no longer throw the ball up with a free hand: in the TOSS frame that arm's stump is flung upward instead and the racket hand flicks up in front of his face, as a one-armed player tosses off the racket face. THERE IS NO TENNIS BALL ANYWHERE IN ANY FRAME. Do not draw a ball in his hand, in the air, on the ground or on the racket — the ball is drawn separately by the game. His hands and the air around him are EMPTY, and the poses are the same as if a ball were there. ",
+ "changed": 0.1
+ },
+ "racket_serve_w2": {
+ "sprites": [
+ "z_rw2bounce",
+ "z_rw2toss",
+ "z_rw2serve"
+ ],
+ "ref_only": [
+ "z_rw1bounce",
+ "z_rw1toss",
+ "z_rw1serve"
+ ],
+ "state": true,
+ "check": "loose",
+ "anchor": 0,
+ "palette_ref": "z_rw1bounce",
+ "palette_tol": [
+ 26,
+ 14,
+ 99,
+ 5
+ ],
+ "subject": "The reference image is a SPRITE SHEET of ONE zombie tennis player, 3 frames. REDRAW THE WHOLE SHEET with ONE THING CHANGED: his HEAD IS COMPLETELY GONE — no head, no face, no hair, no headband anywhere above the shoulders, only a ragged gushing neck stump, so every figure is A CLEAR HEAD SHORTER than in the reference. He is ALREADY missing his left arm in the reference and it STAYS gone — he still has only one arm. EVERYTHING ELSE STAYS EXACTLY AS IT IS IN THE REFERENCE — the same character, the same red shirt, the same WARM CHARCOAL shorts and their exact shade, the same grey-green skin, the same white socks and shoes, the same wooden racket, the same 3 poses in the same order, the same body positions, the same facing, the same heights. Output exactly 3 figures side by side in ONE horizontal row. THERE IS NO TENNIS BALL ANYWHERE IN ANY FRAME. Do not draw a ball in his hand, in the air, on the ground or on the racket — the ball is drawn separately by the game. His hands and the air around him are EMPTY, and the poses are the same as if a ball were there. ",
+ "changed": 0.15
+ },
+ "racket_fall_w1": {
+ "sprites": [
+ "z_rw1fall1",
+ "z_rw1fall2",
+ "z_rw1fall3",
+ "z_rw1rise"
+ ],
+ "ref_only": [
+ "z_rfall1",
+ "z_rfall2",
+ "z_rfall3",
+ "z_rrise"
+ ],
+ "state": true,
+ "check": "loose",
+ "anchor": 0,
+ "palette_ref": "z_rfall1",
+ "palette_tol": [
+ 26,
+ 14,
+ 99,
+ 5
+ ],
+ "subject": "The reference image is a SPRITE SHEET of ONE zombie tennis player, 4 frames. REDRAW THE WHOLE SHEET with ONE THING CHANGED: his LEFT ARM — the one WITHOUT the racket — IS COMPLETELY GONE. He has ONLY ONE ARM in every frame: no left arm, no left hand, no left sleeve anywhere, just a ragged torn stump at the left shoulder gushing blood down his side, and his shirt slashed open across the ribs. Where the reference uses that arm, the stump is thrown out in its place instead. COUNT THE ARMS: there must be EXACTLY ONE ARM in every single frame — the one holding the racket. The other shoulder ends in a stump and nothing hangs from it. A frame with two arms in it is wrong. This is the whole change being asked for; do not copy the reference's second arm across. EVERYTHING ELSE STAYS EXACTLY AS IT IS IN THE REFERENCE — the same character, the same red shirt, the same WARM CHARCOAL shorts and their exact shade, the same grey-green skin, the same white socks and shoes, the same wooden racket, the same 4 poses in the same order, the same body positions, the same facing, the same heights. Output exactly 4 figures side by side in ONE horizontal row. THERE IS NO TENNIS BALL ANYWHERE IN ANY FRAME. Do not draw a ball in his hand, in the air, on the ground or on the racket — the ball is drawn separately by the game. His hands and the air around him are EMPTY, and the poses are the same as if a ball were there. ",
+ "changed": 0.1
+ },
+ "racket_fall_w2": {
+ "sprites": [
+ "z_rw2fall1",
+ "z_rw2fall2",
+ "z_rw2fall3",
+ "z_rw2rise"
+ ],
+ "ref_only": [
+ "z_rw1fall1",
+ "z_rw1fall2",
+ "z_rw1fall3",
+ "z_rw1rise"
+ ],
+ "state": true,
+ "check": "loose",
+ "anchor": 0,
+ "palette_ref": "z_rw1fall1",
+ "palette_tol": [
+ 26,
+ 14,
+ 99,
+ 5
+ ],
+ "subject": "The reference image is a SPRITE SHEET of ONE zombie tennis player, 4 frames. REDRAW THE WHOLE SHEET with ONE THING CHANGED: his HEAD IS COMPLETELY GONE — no head, no face, no hair, no headband anywhere above the shoulders, only a ragged gushing neck stump, so every figure is A CLEAR HEAD SHORTER than in the reference. He is ALREADY missing his left arm in the reference and it STAYS gone — he still has only one arm. EVERYTHING ELSE STAYS EXACTLY AS IT IS IN THE REFERENCE — the same character, the same red shirt, the same WARM CHARCOAL shorts and their exact shade, the same grey-green skin, the same white socks and shoes, the same wooden racket, the same 4 poses in the same order, the same body positions, the same facing, the same heights. Output exactly 4 figures side by side in ONE horizontal row. THERE IS NO TENNIS BALL ANYWHERE IN ANY FRAME. Do not draw a ball in his hand, in the air, on the ground or on the racket — the ball is drawn separately by the game. His hands and the air around him are EMPTY, and the poses are the same as if a ball were there. ",
+ "changed": 0.15
}
}
\ No newline at end of file
diff --git a/assets/z_rbounce.png b/assets/z_rbounce.png
index dd905bf..ab5fcc0 100644
Binary files a/assets/z_rbounce.png and b/assets/z_rbounce.png differ
diff --git a/assets/z_rserve.png b/assets/z_rserve.png
index 31177be..752bd50 100644
Binary files a/assets/z_rserve.png and b/assets/z_rserve.png differ
diff --git a/assets/z_rtoss.png b/assets/z_rtoss.png
index 4f52a5d..f734a56 100644
Binary files a/assets/z_rtoss.png and b/assets/z_rtoss.png differ
diff --git a/assets/z_rw1bounce.png b/assets/z_rw1bounce.png
new file mode 100644
index 0000000..5ac5dc9
Binary files /dev/null and b/assets/z_rw1bounce.png differ
diff --git a/assets/z_rw1fall1.png b/assets/z_rw1fall1.png
new file mode 100644
index 0000000..4cfd9b9
Binary files /dev/null and b/assets/z_rw1fall1.png differ
diff --git a/assets/z_rw1fall2.png b/assets/z_rw1fall2.png
new file mode 100644
index 0000000..357c365
Binary files /dev/null and b/assets/z_rw1fall2.png differ
diff --git a/assets/z_rw1fall3.png b/assets/z_rw1fall3.png
new file mode 100644
index 0000000..2728fe9
Binary files /dev/null and b/assets/z_rw1fall3.png differ
diff --git a/assets/z_rw1rise.png b/assets/z_rw1rise.png
new file mode 100644
index 0000000..04da578
Binary files /dev/null and b/assets/z_rw1rise.png differ
diff --git a/assets/z_rw1serve.png b/assets/z_rw1serve.png
new file mode 100644
index 0000000..ae4b51a
Binary files /dev/null and b/assets/z_rw1serve.png differ
diff --git a/assets/z_rw1toss.png b/assets/z_rw1toss.png
new file mode 100644
index 0000000..bff98ff
Binary files /dev/null and b/assets/z_rw1toss.png differ
diff --git a/assets/z_rw2bounce.png b/assets/z_rw2bounce.png
new file mode 100644
index 0000000..b3de2f9
Binary files /dev/null and b/assets/z_rw2bounce.png differ
diff --git a/assets/z_rw2fall1.png b/assets/z_rw2fall1.png
new file mode 100644
index 0000000..9fb05e3
Binary files /dev/null and b/assets/z_rw2fall1.png differ
diff --git a/assets/z_rw2fall2.png b/assets/z_rw2fall2.png
new file mode 100644
index 0000000..2b2b23a
Binary files /dev/null and b/assets/z_rw2fall2.png differ
diff --git a/assets/z_rw2fall3.png b/assets/z_rw2fall3.png
new file mode 100644
index 0000000..fba7cb8
Binary files /dev/null and b/assets/z_rw2fall3.png differ
diff --git a/assets/z_rw2rise.png b/assets/z_rw2rise.png
new file mode 100644
index 0000000..e9841d2
Binary files /dev/null and b/assets/z_rw2rise.png differ
diff --git a/assets/z_rw2serve.png b/assets/z_rw2serve.png
new file mode 100644
index 0000000..0af574e
Binary files /dev/null and b/assets/z_rw2serve.png differ
diff --git a/assets/z_rw2toss.png b/assets/z_rw2toss.png
new file mode 100644
index 0000000..2027848
Binary files /dev/null and b/assets/z_rw2toss.png differ
diff --git a/index.html b/index.html
index 137f827..10b3a1c 100644
--- a/index.html
+++ b/index.html
@@ -431,6 +431,6 @@ if (location.hostname !== 'splatter-tennis.wuwi.one') {
-
+