Arc, pie cut, in svgwrite

Question:

Does any body have a working example of an arc slice (cheese slice or pac-man) done in svgwrite (python), I have tried this in an attempt to get a NorthWest quadrant strarting at (100,100) , but get a weird shape:

w = dwg.path(d="M 100,50 A 50,50 0 0 1 50,100 l 0,50 z", fill="#ffff00", stroke='none')

I have looked at actual outputs from inkscape generated svg, here is the xml equivalent:

<path
   d="M 100,50 A 50,50 0 0 1 50,100 l 0,50 z"
   style="fill:#ffff00;fill-opacity:1;fill-rule:nonzero;stroke:none"
   sodipodi_type="arc"
   sodipodi_cx="100"
   sodipodi_cy="100"
   sodipodi_rx="50"
   sodipodi_ry="50"
   sodipodi_start="4.71"
   sodipodi_end="3.14"
    />

But i am unable to a) get svgwrite to generate this b) if I take the svg file generated by svgwrite and replace this snippet of code into it, it has no effect, probably because sodipodi commands are inkscape specific.

Any one have a working example of this?

Asked By: Douglas Kastle

||

Answers:

OK after reading more documentation and looking at the link under the example

http://www.w3.org/TR/SVG11/paths.html#PathData

This is an example of a working wedge in svgwrite

    w = dwg.path(d="M0,0 v-150 a150,150 0 0,0 -150,150 z",
             fill="#00ff00", 
             stroke="none",
            )

Which gives me a northwest quadrant.

I wanted to break up my circle in to 10 degree cuts, so I wrote this code:

start_x = 250
start_y = 300
radius = 100
for i in range(36):
    degree0 = 0 + i*10
    degree1 = 10 + i*10
    radians0 = math.radians(degree0)
    radians1 = math.radians(degree1)
    dx0 = radius*(math.sin(radians0))
    dy0 = radius*(math.cos(radians0))
    dx1 = radius*(math.sin(radians1))
    dy1 = radius*(math.cos(radians1))

    m0 = dy0 
    n0 = -dx0 
    m1 = -dy0 + dy1 
    n1 = dx0 - dx1 

    w = dwg.path(d="M {0},{1} l {2},{3} a {4},{4} 0 0,0 {5},{6} z".format(start_x, start_y, m0, n0, radius, m1, n1),
             fill="#00ff00", 
             stroke="none",
            )
    elem.append(w)

Which explicitly gives me all these cuts, that can also be explictly used, if you want to run the trigonometry. This is for segements centred around (250,300) with a radius of 100.

