How to Properly Render Complex SVG Paths with Holes and Nested Regions in Pygame?

I am trying to visualize a map from an SVG file using Pygame, but I'm facing an issue where countries with complex paths (such as those with nested areas or holes) are not being filled correctly. The outer borders are being drawn fine, but the inner areas (holes) are not respected, and the filling is not being rendered as expected.



import pygame

from svgpathtools import parse_path

from svgpathtools import svg2paths2

import re



DEFAULT_FILL = (200, 200, 200)  

DEFAULT_STROKE = (0, 0, 0)

class_styles = {

    "st0": {"fill": "#CFE5E8", "stroke": None, "stroke_width": None},

    "st1": {"fill": "#FFEB64", "stroke": "#808080", "stroke_width": 1},

    "st2": {"fill": "#BACE6B", "stroke": "#808080", "stroke_width": 1},

    "st3": {"fill": "#89B8E4", "stroke": "#808080", "stroke_width": 1},

    "st4": {"fill": "#AC8FC7", "stroke": "#808080", "stroke_width": 1},

    "st5": {"fill": "#8EC79A", "stroke": "#808080", "stroke_width": 1},

    "st6": {"fill": "#E89BBD", "stroke": "#808080", "stroke_width": 1},

    "st7": {"fill": "#F8A758", "stroke": "#808080", "stroke_width": 1},

    "st8": {"fill": "#FBD165", "stroke": "#808080", "stroke_width": 1},

    "st9": {"fill": "#F9F9F9", "stroke": "#808080", "stroke_width": 1}

}



def hex_to_rgb(hex_color):

    hex_color = hex_color.lstrip('#')

    return tuple(int(hex_color[i:i + 2], 16) for i in (0, 2, 4))





def path_to_points(d):

    path = parse_path(d)

    points = []

    for segment in path:

        points.append((int(segment.start.real), int(segment.start.imag)))

    return points





def split_paths(d):

    """Разбивает путь d на отдельные контуры по команде 'M'."""

    sub_paths = re.split(r'(?=M)', d)

    return [path.strip() for path in sub_paths if path.strip()]





def parse_svg_with_svgpathtools(file_path):

    paths, attributes, svg_attributes = svg2paths2(file_path)

    countries = {}

    for attr in attributes:

        country_id = attr.get('id')

        d = attr.get('d')

        class_name = attr.get('class')

        if country_id and d:

            countries[country_id] = {

                "d": d,

                "class": class_name

            }

    return countries





def draw_country(country_data, surface):

    d = country_data["d"]

    class_name = country_data.get("class", "")

    fill = class_styles.get(class_name, {}).get("fill", "#cccccc")

    stroke = class_styles.get(class_name, {}).get("stroke", "#000000")

    stroke_width = class_styles.get(class_name, {}).get("stroke_width", 1)



    fill_color = hex_to_rgb(fill) if fill else DEFAULT_FILL

    stroke_color = hex_to_rgb(stroke) if stroke else DEFAULT_STROKE



    sub_paths = split_paths(d)



    for sub_path in sub_paths:

        points = path_to_points(sub_path)



        if len(points) > 2:

            pygame.draw.polygon(surface, fill_color, points)



        pygame.draw.lines(surface, stroke_color, closed=True, points=points, width=stroke_width)





# Main program

svg_file = 'wrld-21.svg'

countries_data = parse_svg_with_svgpathtools(svg_file)



pygame.init()

info = pygame.display.Info()

WIDTH, HEIGHT = info.current_w // 2, info.current_h // 2

screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.RESIZABLE)

pygame.display.set_caption("Map")



running = True

while running:

    for event in pygame.event.get():

        if event.type == pygame.QUIT:

            running = False



    screen.fill((255, 255, 255))



    for country, data in countries_data.items():

        draw_country(data, screen)



    pygame.display.flip()



pygame.quit()

incorrect and correct fill [enter image description here](https://i.sstatic.net/md3zkFED.jpg)

At first, I thought the borders were being incorrectly copied from the file, but it turned out that the borders are being copied correctly. The issue lies in the incorrect filling.

Upvotes: 0

Views: 24

Answers (0)

Related Questions