8

I have an SVG file, for example, this

<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>

How can I get the list of the points (x, y) for those paths?

I have seen that answer but it's not full, so I'm asking for the full solution.

I would like to have an option to choose how many points would be there or control of the density of the points.

Lucifer
  • 83
  • 4
  • Sharing your research helps everyone. Tell us what you've tried and why it didn’t meet your needs. This demonstrates that you’ve taken the time to try to help yourself, it saves us from reiterating obvious answers, and most of all it helps you get a more specific and relevant answer! See also: [ask] – alex Sep 24 '21 at 10:59

3 Answers3

5

Here you can change the scale, offset, and density of the points:

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()

And you can also visualize those points:

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:

enter image description here

density == 0.1:

enter image description here

density == 0.5:

enter image description here

density == 1:

enter image description here

density == 5:

enter image description here

Gulbu
  • 66
  • 3
4

Let's say you want to have a total of n points. You just need to do this:

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)]

Output of the example above:

>>> 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)]

Clearly, points are complex numbers.

Riccardo Bucco
  • 13,980
  • 4
  • 22
  • 50
1

The python package is self explanatory, other answers are not addressing the problem they way they are supposed to be, basically he asked to get the points

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)
Zain Ul Abidin
  • 2,467
  • 1
  • 17
  • 29