0   : d=" M 250,300 l 100.0,-0.0 a 100,100 0 0,0 -1.5192246987791975,-17.364817766693033 z "
10  : d=" M 250,300 l 98.4807753012208,-17.364817766693033 a 100,100 0 0,0 -4.511513222629958,-16.837196565873835 z "
20  : d=" M 250,300 l 93.96926207859084,-34.20201433256687 a 100,100 0 0,0 -7.366721700146968,-15.797985667433124 z "
30  : d=" M 250,300 l 86.60254037844388,-49.99999999999999 a 100,100 0 0,0 -9.998096066546069,-14.278760968653934 z "
40  : d=" M 250,300 l 76.60444431189781,-64.27876096865393 a 100,100 0 0,0 -12.325683343243867,-12.325683343243881 z "
50  : d=" M 250,300 l 64.27876096865394,-76.60444431189781 a 100,100 0 0,0 -14.278760968653927,-9.998096066546054 z "
60  : d=" M 250,300 l 50.000000000000014,-86.60254037844386 a 100,100 0 0,0 -15.797985667433132,-7.366721700146968 z "
70  : d=" M 250,300 l 34.20201433256688,-93.96926207859083 a 100,100 0 0,0 -16.837196565873843,-4.511513222629972 z "
80  : d=" M 250,300 l 17.36481776669304,-98.4807753012208 a 100,100 0 0,0 -17.364817766693033,-1.5192246987791975 z "
90  : d=" M 250,300 l 6.123233995736766e-15,-100.0 a 100,100 0 0,0 -17.364817766693037,1.5192246987791975 z "
100 : d=" M 250,300 l -17.36481776669303,-98.4807753012208 a 100,100 0 0,0 -16.83719656587384,4.511513222629958 z "
110 : d=" M 250,300 l -34.20201433256687,-93.96926207859084 a 100,100 0 0,0 -15.79798566743311,7.366721700146968 z "
120 : d=" M 250,300 l -49.99999999999998,-86.60254037844388 a 100,100 0 0,0 -14.278760968653962,9.998096066546069 z "
130 : d=" M 250,300 l -64.27876096865394,-76.60444431189781 a 100,100 0 0,0 -12.325683343243853,12.325683343243867 z "
140 : d=" M 250,300 l -76.6044443118978,-64.27876096865394 a 100,100 0 0,0 -9.998096066546083,14.278760968653948 z "
150 : d=" M 250,300 l -86.60254037844388,-49.99999999999999 a 100,100 0 0,0 -7.366721700146954,15.797985667433103 z "
160 : d=" M 250,300 l -93.96926207859083,-34.20201433256689 a 100,100 0 0,0 -4.511513222629972,16.837196565873864 z "
170 : d=" M 250,300 l -98.4807753012208,-17.364817766693026 a 100,100 0 0,0 -1.5192246987791975,17.364817766693015 z "
180 : d=" M 250,300 l -100.0,-1.2246467991473532e-14 a 100,100 0 0,0 1.5192246987791975,17.364817766693058 z "
190 : d=" M 250,300 l -98.4807753012208,17.364817766693047 a 100,100 0 0,0 4.511513222629958,16.83719656587382 z "
200 : d=" M 250,300 l -93.96926207859084,34.20201433256687 a 100,100 0 0,0 7.366721700146982,15.797985667433146 z "
210 : d=" M 250,300 l -86.60254037844386,50.000000000000014 a 100,100 0 0,0 9.998096066546054,14.278760968653913 z "
220 : d=" M 250,300 l -76.60444431189781,64.27876096865393 a 100,100 0 0,0 12.325683343243867,12.325683343243867 z "
230 : d=" M 250,300 l -64.27876096865394,76.6044443118978 a 100,100 0 0,0 14.278760968653899,9.998096066546054 z "
240 : d=" M 250,300 l -50.00000000000004,86.60254037844385 a 100,100 0 0,0 15.797985667433188,7.3667217001469965 z "
250 : d=" M 250,300 l -34.202014332566854,93.96926207859084 a 100,100 0 0,0 16.83719656587382,4.511513222629958 z "
260 : d=" M 250,300 l -17.364817766693033,98.4807753012208 a 100,100 0 0,0 17.364817766693015,1.5192246987791975 z "
270 : d=" M 250,300 l -1.8369701987210297e-14,100.0 a 100,100 0 0,0 17.364817766693015,-1.5192246987791833 z "
280 : d=" M 250,300 l 17.364817766692997,98.48077530122082 a 100,100 0 0,0 16.8371965658739,-4.511513222629986 z "
290 : d=" M 250,300 l 34.2020143325669,93.96926207859083 a 100,100 0 0,0 15.797985667433117,-7.366721700146968 z "
300 : d=" M 250,300 l 50.000000000000014,86.60254037844386 a 100,100 0 0,0 14.278760968653913,-9.998096066546054 z "
310 : d=" M 250,300 l 64.27876096865393,76.60444431189781 a 100,100 0 0,0 12.325683343243853,-12.325683343243853 z "
320 : d=" M 250,300 l 76.60444431189778,64.27876096865396 a 100,100 0 0,0 9.998096066546054,-14.278760968653913 z "
330 : d=" M 250,300 l 86.60254037844383,50.00000000000004 a 100,100 0 0,0 7.366721700147011,-15.797985667433181 z "
340 : d=" M 250,300 l 93.96926207859084,34.20201433256686 a 100,100 0 0,0 4.511513222629958,-16.83719656587382 z "
350 : d=" M 250,300 l 98.4807753012208,17.36481776669304 a 100,100 0 0,0 1.5192246987791975,-17.364817766693015 z "
Answered By: Douglas Kastle

