Add Infinity_Vis_1 performance core
This commit is contained in:
164
Infinity_Vis_1/infinity_vis_1/models.py
Normal file
164
Infinity_Vis_1/infinity_vis_1/models.py
Normal file
@@ -0,0 +1,164 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Literal
|
||||
|
||||
|
||||
RGB = tuple[int, int, int]
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class SegmentSpec:
|
||||
name: str
|
||||
side: str
|
||||
start_channel: int
|
||||
led_count: int
|
||||
reverse: bool = False
|
||||
orientation_rad: float = 0.0
|
||||
x0: float = 0.0
|
||||
y0: float = 0.0
|
||||
x1: float = 0.0
|
||||
y1: float = 0.0
|
||||
|
||||
@property
|
||||
def byte_offset(self) -> int:
|
||||
return max(0, self.start_channel - 1)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class TileSpec:
|
||||
tile_id: str
|
||||
row: int
|
||||
col: int
|
||||
led_total: int
|
||||
controller_ip: str = ""
|
||||
screen_name: str = ""
|
||||
controller_host: str = ""
|
||||
controller_name: str = ""
|
||||
controller_mac: str = ""
|
||||
universe: int = 0
|
||||
subnet: int = 0
|
||||
enabled: bool = True
|
||||
brightness: float = 1.0
|
||||
x0: float = 0.0
|
||||
y0: float = 0.0
|
||||
x1: float = 0.0
|
||||
y1: float = 0.0
|
||||
segments: list[SegmentSpec] = field(default_factory=list)
|
||||
|
||||
@property
|
||||
def controller_payload_bytes(self) -> int:
|
||||
return max(0, self.led_total) * 3
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class ControllerTileRoute:
|
||||
tile_id: str
|
||||
controller_offset_bytes: int
|
||||
segment_offsets: tuple[tuple[int, int], ...]
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class ControllerSpec:
|
||||
host: str
|
||||
payload_bytes: int
|
||||
tiles: tuple[ControllerTileRoute, ...]
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class MappingSpec:
|
||||
name: str = "Infinity Vis 1"
|
||||
rows: int = 3
|
||||
cols: int = 6
|
||||
tiles: list[TileSpec] = field(default_factory=list)
|
||||
file_path: Path | None = None
|
||||
protocol: str = "ddp"
|
||||
subnet: int = 0
|
||||
color_format: str = "rgb"
|
||||
tile_behavior: str = "solid_color_per_tile"
|
||||
global_gamma: float = 2.2
|
||||
|
||||
def ordered_tiles(self) -> list[TileSpec]:
|
||||
return sorted(self.tiles, key=lambda tile: (tile.row, tile.col, tile.tile_id))
|
||||
|
||||
def tile_lookup(self) -> dict[str, TileSpec]:
|
||||
return {tile.tile_id: tile for tile in self.tiles}
|
||||
|
||||
def build_controllers(self) -> list[ControllerSpec]:
|
||||
groups: dict[str, list[TileSpec]] = {}
|
||||
for tile in self.ordered_tiles():
|
||||
if not tile.enabled:
|
||||
continue
|
||||
host = tile.controller_ip.strip()
|
||||
if not host:
|
||||
continue
|
||||
groups.setdefault(host, []).append(tile)
|
||||
|
||||
controllers: list[ControllerSpec] = []
|
||||
for host, tiles in groups.items():
|
||||
running_offset = 0
|
||||
routes: list[ControllerTileRoute] = []
|
||||
for tile in tiles:
|
||||
segment_offsets = tuple(
|
||||
(running_offset + segment.byte_offset, segment.led_count)
|
||||
for segment in sorted(tile.segments, key=lambda item: (item.start_channel, item.name))
|
||||
)
|
||||
routes.append(
|
||||
ControllerTileRoute(
|
||||
tile_id=tile.tile_id,
|
||||
controller_offset_bytes=running_offset,
|
||||
segment_offsets=segment_offsets,
|
||||
)
|
||||
)
|
||||
running_offset += tile.controller_payload_bytes
|
||||
controllers.append(
|
||||
ControllerSpec(
|
||||
host=host,
|
||||
payload_bytes=running_offset,
|
||||
tiles=tuple(routes),
|
||||
)
|
||||
)
|
||||
return sorted(controllers, key=lambda item: item.host)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class PatternParams:
|
||||
brightness: float = 1.0
|
||||
tempo_multiplier: float = 1.0
|
||||
direction: str = "left_to_right"
|
||||
checker_mode: str = "classic"
|
||||
scan_style: str = "line"
|
||||
angle: float = 0.0
|
||||
on_width: float = 1.0
|
||||
off_width: float = 1.0
|
||||
strobe_mode: str = "global"
|
||||
stopwatch_mode: str = "sync"
|
||||
color_mode: str = "dual"
|
||||
primary_color: str = "#4D7CFF"
|
||||
secondary_color: str = "#0E1630"
|
||||
palette: str = "Laser Club"
|
||||
symmetry: str = "none"
|
||||
center_pulse_mode: str = "expand"
|
||||
step_size: float = 1.0
|
||||
block_size: float = 1.0
|
||||
pixel_group_size: float = 1.0
|
||||
strobe_duty_cycle: float = 0.5
|
||||
randomness: float = 0.35
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class SceneState:
|
||||
pattern_id: str = "solid"
|
||||
params: PatternParams = field(default_factory=PatternParams)
|
||||
tempo_bpm: float = 120.0
|
||||
utility_mode: Literal["none", "blackout", "single_tile", "identify"] = "none"
|
||||
selected_tile_id: str | None = None
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class FrameSnapshot:
|
||||
timestamp: float
|
||||
pattern_id: str
|
||||
tile_colors: list[RGB]
|
||||
controller_payloads: dict[str, bytes]
|
||||
Reference in New Issue
Block a user