diff --git a/subwave_nowplaying.star b/subwave_nowplaying.star index ab4c32e..aa93254 100644 --- a/subwave_nowplaying.star +++ b/subwave_nowplaying.star @@ -553,7 +553,26 @@ def _glitch_jitter(zoom): jittered = animation.Transformation(child = zoom, duration = total_ticks, keyframes = keyframes) return render.Box(width = ART_SLOT, height = ART_SLOT, child = jittered) -SCANLINE_PERIOD = 4 # 1px line, 3px gap +# Line count and spacing are derived from each other, not picked +# independently: the small-sawtooth sweep below only loops seamlessly +# when sweeping by exactly one full repeat-period of the line pattern, +# which only exists (as a *small* shift, not the full ART_SLOT) when +# SCANLINE_COUNT evenly divides ART_SLOT. 6 lines was tried first (an +# explicit skip-tuple dropping 2 of 8 period-4 positions) and looked +# uneven/inconsistent during the sweep -- traced to exactly this: with 2 +# positions skipped, the pattern's true repeat period became the full 32 +# (not 4), so shifting by 4 and snapping back no longer reproduced an +# identical-looking frame. 4 divides 32 cleanly (spacing/sweep = 8px); +# the only other clean option near the original density is 8 (spacing +# 4px, the original count). Confirmed separately (pixel-sampled test +# renders) that pixlet's Transformation+Translate can't reveal content +# beyond what's visible at rest -- shifting only moves already-visible +# pixels, it doesn't pull in new rows from an off-screen duplicate -- so +# a single large sweep (shifting by the full ART_SLOT, tried as an +# alternative to this small-sawtooth approach) isn't viable either; +# small periodic shifts are the only mechanism that actually works here. +SCANLINE_COUNT = 4 +SCANLINE_SPACING = ART_SLOT // SCANLINE_COUNT SCANLINE_SWEEP_DURATION = 160 # 8s total at the default 50ms/tick SCANLINE_CYCLES = 8 # how many times the sawtooth below repeats over that _SCANLINE_CYCLE_TICKS = SCANLINE_SWEEP_DURATION // SCANLINE_CYCLES @@ -573,32 +592,20 @@ def _glitch_scanlines(): # top" looked like. # # This avoids all of that: the buffer is only ONE extra - # SCANLINE_PERIOD taller than the art slot (the same small, proven- + # SCANLINE_SPACING taller than the art slot (the same small, proven- # safe oversized margin used everywhere else in this file -- e.g. the # zoom's own ART_SIZE vs. ART_SLOT), and instead of one big sweep it's - # a repeating sawtooth -- ramp down by exactly one SCANLINE_PERIOD, + # a repeating sawtooth -- ramp down by exactly one SCANLINE_SPACING, # then snap back to 0 and repeat. Since the pattern repeats every - # SCANLINE_PERIOD anyway, a one-period displacement looks identical to - # the starting position, so the snap is invisible -- a standard - # infinite-scrolling-texture trick -- while the translate distance - # never has to leave the small range already confirmed safe. - # SCANLINE_PERIOD=4 over ART_SLOT=32 gives 8 evenly-spaced candidate - # positions (0, 4, 8, ..., 28); skipping 2 of those 8 (rather than - # changing the period itself, which the sweep's periodicity depends - # on) to land on 6 visible lines. Skipping two *interspersed* - # positions instead of the last two in the run -- which left an 8px - # gap with nothing in it at the bottom of every cycle -- spreads the - # resulting two, slightly-larger gaps across the full height instead. - # Removing two slots exactly opposite each other (half the 8-slot - # cycle apart) is the most even spacing achievable while keeping every - # line on a multiple of SCANLINE_PERIOD: gaps come out 4,4,8,4,4,8 -- - # symmetric, vs. e.g. removing two adjacent-ish slots which leaves an - # uneven 4,8,4,8,4,4 pattern. - SCANLINE_SKIP = (0, 16) - tall = ART_SLOT + SCANLINE_PERIOD + # SCANLINE_SPACING (see SCANLINE_COUNT above for why), a one-period + # displacement looks identical to the starting position, so the snap + # is invisible -- a standard infinite-scrolling-texture trick -- while + # the translate distance never has to leave the small range already + # confirmed safe. + tall = ART_SLOT + SCANLINE_SPACING lines = [] for y in range(tall): - if y % SCANLINE_PERIOD == 0 and (y % ART_SLOT) not in SCANLINE_SKIP: + if y % SCANLINE_SPACING == 0: lines.append(render.Box(width = ART_SLOT, height = 1, color = "#00000080")) else: lines.append(render.Box(width = ART_SLOT, height = 1, color = "#00000000")) @@ -614,7 +621,7 @@ def _glitch_scanlines(): )) keyframes.append(animation.Keyframe( percentage = float(ramp_end) / SCANLINE_SWEEP_DURATION, - transforms = [animation.Translate(0, SCANLINE_PERIOD)], + transforms = [animation.Translate(0, SCANLINE_SPACING)], curve = "linear", ))