valetudo-map-parser 0.1.9b44__py3-none-any.whl → 0.1.9b46__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.
@@ -13,11 +13,11 @@ import asyncio
13
13
  import logging
14
14
  import math
15
15
 
16
- # cv2 is imported but not used directly in this file
17
- # It's needed for other modules that import from here
18
16
  import numpy as np
19
17
  from PIL import ImageDraw, ImageFont
18
+ from scipy import ndimage
20
19
 
20
+ from .colors import ColorsManagement
21
21
  from .types import Color, NumpyArray, PilPNG, Point, Tuple, Union
22
22
 
23
23
 
@@ -174,11 +174,50 @@ 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
+
181
191
  x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
192
+
193
+ # Sample background color at the endpoints and blend with foreground color if requested
194
+ if blend:
195
+ # Sample at start point
196
+ if 0 <= y1 < layer.shape[0] and 0 <= x1 < layer.shape[1]:
197
+ start_blended_color = ColorsManagement.sample_and_blend_color(
198
+ layer, x1, y1, color
199
+ )
200
+ else:
201
+ start_blended_color = color
202
+
203
+ # Sample at end point
204
+ if 0 <= y2 < layer.shape[0] and 0 <= x2 < layer.shape[1]:
205
+ end_blended_color = ColorsManagement.sample_and_blend_color(
206
+ layer, x2, y2, color
207
+ )
208
+ else:
209
+ end_blended_color = color
210
+
211
+ # Use the average of the two blended colors
212
+ blended_color = (
213
+ (start_blended_color[0] + end_blended_color[0]) // 2,
214
+ (start_blended_color[1] + end_blended_color[1]) // 2,
215
+ (start_blended_color[2] + end_blended_color[2]) // 2,
216
+ (start_blended_color[3] + end_blended_color[3]) // 2,
217
+ )
218
+ else:
219
+ blended_color = color
220
+
182
221
  dx = abs(x2 - x1)
183
222
  dy = abs(y2 - y1)
184
223
  sx = 1 if x1 < x2 else -1
