fix(render): V2 panel ribbon-at-bottom mounting compensation
CI / test (push) Has been cancelled

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) <noreply@anthropic.com>
This commit is contained in:
2026-05-17 13:14:06 -04:00
parent 019a3363c5
commit b355572a78
2 changed files with 27 additions and 0 deletions
+19
View File
@@ -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
{
@@ -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) {