First upload, 18 controller version
This commit is contained in:
125
app/core/pattern_compat.py
Normal file
125
app/core/pattern_compat.py
Normal file
@@ -0,0 +1,125 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from app.core.types import PatternParameters, clamp
|
||||
|
||||
|
||||
SCAN_ANGLES: tuple[int, ...] = (0, 45, 90, 135, 180, 225, 270, 315)
|
||||
|
||||
_LEGACY_SCAN_IDS = {
|
||||
"row_chase",
|
||||
"row_scan",
|
||||
"column_chase",
|
||||
"column_scan",
|
||||
"diagonal_scan",
|
||||
}
|
||||
|
||||
_DEFAULT_SCAN_PARAMS = PatternParameters()
|
||||
|
||||
|
||||
def nearest_scan_angle(value: float) -> int:
|
||||
angle = int(round(float(value))) % 360
|
||||
return min(SCAN_ANGLES, key=lambda candidate: min((candidate - angle) % 360, (angle - candidate) % 360))
|
||||
|
||||
|
||||
def coerce_bool(value: object, default: bool = False) -> bool:
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
if isinstance(value, (int, float)):
|
||||
return bool(value)
|
||||
if isinstance(value, str):
|
||||
normalized = value.strip().lower()
|
||||
if normalized in {"1", "true", "yes", "on"}:
|
||||
return True
|
||||
if normalized in {"0", "false", "no", "off"}:
|
||||
return False
|
||||
return default
|
||||
|
||||
|
||||
def _flip_angle_horizontal(angle: int) -> int:
|
||||
return (180 - angle) % 360
|
||||
|
||||
|
||||
def _flip_angle_vertical(angle: int) -> int:
|
||||
return (-angle) % 360
|
||||
|
||||
|
||||
def normalize_pattern_request(
|
||||
pattern_id: str,
|
||||
params: PatternParameters | None = None,
|
||||
rows: int | None = None,
|
||||
cols: int | None = None,
|
||||
) -> tuple[str, PatternParameters | None]:
|
||||
if pattern_id == "pixel_sparkle":
|
||||
if params is None:
|
||||
return "strobe", None
|
||||
payload = params.to_dict()
|
||||
payload["strobe_mode"] = "random_pixels"
|
||||
return "strobe", PatternParameters.from_dict(payload)
|
||||
|
||||
if pattern_id == "random_blocks":
|
||||
return "sparkle", params
|
||||
|
||||
if pattern_id == "scan" and params is not None:
|
||||
payload = params.to_dict()
|
||||
angle = nearest_scan_angle(getattr(params, "angle", payload.get("angle", 0.0)))
|
||||
if coerce_bool(getattr(params, "flip_horizontal", False)):
|
||||
angle = _flip_angle_horizontal(angle)
|
||||
if coerce_bool(getattr(params, "flip_vertical", False)):
|
||||
angle = _flip_angle_vertical(angle)
|
||||
payload["angle"] = angle
|
||||
payload.pop("band_thickness", None)
|
||||
payload.pop("flip_horizontal", None)
|
||||
payload.pop("flip_vertical", None)
|
||||
return "scan", PatternParameters.from_dict(payload)
|
||||
|
||||
if pattern_id not in _LEGACY_SCAN_IDS:
|
||||
return pattern_id, params
|
||||
|
||||
payload = params.to_dict() if params is not None else {}
|
||||
if params is not None:
|
||||
payload["flip_horizontal"] = getattr(params, "flip_horizontal", payload.get("flip_horizontal", False))
|
||||
payload["flip_vertical"] = getattr(params, "flip_vertical", payload.get("flip_vertical", False))
|
||||
payload["band_thickness"] = getattr(params, "band_thickness", payload.get("band_thickness", params.on_width))
|
||||
direction = str(payload.get("direction", "left_to_right"))
|
||||
diagonal_mode = str(payload.get("diagonal_scan_mode", "line"))
|
||||
|
||||
def preferred_value(key: str, legacy_value):
|
||||
current_value = payload.get(key, getattr(_DEFAULT_SCAN_PARAMS, key))
|
||||
default_value = getattr(_DEFAULT_SCAN_PARAMS, key)
|
||||
return legacy_value if current_value == default_value else current_value
|
||||
|
||||
if pattern_id in {"row_chase", "row_scan"}:
|
||||
angle = 90 if direction != "bottom_to_top" else 270
|
||||
on_width = float(preferred_value("on_width", payload.get("block_size", 1.0)))
|
||||
off_width = float(preferred_value("off_width", 0.0))
|
||||
scan_style = str(preferred_value("scan_style", "line"))
|
||||
elif pattern_id in {"column_chase", "column_scan"}:
|
||||
angle = 0 if direction != "right_to_left" else 180
|
||||
on_width = float(preferred_value("on_width", payload.get("block_size", 1.0)))
|
||||
off_width = float(preferred_value("off_width", 0.0))
|
||||
scan_style = str(preferred_value("scan_style", "line"))
|
||||
else:
|
||||
angle = {
|
||||
"left_to_right": 315,
|
||||
"right_to_left": 135,
|
||||
"top_to_bottom": 315,
|
||||
"bottom_to_top": 135,
|
||||
}.get(direction, 315)
|
||||
scan_style = "bands" if diagonal_mode == "bands" else "line"
|
||||
scan_style = str(preferred_value("scan_style", scan_style))
|
||||
on_width = float(preferred_value("on_width", 2.0 if scan_style == "bands" else 0.5))
|
||||
off_width = float(preferred_value("off_width", 1.5 if scan_style == "bands" else 0.0))
|
||||
|
||||
angle = nearest_scan_angle(preferred_value("angle", angle))
|
||||
if coerce_bool(preferred_value("flip_horizontal", False)):
|
||||
angle = _flip_angle_horizontal(angle)
|
||||
if coerce_bool(preferred_value("flip_vertical", False)):
|
||||
angle = _flip_angle_vertical(angle)
|
||||
payload["angle"] = angle
|
||||
payload["scan_style"] = str(scan_style)
|
||||
payload["on_width"] = clamp(float(on_width), 0.1, 2.0)
|
||||
payload["off_width"] = clamp(float(off_width), 0.0, 2.0)
|
||||
payload.pop("band_thickness", None)
|
||||
payload.pop("flip_horizontal", None)
|
||||
payload.pop("flip_vertical", None)
|
||||
return "scan", PatternParameters.from_dict(payload)
|
||||
Reference in New Issue
Block a user