如何获取 svg 路径的点

How to get points of the svg paths

我有一个 SVG 文件,例如这个

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
  <path fill="none" stroke="red"
    d="M 10,30
       A 20,20 0,0,1 50,30
       A 20,20 0,0,1 90,30
       Q 90,60 50,90
       Q 10,60 10,30 z" />
</svg>

如何获取这些路径的点 (x, y) 列表?

我已经看到 that 答案,但它还不够完整,所以我要求完整的解决方案。

我想要一个选项来选择有多少个点或控制点的密度。

假设您想要总共获得 n 分。你只需要这样做:

import numpy as np
import svg.path

n = 10
path = '''M 10,30
A 20,20 0,0,1 50,30
A 20,20 0,0,1 90,30
Q 90,60 50,90
Q 10,60 10,30 z'''

pp = svg.path.parse_path(path)
points = [pp.point(pos) for pos in np.linspace(0, 1, n)]

上面例子的输出:

>>> points
[(10+30j),
 (29.151135102977165+10.01802241051696j),
 (49.92794283866024+28.30380006511909j),
 (67.45952198841553+10.162006868822246j),
 (89.712290578091+26.619822465475373j),
 (84.71079732451658+51.81807142699621j),
 (66.11578778154251+76.36355357024713j),
 (36.776919717540096+79.09096428650194j),
 (16.694229929385042+54.545482143251j),
 (10+30j)]

显然,点是复数。

在这里您可以更改点的比例、偏移和密度:

from svg.path import parse_path
from xml.dom import minidom


def get_point_at(path, distance, scale, offset):
    pos = path.point(distance)
    pos += offset
    pos *= scale
    return pos.real, pos.imag


def points_from_path(path, density, scale, offset):
    step = int(path.length() * density)
    last_step = step - 1

    if last_step == 0:
        yield get_point_at(path, 0, scale, offset)
        return

    for distance in range(step):
        yield get_point_at(
            path, distance / last_step, scale, offset)


def points_from_doc(doc, density=5, scale=1, offset=0):
    offset = offset[0] + offset[1] * 1j
    points = []
    for element in doc.getElementsByTagName("path"):
        for path in parse_path(element.getAttribute("d")):
            points.extend(points_from_path(
                path, density, scale, offset))

    return points


string = """<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
    <path fill="none" stroke="red"
        d="M 10,30
            A 20,20 0,0,1 50,30
            A 20,20 0,0,1 90,30
            Q 90,60 50,90
            Q 10,60 10,30 z" />
</svg>"""

doc = minidom.parseString(string)
points = points_from_doc(doc, density=1, scale=5, offset=(0, 5))
doc.unlink()

你还可以想象那些点:

import pygame
from svg.path import parse_path
from xml.dom import minidom


...  # other functions and string


def main():
    screen = pygame.display.set_mode([500, 500])
    screen.fill((255, 255, 255))

    doc = minidom.parseString(string)
    points = points_from_doc(doc, 0.05, 5, (0, 5))
    doc.unlink()

    for point in points:
        pygame.draw.circle(screen, (0, 0, 255), point, 1)

    pygame.display.flip()

    while 1:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return


pygame.init()
main()
pygame.quit()

density == 0.05:

density == 0.1:

density == 0.5:

density == 1:

density == 5:

python 包是不言自明的,其他答案并没有按照他们应该的方式解决问题,基本上他要求得到分数

from xml.dom import minidom
from svg.path import parse_path

with open('test.txt') as f:
    content = '\n'.join([line.strip() for line in f.readlines()])
    svg_dom = minidom.parseString(content)

    path_strings = [path.getAttribute('d') for path in svg_dom.getElementsByTagName('path')]

    for path_string in path_strings:
        path_data = parse_path(path_string)
        print(path_data.d())
        #it prints all data in single string
        #prints M 10,30 A 20,20 0 0,1 50,30 A 20,20 0 0,1 90,30 Q 90,60 50,90 Q 10,60 10,30 Z
        
        for p in path_data:
            #but you can always iterate to get even deep control
            print(p.start, p.end) #prints only the star and end points of each path
            #e.g. (10+30j) (10+30j)