From b355572a780144a2b96d7219b861368078266319 Mon Sep 17 00:00:00 2001 From: Matt Edholm Date: Sun, 17 May 2026 13:14:06 -0400 Subject: [PATCH] fix(render): V2 panel ribbon-at-bottom mounting compensation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeviceModel gains physicalRotationDegrees() — extra rotation applied after the orientation-fit step, compensating for the panel's physical mounting vs its scan-zero corner. V2 (13.3" Spectra-6) is mounted with the ribbon at the bottom of a portrait photo (opposite scan-zero), so needs 180°. V1 → 0. Existing rendered .bin files will need a re-render to take effect — run 'app:rerender-assets' on prod after deploy. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Enum/DeviceModel.php | 19 +++++++++++++++++++ .../RenderImageMessageHandler.php | 8 ++++++++ 2 files changed, 27 insertions(+) diff --git a/src/Enum/DeviceModel.php b/src/Enum/DeviceModel.php index a046339..abad0ad 100644 --- a/src/Enum/DeviceModel.php +++ b/src/Enum/DeviceModel.php @@ -39,6 +39,25 @@ enum DeviceModel: string }; } + /** + * Extra rotation in degrees applied AFTER the orientation-fit rotation, + * compensating for how the panel is physically mounted in its frame + * relative to the panel's scan-zero corner. + * + * V2 (13.3" Spectra-6): mounted with the ribbon at the bottom of a + * portrait photo (chosen physical mounting), opposite the panel's + * scan-zero corner. Needs a 180° flip so images render right-side-up. + * + * V1: no compensation — physical mount matches scan-zero. + */ + public function physicalRotationDegrees(): int + { + return match ($this) { + self::V1 => 0, + self::V2 => 180, + }; + } + /** User-facing width (pre-rotation) for the given orientation. */ public function width(Orientation $orientation): int { diff --git a/src/MessageHandler/RenderImageMessageHandler.php b/src/MessageHandler/RenderImageMessageHandler.php index f95a96c..92ce3f4 100644 --- a/src/MessageHandler/RenderImageMessageHandler.php +++ b/src/MessageHandler/RenderImageMessageHandler.php @@ -148,6 +148,14 @@ final class RenderImageMessageHandler $imagick->rotateImage(new \ImagickPixel('white'), -90); } + // Compensate for how the panel is physically mounted in its frame + // relative to its scan-zero corner. V2 ships ribbon-at-bottom of + // portrait, opposite scan-zero → needs 180° flip. V1 → 0. + $physRot = $model->physicalRotationDegrees(); + if ($physRot !== 0) { + $imagick->rotateImage(new \ImagickPixel('white'), $physRot); + } + // Build a strip of 6 palette pixels for remapImage $palImagick = new \Imagick(); foreach (self::PALETTE as $rgb) {