Skip to content

Commit b759c45

Browse files
sca075Sandro Cantarella
andauthored
Simplify Drawing with new MCVrender (#16)
* change local _LOGGER to LOGGER and shared.py optimized parsed zone clean tested for rand. Signed-off-by: Sandro Cantarella <sandro@Sandros-Mac-mini.fritz.box> * typing and other parser touches. Signed-off-by: Sandro Cantarella <sandro@Sandros-Mac-mini.fritz.box> * bump mvcrender and change the async to sync usage. Signed-off-by: Sandro Cantarella <sandro@Sandros-Mac-mini.fritz.box> * bump mvcrender isort and ruff and replace blending and some drawing function with C based from mcvrender. Signed-off-by: Sandro Cantarella <sandro@509dc541-f930-4e46-87da-4482e9d9a7bb.fritz.box> * corrections done Signed-off-by: Sandro Cantarella <sandro@13de464c-e6f5-4665-b8d7-d6acf4207b24.fritz.box> * corrections duplicate logger Signed-off-by: Sandro Cantarella <sandro@13de464c-e6f5-4665-b8d7-d6acf4207b24.fritz.box> --------- Signed-off-by: Sandro Cantarella <sandro@Sandros-Mac-mini.fritz.box> Signed-off-by: Sandro Cantarella <sandro@509dc541-f930-4e46-87da-4482e9d9a7bb.fritz.box> Signed-off-by: Sandro Cantarella <sandro@13de464c-e6f5-4665-b8d7-d6acf4207b24.fritz.box> Co-authored-by: Sandro Cantarella <sandro@13de464c-e6f5-4665-b8d7-d6acf4207b24.fritz.box>
1 parent 3bfa659 commit b759c45

File tree

16 files changed

+175
-537
lines changed

16 files changed

+175
-537
lines changed

SCR/valetudo_map_parser/__init__.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,26 @@
66
from .config.colors import ColorsManagement
77
from .config.drawable import Drawable
88
from .config.drawable_elements import DrawableElement, DrawingConfig
9-
from .config.enhanced_drawable import EnhancedDrawable
109
from .config.rand256_parser import RRMapParser
1110
from .config.shared import CameraShared, CameraSharedManager
11+
from .config.status_text.status_text import StatusText
12+
from .config.status_text.translations import translations as STATUS_TEXT_TRANSLATIONS
1213
from .config.types import (
1314
CameraModes,
15+
ImageSize,
16+
JsonType,
17+
NumpyArray,
18+
PilPNG,
1419
RoomsProperties,
1520
RoomStore,
1621
SnapshotStore,
1722
TrimCropData,
1823
UserLanguageStore,
19-
JsonType,
20-
PilPNG,
21-
NumpyArray,
22-
ImageSize,
2324
)
24-
from .config.status_text.status_text import StatusText
25-
from .config.status_text.translations import translations as STATUS_TEXT_TRANSLATIONS
2625
from .hypfer_handler import HypferMapImageHandler
27-
from .rand256_handler import ReImageHandler
28-
from .rooms_handler import RoomsHandler, RandRoomsHandler
2926
from .map_data import HyperMapData
27+
from .rand256_handler import ReImageHandler
28+
from .rooms_handler import RandRoomsHandler, RoomsHandler
3029

3130

3231
def get_default_font_path() -> str:
@@ -51,7 +50,6 @@ def get_default_font_path() -> str:
5150
"Drawable",
5251
"DrawableElement",
5352
"DrawingConfig",
54-
"EnhancedDrawable",
5553
"SnapshotStore",
5654
"UserLanguageStore",
5755
"RoomStore",

SCR/valetudo_map_parser/config/color_utils.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
"""Utility functions for color operations in the map parser."""
22

3-
from typing import Optional, Tuple
3+
from typing import Optional
44

5-
from .colors import ColorsManagement
65
from .types import Color, NumpyArray
76

87

@@ -36,8 +35,8 @@ def get_blended_color(
3635
# Sample background at midpoint
3736
mid_x, mid_y = (x0 + x1) // 2, (y0 + y1) // 2
3837
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
38+
return Color(arr[mid_y, mid_x])
39+
return Color(0, 0, 0, 0) # Default if out of bounds
4140

4241
# Calculate direction vector for offset sampling
4342
dx = x1 - x0

SCR/valetudo_map_parser/config/drawable.py

Lines changed: 36 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
from pathlib import Path
1515

1616
import numpy as np
17+
from mvcrender.blend import get_blended_color, sample_and_blend_color
18+
from mvcrender.draw import circle_u8, line_u8
1719
from PIL import Image, ImageDraw, ImageFont
1820

19-
from .color_utils import get_blended_color
20-
from .colors import ColorsManagement
2121
from .types import Color, NumpyArray, PilPNG, Point, Tuple, Union
2222

2323

@@ -85,7 +85,7 @@ async def from_json_to_image(
8585
and 0 <= center_x < image_array.shape[1]
8686
):
8787
# Get blended color
88-
blended_color = ColorsManagement.sample_and_blend_color(
88+
blended_color = sample_and_blend_color(
8989
image_array, center_x, center_y, full_color
9090
)
9191
# Apply blended color to the region
@@ -131,9 +131,7 @@ async def battery_charger(
131131
center_x = (start_col + end_col) // 2
132132

133133
# Get blended color
134-
blended_color = ColorsManagement.sample_and_blend_color(
135-
layers, center_x, center_y, color
136-
)
134+
blended_color = sample_and_blend_color(layers, center_x, center_y, color)
137135

138136
# Apply blended color
139137
layers[start_row:end_row, start_col:end_col] = blended_color
@@ -165,9 +163,7 @@ async def go_to_flag(
165163

166164
# Blend flag color if needed
167165
if flag_alpha < 255:
168-
flag_color = ColorsManagement.sample_and_blend_color(
169-
layer, x, y, flag_color
170-
)
166+
flag_color = sample_and_blend_color(layer, x, y, flag_color)
171167

172168
# Create pole color with alpha
173169
pole_color: Color = (
@@ -179,9 +175,7 @@ async def go_to_flag(
179175

180176
# Blend pole color if needed
181177
if pole_alpha < 255:
182-
pole_color = ColorsManagement.sample_and_blend_color(
183-
layer, x, y, pole_color
184-
)
178+
pole_color = sample_and_blend_color(layer, x, y, pole_color)
185179

186180
flag_size = 50
187181
pole_width = 6
@@ -246,62 +240,19 @@ def point_inside(x: int, y: int, points: list[Tuple[int, int]]) -> bool:
246240

247241
@staticmethod
248242
def _line(
249-
layer: np.ndarray,
243+
layer: NumpyArray,
250244
x1: int,
251245
y1: int,
252246
x2: int,
253247
y2: int,
254248
color: Color,
255249
width: int = 3,
256-
) -> np.ndarray:
257-
"""Draw a line on a NumPy array (layer) from point A to B using Bresenham's algorithm.
258-
259-
Args:
260-
layer: The numpy array to draw on (H, W, C)
261-
x1, y1: Start point coordinates
262-
x2, y2: End point coordinates
263-
color: Color to draw with (tuple or array)
264-
width: Width of the line in pixels
265-
"""
266-
x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
267-
268-
blended_color = get_blended_color(x1, y1, x2, y2, layer, color)
269-
270-
dx = abs(x2 - x1)
271-
dy = abs(y2 - y1)
272-
sx = 1 if x1 < x2 else -1
273-
sy = 1 if y1 < y2 else -1
274-
err = dx - dy
275-
276-
half_w = width // 2
277-
h, w = layer.shape[:2]
278-
279-
while True:
280-
# Draw a filled circle for thickness
281-
yy, xx = np.ogrid[-half_w : half_w + 1, -half_w : half_w + 1]
282-
mask = xx**2 + yy**2 <= half_w**2
283-
y_min = max(0, y1 - half_w)
284-
y_max = min(h, y1 + half_w + 1)
285-
x_min = max(0, x1 - half_w)
286-
x_max = min(w, x1 + half_w + 1)
287-
288-
sub_mask = mask[
289-
(y_min - (y1 - half_w)) : (y_max - (y1 - half_w)),
290-
(x_min - (x1 - half_w)) : (x_max - (x1 - half_w)),
291-
]
292-
layer[y_min:y_max, x_min:x_max][sub_mask] = blended_color
293-
294-
if x1 == x2 and y1 == y2:
295-
break
296-
297-
e2 = 2 * err
298-
if e2 > -dy:
299-
err -= dy
300-
x1 += sx
301-
if e2 < dx:
302-
err += dx
303-
y1 += sy
304-
250+
) -> NumpyArray:
251+
"""Segment-aware preblend, then stamp a solid line."""
252+
width = int(max(1, width))
253+
# Preblend once for this segment
254+
seg = get_blended_color(int(x1), int(y1), int(x2), int(y2), layer, color)
255+
line_u8(layer, int(x1), int(y1), int(x2), int(y2), seg, width)
305256
return layer
306257

307258
@staticmethod
@@ -337,11 +288,8 @@ async def lines(
337288
if x0 == x1 and y0 == y1:
338289
continue
339290

340-
# Get blended color for this line segment
341-
blended_color = get_blended_color(x0, y0, x1, y1, arr, color)
342-
343291
# Use the optimized line drawing method
344-
arr = Drawable._line(arr, x0, y0, x1, y1, blended_color, width)
292+
arr = Drawable._line(arr, x0, y0, x1, y1, color, width)
345293

346294
return arr
347295

@@ -355,35 +303,31 @@ def _filled_circle(
355303
outline_width: int = 0,
356304
) -> NumpyArray:
357305
"""
358-
Draw a filled circle on the image using NumPy.
359-
Optimized to only process the bounding box of the circle.
306+
Draw a filled circle and optional outline using mvcrender.draw.circle_u8.
307+
If alpha<255, preblend once at the center and stamp solid.
360308
"""
361-
y, x = center
362-
height, width = image.shape[:2]
363-
364-
# Calculate the bounding box of the circle
365-
min_y = max(0, y - radius - outline_width)
366-
max_y = min(height, y + radius + outline_width + 1)
367-
min_x = max(0, x - radius - outline_width)
368-
max_x = min(width, x + radius + outline_width + 1)
369-
370-
# Create coordinate arrays for the bounding box
371-
y_indices, x_indices = np.ogrid[min_y:max_y, min_x:max_x]
372-
373-
# Calculate distances from center
374-
dist_sq = (y_indices - y) ** 2 + (x_indices - x) ** 2
309+
cy, cx = (
310+
int(center[0]),
311+
int(center[1]),
312+
) # incoming Point is (y,x) in your codebase
313+
h, w = image.shape[:2]
314+
if not (0 <= cx < w and 0 <= cy < h):
315+
return image
375316

376-
# Create masks for the circle and outline
377-
circle_mask = dist_sq <= radius**2
317+
fill_rgba = color
318+
if fill_rgba[3] < 255:
319+
fill_rgba = sample_and_blend_color(image, cx, cy, fill_rgba)
378320

379-
# Apply the fill color
380-
image[min_y:max_y, min_x:max_x][circle_mask] = color
321+
circle_u8(image, int(cx), int(cy), int(radius), fill_rgba, -1)
381322

382-
# Draw the outline if needed
383-
if outline_width > 0 and outline_color is not None:
384-
outer_mask = dist_sq <= (radius + outline_width) ** 2
385-
outline_mask = outer_mask & ~circle_mask
386-
image[min_y:max_y, min_x:max_x][outline_mask] = outline_color
323+
if outline_color is not None and outline_width > 0:
324+
out_rgba = outline_color
325+
if out_rgba[3] < 255:
326+
out_rgba = sample_and_blend_color(image, cx, cy, out_rgba)
327+
# outlined stroke thickness = outline_width
328+
circle_u8(
329+
image, int(cx), int(cy), int(radius), out_rgba, int(outline_width)
330+
)
387331

388332
return image
389333

@@ -835,9 +779,7 @@ async def async_draw_obstacles(
835779
continue
836780

837781
if need_blending:
838-
obs_color = ColorsManagement.sample_and_blend_color(
839-
image, x, y, color
840-
)
782+
obs_color = sample_and_blend_color(image, x, y, color)
841783
else:
842784
obs_color = color
843785

SCR/valetudo_map_parser/config/drawable_elements.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
from enum import IntEnum
1010
from typing import Dict, List, Tuple, Union
1111

12-
import numpy as np
13-
1412
from .colors import DefaultColors, SupportedColor
1513
from .types import LOGGER
1614

0 commit comments

Comments
 (0)