90 lines
2.5 KiB
Python
90 lines
2.5 KiB
Python
from __future__ import annotations
|
|
|
|
from app.config.models import SegmentConfig, TileConfig
|
|
from app.core.types import clamp
|
|
|
|
NormalizedPoint = tuple[float, float]
|
|
NormalizedInsets = tuple[float, float]
|
|
|
|
_SEGMENT_SIDE_ALIASES = {
|
|
"left": "left",
|
|
"right": "right",
|
|
"top": "top",
|
|
"bottom": "bottom",
|
|
"l": "left",
|
|
"r": "right",
|
|
"t": "top",
|
|
"b": "bottom",
|
|
}
|
|
|
|
|
|
def segment_side(tile: TileConfig, segment: SegmentConfig) -> str | None:
|
|
side = (segment.side or "").strip().lower()
|
|
if side in _SEGMENT_SIDE_ALIASES:
|
|
return _SEGMENT_SIDE_ALIASES[side]
|
|
|
|
width = max(0.001, tile.x1 - tile.x0)
|
|
height = max(0.001, tile.y1 - tile.y0)
|
|
delta_x = abs(segment.x1 - segment.x0) / width
|
|
delta_y = abs(segment.y1 - segment.y0) / height
|
|
mid_x = (segment.x0 + segment.x1) / 2.0
|
|
mid_y = (segment.y0 + segment.y1) / 2.0
|
|
|
|
if delta_x >= delta_y:
|
|
return "top" if mid_y <= tile.y0 + height * 0.5 else "bottom"
|
|
return "left" if mid_x <= tile.x0 + width * 0.5 else "right"
|
|
|
|
|
|
def segment_led_position(
|
|
tile: TileConfig,
|
|
segment: SegmentConfig,
|
|
amount: float,
|
|
*,
|
|
insets: NormalizedInsets = (0.0, 0.0),
|
|
apply_reverse: bool = False,
|
|
) -> NormalizedPoint:
|
|
amount = clamp(float(amount))
|
|
if apply_reverse and segment.reverse:
|
|
amount = 1.0 - amount
|
|
|
|
inset_x = clamp(float(insets[0]), 0.0, 0.49)
|
|
inset_y = clamp(float(insets[1]), 0.0, 0.49)
|
|
side = segment_side(tile, segment)
|
|
|
|
if side == "left":
|
|
return inset_x, inset_y + (1.0 - inset_y * 2.0) * amount
|
|
if side == "right":
|
|
return 1.0 - inset_x, inset_y + (1.0 - inset_y * 2.0) * amount
|
|
if side == "top":
|
|
return inset_x + (1.0 - inset_x * 2.0) * amount, inset_y
|
|
if side == "bottom":
|
|
return inset_x + (1.0 - inset_x * 2.0) * amount, 1.0 - inset_y
|
|
|
|
width = max(0.001, tile.x1 - tile.x0)
|
|
height = max(0.001, tile.y1 - tile.y0)
|
|
x_pos = segment.x0 + (segment.x1 - segment.x0) * amount
|
|
y_pos = segment.y0 + (segment.y1 - segment.y0) * amount
|
|
return (
|
|
clamp((x_pos - tile.x0) / width),
|
|
clamp((y_pos - tile.y0) / height),
|
|
)
|
|
|
|
|
|
def segment_led_positions(
|
|
tile: TileConfig,
|
|
segment: SegmentConfig,
|
|
*,
|
|
insets: NormalizedInsets = (0.0, 0.0),
|
|
) -> list[NormalizedPoint]:
|
|
count = max(1, segment.led_count)
|
|
return [
|
|
segment_led_position(
|
|
tile,
|
|
segment,
|
|
0.0 if count == 1 else index / (count - 1),
|
|
insets=insets,
|
|
apply_reverse=True,
|
|
)
|
|
for index in range(count)
|
|
]
|