Programmatically generate SVG (vector) images, animations, and interactive Jupyter widgets

Add a readme and license

+8
LICENSE.txt
···
+
Copyright 2017 Casey Duckering
+
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+154
README.md
···
+
# drawSvg
+
+
This is a Python 3 library for programmatically generating SVG images (vector drawings) and rendering them or displaying them in an iPython notebook.
+
+
Most common SVG tags are supported and others can easily be added by writing a small subclass of `DrawableBasicElement` or `DrawableParentElement`.
+
+
# Install
+
drawSvg is available on PyPI:
+
+
```
+
$ pip3 install drawSvg
+
```
+
+
# Examples
+
+
### Basic drawing elements
+
```python
+
d = draw.Drawing(200, 100, origin='center')
+
+
d.append(draw.Lines(-80, -45,
+
70, -49,
+
95, 49,
+
-90, 40,
+
close=False,
+
fill='#eeee00',
+
stroke='black'))
+
+
d.append(draw.Rectangle(0,0,40,50, fill='#1248ff'))
+
d.append(draw.Circle(-40, -10, 30,
+
fill='red', stroke_width=2, stroke='black'))
+
+
p = draw.Path(stroke_width=2, stroke='green',
+
fill='black', fill_opacity=0.5)
+
p.M(-30,5) # Start path at point (-30, 5)
+
p.l(60,30) # Draw line to (60, 30)
+
p.h(-70) # Draw horizontal line to x=-70
+
p.Z() # Draw line to start
+
d.append(p)
+
+
d.append(draw.ArcLine(60,-20,20,60,270,
+
stroke='red', stroke_width=5, fill='red', fill_opacity=0.2))
+
d.append(draw.Arc(60,-20,20,60,270,cw=False,
+
stroke='green', stroke_width=3, fill='none'))
+
d.append(draw.Arc(60,-20,20,270,60,cw=True,
+
stroke='blue', stroke_width=1, fill='black', fill_opacity=0.3))
+
+
d.setPixelScale(2) # Set number of pixels per geometry unit
+
#d.setRenderSize(400,200) # Alternative to setPixelScale
+
d.saveSvg('example.svg')
+
d.savePng('example.png')
+
+
# Display in iPython notebook
+
d.rasterize() # Display as PNG
+
d # Display as SVG
+
```
+
+
![Example output image](https://github.com/cduck/drawSvg/raw/master/example1.png)
+
+
### Gradients
+
```python
+
d = draw.Drawing(1.5, 0.8, origin='center')
+
+
d.draw(draw.Rectangle(-0.75,-0.5,1.5,1, fill='#ddd'))
+
+
# Create gradient
+
gradient = draw.RadialGradient(0,-0.35,0.7*10)
+
gradient.addStop(0.5/0.7/10, 'green', 1)
+
gradient.addStop(1/10, 'red', 0)
+
+
# Draw a shape to fill with the gradient
+
p = draw.Path(fill=gradient, stroke='black', stroke_width=0.002)
+
p.arc(0,-0.35,0.7,30,120)
+
p.arc(0,-0.35,0.5,120,30,cw=True, includeL=True)
+
p.Z()
+
d.append(p)
+
+
# Draw another shape to fill with the same gradient
+
p = draw.Path(fill=gradient, stroke='red', stroke_width=0.002)
+
p.arc(0,-0.35,0.75,130,160)
+
p.arc(0,-0.35,0,160,130,cw=True, includeL=True)
+
p.Z()
+
d.append(p)
+
+
# Another gradient
+
gradient2 = draw.LinearGradient(0.1,-0.35,0.1+0.6,-0.35+0.2)
+
gradient2.addStop(0, 'green', 1)
+
gradient2.addStop(1, 'red', 0)
+
d.append(draw.Rectangle(0.1,-0.35,0.6,0.2,
+
stroke='black', stroke_width=0.002,
+
fill=gradient2))
+
+
# Display
+
d.setRenderSize(w=600)
+
d
+
```
+
+
![Example output image](https://github.com/cduck/drawSvg/raw/master/example2.png)
+
+
### Duplicate geometry and clip paths
+
```python
+
d = draw.Drawing(1.4, 1.4, origin='center')
+
+
# Define clip path
+
clip = draw.ClipPath()
+
clip.append(draw.Rectangle(-.25,.25-1,1,1))
+
+
# Draw a cropped circle
+
c = draw.Circle(0,0,0.5, stroke_width='0.01', stroke='black',
+
fill_opacity=0.3, clip_path=clip,
+
id='circle')
+
d.append(c)
+
+
# Make a transparent copy, cropped again
+
g = draw.Group(opacity=0.5, clip_path=clip)
+
g.append(draw.Use('circle', 0.25,0.1))
+
d.append(g)
+
+
# Display
+
d.setRenderSize(400)
+
d.rasterize()
+
```
+
+
![Example output image](https://github.com/cduck/drawSvg/raw/master/example3.png)
+
+
### Implementing other SVG tags
+
```python
+
# Subclass DrawingBasicElement if it cannot have child nodes
+
# Subclass DrawingParentElement otherwise
+
# Subclass DrawingDef if it must go between <def></def> tags in an SVG
+
class Hyperlink(draw.DrawingParentElement):
+
TAG_NAME = 'a'
+
def __init__(self, href, target=None, **kwargs):
+
# Other init logic...
+
# Keyword arguments to super().__init__() correspond to SVG node
+
# arguments: stroke_width=5 -> stroke-width="5"
+
super().__init__(href=href, target=target, **kwargs)
+
+
d = draw.Drawing(1, 1.2, origin='center')
+
+
# Create hyperlink
+
hlink = Hyperlink('https://www.python.org', target='_blank',
+
transform='skewY(-30)')
+
# Add child elements
+
hlink.append(draw.Circle(0,0,0.5, fill='green'))
+
hlink.append(draw.Text('Hyperlink',0.2, 0,0, center=0.6, fill='white'))
+
+
# Draw and display
+
d.append(hlink)
+
d.setRenderSize(200)
+
d
+
```
+
+
![Example output image](https://github.com/cduck/drawSvg/blob/master/example4.svg)
+
example1.png

This is a binary file and will not be displayed.

+13
example1.svg
···
+
<?xml version="1.0" encoding="UTF-8"?>
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+
width="400" height="200" viewBox="-100.0 -50.0 200 100">
+
<defs>
+
</defs>
+
<path d="M-80,45 L70,49 L95,-49 L-90,-40" fill="#eeee00" stroke="black" />
+
<rect x="0" y="-50" width="40" height="50" fill="#1248ff" />
+
<circle cx="-40" cy="10" r="30" fill="red" stroke-width="2" stroke="black" />
+
<path d="M-30,-5 l60,-30 h-70 Z" stroke-width="2" stroke="green" fill="black" fill-opacity="0.5" />
+
<circle cx="60" cy="20" r="20" stroke-dasharray="73.30382858376184 52.35987755982988" stroke-dashoffset="-31.41592653589793" stroke="red" stroke-width="5" fill="red" fill-opacity="0.2" />
+
<path d="M70.0,2.679491924311229 A20,20,0,1,0,59.99999999999999,40.0" stroke="green" stroke-width="3" fill="none" />
+
<path d="M59.99999999999999,40.0 A20,20,0,1,1,70.0,2.679491924311229" stroke="blue" stroke-width="1" fill="black" fill-opacity="0.3" />
+
</svg>
example2.png

This is a binary file and will not be displayed.

+18
example2.svg
···
+
<?xml version="1.0" encoding="UTF-8"?>
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+
width="600" height="320.0" viewBox="-0.75 -0.4 1.5 0.8">
+
<defs>
+
<radialGradient cx="0" cy="0.35" r="7.0" gradientUnits="userSpaceOnUse" id="d0">
+
<stop offset="0.07142857142857142" stop-color="green" stop-opacity="1" />
+
<stop offset="0.1" stop-color="red" stop-opacity="0" />
+
</radialGradient>
+
<linearGradient x1="0.1" y1="0.35" x2="0.7" y2="0.14999999999999997" gradientUnits="userSpaceOnUse" id="d1">
+
<stop offset="0" stop-color="green" stop-opacity="1" />
+
<stop offset="1" stop-color="red" stop-opacity="0" />
+
</linearGradient>
+
</defs>
+
<rect x="-0.75" y="-0.5" width="1.5" height="1" fill="#ddd" />
+
<path d="M0.6062177826491071,5.551115123125783e-17 A0.7,0.7,0,0,0,-0.3499999999999998,-0.2562177826491071 L-0.2499999999999999,-0.08301270189221943 A0.5,0.5,0,0,1,0.43301270189221935,0.1 Z" fill="url(#d0)" stroke="black" stroke-width="0.002" />
+
<path d="M-0.48209070726490444,-0.22453333233923367 A0.75,0.75,0,0,0,-0.7047694655894312,0.09348489250574832 L0.0,0.35 A0,0,0,0,1,0.0,0.35 Z" fill="url(#d0)" stroke="red" stroke-width="0.002" />
+
<rect x="0.1" y="0.14999999999999997" width="0.6" height="0.2" stroke="black" stroke-width="0.002" fill="url(#d1)" />
+
</svg>
example3.png

This is a binary file and will not be displayed.

+13
example3.svg
···
+
<?xml version="1.0" encoding="UTF-8"?>
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+
width="400" height="400.0" viewBox="-0.7 -0.7 1.4 1.4">
+
<defs>
+
<clipPath id="d0">
+
<rect x="-0.25" y="-0.25" width="1" height="1" />
+
</clipPath>
+
</defs>
+
<circle cx="0" cy="0" r="0.5" stroke-width="0.01" stroke="black" fill-opacity="0.3" clip-path="url(#d0)" id="circle" />
+
<g opacity="0.5" clip-path="url(#d0)">
+
<use xlink:href="#circle" x="0.25" y="-0.1" />
+
</g>
+
</svg>
example4.png

This is a binary file and will not be displayed.

+13
example4.svg
···
+
<?xml version="1.0" encoding="UTF-8"?>
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+
width="200" height="240.0" viewBox="-0.5 -0.6 1 1.2">
+
<defs>
+
</defs>
+
<g id="ellipse" transform="skewY(-30)">
+
<circle cx="0" cy="0" r="0.5" fill="green" />
+
<text x="0" y="0" font-size="0.2" fill="white" text-anchor="middle" transform="translate(0,0.06)">Hyperlink</text>
+
</g>
+
<a href="https://www.python.org" target="_blank">
+
<use xlink:href="#ellipse" x="0" y="0" />
+
</a>
+
</svg>