valetudo-map-parser 0.1.9b44__py3-none-any.whl → 0.1.9b45__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.
@@ -194,6 +194,34 @@ class ColorsManagment:
194
194
 
195
195
  return (out_r, out_g, out_b, out_a)
196
196
 
197
+ @staticmethod
198
+ def sample_and_blend_color(array, x: int, y: int, foreground: Color) -> Color:
199
+ """
200
+ Sample the background color from the array at coordinates (x,y) and blend with foreground color.
201
+
202
+ Args:
203
+ array: The RGBA numpy array representing the image
204
+ x, y: Coordinates to sample the background color from
205
+ foreground: Foreground RGBA color (r,g,b,a) to blend on top
206
+
207
+ Returns:
208
+ Blended RGBA color
209
+ """
210
+ # Ensure coordinates are within bounds
211
+ if array is None:
212
+ return foreground
213
+
214
+ height, width = array.shape[:2]
215
+ if not (0 <= y < height and 0 <= x < width):
216
+ return foreground # Return foreground if coordinates are out of bounds
217
+
218
+ # Sample background color from the array
219
+ # The array is in RGBA format with shape (height, width, 4)
220
+ background = tuple(array[y, x])
221
+
222
+ # Blend the colors
223
+ return ColorsManagment.blend_colors(background, foreground)
224
+
197
225
  def get_user_colors(self) -> List[Color]:
198
226
  """Return the list of RGBA colors for user-defined map elements."""
199
227
  return self.user_colors
@@ -174,11 +174,47 @@ class Drawable:
174
174
  y2: int,
175
175
  color: Color,
176
176
  width: int = 3,
177
+ blend: bool = True,
177
178
  ) -> NumpyArray:
178
179
  """
179
180
  Draw a line on a NumPy array (layer) from point A to B using Bresenham's algorithm.
181
+
182
+ Args:
183
+ layer: The numpy array to draw on
184
+ x1, y1: Start point coordinates
185
+ x2, y2: End point coordinates
186
+ color: Color to draw with
187
+ width: Width of the line
188
+ blend: Whether to blend the color with the background
180
189
  """
190
+ from .colors import ColorsManagment
191
+
181
192
  x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
193
+
194
+ # Sample background color at the endpoints and blend with foreground color if requested
195
+ if blend:
196
+ # Sample at start point
197
+ if 0 <= y1 < layer.shape[0] and 0 <= x1 < layer.shape[1]:
198
+ start_blended_color = ColorsManagment.sample_and_blend_color(layer, x1, y1, color)
199
+ else:
200
+ start_blended_color = color
201
+
202
+ # Sample at end point
203
+ if 0 <= y2 < layer.shape[0] and 0 <= x2 < layer.shape[1]:
204
+ end_blended_color = ColorsManagment.sample_and_blend_color(layer, x2, y2, color)
205
+ else:
206
+ end_blended_color = color
207
+
208
+ # Use the average of the two blended colors
209
+ blended_color = (
210
+ (start_blended_color[0] + end_blended_color[0]) // 2,
211
+ (start_blended_color[1] + end_blended_color[1]) // 2,
212
+ (start_blended_color[2] + end_blended_color[2]) // 2,
213
+ (start_blended_color[3] + end_blended_color[3]) // 2
214
+ )
215
+ else:
216
+ blended_color = color
217
+
182
218
  dx = abs(x2 - x1)
183
219
  dy = abs(y2 - y1)
184
220
  sx = 1 if x1 < x2 else -1