@@ -189,7 +228,7 @@ class Drawable:
189
228
  for i in range(-width // 2, (width + 1) // 2):
190
229
  for j in range(-width // 2, (width + 1) // 2):
191
230
  if 0 <= x1 + i < layer.shape[1] and 0 <= y1 + j < layer.shape[0]:
192
- layer[y1 + j, x1 + i] = color
231
+ layer[y1 + j, x1 + i] = blended_color
193
232
  if x1 == x2 and y1 == y2:
194
233
  break
195
234
  e2 = 2 * err
@@ -220,16 +259,38 @@ class Drawable:
220
259
  """
221
260
  Join the coordinates creating a continuous line (path).
222
261
  """
223
- import logging
224
- _LOGGER = logging.getLogger(__name__)
225
- _LOGGER.debug("Drawing lines with %d coordinates, width %d, color %s", len(coords), width, color)
262
+
226
263
  for coord in coords:
227
264
  x0, y0 = coord[0]
228
265
  try:
229
266
  x1, y1 = coord[1]
230
267
  except IndexError:
231
268
  x1, y1 = x0, y0
232
- _LOGGER.debug("Drawing line from (%d, %d) to (%d, %d)", x0, y0, x1, y1)
269
+
270
+ # Sample background color at the endpoints and blend with foreground color
271
+ # This is more efficient than sampling at every pixel
272
+ if 0 <= y0 < arr.shape[0] and 0 <= x0 < arr.shape[1]:
273
+ start_blended_color = ColorsManagement.sample_and_blend_color(
274
+ arr, x0, y0, color
275
+ )
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 = ColorsManagement.sample_and_blend_color(
281
+ arr, x1, y1, color
282
+ )
283
+ else:
284
+ end_blended_color = color
285
+
286
+ # Use the average of the two blended colors
287
+ blended_color = (
288
+ (start_blended_color[0] + end_blended_color[0]) // 2,
289
+ (start_blended_color[1] + end_blended_color[1]) // 2,
290
+ (start_blended_color[2] + end_blended_color[2]) // 2,
291
+ (start_blended_color[3] + end_blended_color[3]) // 2,
292
+ )
293
+
233
294
  dx = abs(x1 - x0)
234
295
  dy = abs(y1 - y0)
235
296
  sx = 1 if x0 < x1 else -1
@@ -252,8 +313,9 @@ class Drawable:
252
313
  x, y = pixel
253
314
  for i in range(width):
254
315
  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
316
+ px, py = x + i, y + j
317
+ if 0 <= px < arr.shape[1] and 0 <= py < arr.shape[0]:
318
+ arr[py, px] = blended_color
257
319
  return arr
258
320
 
259
321
  @staticmethod
@@ -360,8 +422,9 @@ class Drawable:
360
422
  @staticmethod
361
423
  async def zones(layers: NumpyArray, coordinates, color: Color) -> NumpyArray:
362
424
  """
363
- Draw the zones on the input layer.
425
+ Draw the zones on the input layer with color blending.
364
426
  """
427
+
365
428
  dot_radius = 1 # Number of pixels for the dot
366
429
  dot_spacing = 4 # Space between dots
367
430
  for zone in coordinates:
@@ -370,10 +433,26 @@ class Drawable:
370
433
  max_x = max(points[::2])
371
434
  min_y = min(points[1::2])
372
435
  max_y = max(points[1::2])
436
+
437
+ # Sample a point from the zone to get the background color
438
+ # Use the center of the zone for sampling
439
+ sample_x = (min_x + max_x) // 2
440
+ sample_y = (min_y + max_y) // 2
441
+
442
+ # Blend the color with the background color at the sample point
443
+ if 0 <= sample_y < layers.shape[0] and 0 <= sample_x < layers.shape[1]:
444
+ blended_color = ColorsManagement.sample_and_blend_color(
445
+ layers, sample_x, sample_y, color
446
+ )
447
+ else:
448
+ blended_color = color
449
+
373
450
  for y in range(min_y, max_y, dot_spacing):
374
451
  for x in range(min_x, max_x, dot_spacing):
375
452
  for _ in range(dot_radius):
376
- layers = Drawable._ellipse(layers, (x, y), dot_radius, color)
453
+ layers = Drawable._ellipse(
454
+ layers, (x, y), dot_radius, blended_color
455
+ )
377
456
  return layers
378
457
 
379
458
  @staticmethod
@@ -468,7 +547,7 @@ class Drawable:
468
547
 
469
548
  @staticmethod
470
549
  async def async_draw_obstacles(
471
- image: np.ndarray, obstacle_info_list, color: Tuple[int, int, int, int]
550
+ image: np.ndarray, obstacle_info_list, color: Color
472
551
  ) -> np.ndarray:
473
552
  """
474
553
  Optimized async version of draw_obstacles using asyncio.gather().
@@ -518,3 +597,60 @@ class Drawable:
518
597
  else:
519
598
  draw.text((x, y), text, font=font, fill=color)
520
599
  x += draw.textlength(text, font=default_font)
600
+
601
+ @staticmethod
602
+ def draw_filled_polygon(image_array, vertices, color):
603
+ """
604
+ Draw a filled polygon on the image array using scipy.ndimage.
605
+
606
+ Args:
607
+ image_array: NumPy array of shape (height, width, 4) containing RGBA image data
608
+ vertices: List of (x,y) tuples defining the polygon vertices
609
+ color: RGBA color tuple to fill the polygon with
610
+
611
+ Returns:
612
+ Modified image array with the filled polygon
613
+ """
614
+ if len(vertices) < 3:
615
+ return image_array # Need at least 3 points for a polygon
616
+
617
+ height, width = image_array.shape[:2]
618
+
619
+ # Create a mask for the polygon
620
+ polygon_mask = np.zeros((height, width), dtype=bool)
621
+
622
+ # Convert vertices to numpy arrays for processing
623
+ vertices_array = np.array(vertices)
624
+ x_coords = vertices_array[:, 0]
625
+ y_coords = vertices_array[:, 1]
626
+
627
+ # Clip coordinates to image boundaries
628
+ x_coords = np.clip(x_coords, 0, width - 1)
629
+ y_coords = np.clip(y_coords, 0, height - 1)
630
+
631
+ # Create a polygon using scipy.ndimage
632
+ # First create the boundary
633
+ for i in range(len(vertices)):
634
+ x1, y1 = int(x_coords[i]), int(y_coords[i])
635
+ x2, y2 = (
636
+ int(x_coords[(i + 1) % len(vertices)]),
637
+ int(y_coords[(i + 1) % len(vertices)]),
638
+ )
639
+
640
+ # Draw line between consecutive vertices
641
+ length = max(abs(x2 - x1), abs(y2 - y1)) * 2
642
+ if length == 0:
643
+ continue
644
+
645
+ t = np.linspace(0, 1, length)
646
+ x = np.round(x1 * (1 - t) + x2 * t).astype(int)
647
+ y = np.round(y1 * (1 - t) + y2 * t).astype(int)
648
+
649
+ # Add boundary points to mask
650
+ polygon_mask[y, x] = True
651
+
652
+ # Fill the polygon using scipy.ndimage.binary_fill_holes
653
+ filled_polygon = ndimage.binary_fill_holes(polygon_mask)
654
+
655
+ # Apply color to the filled polygon
656
+ return ColorsManagement.batch_blend_colors(image_array, filled_polygon, color)