diff --git a/assets/_runroll.py b/assets/_runroll.py index 5f180c0..2d047a1 100644 --- a/assets/_runroll.py +++ b/assets/_runroll.py @@ -98,12 +98,21 @@ def prompt(k): 'garments, the hair and every one of its colours exactly. The poses in image 1 exist only to ' 'show the character — do NOT copy the poses.\n\n' f'EVERY ONE OF THE {N} FIGURES GRIPS THE WOODEN TENNIS RACKET IN THE RIGHT HAND, carried low ' - 'at the side mid-run. A figure with an empty hand is WRONG.\n\n' + 'at the side mid-run. A figure with an empty hand is WRONG. THE RACKET STAYS ON THE RIGHT ' + 'SIDE OF THE BODY IN EVERY SINGLE FIGURE — it never swings across the body, never tucks ' + 'behind the back to the left, never changes sides between neighbouring figures. The right ' + 'arm swings a LITTLE; the racket head stays visibly right of the body\'s midline throughout. ' + 'A single figure with the racket on the left side makes the whole animation flicker and is ' + 'WRONG.\n\n' f'THE {N} FIGURES ARE ONE CONTINUOUS RUN CYCLE, {c["view"]}. Evenly spaced, every figure on ' - 'one shared ground line, every figure at the same scale as its neighbours.\n\n' + 'one shared ground line, every figure at the same scale as its neighbours. THE FIGURES ' + 'NEVER TOUCH OR OVERLAP: leave a clear band of empty magenta between every pair of ' + 'neighbours — no part of any figure, the racket included, reaches into its neighbour\'s ' + 'space. A racket overlapping the next figure is WRONG.\n\n' 'The cycle, frame by frame: 1 the right foot planted under the body, left knee driving up and ' 'forward; 2 push-off, the right leg trailing straight behind, the left leg reaching forward; ' - '3 full flight, both feet off the ground, the stride at its widest; 4 the left foot landing ' + '3 full flight, both feet off the ground, the stride at its widest — THE RACKET STILL ' + 'GRIPPED IN THE RIGHT HAND in this airborne figure, exactly as in every other; 4 the left foot landing ' 'ahead of the body. Frames 5 to 8 are the SAME four moments with the legs swapped — the left ' 'foot planted, the right knee driving. Across the row the gap between the feet opens, closes, ' 'opens and closes again. The arms counter-swing: the opposite arm to the driving knee comes ' @@ -194,6 +203,19 @@ def cut(k, install=False, n=0): pad = Image.new('RGBA', (im.width + abs(off), im.height), (0, 0, 0, 0)) pad.paste(im, (0 if off > 0 else abs(off), 0)) pad.save(o) + # THE RACKET-SIDE GATE. One frame with the racket swung across the body makes the whole cycle + # flicker left-right at stride rate — Oliver: "the racket she is holding flashing left and + # right very fast" (the female cycle's frame 4 was the offender). racket_side locates the bed + # (pale blob or keyed hole); every frame must keep it right of centre, or at worst dead-centre. + # ...as a WARNING, not a gate: on back-view art the bed is part-occluded and the centroid + # reads ±0.05 of noise — it never flagged the original offender (old frame 4 measured -0.02). + # The montage review is the real gate; a strongly negative number here is what to look at. + from _famref import racket_side + sides = [racket_side(Image.open(o)) for o in outs] + print(' racket sides:', ['%s%+.2f' % ('?' if s is None else '', s or 0) for s in sides]) + crossed = [i + 1 for i, s in enumerate(sides) if s is not None and s < -0.12] + if crossed: + print(f' WARNING: racket may cross LEFT in frame(s) {crossed} — check the montage hard') # THE STRIDE GATE, on what was actually cut: every neighbouring pair (wrapping — it loops) must # differ in the legs, or the cycle holds a duplicated pose. Threshold calibrated against # _famref's (30% = a real stride pair; 12 catches only true near-duplicates). diff --git a/assets/p_run1.png b/assets/p_run1.png index d7852d6..b5a128b 100644 Binary files a/assets/p_run1.png and b/assets/p_run1.png differ diff --git a/assets/p_run1_f.png b/assets/p_run1_f.png index 480bf80..1f58e2e 100644 Binary files a/assets/p_run1_f.png and b/assets/p_run1_f.png differ diff --git a/assets/p_run2.png b/assets/p_run2.png index f17ad9e..afc1fb8 100644 Binary files a/assets/p_run2.png and b/assets/p_run2.png differ diff --git a/assets/p_run2_f.png b/assets/p_run2_f.png index 0ed39f8..50d566d 100644 Binary files a/assets/p_run2_f.png and b/assets/p_run2_f.png differ diff --git a/assets/p_run3.png b/assets/p_run3.png index fe5efe4..4a9d079 100644 Binary files a/assets/p_run3.png and b/assets/p_run3.png differ diff --git a/assets/p_run3_f.png b/assets/p_run3_f.png index 5c777b6..041df1d 100644 Binary files a/assets/p_run3_f.png and b/assets/p_run3_f.png differ diff --git a/assets/p_run4.png b/assets/p_run4.png index c425704..d4ab33f 100644 Binary files a/assets/p_run4.png and b/assets/p_run4.png differ diff --git a/assets/p_run4_f.png b/assets/p_run4_f.png index a3aebbf..f7fcd4d 100644 Binary files a/assets/p_run4_f.png and b/assets/p_run4_f.png differ diff --git a/assets/p_run5.png b/assets/p_run5.png index 79bb710..b4b2b54 100644 Binary files a/assets/p_run5.png and b/assets/p_run5.png differ diff --git a/assets/p_run5_f.png b/assets/p_run5_f.png index f6134cf..eb58085 100644 Binary files a/assets/p_run5_f.png and b/assets/p_run5_f.png differ diff --git a/assets/p_run6.png b/assets/p_run6.png index f34e79f..84bc174 100644 Binary files a/assets/p_run6.png and b/assets/p_run6.png differ diff --git a/assets/p_run6_f.png b/assets/p_run6_f.png index 7eda2d8..5ab4c35 100644 Binary files a/assets/p_run6_f.png and b/assets/p_run6_f.png differ diff --git a/index.html b/index.html index cf3c19c..d4fd954 100644 --- a/index.html +++ b/index.html @@ -431,6 +431,6 @@ if (location.hostname !== 'splatter-tennis.wuwi.one') { - + diff --git a/src/render.js b/src/render.js index e4e96bc..4b0d530 100644 --- a/src/render.js +++ b/src/render.js @@ -221,7 +221,7 @@ function farB(z) { // Asset cache-buster — bump with main.js?v=N (see the img.src note below). EXPORTED because the // menu screens hang tags on the same files, and two copies of this number is one copy too // many: the one that does not get bumped is the one that serves a cached 404. -export const SPRV = 110; +export const SPRV = 111; // The boot bar in main.js watches this. Nothing here CHANGES when things load — every sprite was // already requested at import — it just makes the wait visible and holds the menu back until the // last sheet has landed. See the onerror note below for the other half of "sometimes missing".