Thanks Douglas! I turned your arc-drawing answer into a function, in case it helps others:

def addArc(dwg, current_group, p0, p1, radius):
    """ Adds an arc that bulges to the right as it moves from p0 to p1 """
    args = {'x0':p0[0], 
        'y0':p0[1], 
        'xradius':radius, 
        'yradius':radius, 
        'ellipseRotation':0, #has no effect for circles
        'x1':(p1[0]-p0[0]), 
        'y1':(p1[1]-p0[1])}
    current_group.add(dwg.path(d="M %(x0)f,%(y0)f a %(xradius)f,%(yradius)f %(ellipseRotation)f 0,0 %(x1)f,%(y1)f"%args,
             fill="none", 
             stroke='red', stroke_width=line_stroke_width
            ))


# usage example:
import svgwrite
dwg = svgwrite.Drawing(filename="test.svg", debug=True, size=(4000,1000))
current_group = dwg.add(dwg.g(id=name, stroke='red', stroke_width=3, fill='none', fill_opacity=0 ))
addArc(dwg, current_group, p0=[10,10], p1=[40,10], radius=80)
dwg.save()

Obviously you can follow the above arc-drawing function with dwg.line() to draw pie slices or whatnot.

Answered By: Luke

Here’s another implementation of a function that uses similar arguments that those given in cv2.ellipse. Maybe it is useful for someone 🙂

import math
import svgwrite


def arc(svg, position, radius, rotation, start, end, color="white"):
    x0, y0 = position[0] + radius, position[1]
    x1, y1 = position[0] + radius, position[1]
    rad_start = math.radians(start % 360)
    rad_end = math.radians(end % 360)
    x0 -= (1 - math.cos(rad_start)) * radius
    y0 += math.sin(rad_start) * radius
    x1 -= (1 - math.cos(rad_end)) * radius
    y1 += math.sin(rad_end) * radius

    args = {'x0': x0,
            'y0': y0,
            'x1': x1,
            'y1': y1,
            'xradius': radius,
            'yradius': radius,
            'ellipseRotation': 0,
            'swap': 1 if end > start else 0,
            'large': 1 if abs(start - end) > 180 else 0,
    }

    # 'a/A' params: (rx,ry x-axis-rotation large-arc-flag,sweep-flag x,y)+ (case dictates relative/absolute pos)
    path = """M %(x0)f,%(y0)f
              A %(xradius)f,%(yradius)f %(ellipseRotation)f %(large)d,%(swap)d %(x1)f,%(y1)f
    """ % args
    arc = svg.path(d=path, fill="none", stroke=color, stroke_width=3)
    arc.rotate(rotation, position)
    svg.add(arc)

    # start/end points, just for reference
    svg.add(svg.circle((x0, y0), r=2, stroke="green", fill="green"))
    svg.add(svg.circle((x1, y1), r=2, stroke="red", fill="red"))


# test it
svg = svgwrite.Drawing(filename="test.svg", size=(300, 300))
p0 = (150, 150)

svg.add(svg.rect((100, 100), (100, 100), stroke="orange", stroke_width=1, fill="none"))
svg.add(svg.circle(p0, r=2, stroke="orange", fill="orange"))
arc(svg, p0, 50, 0, 0, 210, color="black")
svg.save()

Which should give you something like this:

'arc' from 0 to 210 degrees

Answered By: oscarah
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.