valetudo-map-parser 0.1.9b50__tar.gz → 0.1.9b52__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/PKG-INFO +1 -1
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/__init__.py +2 -0
- valetudo_map_parser-0.1.9b52/SCR/valetudo_map_parser/config/color_utils.py +105 -0
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/config/colors.py +55 -70
- valetudo_map_parser-0.1.9b52/SCR/valetudo_map_parser/config/drawable_elements.py +307 -0
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/config/enhanced_drawable.py +75 -193
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/config/shared.py +0 -1
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/config/types.py +19 -33
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/config/utils.py +0 -67
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/hypfer_draw.py +222 -57
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/hypfer_handler.py +48 -155
- valetudo_map_parser-0.1.9b52/SCR/valetudo_map_parser/hypfer_rooms_handler.py +406 -0
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/map_data.py +0 -30
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/rand25_handler.py +2 -84
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/pyproject.toml +1 -1
- valetudo_map_parser-0.1.9b50/SCR/valetudo_map_parser/config/color_utils.py +0 -49
- valetudo_map_parser-0.1.9b50/SCR/valetudo_map_parser/config/drawable_elements.py +0 -983
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/LICENSE +0 -0
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/NOTICE.txt +0 -0
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/README.md +0 -0
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/config/__init__.py +0 -0
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/config/auto_crop.py +0 -0
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/config/drawable.py +0 -0
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/config/optimized_element_map.py +0 -0
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/config/rand25_parser.py +0 -0
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/config/room_outline.py +0 -0
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/py.typed +0 -0
- {valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/reimg_draw.py +0 -0
{valetudo_map_parser-0.1.9b50 → valetudo_map_parser-0.1.9b52}/SCR/valetudo_map_parser/__init__.py
RENAMED
@@ -15,11 +15,13 @@ from .config.types import (
|
|
15
15
|
TrimCropData,
|
16
16
|
UserLanguageStore,
|
17
17
|
)
|
18
|
+
from .hypfer_rooms_handler import HypferRoomsHandler
|
18
19
|
from .hypfer_handler import HypferMapImageHandler
|
19
20
|
from .rand25_handler import ReImageHandler
|
20
21
|
|
21
22
|
|
22
23
|
__all__ = [
|
24
|
+
"HypferRoomsHandler",
|
23
25
|
"HypferMapImageHandler",
|
24
26
|
"ReImageHandler",
|
25
27
|
"RRMapParser",
|
@@ -0,0 +1,105 @@
|
|
1
|
+
"""Utility functions for color operations in the map parser."""
|
2
|
+
|
3
|
+
from typing import Tuple, Optional
|
4
|
+
|
5
|
+
from .colors import ColorsManagement
|
6
|
+
from .types import NumpyArray, Color
|
7
|
+
|
8
|
+
|
9
|
+
def get_blended_color(
|
10
|
+
x0: int,
|
11
|
+
y0: int,
|
12
|
+
x1: int,
|
13
|
+
y1: int,
|
14
|
+
arr: Optional[NumpyArray],
|
15
|
+
color: Color,
|
16
|
+
) -> Color:
|
17
|
+
"""
|
18
|
+
Get a blended color for a pixel based on the current element map and the new element to draw.
|
19
|
+
|
20
|
+
This function:
|
21
|
+
1. Gets the background colors at the start and end points (with offset to avoid sampling already drawn pixels)
|
22
|
+
2. Directly blends the foreground color with the background using straight alpha
|
23
|
+
3. Returns the average of the two blended colors
|
24
|
+
|
25
|
+
Returns:
|
26
|
+
Blended RGBA color to use for drawing
|
27
|
+
"""
|
28
|
+
# Extract foreground color components
|
29
|
+
fg_r, fg_g, fg_b, fg_a = color
|
30
|
+
fg_alpha = fg_a / 255.0 # Convert to 0-1 range
|
31
|
+
|
32
|
+
# Fast path for fully opaque or transparent foreground
|
33
|
+
if fg_a == 255:
|
34
|
+
return color
|
35
|
+
if fg_a == 0:
|
36
|
+
# Sample background at midpoint
|
37
|
+
mid_x, mid_y = (x0 + x1) // 2, (y0 + y1) // 2
|
38
|
+
if 0 <= mid_y < arr.shape[0] and 0 <= mid_x < arr.shape[1]:
|
39
|
+
return tuple(arr[mid_y, mid_x])
|
40
|
+
return (0, 0, 0, 0) # Default if out of bounds
|
41
|
+
|
42
|
+
# Calculate direction vector for offset sampling
|
43
|
+
dx = x1 - x0
|
44
|
+
dy = y1 - y0
|
45
|
+
length = max(1, (dx**2 + dy**2) ** 0.5) # Avoid division by zero
|
46
|
+
offset = 5 # 5-pixel offset to avoid sampling already drawn pixels
|
47
|
+
|
48
|
+
# Calculate offset coordinates for start point (move away from the line)
|
49
|
+
offset_x0 = int(x0 - (offset * dx / length))
|
50
|
+
offset_y0 = int(y0 - (offset * dy / length))
|
51
|
+
|
52
|
+
# Calculate offset coordinates for end point (move away from the line)
|
53
|
+
offset_x1 = int(x1 + (offset * dx / length))
|
54
|
+
offset_y1 = int(y1 + (offset * dy / length))
|
55
|
+
|
56
|
+
# Sample background at offset start point
|
57
|
+
if 0 <= offset_y0 < arr.shape[0] and 0 <= offset_x0 < arr.shape[1]:
|
58
|
+
bg_color_start = arr[offset_y0, offset_x0]
|
59
|
+
# Direct straight alpha blending
|
60
|
+
start_r = int(fg_r * fg_alpha + bg_color_start[0] * (1 - fg_alpha))
|
61
|
+
start_g = int(fg_g * fg_alpha + bg_color_start[1] * (1 - fg_alpha))
|
62
|
+
start_b = int(fg_b * fg_alpha + bg_color_start[2] * (1 - fg_alpha))
|
63
|
+
start_a = int(fg_a + bg_color_start[3] * (1 - fg_alpha))
|
64
|
+
start_blended_color = (start_r, start_g, start_b, start_a)
|
65
|
+
else:
|
66
|
+
# If offset point is out of bounds, try original point
|
67
|
+
if 0 <= y0 < arr.shape[0] and 0 <= x0 < arr.shape[1]:
|
68
|
+
bg_color_start = arr[y0, x0]
|
69
|
+
start_r = int(fg_r * fg_alpha + bg_color_start[0] * (1 - fg_alpha))
|
70
|
+
start_g = int(fg_g * fg_alpha + bg_color_start[1] * (1 - fg_alpha))
|
71
|
+
start_b = int(fg_b * fg_alpha + bg_color_start[2] * (1 - fg_alpha))
|
72
|
+
start_a = int(fg_a + bg_color_start[3] * (1 - fg_alpha))
|
73
|
+
start_blended_color = (start_r, start_g, start_b, start_a)
|
74
|
+
else:
|
75
|
+
start_blended_color = color
|
76
|
+
|
77
|
+
# Sample background at offset end point
|
78
|
+
if 0 <= offset_y1 < arr.shape[0] and 0 <= offset_x1 < arr.shape[1]:
|
79
|
+
bg_color_end = arr[offset_y1, offset_x1]
|
80
|
+
# Direct straight alpha blending
|
81
|
+
end_r = int(fg_r * fg_alpha + bg_color_end[0] * (1 - fg_alpha))
|
82
|
+
end_g = int(fg_g * fg_alpha + bg_color_end[1] * (1 - fg_alpha))
|
83
|
+
end_b = int(fg_b * fg_alpha + bg_color_end[2] * (1 - fg_alpha))
|
84
|
+
end_a = int(fg_a + bg_color_end[3] * (1 - fg_alpha))
|
85
|
+
end_blended_color = (end_r, end_g, end_b, end_a)
|
86
|
+
else:
|
87
|
+
# If offset point is out of bounds, try original point
|
88
|
+
if 0 <= y1 < arr.shape[0] and 0 <= x1 < arr.shape[1]:
|
89
|
+
bg_color_end = arr[y1, x1]
|
90
|
+
end_r = int(fg_r * fg_alpha + bg_color_end[0] * (1 - fg_alpha))
|
91
|
+
end_g = int(fg_g * fg_alpha + bg_color_end[1] * (1 - fg_alpha))
|
92
|
+
end_b = int(fg_b * fg_alpha + bg_color_end[2] * (1 - fg_alpha))
|
93
|
+
end_a = int(fg_a + bg_color_end[3] * (1 - fg_alpha))
|
94
|
+
end_blended_color = (end_r, end_g, end_b, end_a)
|
95
|
+
else:
|
96
|
+
end_blended_color = color
|
97
|
+
|
98
|
+
# Use the average of the two blended colors
|
99
|
+
blended_color = (
|
100
|
+
(start_blended_color[0] + end_blended_color[0]) // 2,
|
101
|
+
(start_blended_color[1] + end_blended_color[1]) // 2,
|
102
|
+
(start_blended_color[2] + end_blended_color[2]) // 2,
|
103
|
+
(start_blended_color[3] + end_blended_color[3]) // 2,
|
104
|
+
)
|
105
|
+
return blended_color
|
@@ -407,61 +407,56 @@ class ColorsManagement:
|
|
407
407
|
def blend_colors(background: Color, foreground: Color) -> Color:
|
408
408
|
"""
|
409
409
|
Blend foreground color with background color based on alpha values.
|
410
|
-
|
411
|
-
This is used when drawing elements that overlap on the map.
|
412
|
-
The alpha channel determines how much of the foreground color is visible.
|
413
|
-
Uses optimized calculations for better performance.
|
410
|
+
Optimized version with more fast paths and simplified calculations.
|
414
411
|
|
415
412
|
:param background: Background RGBA color (r,g,b,a)
|
416
413
|
:param foreground: Foreground RGBA color (r,g,b,a) to blend on top
|
417
414
|
:return: Blended RGBA color
|
418
415
|
"""
|
419
|
-
#
|
420
|
-
|
421
|
-
fg_r, fg_g, fg_b, fg_a = foreground
|
416
|
+
# Fast paths for common cases
|
417
|
+
fg_a = foreground[3]
|
422
418
|
|
423
|
-
|
424
|
-
if fg_a == 255:
|
419
|
+
if fg_a == 255: # Fully opaque foreground
|
425
420
|
return foreground
|
426
|
-
if fg_a == 0:
|
427
|
-
return background
|
428
421
|
|
429
|
-
#
|
430
|
-
|
431
|
-
fg_alpha = fg_a / 255.0
|
432
|
-
bg_alpha = bg_a / 255.0
|
422
|
+
if fg_a == 0: # Fully transparent foreground
|
423
|
+
return background
|
433
424
|
|
434
|
-
|
435
|
-
|
425
|
+
bg_a = background[3]
|
426
|
+
if bg_a == 0: # Fully transparent background
|
427
|
+
return foreground
|
436
428
|
|
437
|
-
#
|
438
|
-
|
439
|
-
|
429
|
+
# Extract components (only after fast paths)
|
430
|
+
bg_r, bg_g, bg_b = background[:3]
|
431
|
+
fg_r, fg_g, fg_b = foreground[:3]
|
440
432
|
|
441
|
-
#
|
442
|
-
|
443
|
-
|
444
|
-
inv_alpha_ratio = 1.0 - alpha_ratio
|
433
|
+
# Pre-calculate the blend factor once (avoid repeated division)
|
434
|
+
blend = fg_a / 255.0
|
435
|
+
inv_blend = 1.0 - blend
|
445
436
|
|
446
|
-
|
447
|
-
|
448
|
-
|
437
|
+
# Simple linear interpolation for RGB channels
|
438
|
+
# This is faster than the previous implementation
|
439
|
+
out_r = int(fg_r * blend + bg_r * inv_blend)
|
440
|
+
out_g = int(fg_g * blend + bg_g * inv_blend)
|
441
|
+
out_b = int(fg_b * blend + bg_b * inv_blend)
|
449
442
|
|
450
|
-
#
|
451
|
-
out_a = int(
|
443
|
+
# Alpha blending - simplified calculation
|
444
|
+
out_a = int(fg_a + bg_a * inv_blend)
|
452
445
|
|
453
|
-
#
|
454
|
-
|
455
|
-
out_g = max(0, min(255, out_g))
|
456
|
-
out_b = max(0, min(255, out_b))
|
446
|
+
# No need for min/max checks as the blend math keeps values in range
|
447
|
+
# when input values are valid (0-255)
|
457
448
|
|
458
449
|
return [out_r, out_g, out_b, out_a]
|
459
450
|
|
451
|
+
# Cache for recently sampled background colors
|
452
|
+
_bg_color_cache = {}
|
453
|
+
_cache_size = 1024 # Limit cache size to avoid memory issues
|
454
|
+
|
460
455
|
@staticmethod
|
461
456
|
def sample_and_blend_color(array, x: int, y: int, foreground: Color) -> Color:
|
462
457
|
"""
|
463
458
|
Sample the background color from the array at coordinates (x,y) and blend with foreground color.
|
464
|
-
|
459
|
+
Optimized version with caching and faster sampling.
|
465
460
|
|
466
461
|
Args:
|
467
462
|
array: The RGBA numpy array representing the image
|
@@ -472,53 +467,43 @@ class ColorsManagement:
|
|
472
467
|
Returns:
|
473
468
|
Blended RGBA color
|
474
469
|
"""
|
475
|
-
#
|
470
|
+
# Fast path for fully opaque foreground - no need to sample or blend
|
471
|
+
if foreground[3] == 255:
|
472
|
+
return foreground
|
473
|
+
|
474
|
+
# Ensure array exists
|
476
475
|
if array is None:
|
477
476
|
return foreground
|
478
477
|
|
478
|
+
# Check if coordinates are within bounds
|
479
479
|
height, width = array.shape[:2]
|
480
480
|
if not (0 <= y < height and 0 <= x < width):
|
481
|
-
return foreground # Return foreground if coordinates are out of bounds
|
482
|
-
|
483
|
-
# Fast path for fully opaque foreground
|
484
|
-
if foreground[3] == 255:
|
485
481
|
return foreground
|
486
482
|
|
487
|
-
#
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
coordinates = np.array([[y, x]])
|
496
|
-
|
497
|
-
# Sample each channel separately with nearest neighbor interpolation
|
498
|
-
# This is faster than sampling all channels at once for large arrays
|
499
|
-
r = ndimage.map_coordinates(
|
500
|
-
array[..., 0], coordinates.T, order=0, mode="nearest"
|
501
|
-
)[0]
|
502
|
-
g = ndimage.map_coordinates(
|
503
|
-
array[..., 1], coordinates.T, order=0, mode="nearest"
|
504
|
-
)[0]
|
505
|
-
b = ndimage.map_coordinates(
|
506
|
-
array[..., 2], coordinates.T, order=0, mode="nearest"
|
507
|
-
)[0]
|
508
|
-
a = ndimage.map_coordinates(
|
509
|
-
array[..., 3], coordinates.T, order=0, mode="nearest"
|
510
|
-
)[0]
|
511
|
-
background = (int(r), int(g), int(b), int(a))
|
512
|
-
else:
|
513
|
-
# For smaller arrays, direct indexing is faster
|
514
|
-
background = tuple(array[y, x])
|
515
|
-
except (IndexError, ValueError):
|
516
|
-
# Fallback to direct indexing if ndimage fails
|
483
|
+
# Check cache for this coordinate
|
484
|
+
cache_key = (id(array), x, y)
|
485
|
+
cache = ColorsManagement._bg_color_cache
|
486
|
+
|
487
|
+
if cache_key in cache:
|
488
|
+
background = cache[cache_key]
|
489
|
+
else:
|
490
|
+
# Sample the background color using direct indexing (fastest method)
|
517
491
|
try:
|
518
|
-
background = tuple(array[y, x])
|
492
|
+
background = tuple(map(int, array[y, x]))
|
493
|
+
|
494
|
+
# Update cache (with simple LRU-like behavior)
|
495
|
+
if len(cache) >= ColorsManagement._cache_size:
|
496
|
+
# Remove a random entry if cache is full
|
497
|
+
cache.pop(next(iter(cache)))
|
498
|
+
cache[cache_key] = background
|
499
|
+
|
519
500
|
except (IndexError, ValueError):
|
520
501
|
return foreground
|
521
502
|
|
503
|
+
# Fast path for fully transparent foreground
|
504
|
+
if foreground[3] == 0:
|
505
|
+
return background
|
506
|
+
|
522
507
|
# Blend the colors
|
523
508
|
return ColorsManagement.blend_colors(background, foreground)
|
524
509
|
|
@@ -0,0 +1,307 @@
|
|
1
|
+
"""
|
2
|
+
Drawable Elements Configuration.
|
3
|
+
Defines the elements that can be drawn on the map and their properties.
|
4
|
+
Version: 0.1.9
|
5
|
+
"""
|
6
|
+
|
7
|
+
from __future__ import annotations
|
8
|
+
|
9
|
+
from enum import IntEnum
|
10
|
+
from typing import Dict, List, Tuple, Union
|
11
|
+
import numpy as np
|
12
|
+
from .types import LOGGER
|
13
|
+
|
14
|
+
from .colors import DefaultColors, SupportedColor
|
15
|
+
|
16
|
+
# Type aliases
|
17
|
+
Color = Tuple[int, int, int, int] # RGBA color
|
18
|
+
PropertyDict = Dict[str, Union[Color, float, int]]
|
19
|
+
|
20
|
+
|
21
|
+
class DrawableElement(IntEnum):
|
22
|
+
"""Enumeration of drawable map elements with unique integer codes."""
|
23
|
+
|
24
|
+
# Base elements
|
25
|
+
FLOOR = 1
|
26
|
+
WALL = 2
|
27
|
+
ROBOT = 3
|
28
|
+
CHARGER = 4
|
29
|
+
VIRTUAL_WALL = 5
|
30
|
+
RESTRICTED_AREA = 6
|
31
|
+
NO_MOP_AREA = 7
|
32
|
+
OBSTACLE = 8
|
33
|
+
PATH = 9
|
34
|
+
PREDICTED_PATH = 10
|
35
|
+
GO_TO_TARGET = 11
|
36
|
+
|
37
|
+
# Rooms (101-115 for up to 15 rooms)
|
38
|
+
ROOM_1 = 101
|
39
|
+
ROOM_2 = 102
|
40
|
+
ROOM_3 = 103
|
41
|
+
ROOM_4 = 104
|
42
|
+
ROOM_5 = 105
|
43
|
+
ROOM_6 = 106
|
44
|
+
ROOM_7 = 107
|
45
|
+
ROOM_8 = 108
|
46
|
+
ROOM_9 = 109
|
47
|
+
ROOM_10 = 110
|
48
|
+
ROOM_11 = 111
|
49
|
+
ROOM_12 = 112
|
50
|
+
ROOM_13 = 113
|
51
|
+
ROOM_14 = 114
|
52
|
+
ROOM_15 = 115
|
53
|
+
|
54
|
+
|
55
|
+
class DrawingConfig:
|
56
|
+
"""Configuration for which elements to draw and their properties."""
|
57
|
+
|
58
|
+
def __init__(self):
|
59
|
+
"""Initialize with all elements enabled by default."""
|
60
|
+
# Dictionary of element_code -> enabled status
|
61
|
+
self._enabled_elements = {element: True for element in DrawableElement}
|
62
|
+
|
63
|
+
# Dictionary of element_code -> drawing properties (color, opacity, etc.)
|
64
|
+
self._element_properties: Dict[DrawableElement, PropertyDict] = {}
|
65
|
+
|
66
|
+
# Initialize default properties
|
67
|
+
self._set_default_properties()
|
68
|
+
|
69
|
+
def _set_default_properties(self):
|
70
|
+
"""Set default drawing properties for each element."""
|
71
|
+
# Set properties for rooms using DefaultColors
|
72
|
+
for room_id in range(1, 16):
|
73
|
+
room_element = getattr(DrawableElement, f"ROOM_{room_id}")
|
74
|
+
room_key = SupportedColor.room_key(room_id - 1)
|
75
|
+
rgb = DefaultColors.DEFAULT_ROOM_COLORS.get(room_key, (135, 206, 250))
|
76
|
+
alpha = DefaultColors.DEFAULT_ALPHA.get(f"alpha_room_{room_id - 1}", 255.0)
|
77
|
+
|
78
|
+
self._element_properties[room_element] = {
|
79
|
+
"color": (*rgb, int(alpha)),
|
80
|
+
"opacity": alpha / 255.0,
|
81
|
+
"z_index": 10, # Drawing order
|
82
|
+
}
|
83
|
+
|
84
|
+
# Map DrawableElement to SupportedColor
|
85
|
+
element_color_mapping = {
|
86
|
+
DrawableElement.FLOOR: SupportedColor.MAP_BACKGROUND,
|
87
|
+
DrawableElement.WALL: SupportedColor.WALLS,
|
88
|
+
DrawableElement.ROBOT: SupportedColor.ROBOT,
|
89
|
+
DrawableElement.CHARGER: SupportedColor.CHARGER,
|
90
|
+
DrawableElement.VIRTUAL_WALL: SupportedColor.NO_GO,
|
91
|
+
DrawableElement.RESTRICTED_AREA: SupportedColor.NO_GO,
|
92
|
+
DrawableElement.PATH: SupportedColor.PATH,
|
93
|
+
DrawableElement.PREDICTED_PATH: SupportedColor.PREDICTED_PATH,
|
94
|
+
DrawableElement.GO_TO_TARGET: SupportedColor.GO_TO,
|
95
|
+
DrawableElement.NO_MOP_AREA: SupportedColor.NO_GO, # Using NO_GO for no-mop areas
|
96
|
+
DrawableElement.OBSTACLE: SupportedColor.NO_GO, # Using NO_GO for obstacles
|
97
|
+
}
|
98
|
+
|
99
|
+
# Set z-index for each element type
|
100
|
+
z_indices = {
|
101
|
+
DrawableElement.FLOOR: 0,
|
102
|
+
DrawableElement.WALL: 20,
|
103
|
+
DrawableElement.ROBOT: 50,
|
104
|
+
DrawableElement.CHARGER: 40,
|
105
|
+
DrawableElement.VIRTUAL_WALL: 30,
|
106
|
+
DrawableElement.RESTRICTED_AREA: 25,
|
107
|
+
DrawableElement.NO_MOP_AREA: 25,
|
108
|
+
DrawableElement.OBSTACLE: 15,
|
109
|
+
DrawableElement.PATH: 35,
|
110
|
+
DrawableElement.PREDICTED_PATH: 34,
|
111
|
+
DrawableElement.GO_TO_TARGET: 45,
|
112
|
+
}
|
113
|
+
|
114
|
+
# Set properties for other elements using DefaultColors
|
115
|
+
for element, color_key in element_color_mapping.items():
|
116
|
+
rgb = DefaultColors.COLORS_RGB.get(color_key, (0, 0, 0))
|
117
|
+
alpha_key = f"alpha_{color_key}"
|
118
|
+
alpha = DefaultColors.DEFAULT_ALPHA.get(alpha_key, 255.0)
|
119
|
+
|
120
|
+
# Special case for semi-transparent elements
|
121
|
+
if element in [
|
122
|
+
DrawableElement.RESTRICTED_AREA,
|
123
|
+
DrawableElement.NO_MOP_AREA,
|
124
|
+
DrawableElement.PREDICTED_PATH,
|
125
|
+
]:
|
126
|
+
alpha = 125.0 # Semi-transparent by default
|
127
|
+
|
128
|
+
self._element_properties[element] = {
|
129
|
+
"color": (*rgb, int(alpha)),
|
130
|
+
"opacity": alpha / 255.0,
|
131
|
+
"z_index": z_indices.get(element, 0),
|
132
|
+
}
|
133
|
+
|
134
|
+
def enable_element(self, element_code: DrawableElement) -> None:
|
135
|
+
"""Enable drawing of a specific element."""
|
136
|
+
if element_code in self._enabled_elements:
|
137
|
+
self._enabled_elements[element_code] = True
|
138
|
+
LOGGER.info(
|
139
|
+
"Enabled element %s (%s)", element_code.name, element_code.value
|
140
|
+
)
|
141
|
+
LOGGER.info(
|
142
|
+
"Element %s is now enabled: %s",
|
143
|
+
element_code.name,
|
144
|
+
self._enabled_elements[element_code],
|
145
|
+
)
|
146
|
+
|
147
|
+
def disable_element(self, element_code: DrawableElement) -> None:
|
148
|
+
"""Disable drawing of a specific element."""
|
149
|
+
if element_code in self._enabled_elements:
|
150
|
+
self._enabled_elements[element_code] = False
|
151
|
+
LOGGER.info(
|
152
|
+
"Disabled element %s (%s)", element_code.name, element_code.value
|
153
|
+
)
|
154
|
+
LOGGER.info(
|
155
|
+
"Element %s is now enabled: %s",
|
156
|
+
element_code.name,
|
157
|
+
self._enabled_elements[element_code],
|
158
|
+
)
|
159
|
+
|
160
|
+
def set_elements(self, element_codes: List[DrawableElement]) -> None:
|
161
|
+
"""Enable only the specified elements, disable all others."""
|
162
|
+
# First disable all
|
163
|
+
for element in self._enabled_elements:
|
164
|
+
self._enabled_elements[element] = False
|
165
|
+
|
166
|
+
# Then enable specified ones
|
167
|
+
for element in element_codes:
|
168
|
+
if element in self._enabled_elements:
|
169
|
+
self._enabled_elements[element] = True
|
170
|
+
|
171
|
+
def is_enabled(self, element_code: DrawableElement) -> bool:
|
172
|
+
"""Check if an element is enabled for drawing."""
|
173
|
+
enabled = self._enabled_elements.get(element_code, False)
|
174
|
+
LOGGER.debug(
|
175
|
+
"Checking if element %s is enabled: %s",
|
176
|
+
element_code.name if hasattr(element_code, "name") else element_code,
|
177
|
+
enabled,
|
178
|
+
)
|
179
|
+
return enabled
|
180
|
+
|
181
|
+
def set_property(
|
182
|
+
self, element_code: DrawableElement, property_name: str, value
|
183
|
+
) -> None:
|
184
|
+
"""Set a drawing property for an element."""
|
185
|
+
if element_code in self._element_properties:
|
186
|
+
self._element_properties[element_code][property_name] = value
|
187
|
+
|
188
|
+
def get_property(
|
189
|
+
self, element_code: DrawableElement, property_name: str, default=None
|
190
|
+
):
|
191
|
+
"""Get a drawing property for an element."""
|
192
|
+
if element_code in self._element_properties:
|
193
|
+
return self._element_properties[element_code].get(property_name, default)
|
194
|
+
return default
|
195
|
+
|
196
|
+
def get_enabled_elements(self) -> List[DrawableElement]:
|
197
|
+
"""Get list of enabled element codes."""
|
198
|
+
return [
|
199
|
+
element for element, enabled in self._enabled_elements.items() if enabled
|
200
|
+
]
|
201
|
+
|
202
|
+
def get_drawing_order(self) -> List[DrawableElement]:
|
203
|
+
"""Get list of enabled elements in drawing order (by z_index)."""
|
204
|
+
enabled = self.get_enabled_elements()
|
205
|
+
return sorted(enabled, key=lambda e: self.get_property(e, "z_index", 0))
|
206
|
+
|
207
|
+
def update_from_device_info(self, device_info: dict) -> None:
|
208
|
+
"""Update configuration based on device info dictionary."""
|
209
|
+
# Map DrawableElement to SupportedColor
|
210
|
+
element_color_mapping = {
|
211
|
+
DrawableElement.FLOOR: SupportedColor.MAP_BACKGROUND,
|
212
|
+
DrawableElement.WALL: SupportedColor.WALLS,
|
213
|
+
DrawableElement.ROBOT: SupportedColor.ROBOT,
|
214
|
+
DrawableElement.CHARGER: SupportedColor.CHARGER,
|
215
|
+
DrawableElement.VIRTUAL_WALL: SupportedColor.NO_GO,
|
216
|
+
DrawableElement.RESTRICTED_AREA: SupportedColor.NO_GO,
|
217
|
+
DrawableElement.PATH: SupportedColor.PATH,
|
218
|
+
DrawableElement.PREDICTED_PATH: SupportedColor.PREDICTED_PATH,
|
219
|
+
DrawableElement.GO_TO_TARGET: SupportedColor.GO_TO,
|
220
|
+
DrawableElement.NO_MOP_AREA: SupportedColor.NO_GO,
|
221
|
+
DrawableElement.OBSTACLE: SupportedColor.NO_GO,
|
222
|
+
}
|
223
|
+
|
224
|
+
# Update room colors from device info
|
225
|
+
for room_id in range(1, 16):
|
226
|
+
room_element = getattr(DrawableElement, f"ROOM_{room_id}")
|
227
|
+
color_key = SupportedColor.room_key(room_id - 1)
|
228
|
+
alpha_key = f"alpha_room_{room_id - 1}"
|
229
|
+
|
230
|
+
if color_key in device_info:
|
231
|
+
rgb = device_info[color_key]
|
232
|
+
alpha = device_info.get(alpha_key, 255.0)
|
233
|
+
|
234
|
+
# Create RGBA color
|
235
|
+
rgba = (*rgb, int(alpha))
|
236
|
+
|
237
|
+
# Update color and opacity
|
238
|
+
self.set_property(room_element, "color", rgba)
|
239
|
+
self.set_property(room_element, "opacity", alpha / 255.0)
|
240
|
+
|
241
|
+
LOGGER.debug(
|
242
|
+
"Updated room %d color to %s with alpha %s", room_id, rgb, alpha
|
243
|
+
)
|
244
|
+
|
245
|
+
# Update other element colors
|
246
|
+
for element, color_key in element_color_mapping.items():
|
247
|
+
if color_key in device_info:
|
248
|
+
rgb = device_info[color_key]
|
249
|
+
alpha_key = f"alpha_{color_key}"
|
250
|
+
alpha = device_info.get(alpha_key, 255.0)
|
251
|
+
|
252
|
+
# Special case for semi-transparent elements
|
253
|
+
if element in [
|
254
|
+
DrawableElement.RESTRICTED_AREA,
|
255
|
+
DrawableElement.NO_MOP_AREA,
|
256
|
+
DrawableElement.PREDICTED_PATH,
|
257
|
+
]:
|
258
|
+
if alpha > 200: # If alpha is too high for these elements
|
259
|
+
alpha = 125.0 # Use a more appropriate default
|
260
|
+
|
261
|
+
# Create RGBA color
|
262
|
+
rgba = (*rgb, int(alpha))
|
263
|
+
|
264
|
+
# Update color and opacity
|
265
|
+
self.set_property(element, "color", rgba)
|
266
|
+
self.set_property(element, "opacity", alpha / 255.0)
|
267
|
+
|
268
|
+
LOGGER.debug(
|
269
|
+
"Updated element %s color to %s with alpha %s",
|
270
|
+
element.name,
|
271
|
+
rgb,
|
272
|
+
alpha,
|
273
|
+
)
|
274
|
+
|
275
|
+
# Check for disabled elements using specific boolean flags
|
276
|
+
# Map element disable flags to DrawableElement enum values
|
277
|
+
element_disable_mapping = {
|
278
|
+
"disable_floor": DrawableElement.FLOOR,
|
279
|
+
"disable_wall": DrawableElement.WALL,
|
280
|
+
"disable_robot": DrawableElement.ROBOT,
|
281
|
+
"disable_charger": DrawableElement.CHARGER,
|
282
|
+
"disable_virtual_walls": DrawableElement.VIRTUAL_WALL,
|
283
|
+
"disable_restricted_areas": DrawableElement.RESTRICTED_AREA,
|
284
|
+
"disable_no_mop_areas": DrawableElement.NO_MOP_AREA,
|
285
|
+
"disable_obstacles": DrawableElement.OBSTACLE,
|
286
|
+
"disable_path": DrawableElement.PATH,
|
287
|
+
"disable_predicted_path": DrawableElement.PREDICTED_PATH,
|
288
|
+
"disable_go_to_target": DrawableElement.GO_TO_TARGET,
|
289
|
+
}
|
290
|
+
|
291
|
+
# Process base element disable flags
|
292
|
+
for disable_key, element in element_disable_mapping.items():
|
293
|
+
if device_info.get(disable_key, False):
|
294
|
+
self.disable_element(element)
|
295
|
+
LOGGER.info(
|
296
|
+
"Disabled %s element from device_info setting", element.name
|
297
|
+
)
|
298
|
+
|
299
|
+
# Process room disable flags (1-15)
|
300
|
+
for room_id in range(1, 16):
|
301
|
+
disable_key = f"disable_room_{room_id}"
|
302
|
+
if device_info.get(disable_key, False):
|
303
|
+
room_element = getattr(DrawableElement, f"ROOM_{room_id}")
|
304
|
+
self.disable_element(room_element)
|
305
|
+
LOGGER.info(
|
306
|
+
"Disabled ROOM_%d element from device_info setting", room_id
|
307
|
+
)
|