First upload, 18 controller version
This commit is contained in:
89
app/core/geometry.py
Normal file
89
app/core/geometry.py
Normal file
@@ -0,0 +1,89 @@
|
||||
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)
|
||||
]
|
||||
Reference in New Issue
Block a user