implementing svg arc curves in python

  • Last Update :
  • Techknowledgy :

I think I have found some errors in your code:

theta = degrees(acos(p / n))
if uy > 0:
   theta = -theta
self.theta = theta % 360

The condition uy > 0 is wrong, correct is uy < 0 (the directed angle from (1, 0) to (ux, uy) is negative if uy < 0):

theta = degrees(acos(p / n))
if uy < 0:
   theta = -theta
self.theta = theta % 360

Then

if self.arc == self.sweep:
   angle = radians(self.theta - (self.delta * pos))
else:
   angle = radians(self.delta + (self.delta * pos))

And finally

x = sin(angle) * self.radius.real + self.center.real
y = cos(angle) * self.radius.imag + self.center.imag

Here sin and cos are mixed up, correct is

x = cos(angle) * self.radius.real + self.center.real
y = sin(angle) * self.radius.imag + self.center.imag

Suggestion : 2

A collection of tools for manipulating and analyzing SVG Path objects and Bezier curves.,svgpathtools is a collection of tools for manipulating and analyzing SVG Path objects and Bézier curves.,Additionally, the submodule bezier.py contains tools for for working with general nth order Bezier curves stored as n-tuples.,svgpathtools contains functions designed to easily read, write and display SVG files as well as a large selection of geometrically-oriented tools to transform and analyze path elements.

Setup

$ pip install svgpathtools

You can download the source from Github and install by using the command (from inside the folder containing setup.py):

$ python setup.py install

<u id="f1">1</u> Warning: Some of the functionality in this library has not been tested on discontinuous Path objects. A simple workaround is provided, however, by the Path.continuous_subpaths() method.

from __future__
import division, print_function

The Path class is a mutable sequence, so it behaves much like a list. So segments can appended, inserted, set by index, deleted, enumerated, sliced out, etc.

# Let 's append another to the end of it
path.append(CubicBezier(250 + 350 j, 275 + 350 j, 250 + 225 j, 200 + 100 j))
print(path)

# Let 's replace the first segment with a Line object
path[0] = Line(200 + 100 j, 200 + 300 j)
print(path)

# You may have noticed that this path is connected and now is also closed(i.e.path.start == path.end)
print("path is continuous? ", path.iscontinuous())
print("path is closed? ", path.isclosed())

# The curve the path follows is not, however, smooth(differentiable)
from svgpathtools
import kinks, smoothed_path
print("path contains non-differentiable points? ", len(kinks(path)) > 0)

# If we want, we can smooth these out(Experimental and only
   for line / cubic paths)
# Note: smoothing will always works(except on 180 degree turns), but you may want
# to play with the maxjointsize and tightness parameters to get pleasing results
# Note also: smoothing will increase the number of segments in a path
spath = smoothed_path(path)
print("spath contains non-differentiable points? ", len(kinks(spath)) > 0)
print(spath)

# Let 's take a quick look at the path and its smoothed relative
# The following commands will open two browser windows to display path and spaths
from svgpathtools
import disvg
from time
import sleep
disvg(path)
sleep(1) # needed when not giving the SVGs unique names(or not using timestamp)
disvg(spath)
print("Notice that path contains {} segments and spath contains {} segments."
   "".format(len(path), len(spath)))

The svg2paths() function converts an svgfile to a list of Path objects and a separate list of dictionaries containing the attributes of each said path.
Note: Line, Polyline, Polygon, and Path SVG elements can all be converted to Path objects using this function.

# Read SVG into a list of path objects and list of dictionaries of attributes
from svgpathtools
import svg2paths, wsvg
paths, attributes = svg2paths('test.svg')

# Update: You can now also extract the svg - attributes by setting
# return_svg_attributes = True, or with the convenience
function svg2paths2
from svgpathtools
import svg2paths2
paths, attributes, svg_attributes = svg2paths2('test.svg')

# Let 's print out the first path object and the color it was in the SVG
# We 'll see it is composed of two CubicBezier objects and, in the SVG file it 
# came from, it was red
redpath = paths[0]
redpath_attribs = attributes[0]
print(redpath)
print(redpath_attribs['stroke'])

Suggestion : 3

Last modified: Jun 8, 2022, by MDN contributors

M x y
   (or)
m dx dy
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">

   <path d="M10 10" />

   <!-- Points -->
   <circle cx="10" cy="10" r="2" fill="red" />

</svg>
 L x y
    (or)
 l dx dy
 H x
    (or)
 h dx

 V y
    (or)
 v dy
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">

   <path d="M 10 10 H 90 V 90 H 10 L 10 10" />

   <!-- Points -->
   <circle cx="10" cy="10" r="2" fill="red" />
   <circle cx="90" cy="90" r="2" fill="red" />
   <circle cx="90" cy="10" r="2" fill="red" />
   <circle cx="10" cy="90" r="2" fill="red" />

</svg>
 Z
    (or)
 z

Suggestion : 4

This chapter describes the syntax, behavior and DOM interfaces for SVG paths. Various implementation notes for SVG paths can be found in ‘path’ element implementation Notes., The Implementation Notes appendix has relevant formulae for software that needs to convert SVG arc notation to a format that uses center points and arc sweeps. ,Refer to the section on Out-of-range elliptical arc parameters for detailed implementation notes for the path data elliptical arc commands.,The value none indicates that there is no path data for the element. For ‘path’ elements, this means that the element does not render or contribute to the bounding box of ancestor container elements.

