Dividing the centering shift by top_scale (previous commit) avoided the strong-zoom black border, but made the shift a nonlinear function of the scale value being interpolated -- mid-animation, the pan no longer tracked the zoom at a constant rate, which read as jerky/skippy motion. Reverted shift to the original linear formula, (1-s)*(ART_SIZE/2), which moves smoothly in lockstep with Scale's own eased interpolation. Instead, capped ZOOM_LEVELS["strong"] at 1.45 (down from 1.7) -- the highest value where this plain linear formula stays safely inside the art's bounds, verified with the calibration tool (no black border across the full animation cycle, at both 1.45x and against a live station).
51 lines
2.2 KiB
Python
51 lines
2.2 KiB
Python
"""
|
|
Dev tool, not part of the deployed app: renders dev/calibration.png through
|
|
the exact same zoom logic as _art_widget in subwave_nowplaying.star, so you
|
|
can visually check how much margin gets cropped at rest and at full zoom,
|
|
and whether the crop is even on all sides.
|
|
|
|
calibration.png is a 360x360 grid: concentric 10%-wide color rings (magenta
|
|
outermost, then yellow, then cyan) plus four corner markers (red TL, green
|
|
TR, blue BL, white BR) and a center crosshair. At rest, you should see a
|
|
thin, EVEN sliver of magenta on all four edges. As it zooms in, each corner
|
|
marker should get clipped by the same amount -- if one side/corner loses
|
|
noticeably more than the others, the zoom isn't centered.
|
|
|
|
Usage:
|
|
pixlet render dev/calibration_test.star -o calib.gif
|
|
pixlet render dev/calibration_test.star zoom_scale=1.0 -o calib_rest.gif # rest only
|
|
|
|
Keep ART_SIZE/ART_SLOT/the zoom math below in sync with the real
|
|
_art_widget if that ever changes.
|
|
"""
|
|
|
|
load("render.star", "render")
|
|
load("animation.star", "animation")
|
|
load("calibration.png", CALIB = "file")
|
|
|
|
ART_SIZE = 36
|
|
ART_SLOT = 32
|
|
ART_ZOOM_DURATION = 200
|
|
|
|
def _art_widget(art_bytes, zoom_scale):
|
|
art = render.Image(src = art_bytes, width = ART_SIZE, height = ART_SIZE)
|
|
top_scale = max(zoom_scale, 1.0)
|
|
shift = (1.0 - top_scale) * (ART_SIZE / 2.0)
|
|
zoom = animation.Transformation(
|
|
child = art,
|
|
duration = ART_ZOOM_DURATION,
|
|
keyframes = [
|
|
animation.Keyframe(percentage = 0.0, transforms = [animation.Scale(1.0, 1.0), animation.Translate(0, 0)]),
|
|
animation.Keyframe(percentage = 0.5, transforms = [animation.Scale(top_scale, top_scale), animation.Translate(shift, shift)], curve = "ease_in_out"),
|
|
animation.Keyframe(percentage = 1.0, transforms = [animation.Scale(1.0, 1.0), animation.Translate(0, 0)], curve = "ease_in_out"),
|
|
],
|
|
)
|
|
return render.Box(width = ART_SLOT, height = ART_SLOT, child = zoom)
|
|
|
|
def main(config):
|
|
zoom_scale = float(config.get("zoom_scale", "1.4"))
|
|
return render.Root(child = render.Row(children = [
|
|
_art_widget(CALIB.readall(), zoom_scale),
|
|
render.Box(width = 32, height = 32, color = "#111111"),
|
|
]))
|