valetudo-map-parser 0.1.9b49__py3-none-any.whl → 0.1.9b51__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.
@@ -0,0 +1,406 @@
1
+ """
2
+ Hipfer Rooms Handler Module.
3
+ Handles room data extraction and processing for Valetudo Hipfer vacuum maps.
4
+ Provides async methods for room outline extraction and properties management.
5
+ Version: 0.1.9
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from math import sqrt
11
+ from typing import Any, Dict, Optional, List, Tuple
12
+
13
+ import numpy as np
14
+
15
+ from .config.drawable_elements import DrawableElement, DrawingConfig
16
+ from .config.types import LOGGER, RoomsProperties, RoomStore
17
+
18
+
19
+ class HypferRoomsHandler:
20
+ """
21
+ Handler for extracting and managing room data from Hipfer vacuum maps.
22
+
23
+ This class provides methods to:
24
+ - Extract room outlines using the Ramer-Douglas-Peucker algorithm
25
+ - Process room properties from JSON data
26
+ - Generate room masks and extract contours
27
+
28
+ All methods are async for better integration with the rest of the codebase.
29
+ """
30
+
31
+ def __init__(self, vacuum_id: str, drawing_config: Optional[DrawingConfig] = None):
32
+ """
33
+ Initialize the HipferRoomsHandler.
34
+
35
+ Args:
36
+ vacuum_id: Identifier for the vacuum
37
+ drawing_config: Configuration for which elements to draw (optional)
38
+ """
39
+ self.vacuum_id = vacuum_id
40
+ self.drawing_config = drawing_config
41
+
42
+ @staticmethod
43
+ def sublist(data: list, chunk_size: int) -> list:
44
+ return [data[i : i + chunk_size] for i in range(0, len(data), chunk_size)]
45
+
46
+ @staticmethod
47
+ def perpendicular_distance(
48
+ point: tuple[int, int], line_start: tuple[int, int], line_end: tuple[int, int]
49
+ ) -> float:
50
+ """Calculate the perpendicular distance from a point to a line."""
51
+ if line_start == line_end:
52
+ return sqrt(
53
+ (point[0] - line_start[0]) ** 2 + (point[1] - line_start[1]) ** 2
54
+ )
55
+
56
+ x, y = point
57
+ x1, y1 = line_start
58
+ x2, y2 = line_end
59
+
60
+ # Calculate the line length
61
+ line_length = sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
62
+ if line_length == 0:
63
+ return 0
64
+
65
+ # Calculate the distance from the point to the line
66
+ return abs((y2 - y1) * x - (x2 - x1) * y + x2 * y1 - y2 * x1) / line_length
67
+
68
+ async def rdp(
69
+ self, points: List[Tuple[int, int]], epsilon: float
70
+ ) -> List[Tuple[int, int]]:
71
+ """Ramer-Douglas-Peucker algorithm for simplifying a curve."""
72
+ if len(points) <= 2:
73
+ return points
74
+
75
+ # Find the point with the maximum distance
76
+ dmax = 0
77
+ index = 0
78
+ for i in range(1, len(points) - 1):
79
+ d = self.perpendicular_distance(points[i], points[0], points[-1])
80
+ if d > dmax:
81
+ index = i
82
+ dmax = d
83
+
84
+ # If max distance is greater than epsilon, recursively simplify
85
+ if dmax > epsilon:
86
+ # Recursive call
87
+ first_segment = await self.rdp(points[: index + 1], epsilon)
88
+ second_segment = await self.rdp(points[index:], epsilon)
89
+
90
+ # Build the result list (avoiding duplicating the common point)
91
+ return first_segment[:-1] + second_segment
92
+ else:
93
+ return [points[0], points[-1]]
94
+
95
+ async def async_get_corners(
96
+ self, mask: np.ndarray, epsilon_factor: float = 0.05
97
+ ) -> List[Tuple[int, int]]:
98
+ """
99
+ Get the corners of a room shape as a list of (x, y) tuples.
100
+ Uses contour detection and Douglas-Peucker algorithm to simplify the contour.
101
+
102
+ Args:
103
+ mask: Binary mask of the room (1 for room, 0 for background)
104
+ epsilon_factor: Controls the level of simplification (higher = fewer points)
105
+
106
+ Returns:
107
+ List of (x, y) tuples representing the corners of the room
108
+ """
109
+ # Find contours in the mask
110
+ contour = await self.async_moore_neighbor_trace(mask)
111
+
112
+ if not contour:
113
+ # Fallback to bounding box if contour detection fails
114
+ y_indices, x_indices = np.where(mask > 0)
115
+ if len(x_indices) == 0 or len(y_indices) == 0:
116
+ return []
117
+
118
+ x_min, x_max = np.min(x_indices), np.max(x_indices)
119
+ y_min, y_max = np.min(y_indices), np.max(y_indices)
120
+
121
+ return [
122
+ (x_min, y_min), # Top-left
123
+ (x_max, y_min), # Top-right
124
+ (x_max, y_max), # Bottom-right
125
+ (x_min, y_max), # Bottom-left
126
+ (x_min, y_min), # Back to top-left to close the polygon
127
+ ]
128
+
129
+ # Calculate the perimeter of the contour
130
+ perimeter = 0
131
+ for i in range(len(contour) - 1):
132
+ x1, y1 = contour[i]
133
+ x2, y2 = contour[i + 1]
134
+ perimeter += np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
135
+
136
+ # Apply Douglas-Peucker algorithm to simplify the contour
137
+ epsilon = epsilon_factor * perimeter
138
+ simplified_contour = await self.rdp(contour, epsilon=epsilon)
139
+
140
+ # Ensure the contour has at least 3 points to form a polygon
141
+ if len(simplified_contour) < 3:
142
+ # Fallback to bounding box
143
+ y_indices, x_indices = np.where(mask > 0)
144
+ x_min, x_max = int(np.min(x_indices)), int(np.max(x_indices))
145
+ y_min, y_max = int(np.min(y_indices)), int(np.max(y_indices))
146
+
147
+ LOGGER.debug(
148
+ f"{self.vacuum_id}: Too few points in contour, using bounding box"
149
+ )
150
+ return [
151
+ (x_min, y_min), # Top-left
152
+ (x_max, y_min), # Top-right
153
+ (x_max, y_max), # Bottom-right
154
+ (x_min, y_max), # Bottom-left
155
+ (x_min, y_min), # Back to top-left to close the polygon
156
+ ]
157
+
158
+ # Ensure the contour is closed
159
+ if simplified_contour[0] != simplified_contour[-1]:
160
+ simplified_contour.append(simplified_contour[0])
161
+
162
+ return simplified_contour
163
+
164
+ @staticmethod
165
+ async def async_moore_neighbor_trace(mask: np.ndarray) -> List[Tuple[int, int]]:
166
+ """
167
+ Trace the contour of a binary mask using an optimized Moore-Neighbor tracing.
168
+
169
+ Args:
170
+ mask: Binary mask of the room (1 for room, 0 for background)
171
+
172
+ Returns:
173
+ List of (x, y) tuples representing the contour
174
+ """
175
+ # Convert to uint8 and pad
176
+ padded = np.pad(mask.astype(np.uint8), 1, mode="constant")
177
+ height, width = padded.shape
178
+
179
+ # Find the first non-zero point efficiently (scan row by row)
180
+ # This is much faster than np.argwhere() for large arrays
181
+ start = None
182
+ for y in range(height):
183
+ # Use NumPy's any() to quickly check if there are any 1s in this row
184
+ if np.any(padded[y]):
185
+ # Find the first 1 in this row
186
+ x = np.where(padded[y] == 1)[0][0]
187
+ start = (int(x), int(y))
188
+ break
189
+
190
+ if start is None:
191
+ return []
192
+
193
+ # Pre-compute directions
194
+ directions = [
195
+ (-1, -1),
196
+ (-1, 0),
197
+ (-1, 1),
198
+ (0, 1),
199
+ (1, 1),
200
+ (1, 0),
201
+ (1, -1),
202
+ (0, -1),
203
+ ]
204
+
205
+ # Use a 2D array for visited tracking (faster than set)
206
+ visited = np.zeros((height, width), dtype=bool)
207
+
208
+ # Initialize contour
209
+ contour = []
210
+ contour.append((int(start[0] - 1), int(start[1] - 1))) # Adjust for padding
211
+
212
+ current = start
213
+ prev_dir = 7
214
+ visited[current[1], current[0]] = True
215
+
216
+ # Main tracing loop
217
+ while True:
218
+ found = False
219
+
220
+ # Check all 8 directions
221
+ for i in range(8):
222
+ dir_idx = (prev_dir + i) % 8
223
+ dx, dy = directions[dir_idx]
224
+ nx, ny = current[0] + dx, current[1] + dy
225
+
226
+ # Bounds check and value check
227
+ if (
228
+ 0 <= ny < height
229
+ and 0 <= nx < width
230
+ and padded[ny, nx] == 1
231
+ and not visited[ny, nx]
232
+ ):
233
+ current = (nx, ny)
234
+ visited[ny, nx] = True
235
+ contour.append(
236
+ (int(nx - 1), int(ny - 1))
237
+ ) # Adjust for padding and convert to int
238
+ prev_dir = (dir_idx + 5) % 8
239
+ found = True
240
+ break
241
+
242
+ # Check termination conditions
243
+ if not found or (
244
+ len(contour) > 3
245
+ and (int(current[0] - 1), int(current[1] - 1)) == contour[0]
246
+ ):
247
+ break
248
+
249
+ return contour
250
+
251
+ async def async_extract_room_properties(
252
+ self, json_data: Dict[str, Any]
253
+ ) -> RoomsProperties:
254
+ """
255
+ Extract room properties from the JSON data.
256
+
257
+ Args:
258
+ json_data: JSON data from the vacuum
259
+
260
+ Returns:
261
+ Dictionary of room properties
262
+ """
263
+ room_properties = {}
264
+ pixel_size = json_data.get("pixelSize", 5)
265
+ height = json_data["size"]["y"]
266
+ width = json_data["size"]["x"]
267
+ vacuum_id = self.vacuum_id
268
+ room_id_counter = 0
269
+
270
+ for layer in json_data.get("layers", []):
271
+ if layer.get("__class") == "MapLayer" and layer.get("type") == "segment":
272
+ meta_data = layer.get("metaData", {})
273
+ segment_id = meta_data.get("segmentId")
274
+ name = meta_data.get("name", f"Room {segment_id}")
275
+
276
+ # Check if this room is disabled in the drawing configuration
277
+ # The room_id_counter is 0-based, but DrawableElement.ROOM_X is 1-based
278
+ current_room_id = room_id_counter + 1
279
+ room_id_counter = (
280
+ room_id_counter + 1
281
+ ) % 16 # Cycle room_id back to 0 after 15
282
+
283
+ if 1 <= current_room_id <= 15 and self.drawing_config is not None:
284
+ room_element = getattr(
285
+ DrawableElement, f"ROOM_{current_room_id}", None
286
+ )
287
+ if room_element and not self.drawing_config.is_enabled(
288
+ room_element
289
+ ):
290
+ LOGGER.debug(
291
+ "%s: Room %d is disabled and will be skipped",
292
+ self.vacuum_id,
293
+ current_room_id,
294
+ )
295
+ continue
296
+
297
+ compressed_pixels = layer.get("compressedPixels", [])
298
+ pixels = self.sublist(compressed_pixels, 3)
299
+
300
+ # Create a binary mask for the room
301
+ if not pixels:
302
+ LOGGER.warning(f"Skipping segment {segment_id}: no pixels found")
303
+ continue
304
+
305
+ mask = np.zeros((height, width), dtype=np.uint8)
306
+ for x, y, length in pixels:
307
+ if 0 <= y < height and 0 <= x < width and x + length <= width:
308
+ mask[y, x : x + length] = 1
309
+
310
+ # Find the room outline using the improved get_corners function
311
+ # Adjust epsilon_factor to control the level of simplification (higher = fewer points)
312
+ outline = await self.async_get_corners(mask, epsilon_factor=0.05)
313
+
314
+ if not outline:
315
+ LOGGER.warning(
316
+ f"Skipping segment {segment_id}: failed to generate outline"
317
+ )
318
+ continue
319
+
320
+ # Calculate the center of the room
321
+ xs, ys = zip(*outline)
322
+ x_min, x_max = min(xs), max(xs)
323
+ y_min, y_max = min(ys), max(ys)
324
+
325
+ # Scale coordinates by pixel_size
326
+ scaled_outline = [(x * pixel_size, y * pixel_size) for x, y in outline]
327
+
328
+ room_id = str(segment_id)
329
+ room_properties[room_id] = {
330
+ "number": segment_id,
331
+ "outline": scaled_outline, # Already includes the closing point
332
+ "name": name,
333
+ "x": ((x_min + x_max) * pixel_size) // 2,
334
+ "y": ((y_min + y_max) * pixel_size) // 2,
335
+ }
336
+
337
+ RoomStore(vacuum_id, room_properties)
338
+ return room_properties
339
+
340
+ async def get_room_at_position(
341
+ self, x: int, y: int, room_properties: Optional[RoomsProperties] = None
342
+ ) -> Optional[Dict[str, Any]]:
343
+ """
344
+ Get the room at a specific position.
345
+
346
+ Args:
347
+ x: X coordinate
348
+ y: Y coordinate
349
+ room_properties: Room properties dictionary (optional)
350
+
351
+ Returns:
352
+ Room data dictionary or None if no room at position
353
+ """
354
+ if room_properties is None:
355
+ room_store = RoomStore(self.vacuum_id)
356
+ room_properties = room_store.get_rooms()
357
+
358
+ if not room_properties:
359
+ return None
360
+
361
+ for room_id, room_data in room_properties.items():
362
+ outline = room_data.get("outline", [])
363
+ if not outline or len(outline) < 3:
364
+ continue
365
+
366
+ # Check if point is inside the polygon
367
+ if self.point_in_polygon(x, y, outline):
368
+ return {
369
+ "id": room_id,
370
+ "name": room_data.get("name", f"Room {room_id}"),
371
+ "x": room_data.get("x", 0),
372
+ "y": room_data.get("y", 0),
373
+ }
374
+
375
+ return None
376
+
377
+ @staticmethod
378
+ def point_in_polygon(x: int, y: int, polygon: List[Tuple[int, int]]) -> bool:
379
+ """
380
+ Check if a point is inside a polygon using ray casting algorithm.
381
+
382
+ Args:
383
+ x: X coordinate of the point
384
+ y: Y coordinate of the point
385
+ polygon: List of (x, y) tuples forming the polygon
386
+
387
+ Returns:
388
+ True if the point is inside the polygon, False otherwise
389
+ """
390
+ n = len(polygon)
391
+ inside = False
392
+
393
+ p1x, p1y = polygon[0]
394
+ xinters = None # Initialize with default value
395
+ for i in range(1, n + 1):
396
+ p2x, p2y = polygon[i % n]
397
+ if y > min(p1y, p2y):
398
+ if y <= max(p1y, p2y):
399
+ if x <= max(p1x, p2x):
400
+ if p1y != p2y:
401
+ xinters = (y - p1y) * (p2x - p1x) / (p2y - p1y) + p1x
402
+ if p1x == p2x or x <= xinters:
403
+ inside = not inside
404
+ p1x, p1y = p2x, p2y
405
+
406
+ return inside
@@ -16,36 +16,6 @@ from .config.types import Colors, ImageSize, JsonType, NumpyArray
16
16
  class ImageData:
17
17
  """Class to handle the image data."""
18
18
 
19
- @staticmethod
20
- async def async_extract_color_coordinates(
21
- source_array: NumpyArray, search_for_colours_list: Colors
22
- ) -> list:
23
- """Search for specific colors in an image array and return their coordinates."""
24
- # Initialize an empty list to store color and coordinates tuples
25
- color_coordinates_list = []
26
-
27
- # Iterate over the search_for_colours_list
28
- for color_to_search in search_for_colours_list:
29
- # Initialize an empty list to store coordinates for the current color
30
- color_coordinates = []
31
-
32
- # Iterate over the image array
33
- for y in range(source_array.shape[0]):
34
- for x in range(source_array.shape[1]):
35
- # Extract the pixel color at the current coordinates
36
- pixel_color = source_array[y, x]
37
-
38
- # Check if the current pixel color matches the color_to_search
39
- if np.all(pixel_color == color_to_search):
40
- # Record the coordinates for the current color
41
- color_coordinates.append((x, y))
42
-
43
- # Append the color and its coordinates to the final list
44
- color_coordinates_list.append((color_to_search, color_coordinates))
45
-
46
- # Return the final list of color and coordinates tuples
47
- return color_coordinates_list
48
-
49
19
  @staticmethod
50
20
  def sublist(lst, n):
51
21
  """Sub lists of specific n number of elements"""
@@ -29,13 +29,10 @@ from .config.types import (
29
29
  )
30
30
  from .config.utils import (
31
31
  BaseHandler,
32
- get_element_at_position,
33
- get_room_at_position,
34
32
  initialize_drawing_config,
35
33
  manage_drawable_elements,
36
34
  prepare_resize_params,
37
35
  async_extract_room_outline,
38
- update_element_map_with_robot,
39
36
  )
40
37
  from .map_data import RandImageData
41
38
  from .reimg_draw import ImageDraw
@@ -64,7 +61,6 @@ class ReImageHandler(BaseHandler, AutoCrop):
64
61
  self.drawing_config, self.draw, self.enhanced_draw = initialize_drawing_config(
65
62
  self
66
63
  )
67
- self.element_map = None # Map of element codes
68
64
  self.go_to = None # Go to position data
69
65
  self.img_base_layer = None # Base image layer
70
66
  self.img_rotate = shared_data.image_rotate # Image rotation
@@ -96,36 +92,8 @@ class ReImageHandler(BaseHandler, AutoCrop):
96
92
  min_x, max_x = min(x_values), max(x_values)
97
93
  min_y, max_y = min(y_values), max(y_values)
98
94
 
99
- # If we don't have an element map, return a rectangular outline
100
- if not hasattr(self, "element_map") or self.element_map is None:
101
- # Return rectangular outline
102
- return [(min_x, min_y), (max_x, min_y), (max_x, max_y), (min_x, max_y)]
103
-
104
- # Create a binary mask for this room using the pixel data
105
- # This is more reliable than using the element_map since we're directly using the pixel data
106
- height, width = self.element_map.shape
107
- room_mask = np.zeros((height, width), dtype=np.uint8)
108
-
109
- # Fill the mask with room pixels using the pixel data
110
- for x, y, _ in pixels: # Using _ instead of z since z is unused
111
- # Make sure we're within bounds
112
- if 0 <= y < height and 0 <= x < width:
113
- # Mark a pixel at this position
114
- room_mask[y, x] = 1
115
-
116
- # Debug log to check if we have any room pixels
117
- num_room_pixels = np.sum(room_mask)
118
- _LOGGER.debug(
119
- "%s: Room %s mask has %d pixels",
120
- self.file_name,
121
- str(room_id_int),
122
- int(num_room_pixels),
123
- )
124
-
125
- # Use the shared utility function to extract the room outline
126
- return await async_extract_room_outline(
127
- room_mask, min_x, min_y, max_x, max_y, self.file_name, room_id_int
128
- )
95
+ # Always return a rectangular outline since element_map is removed
96
+ return [(min_x, min_y), (max_x, min_y), (max_x, max_y), (min_x, max_y)]
129
97
 
130
98
  async def extract_room_properties(
131
99
  self, json_data: JsonType, destinations: JsonType
@@ -322,20 +290,11 @@ class ReImageHandler(BaseHandler, AutoCrop):
322
290
  async def _draw_map_elements(
323
291
  self, img_np_array, m_json, colors, robot_position, robot_position_angle
324
292
  ):
325
- # Create element map for tracking what's drawn where if it doesn't exist
326
- if self.element_map is None:
327
- self.element_map = np.zeros(
328
- (img_np_array.shape[0], img_np_array.shape[1]), dtype=np.int32
329
- )
330
- self.element_map[:] = DrawableElement.FLOOR
331
-
332
293
  # Draw charger if enabled
333
294
  if self.drawing_config.is_enabled(DrawableElement.CHARGER):
334
295
  img_np_array, self.charger_pos = await self.imd.async_draw_charger(
335
296
  img_np_array, m_json, colors["charger"]
336
297
  )
337
- # Update element map for charger position
338
- self._update_element_map_for_charger()
339
298
 
340
299
  # Draw zones if enabled
341
300
  if self.drawing_config.is_enabled(DrawableElement.RESTRICTED_AREA):
@@ -372,10 +331,6 @@ class ReImageHandler(BaseHandler, AutoCrop):
372
331
  img_np_array, robot_position, robot_position_angle, robot_color
373
332
  )
374
333
 
375
- # Update element map for robot position
376
- update_element_map_with_robot(
377
- self.element_map, robot_position, DrawableElement.ROBOT
378
- )
379
334
  img_np_array = await self.async_auto_trim_and_zoom_image(
380
335
  img_np_array,
381
336
  detect_colour=colors["background"],
@@ -536,40 +491,3 @@ class ReImageHandler(BaseHandler, AutoCrop):
536
491
  property_name=property_name,
537
492
  value=value,
538
493
  )
539
-
540
- def get_element_at_position(self, x: int, y: int) -> DrawableElement:
541
- """Get the element code at a specific position."""
542
- return get_element_at_position(self.element_map, x, y)
543
-
544
- def get_room_at_position(self, x: int, y: int) -> int:
545
- """Get the room ID at a specific position, or None if not a room."""
546
- return get_room_at_position(self.element_map, x, y, DrawableElement.ROOM_1)
547
-
548
- def _update_element_map_for_charger(self):
549
- """Helper method to update the element map for the charger position."""
550
- if not self.charger_pos or self.element_map is None:
551
- return
552
-
553
- charger_radius = 15
554
- # Handle both dictionary format {'x': x, 'y': y} and list format [x, y]
555
- charger_x = (
556
- self.charger_pos.get("x")
557
- if isinstance(self.charger_pos, dict)
558
- else self.charger_pos[0]
559
- )
560
- charger_y = (
561
- self.charger_pos.get("y")
562
- if isinstance(self.charger_pos, dict)
563
- else self.charger_pos[1]
564
- )
565
-
566
- for dy in range(-charger_radius, charger_radius + 1):
567
- for dx in range(-charger_radius, charger_radius + 1):
568
- # Check if the point is within the circular charger area
569
- if dx * dx + dy * dy <= charger_radius * charger_radius:
570
- cx, cy = int(charger_x + dx), int(charger_y + dy)
571
- # Check if the coordinates are within the element map bounds
572
- if (0 <= cy < self.element_map.shape[0]) and (
573
- 0 <= cx < self.element_map.shape[1]
574
- ):
575
- self.element_map[cy, cx] = DrawableElement.CHARGER
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: valetudo-map-parser
3
- Version: 0.1.9b49
3
+ Version: 0.1.9b51
4
4
  Summary: A Python library to parse Valetudo map data returning a PIL Image object.
5
5
  License: Apache-2.0
6
6
  Author: Sandro Cantarella
@@ -0,0 +1,26 @@
1
+ valetudo_map_parser/__init__.py,sha256=cewtLadNSOg3X2Ts2SuG8mTJqo0ncsFRg_sQ4VkM4ow,1037
2
+ valetudo_map_parser/config/__init__.py,sha256=DQ9plV3ZF_K25Dp5ZQHPDoG-40dQoJNdNi-dfNeR3Zc,48
3
+ valetudo_map_parser/config/auto_crop.py,sha256=6OvRsWzXMXBaSEvgwpaaisNdozDKiDyTmPjknFxoUMc,12624
4
+ valetudo_map_parser/config/color_utils.py,sha256=D4NXRhuPdQ7UDKM3vLNYR0HnACl9AB75EnfCp5tGliI,4502
5
+ valetudo_map_parser/config/colors.py,sha256=5nk8QbNomv_WeE2rOLIswroGsxV4RZMd0slXrPlt-MY,29297
6
+ valetudo_map_parser/config/drawable.py,sha256=VhaNK62EBCxcQdNg7-dyUNepsYX6IFzej3raOt7_rVU,34268
7
+ valetudo_map_parser/config/drawable_elements.py,sha256=bkEwdbx1upt9vaPaqE_VR1rtwRsaiH-IVKc3mHNa8IY,12065
8
+ valetudo_map_parser/config/enhanced_drawable.py,sha256=T6HCUNcXpnPH3mbjzi-pq5bbPOhebj7jLNS-qyDh0HM,12646
9
+ valetudo_map_parser/config/optimized_element_map.py,sha256=52BCnkvVv9bre52LeVIfT8nhnEIpc0TuWTv1xcNu0Rk,15744
10
+ valetudo_map_parser/config/rand25_parser.py,sha256=kIayyqVZBfQfAMkiArzqrrj9vqZB3pkgT0Y5ufrQmGA,16448
11
+ valetudo_map_parser/config/room_outline.py,sha256=D20D-yeyKnlmVbW9lI7bsPtQGn2XkcWow6YNOEPnWVg,4800
12
+ valetudo_map_parser/config/shared.py,sha256=GIEMF-M6BVA6SFBrql7chV7TciWNMLJ8geqwHB0NrW8,11253
13
+ valetudo_map_parser/config/types.py,sha256=e-eZSwbPm3m5JfCDaKhnUFspmcRFSv74huxegkSBDXM,17566
14
+ valetudo_map_parser/config/utils.py,sha256=RsMjpjVqNbkI502yhLiRaB0GjCADqmRRcz-TkC6zklQ,31073
15
+ valetudo_map_parser/hypfer_draw.py,sha256=P8CrKysLaBb63ZArfqxN2Og6JCU6sPHPFHOte5noCGg,26654
16
+ valetudo_map_parser/hypfer_handler.py,sha256=WYFrp-q5wBsy0cTcVQUCXXVGTtW30z2W2dYvjKz2el8,23292
17
+ valetudo_map_parser/hypfer_rooms_handler.py,sha256=3QL8QZc6aMXxYn_L6gv1iL204I0rAzutWPVzc_4MfJ4,14505
18
+ valetudo_map_parser/map_data.py,sha256=zQKE8EzWxR0r0qyfD1QQq51T1wFrpcIeXtnpm92-LXQ,17743
19
+ valetudo_map_parser/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ valetudo_map_parser/rand25_handler.py,sha256=eLFX_gmGLaWQwvp8hVj8CgcNOfLsYNIdE1OLRcQy_yM,19988
21
+ valetudo_map_parser/reimg_draw.py,sha256=1q8LkNTPHEA9Tsapc_JnVw51kpPYNhaBU-KmHkefCQY,12507
22
+ valetudo_map_parser-0.1.9b51.dist-info/LICENSE,sha256=Lh-qBbuRV0-jiCIBhfV7NgdwFxQFOXH3BKOzK865hRs,10480
23
+ valetudo_map_parser-0.1.9b51.dist-info/METADATA,sha256=Nkc-krdlK7Kl1FdGVmWgZjfaEDSpidfKtWfW17G9M6s,3321
24
+ valetudo_map_parser-0.1.9b51.dist-info/NOTICE.txt,sha256=5lTOuWiU9aiEnJ2go8sc7lTJ7ntMBx0g0GFnNrswCY4,2533
25
+ valetudo_map_parser-0.1.9b51.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
26
+ valetudo_map_parser-0.1.9b51.dist-info/RECORD,,
@@ -1,25 +0,0 @@
1
- valetudo_map_parser/__init__.py,sha256=SOxmq7LkS7eMa2N4atW7ZBbqhGEL7fpj6MsyXZpCMsk,958
2
- valetudo_map_parser/config/__init__.py,sha256=DQ9plV3ZF_K25Dp5ZQHPDoG-40dQoJNdNi-dfNeR3Zc,48
3
- valetudo_map_parser/config/auto_crop.py,sha256=6OvRsWzXMXBaSEvgwpaaisNdozDKiDyTmPjknFxoUMc,12624
4
- valetudo_map_parser/config/color_utils.py,sha256=pJ9Frx5aZ1W2XPJxNToBLIwSYNTIm4Tw_NMMfFrZSaM,1691
5
- valetudo_map_parser/config/colors.py,sha256=zF-6CGe6XA9wtnrWY8hBmqsMNOcL1x5tZY1G6evniQ0,30278
6
- valetudo_map_parser/config/drawable.py,sha256=9-7FBlzWDfr7d_yIeRWXlAxlRxRfKflxi4fTnC3hqjk,33785
7
- valetudo_map_parser/config/drawable_elements.py,sha256=Ulfgf8B4LuLCfx-FfmC7LrP8o9ll_Sncg9mR774_3KE,43140
8
- valetudo_map_parser/config/enhanced_drawable.py,sha256=xNgFUNccstP245VgLFEA9gjB3-VvlSAJSjRgSZ3YFL0,16641
9
- valetudo_map_parser/config/optimized_element_map.py,sha256=52BCnkvVv9bre52LeVIfT8nhnEIpc0TuWTv1xcNu0Rk,15744
10
- valetudo_map_parser/config/rand25_parser.py,sha256=kIayyqVZBfQfAMkiArzqrrj9vqZB3pkgT0Y5ufrQmGA,16448
11
- valetudo_map_parser/config/room_outline.py,sha256=D20D-yeyKnlmVbW9lI7bsPtQGn2XkcWow6YNOEPnWVg,4800
12
- valetudo_map_parser/config/shared.py,sha256=WSl5rYSiTqE6YGAiwi9RILMZIQdFZRzVS8DwqzTZBbw,11309
13
- valetudo_map_parser/config/types.py,sha256=uEJY-yYHHJWW3EZjg7hERSFrC2XuKzzRGT3C0z31Aw0,18359
14
- valetudo_map_parser/config/utils.py,sha256=MP5_s9VFSdDERymujvDuGB8nYCXVuJcqg5tR5H9HCgY,33167
15
- valetudo_map_parser/hypfer_draw.py,sha256=oL_RbX0LEcPvOlMrfBA38qpJkMqqVwR-oAEbZeHqLWM,19898
16
- valetudo_map_parser/hypfer_handler.py,sha256=-nwGlfd-fqmNAHEeQFgCarr1t5v8gJTvb2ngDz_8PbM,27616
17
- valetudo_map_parser/map_data.py,sha256=lSKD-CG-RENOcNUDnUIIpqh74DuGnLmrH46IF1_EjwQ,19117
18
- valetudo_map_parser/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- valetudo_map_parser/rand25_handler.py,sha256=F9o1J6JZRV3CZTS4CG3AHNHZweKRM0nzd4HLmdPZe4w,23617
20
- valetudo_map_parser/reimg_draw.py,sha256=1q8LkNTPHEA9Tsapc_JnVw51kpPYNhaBU-KmHkefCQY,12507
21
- valetudo_map_parser-0.1.9b49.dist-info/LICENSE,sha256=Lh-qBbuRV0-jiCIBhfV7NgdwFxQFOXH3BKOzK865hRs,10480
22
- valetudo_map_parser-0.1.9b49.dist-info/METADATA,sha256=PjAsrik0PogFM7_ZKRYgwCBQl1-uOlc2nd5buY7WuOo,3321
23
- valetudo_map_parser-0.1.9b49.dist-info/NOTICE.txt,sha256=5lTOuWiU9aiEnJ2go8sc7lTJ7ntMBx0g0GFnNrswCY4,2533
24
- valetudo_map_parser-0.1.9b49.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
25
- valetudo_map_parser-0.1.9b49.dist-info/RECORD,,