@@ -189,7 +225,7 @@ class Drawable:
189
225
  for i in range(-width // 2, (width + 1) // 2):
190
226
  for j in range(-width // 2, (width + 1) // 2):
191
227
  if 0 <= x1 + i < layer.shape[1] and 0 <= y1 + j < layer.shape[0]:
192
- layer[y1 + j, x1 + i] = color
228
+ layer[y1 + j, x1 + i] = blended_color
193
229
  if x1 == x2 and y1 == y2:
194
230
  break
195
231
  e2 = 2 * err
@@ -221,6 +257,8 @@ class Drawable:
221
257
  Join the coordinates creating a continuous line (path).
222
258
  """
223
259
  import logging
260
+ from .colors import ColorsManagment
261
+
224
262
  _LOGGER = logging.getLogger(__name__)
225
263
  _LOGGER.debug("Drawing lines with %d coordinates, width %d, color %s", len(coords), width, color)
226
264
  for coord in coords:
@@ -230,6 +268,27 @@ class Drawable:
230
268
  except IndexError:
231
269
  x1, y1 = x0, y0
232
270
  _LOGGER.debug("Drawing line from (%d, %d) to (%d, %d)", x0, y0, x1, y1)
271
+
272
+ # Sample background color at the endpoints and blend with foreground color
273
+ # This is more efficient than sampling at every pixel
274
+ if 0 <= y0 < arr.shape[0] and 0 <= x0 < arr.shape[1]:
275
+ start_blended_color = ColorsManagment.sample_and_blend_color(arr, x0, y0, color)
276
+ else:
277
+ start_blended_color = color
278
+
279
+ if 0 <= y1 < arr.shape[0] and 0 <= x1 < arr.shape[1]:
280
+ end_blended_color = ColorsManagment.sample_and_blend_color(arr, x1, y1, color)
281
+ else:
282
+ end_blended_color = color
283
+
284
+ # Use the average of the two blended colors
285
+ blended_color = (
286
+ (start_blended_color[0] + end_blended_color[0]) // 2,
287
+ (start_blended_color[1] + end_blended_color[1]) // 2,
288
+ (start_blended_color[2] + end_blended_color[2]) // 2,
289
+ (start_blended_color[3] + end_blended_color[3]) // 2
290
+ )
291
+
233
292
  dx = abs(x1 - x0)
234
293
  dy = abs(y1 - y0)
235
294
  sx = 1 if x0 < x1 else -1
@@ -252,8 +311,9 @@ class Drawable:
252
311
  x, y = pixel
253
312
  for i in range(width):
254
313
  for j in range(width):
255
- if 0 <= x + i < arr.shape[1] and 0 <= y + j < arr.shape[0]:
256
- arr[y + j, x + i] = color
314
+ px, py = x + i, y + j
315
+ if 0 <= px < arr.shape[1] and 0 <= py < arr.shape[0]:
316
+ arr[py, px] = blended_color
257
317
  return arr
258
318
 
259
319
  @staticmethod
@@ -360,8 +420,10 @@ class Drawable:
360
420
  @staticmethod
361
421
  async def zones(layers: NumpyArray, coordinates, color: Color) -> NumpyArray:
362
422
  """
363
- Draw the zones on the input layer.
423
+ Draw the zones on the input layer with color blending.
364
424
  """
425
+ from .colors import ColorsManagment
426
+
365
427
  dot_radius = 1 # Number of pixels for the dot
366
428
  dot_spacing = 4 # Space between dots
367
429
  for zone in coordinates:
@@ -370,10 +432,22 @@ class Drawable:
370
432
  max_x = max(points[::2])
371
433
  min_y = min(points[1::2])
372
434
  max_y = max(points[1::2])
435
+
436
+ # Sample a point from the zone to get the background color
437
+ # Use the center of the zone for sampling
438
+ sample_x = (min_x + max_x) // 2
439
+ sample_y = (min_y + max_y) // 2
440
+
441
+ # Blend the color with the background color at the sample point
442
+ if 0 <= sample_y < layers.shape[0] and 0 <= sample_x < layers.shape[1]:
443
+ blended_color = ColorsManagment.sample_and_blend_color(layers, sample_x, sample_y, color)
444
+ else:
445
+ blended_color = color
446
+
373
447
  for y in range(min_y, max_y, dot_spacing):
374
448
  for x in range(min_x, max_x, dot_spacing):
375
449
  for _ in range(dot_radius):
376
- layers = Drawable._ellipse(layers, (x, y), dot_radius, color)
450
+ layers = Drawable._ellipse(layers, (x, y), dot_radius, blended_color)
377
451
  return layers
378
452
 
379
453
  @staticmethod
@@ -26,11 +26,8 @@ from .config.types import (
26
26
  )
27
27
  from .config.utils import (
28
28
  BaseHandler,
29
- blend_colors,
30
- blend_pixel,
31
29
  get_element_at_position,
32
30
  get_room_at_position,
33
- handle_room_outline_error,
34
31
  initialize_drawing_config,
35
32
  manage_drawable_elements,
36
33
  prepare_resize_params,
@@ -159,26 +156,7 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
159
156
  x_max,
160
157
  y_max,
161
158
  ) = await self.data.async_get_rooms_coordinates(pixels, pixel_size)
162
-
163
- # Get rectangular corners as a fallback
164
159
  corners = self.get_corners(x_max, x_min, y_max, y_min)
165
-
166
- # Try to extract a more accurate room outline from the element map
167
- try:
168
- # Extract the room outline using the element map
169
- outline = await self.extract_room_outline_from_map(
170
- segment_id, pixels, pixel_size
171
- )
172
- LOGGER.debug(
173
- "%s: Traced outline for room %s with %d points",
174
- self.file_name,
175
- segment_id,
176
- len(outline),
177
- )
178
- except (ValueError, IndexError, TypeError, ArithmeticError) as e:
179
- handle_room_outline_error(self.file_name, segment_id, e)
180
- outline = corners
181
-
182
160
  room_id = str(segment_id)
183
161
  self.rooms_pos.append(
184
162
  {
@@ -188,7 +166,7 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
188
166
  )
189
167
  room_properties[room_id] = {
190
168
  "number": segment_id,
191
- "outline": outline, # Use the detailed outline from the element map
169
+ "outline": corners,
192
170
  "name": name,
193
171
  "x": ((x_min + x_max) // 2),
194
172
  "y": ((y_min + y_max) // 2),
@@ -597,29 +575,6 @@ class HypferMapImageHandler(BaseHandler, AutoCrop):
597
575
  """Get the room ID at a specific position, or None if not a room."""
598
576
  return get_room_at_position(self.shared.element_map, x, y, DrawableElement.ROOM_1)
599
577
 
600
- @staticmethod
601
- def blend_colors(base_color, overlay_color):
602
- """
603
- Blend two RGBA colors, considering alpha channels.
604
-
605
- Args:
606
- base_color: The base RGBA color
607
- overlay_color: The overlay RGBA color to blend on top
608
-
609
- Returns:
610
- The blended RGBA color
611
- """
612
- return blend_colors(base_color, overlay_color)
613
-
614
- def blend_pixel(self, array, x, y, color, element):
615
- """
616
- Blend a pixel color with the existing color at the specified position.
617
- Also updates the element map if the new element has higher z-index.
618
- """
619
- return blend_pixel(
620
- array, x, y, color, element, self.shared.element_map, self.drawing_config
621
- )
622
-
623
578
  @staticmethod
624
579
  async def async_copy_array(original_array):
625
580
  """Copy the array."""
@@ -130,7 +130,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
130
130
 
131
131
  async def extract_room_properties(
132
132
  self, json_data: JsonType, destinations: JsonType
133
- ) -> RoomsProperties:
133
+ ) -> tuple[RoomsProperties, Any, Any]:
134
134
  """Extract the room properties."""
135
135
  unsorted_id = RandImageData.get_rrm_segments_ids(json_data)
136
136
  size_x, size_y = RandImageData.get_rrm_image_size(json_data)
@@ -153,7 +153,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
153
153
  if not self.outlines:
154
154
  # Return empty data if no outlines are available
155
155
  _LOGGER.debug("%s: No outlines available", self.file_name)
156
- return None, None, None
156
+ return None, None, None # Return empty data for all three return values
157
157
 
158
158
  # If we have outlines, proceed with processing
159
159
  for id_x, room_id in enumerate(unsorted_id):
@@ -231,7 +231,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
231
231
  e,
232
232
  exc_info=True,
233
233
  )
234
- return None, None, None
234
+ return None, None, None # Return empty data in case of error
235
235
 
236
236
  async def get_image_from_rrm(
237
237
  self,
@@ -427,7 +427,7 @@ class ReImageHandler(BaseHandler, AutoCrop):
427
427
 
428
428
  async def get_rooms_attributes(
429
429
  self, destinations: JsonType = None
430
- ) -> RoomsProperties:
430
+ ) -> tuple[RoomsProperties, Any, Any]:
431
431
  """Return the rooms attributes."""
432
432
  if self.room_propriety:
433
433
  return self.room_propriety
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: valetudo-map-parser
3
- Version: 0.1.9b44
3
+ Version: 0.1.9b45
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
@@ -12,7 +12,6 @@ Classifier: Programming Language :: Python :: 3.12
12
12
  Classifier: Programming Language :: Python :: 3.13
13
13
  Requires-Dist: Pillow (>=10.3.0)
14
14
  Requires-Dist: numpy (>=1.26.4)
15
- Requires-Dist: scikit-image (>=0.22.0)
16
15
  Requires-Dist: scipy (>=1.12.0)
17
16
  Project-URL: Bug Tracker, https://github.com/sca075/Python-package-valetudo-map-parser/issues
18
17
  Project-URL: Changelog, https://github.com/sca075/Python-package-valetudo-map-parser/releases
@@ -1,9 +1,9 @@
1
1
  valetudo_map_parser/__init__.py,sha256=INujZn4exaXUjqMN8nZkGJIziDWlW59t65fJ34HJX44,956
2
2
  valetudo_map_parser/config/__init__.py,sha256=DQ9plV3ZF_K25Dp5ZQHPDoG-40dQoJNdNi-dfNeR3Zc,48
3
3
  valetudo_map_parser/config/auto_crop.py,sha256=6OvRsWzXMXBaSEvgwpaaisNdozDKiDyTmPjknFxoUMc,12624
4
- valetudo_map_parser/config/colors.py,sha256=C4hZ2bCavNvTUv-TVS6qLWWM9aDPBEEySTHhSOV95Ug,8535
4
+ valetudo_map_parser/config/colors.py,sha256=ARSijOKRY5M_o_Pm1gHODJn2WOj7QL8C2cUNR-0EKc0,9566
5
5
  valetudo_map_parser/config/colors_man.py,sha256=9b5c6XmpMzhEiunwfIjVkOk1lDyV-UFoasACdkGXfbo,7833
6
- valetudo_map_parser/config/drawable.py,sha256=RYTt38N7BjC09S5hqoubPpW8difz93bQZ9BLjFi51pg,18678
6
+ valetudo_map_parser/config/drawable.py,sha256=gKh9hSHMxqAb4phoehR_X4lembbRrTfiMuQ0wWq_AsI,21800
7
7
  valetudo_map_parser/config/drawable_elements.py,sha256=oBhmhkx1SpzTtbnpZBB2Hd5U2mOIbo7908J8oM38XiY,40841
8
8
  valetudo_map_parser/config/enhanced_drawable.py,sha256=xNgFUNccstP245VgLFEA9gjB3-VvlSAJSjRgSZ3YFL0,16641
9
9
  valetudo_map_parser/config/optimized_element_map.py,sha256=XV1xi-oa8uNTrzFUxHWF8MFP2X-jKJw3SV7HU10lWE4,15514
@@ -13,15 +13,15 @@ valetudo_map_parser/config/shared.py,sha256=WSl5rYSiTqE6YGAiwi9RILMZIQdFZRzVS8Dw
13
13
  valetudo_map_parser/config/types.py,sha256=uEJY-yYHHJWW3EZjg7hERSFrC2XuKzzRGT3C0z31Aw0,18359
14
14
  valetudo_map_parser/config/utils.py,sha256=MP5_s9VFSdDERymujvDuGB8nYCXVuJcqg5tR5H9HCgY,33167
15
15
  valetudo_map_parser/hypfer_draw.py,sha256=oL_RbX0LEcPvOlMrfBA38qpJkMqqVwR-oAEbZeHqLWM,19898
16
- valetudo_map_parser/hypfer_handler.py,sha256=ivMbcfySKk0QH9m-qWvI48TzSkHjuPnxrcTAD6WVCrE,29004
16
+ valetudo_map_parser/hypfer_handler.py,sha256=H4-vzv9cTJctq4RXin5V6L1uXy2rZYIQkd8gO4265NU,27260
17
17
  valetudo_map_parser/map_data.py,sha256=2NfZ0OO20OQPNrzditxaQV-8zSEEJclbD_CtH84HEA0,19121
18
18
  valetudo_map_parser/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- valetudo_map_parser/rand25_handler.py,sha256=2n1Eyj3hHeGZNrpqQqSQlKOmVmCVcaSJyARuIzy42g4,24535
19
+ valetudo_map_parser/rand25_handler.py,sha256=d5NXK4TH4mlkZ5dNmhqWHgL4FauEUmDFz2L8XeHsKhk,24656
20
20
  valetudo_map_parser/reimg_draw.py,sha256=1q8LkNTPHEA9Tsapc_JnVw51kpPYNhaBU-KmHkefCQY,12507
21
21
  valetudo_map_parser/utils/__init__.py,sha256=r-GKKSPqBkMDd2K-vWe7kAix8OBrGN5HXC1RS2tbDwo,130
22
22
  valetudo_map_parser/utils/color_utils.py,sha256=7t-o4oHddE5U0A9ahYBqBKmT0B2VFe_GlQ7cnLs41Us,2425
23
- valetudo_map_parser-0.1.9b44.dist-info/LICENSE,sha256=Lh-qBbuRV0-jiCIBhfV7NgdwFxQFOXH3BKOzK865hRs,10480
24
- valetudo_map_parser-0.1.9b44.dist-info/METADATA,sha256=Y-mTbBw-lK8ejHIHLM0xk-GALE6StvzF1PtJAZz1_2Q,3360
25
- valetudo_map_parser-0.1.9b44.dist-info/NOTICE.txt,sha256=5lTOuWiU9aiEnJ2go8sc7lTJ7ntMBx0g0GFnNrswCY4,2533
26
- valetudo_map_parser-0.1.9b44.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
27
- valetudo_map_parser-0.1.9b44.dist-info/RECORD,,
23
+ valetudo_map_parser-0.1.9b45.dist-info/LICENSE,sha256=Lh-qBbuRV0-jiCIBhfV7NgdwFxQFOXH3BKOzK865hRs,10480
24
+ valetudo_map_parser-0.1.9b45.dist-info/METADATA,sha256=9jDQzZXjZiha6PUbwpH9AUthH4SuY9PPXOm6mIGwSOY,3321
25
+ valetudo_map_parser-0.1.9b45.dist-info/NOTICE.txt,sha256=5lTOuWiU9aiEnJ2go8sc7lTJ7ntMBx0g0GFnNrswCY4,2533
26
+ valetudo_map_parser-0.1.9b45.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
27
+ valetudo_map_parser-0.1.9b45.dist-info/RECORD,,