valetudo-map-parser 0.1.9b55__py3-none-any.whl → 0.1.9b56__py3-none-any.whl
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/rand25_handler.py +164 -82
- {valetudo_map_parser-0.1.9b55.dist-info → valetudo_map_parser-0.1.9b56.dist-info}/METADATA +1 -1
- {valetudo_map_parser-0.1.9b55.dist-info → valetudo_map_parser-0.1.9b56.dist-info}/RECORD +6 -6
- {valetudo_map_parser-0.1.9b55.dist-info → valetudo_map_parser-0.1.9b56.dist-info}/LICENSE +0 -0
- {valetudo_map_parser-0.1.9b55.dist-info → valetudo_map_parser-0.1.9b56.dist-info}/NOTICE.txt +0 -0
- {valetudo_map_parser-0.1.9b55.dist-info → valetudo_map_parser-0.1.9b56.dist-info}/WHEEL +0 -0
@@ -29,7 +29,6 @@ from .config.types import (
|
|
29
29
|
)
|
30
30
|
from .config.utils import (
|
31
31
|
BaseHandler,
|
32
|
-
# async_extract_room_outline,
|
33
32
|
initialize_drawing_config,
|
34
33
|
manage_drawable_elements,
|
35
34
|
prepare_resize_params,
|
@@ -71,37 +70,11 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
71
70
|
self.imd = ImageDraw(self) # Image Draw
|
72
71
|
self.rooms_handler = RandRoomsHandler(self.file_name, self.drawing_config) # Room data handler
|
73
72
|
|
74
|
-
async def extract_room_outline_from_map(self, room_id_int, pixels):
|
75
|
-
"""Extract the outline of a room using the pixel data and element map.
|
76
|
-
|
77
|
-
Args:
|
78
|
-
room_id_int: The room ID as an integer
|
79
|
-
pixels: List of pixel coordinates in the format [[x, y, z], ...]
|
80
|
-
|
81
|
-
Returns:
|
82
|
-
List of points forming the outline of the room
|
83
|
-
"""
|
84
|
-
# Calculate x and y min/max from compressed pixels for rectangular fallback
|
85
|
-
x_values = []
|
86
|
-
y_values = []
|
87
|
-
for x, y, _ in pixels:
|
88
|
-
x_values.append(x)
|
89
|
-
y_values.append(y)
|
90
|
-
|
91
|
-
if not x_values or not y_values:
|
92
|
-
return []
|
93
|
-
|
94
|
-
min_x, max_x = min(x_values), max(x_values)
|
95
|
-
min_y, max_y = min(y_values), max(y_values)
|
96
|
-
|
97
|
-
# Always return a rectangular outline since element_map is removed
|
98
|
-
return [(min_x, min_y), (max_x, min_y), (max_x, max_y), (min_x, max_y)]
|
99
|
-
|
100
73
|
async def extract_room_properties(
|
101
74
|
self, json_data: JsonType, destinations: JsonType
|
102
75
|
) -> RoomsProperties:
|
103
76
|
"""Extract the room properties."""
|
104
|
-
unsorted_id = RandImageData.get_rrm_segments_ids(json_data)
|
77
|
+
# unsorted_id = RandImageData.get_rrm_segments_ids(json_data)
|
105
78
|
size_x, size_y = RandImageData.get_rrm_image_size(json_data)
|
106
79
|
top, left = RandImageData.get_rrm_image_position(json_data)
|
107
80
|
try:
|
@@ -124,11 +97,10 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
124
97
|
|
125
98
|
# Update self.rooms_pos from room_properties for compatibility with other methods
|
126
99
|
self.rooms_pos = []
|
127
|
-
for room_id,
|
128
|
-
self.rooms_pos.append(
|
129
|
-
"name":
|
130
|
-
|
131
|
-
})
|
100
|
+
for room_id, room_data in room_properties.items():
|
101
|
+
self.rooms_pos.append(
|
102
|
+
{"name": room_data["name"], "outline": room_data["outline"]}
|
103
|
+
)
|
132
104
|
|
133
105
|
# get the zones and points data
|
134
106
|
zone_properties = await self.async_zone_propriety(zones_data)
|
@@ -353,79 +325,189 @@ class ReImageHandler(BaseHandler, AutoCrop):
|
|
353
325
|
_LOGGER.debug("Got Rooms Attributes.")
|
354
326
|
return self.room_propriety
|
355
327
|
|
328
|
+
@staticmethod
|
329
|
+
def point_in_polygon(x: int, y: int, polygon: list) -> bool:
|
330
|
+
"""
|
331
|
+
Check if a point is inside a polygon using ray casting algorithm.
|
332
|
+
Enhanced version with better handling of edge cases.
|
333
|
+
|
334
|
+
Args:
|
335
|
+
x: X coordinate of the point
|
336
|
+
y: Y coordinate of the point
|
337
|
+
polygon: List of (x, y) tuples forming the polygon
|
338
|
+
|
339
|
+
Returns:
|
340
|
+
True if the point is inside the polygon, False otherwise
|
341
|
+
"""
|
342
|
+
# Ensure we have a valid polygon with at least 3 points
|
343
|
+
if len(polygon) < 3:
|
344
|
+
return False
|
345
|
+
|
346
|
+
# Make sure the polygon is closed (last point equals first point)
|
347
|
+
if polygon[0] != polygon[-1]:
|
348
|
+
polygon = polygon + [polygon[0]]
|
349
|
+
|
350
|
+
# Use winding number algorithm for better accuracy
|
351
|
+
wn = 0 # Winding number counter
|
352
|
+
|
353
|
+
# Loop through all edges of the polygon
|
354
|
+
for i in range(len(polygon) - 1): # Last vertex is first vertex
|
355
|
+
p1x, p1y = polygon[i]
|
356
|
+
p2x, p2y = polygon[i + 1]
|
357
|
+
|
358
|
+
# Test if a point is left/right/on the edge defined by two vertices
|
359
|
+
if p1y <= y: # Start y <= P.y
|
360
|
+
if p2y > y: # End y > P.y (upward crossing)
|
361
|
+
# Point left of edge
|
362
|
+
if ((p2x - p1x) * (y - p1y) - (x - p1x) * (p2y - p1y)) > 0:
|
363
|
+
wn += 1 # Valid up intersect
|
364
|
+
else: # Start y > P.y
|
365
|
+
if p2y <= y: # End y <= P.y (downward crossing)
|
366
|
+
# Point right of edge
|
367
|
+
if ((p2x - p1x) * (y - p1y) - (x - p1x) * (p2y - p1y)) < 0:
|
368
|
+
wn -= 1 # Valid down intersect
|
369
|
+
|
370
|
+
# If winding number is not 0, the point is inside the polygon
|
371
|
+
return wn != 0
|
372
|
+
|
356
373
|
async def async_get_robot_in_room(
|
357
374
|
self, robot_x: int, robot_y: int, angle: float
|
358
375
|
) -> RobotPosition:
|
359
376
|
"""Get the robot position and return in what room is."""
|
377
|
+
# First check if we already have a cached room and if the robot is still in it
|
378
|
+
if self.robot_in_room:
|
379
|
+
# If we have outline data, use point_in_polygon for accurate detection
|
380
|
+
if "outline" in self.robot_in_room:
|
381
|
+
outline = self.robot_in_room["outline"]
|
382
|
+
if self.point_in_polygon(int(robot_x), int(robot_y), outline):
|
383
|
+
temp = {
|
384
|
+
"x": robot_x,
|
385
|
+
"y": robot_y,
|
386
|
+
"angle": angle,
|
387
|
+
"in_room": self.robot_in_room["room"],
|
388
|
+
}
|
389
|
+
# Handle active zones
|
390
|
+
self.active_zones = self.shared.rand256_active_zone
|
391
|
+
self.zooming = False
|
392
|
+
if self.active_zones and (
|
393
|
+
self.robot_in_room["id"]
|
394
|
+
in range(len(self.active_zones))
|
395
|
+
):
|
396
|
+
self.zooming = bool(
|
397
|
+
self.active_zones[self.robot_in_room["id"]]
|
398
|
+
)
|
399
|
+
else:
|
400
|
+
self.zooming = False
|
401
|
+
return temp
|
402
|
+
# Fallback to bounding box check if no outline data
|
403
|
+
elif all(
|
404
|
+
k in self.robot_in_room for k in ["left", "right", "up", "down"]
|
405
|
+
):
|
406
|
+
if (
|
407
|
+
(self.robot_in_room["right"] <= int(robot_x) <= self.robot_in_room["left"])
|
408
|
+
and (self.robot_in_room["up"] <= int(robot_y) <= self.robot_in_room["down"])
|
409
|
+
):
|
410
|
+
temp = {
|
411
|
+
"x": robot_x,
|
412
|
+
"y": robot_y,
|
413
|
+
"angle": angle,
|
414
|
+
"in_room": self.robot_in_room["room"],
|
415
|
+
}
|
416
|
+
# Handle active zones
|
417
|
+
self.active_zones = self.shared.rand256_active_zone
|
418
|
+
self.zooming = False
|
419
|
+
if self.active_zones and (
|
420
|
+
self.robot_in_room["id"]
|
421
|
+
in range(len(self.active_zones))
|
422
|
+
):
|
423
|
+
self.zooming = bool(
|
424
|
+
self.active_zones[self.robot_in_room["id"]]
|
425
|
+
)
|
426
|
+
else:
|
427
|
+
self.zooming = False
|
428
|
+
return temp
|
360
429
|
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
)
|
430
|
+
# If we don't have a cached room or the robot is not in it, search all rooms
|
431
|
+
last_room = None
|
432
|
+
room_count = 0
|
433
|
+
if self.robot_in_room:
|
434
|
+
last_room = self.robot_in_room
|
367
435
|
|
368
|
-
#
|
369
|
-
|
436
|
+
# Check if the robot is far outside the normal map boundaries
|
437
|
+
# This helps prevent false positives for points very far from any room
|
438
|
+
map_boundary = 50000 # Typical map size is around 25000-30000 units for Rand25
|
439
|
+
if abs(robot_x) > map_boundary or abs(robot_y) > map_boundary:
|
440
|
+
_LOGGER.debug(
|
441
|
+
"%s robot position (%s, %s) is far outside map boundaries.",
|
442
|
+
self.file_name,
|
443
|
+
robot_x,
|
444
|
+
robot_y,
|
445
|
+
)
|
446
|
+
self.robot_in_room = last_room
|
447
|
+
self.zooming = False
|
370
448
|
temp = {
|
371
449
|
"x": robot_x,
|
372
450
|
"y": robot_y,
|
373
451
|
"angle": angle,
|
374
|
-
"in_room":
|
452
|
+
"in_room": last_room["room"] if last_room else "unknown",
|
375
453
|
}
|
376
|
-
self.active_zones = self.shared.rand256_active_zone
|
377
|
-
self.zooming = False
|
378
|
-
if self.active_zones and (
|
379
|
-
(self.robot_in_room["id"]) in range(len(self.active_zones))
|
380
|
-
): # issue #100 Index out of range
|
381
|
-
self.zooming = bool(self.active_zones[self.robot_in_room["id"]])
|
382
454
|
return temp
|
383
|
-
# else we need to search and use the async method
|
384
|
-
_LOGGER.debug("%s Changed room.. searching..", self.file_name)
|
385
|
-
room_count = -1
|
386
|
-
last_room = None
|
387
455
|
|
388
|
-
#
|
456
|
+
# Search through all rooms to find which one contains the robot
|
389
457
|
if not self.rooms_pos:
|
390
|
-
_LOGGER.debug(
|
391
|
-
|
458
|
+
_LOGGER.debug(
|
459
|
+
"%s: No rooms data available for robot position detection.",
|
460
|
+
self.file_name,
|
461
|
+
)
|
462
|
+
self.robot_in_room = last_room
|
463
|
+
self.zooming = False
|
464
|
+
temp = {
|
465
|
+
"x": robot_x,
|
466
|
+
"y": robot_y,
|
467
|
+
"angle": angle,
|
468
|
+
"in_room": last_room["room"] if last_room else "unknown",
|
469
|
+
}
|
470
|
+
return temp
|
392
471
|
|
393
|
-
|
394
|
-
if self.robot_in_room:
|
395
|
-
last_room = self.robot_in_room
|
472
|
+
_LOGGER.debug("%s: Searching for robot in rooms...", self.file_name)
|
396
473
|
for room in self.rooms_pos:
|
397
|
-
|
474
|
+
# Check if the room has an outline (polygon points)
|
475
|
+
if "outline" in room:
|
476
|
+
outline = room["outline"]
|
477
|
+
# Use point_in_polygon for accurate detection with complex shapes
|
478
|
+
if self.point_in_polygon(int(robot_x), int(robot_y), outline):
|
479
|
+
# Robot is in this room
|
480
|
+
self.robot_in_room = {
|
481
|
+
"id": room_count,
|
482
|
+
"room": str(room["name"]),
|
483
|
+
"outline": outline,
|
484
|
+
}
|
485
|
+
temp = {
|
486
|
+
"x": robot_x,
|
487
|
+
"y": robot_y,
|
488
|
+
"angle": angle,
|
489
|
+
"in_room": self.robot_in_room["room"],
|
490
|
+
}
|
491
|
+
_LOGGER.debug(
|
492
|
+
"%s is in %s room (polygon detection).",
|
493
|
+
self.file_name,
|
494
|
+
self.robot_in_room["room"],
|
495
|
+
)
|
496
|
+
return temp
|
398
497
|
room_count += 1
|
399
|
-
|
400
|
-
|
401
|
-
"left": corners[0][0],
|
402
|
-
"right": corners[2][0],
|
403
|
-
"up": corners[0][1],
|
404
|
-
"down": corners[2][1],
|
405
|
-
"room": room["name"],
|
406
|
-
}
|
407
|
-
# Check if the robot coordinates are inside the room's corners
|
408
|
-
if _check_robot_position(robot_x, robot_y):
|
409
|
-
temp = {
|
410
|
-
"x": robot_x,
|
411
|
-
"y": robot_y,
|
412
|
-
"angle": angle,
|
413
|
-
"in_room": self.robot_in_room["room"],
|
414
|
-
}
|
415
|
-
_LOGGER.debug("%s is in %s", self.file_name, self.robot_in_room["room"])
|
416
|
-
del room, corners, robot_x, robot_y # free memory.
|
417
|
-
return temp
|
418
|
-
# After checking all rooms and not finding a match
|
498
|
+
|
499
|
+
# Robot not found in any room
|
419
500
|
_LOGGER.debug(
|
420
|
-
"%s
|
501
|
+
"%s not located within any room coordinates.",
|
502
|
+
self.file_name,
|
421
503
|
)
|
422
|
-
self.zooming = False
|
423
504
|
self.robot_in_room = last_room
|
505
|
+
self.zooming = False
|
424
506
|
temp = {
|
425
507
|
"x": robot_x,
|
426
508
|
"y": robot_y,
|
427
509
|
"angle": angle,
|
428
|
-
"in_room":
|
510
|
+
"in_room": last_room["room"] if last_room else "unknown",
|
429
511
|
}
|
430
512
|
return temp
|
431
513
|
|
@@ -17,11 +17,11 @@ valetudo_map_parser/hypfer_handler.py,sha256=wvkZt6MsUF0gkHZDAwiUgOOawkdvakRzYBV
|
|
17
17
|
valetudo_map_parser/hypfer_rooms_handler.py,sha256=NkpOA6Gdq-2D3lLAxvtNuuWMvPXHxeMY2TO5RZLSHlU,22652
|
18
18
|
valetudo_map_parser/map_data.py,sha256=zQKE8EzWxR0r0qyfD1QQq51T1wFrpcIeXtnpm92-LXQ,17743
|
19
19
|
valetudo_map_parser/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
|
-
valetudo_map_parser/rand25_handler.py,sha256=
|
20
|
+
valetudo_map_parser/rand25_handler.py,sha256=GpY9R9EGWM06KYF4VQ1NxYw0idaJ8lZNkkA5wa0cr-c,22292
|
21
21
|
valetudo_map_parser/reimg_draw.py,sha256=1q8LkNTPHEA9Tsapc_JnVw51kpPYNhaBU-KmHkefCQY,12507
|
22
22
|
valetudo_map_parser/rooms_handler.py,sha256=YP8OLotBH-RXluv398l7TTT2zIBHJp91b8THWxl3NdI,17794
|
23
|
-
valetudo_map_parser-0.1.
|
24
|
-
valetudo_map_parser-0.1.
|
25
|
-
valetudo_map_parser-0.1.
|
26
|
-
valetudo_map_parser-0.1.
|
27
|
-
valetudo_map_parser-0.1.
|
23
|
+
valetudo_map_parser-0.1.9b56.dist-info/LICENSE,sha256=Lh-qBbuRV0-jiCIBhfV7NgdwFxQFOXH3BKOzK865hRs,10480
|
24
|
+
valetudo_map_parser-0.1.9b56.dist-info/METADATA,sha256=LJXXb676sSJ9W7NGafpcJSF4CRGGhbg_lCyrzcxkge0,3321
|
25
|
+
valetudo_map_parser-0.1.9b56.dist-info/NOTICE.txt,sha256=5lTOuWiU9aiEnJ2go8sc7lTJ7ntMBx0g0GFnNrswCY4,2533
|
26
|
+
valetudo_map_parser-0.1.9b56.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
27
|
+
valetudo_map_parser-0.1.9b56.dist-info/RECORD,,
|
File without changes
|
{valetudo_map_parser-0.1.9b55.dist-info → valetudo_map_parser-0.1.9b56.dist-info}/NOTICE.txt
RENAMED
File without changes
|
File without changes
|