""" 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. 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 pixlet render dev/calibration_test.star ox=0.3 oy=0.3 -o calib_offcenter.gif Keep ART_SIZE/ART_SLOT/the zoom math below in sync with the real _art_widget if that ever changes. History, if this ever needs revisiting: a Scale+Translate centered zoom was tried first and produced a periodic rendering "pop", root-caused to pixlet's own source (render/animation/translate.go): Translate's x/y are rounded to the nearest whole pixel every frame by default, while Scale never rounds, so a continuously-interpolating Scale combined with a rounded Translate holds still for several frames then jumps a whole pixel at once. `rounding = "none"` fixed that. But the Translate's *magnitude* was also wrong: it assumed Scale pivots at the top-left corner by default, when pixlet's docs say the default `origin` is already the center (0.5, 0.5) -- so the "centering" Translate was overcorrecting, visibly skewing the zoom toward the bottom-right instead. Replaced with `origin` directly (which does, correctly, what that Translate was clumsily approximating), confirmed via this calibration tool with genuinely different (non-default) origin values -- they do change the output, despite an earlier, flawed test (origin set to its own default and compared against itself) suggesting `origin` had no effect at all. """ 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, origin_x, origin_y): art = render.Image(src = art_bytes, width = ART_SIZE, height = ART_SIZE) top_scale = max(zoom_scale, 1.0) zoom = animation.Transformation( child = art, duration = ART_ZOOM_DURATION, origin = animation.Origin(origin_x, origin_y), keyframes = [ animation.Keyframe(percentage = 0.0, transforms = [animation.Scale(1.0, 1.0)]), animation.Keyframe(percentage = 0.5, transforms = [animation.Scale(top_scale, top_scale)], curve = "ease_in_out"), animation.Keyframe(percentage = 1.0, transforms = [animation.Scale(1.0, 1.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")) origin_x = float(config.get("ox", "0.5")) origin_y = float(config.get("oy", "0.5")) return render.Root(child = render.Row(children = [ _art_widget(CALIB.readall(), zoom_scale, origin_x, origin_y), render.Box(width = 32, height = 32, color = "#111111"), ]))