Files
RFP_Infinity-Vis/app/core/geometry.py

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)
]