<?xml version="1.0" standalone="no"?>
<svg width="4cm" height="4cm" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg" version="1.1">
   <title>Example triangle01- simple example of a 'path'</title>
   <desc>A path that draws a triangle</desc>
   <rect x="1" y="1" width="398" height="398" fill="none" stroke="blue" />
   <path d="M 100 100 L 300 100 L 200 300 z" fill="red" stroke="blue" stroke-width="3" />
</svg>
<?xml version="1.0" standalone="no"?>
<svg width="5cm" height="4cm" viewBox="0 0 500 400"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <title>Example cubic01- cubic Bézier commands in path data</title>
  <desc>Picture showing a simple example of path data
        using both a "C" and an "S" command,
        along with annotations showing the control points
        and end points</desc>
  <style type="text/css"><![CDATA[
    .Border { fill:none; stroke:blue; stroke-width:1 }
    .Connect { fill:none; stroke:#888888; stroke-width:2 }
    .SamplePath { fill:none; stroke:red; stroke-width:5 }
    .EndPoint { fill:none; stroke:#888888; stroke-width:2 }
    .CtlPoint { fill:#888888; stroke:none }
    .AutoCtlPoint { fill:none; stroke:blue; stroke-width:4 }
    .Label { font-size:22; font-family:Verdana }
  ]]></style>

  <rect class="Border" x="1" y="1" width="498" height="398" />

  <polyline class="Connect" points="100,200 100,100" />
  <polyline class="Connect" points="250,100 250,200" />
  <polyline class="Connect" points="250,200 250,300" />
  <polyline class="Connect" points="400,300 400,200" />
  <path class="SamplePath" d="M100,200 C100,100 250,100 250,200
                                       S400,300 400,200" />
  <circle class="EndPoint" cx="100" cy="200" r="10" />
  <circle class="EndPoint" cx="250" cy="200" r="10" />
  <circle class="EndPoint" cx="400" cy="200" r="10" />
  <circle class="CtlPoint" cx="100" cy="100" r="10" />
  <circle class="CtlPoint" cx="250" cy="100" r="10" />
  <circle class="CtlPoint" cx="400" cy="300" r="10" />
  <circle class="AutoCtlPoint" cx="250" cy="300" r="9" />
  <text class="Label" x="25" y="70">M100,200 C100,100 250,100 250,200</text>
  <text class="Label" x="325" y="350"
        style="text-anchor:middle">S400,300 400,200</text>
</svg>
<?xml version="1.0" standalone="no"?>
<svg width="12cm" height="6cm" viewBox="0 0 1200 600" xmlns="http://www.w3.org/2000/svg" version="1.1">
   <title>Example quad01 - quadratic Bézier commands in path data</title>
   <desc>Picture showing a "Q" a "T" command,
      along with annotations showing the control points
      and end points</desc>
   <rect x="1" y="1" width="1198" height="598" fill="none" stroke="blue" stroke-width="1" />

   <path d="M200,300 Q400,50 600,300 T1000,300" fill="none" stroke="red" stroke-width="5" />
   <!-- End points -->
   <g fill="black">
      <circle cx="200" cy="300" r="10" />
      <circle cx="600" cy="300" r="10" />
      <circle cx="1000" cy="300" r="10" />
   </g>
   <!-- Control points and lines from end points to control points -->
   <g fill="#888888">
      <circle cx="400" cy="50" r="10" />
      <circle cx="800" cy="550" r="10" />
   </g>
   <path d="M200,300 L400,50 L600,300
           L800,550 L1000,300" fill="none" stroke="#888888" stroke-width="2" />
</svg>
<?xml version="1.0" standalone="no"?>
<svg width="12cm" height="5.25cm" viewBox="0 0 1200 400" xmlns="http://www.w3.org/2000/svg" version="1.1">
   <title>Example arcs01 - arc commands in path data</title>
   <desc>Picture of a pie chart with two pie wedges and
      a picture of a line with arc blips</desc>
   <rect x="1" y="1" width="1198" height="398" fill="none" stroke="blue" stroke-width="1" />

   <path d="M300,200 h-150 a150,150 0 1,0 150,-150 z" fill="red" stroke="blue" stroke-width="5" />
   <path d="M275,175 v-150 a150,150 0 0,0 -150,150 z" fill="yellow" stroke="blue" stroke-width="5" />

   <path d="M600,350 l 50,-25
           a25,25 -30 0,1 50,-25 l 50,-25
           a25,50 -30 0,1 50,-25 l 50,-25
           a25,75 -30 0,1 50,-25 l 50,-25
           a25,100 -30 0,1 50,-25 l 50,-25" fill="none" stroke="red" stroke-width="5" />
</svg>

An SVGPathElement object represents a path in the DOM.

[Exposed = Window]
interface SVGPathElement: SVGGeometryElement {};

Suggestion : 5

This example showcases the PathPatch object to create a Bezier polycurve path patch.,The use of the following functions, methods, classes and modules is shown in this example:, Demonstrates plotting contour (level) curves in 3D using the extend3d option , Demo of the histogram function's different histtype settings

import matplotlib.path as mpath
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt

Path = mpath.Path

fig, ax = plt.subplots()
pp1 = mpatches.PathPatch(
   Path([(0, 0), (1, 0), (1, 1), (0, 0)],
      [Path.MOVETO, Path.CURVE3, Path.CURVE3, Path.CLOSEPOLY]),
   fc = "none", transform = ax.transData)

ax.add_patch(pp1)
ax.plot([0.75], [0.25], "ro")
ax.set_title('The red point should be on the path')

plt.show()