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

Compare changes

Choose any two refs to compare.

Changed files
+7156 -1363
docs
drawSvg
drawsvg
examples
+1 -1
.gitignore
···
/MANIFEST
/dist
/*.egg-info
-
+
*.ipynb_checkpoints
+3
FUNDING.yml
···
+
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository
+
+
github: [cduck]
+1 -1
LICENSE.txt
···
-
Copyright 2017 Casey Duckering
+
Copyright 2023 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:
+350 -89
README.md
···
-
# drawSvg
+
[![drawsvg logo](https://raw.githubusercontent.com/cduck/drawsvg/master/examples/logo.svg?sanitize=true)](https://github.com/cduck/drawSvg/blob/master/examples/logo.svg)
-
A Python 3 library for programmatically generating SVG images (vector drawings) and rendering them or displaying them in an iPython notebook.
+
A Python 3 library for programmatically generating SVG images and animations that can render and display your drawings in a Jupyter notebook or Jupyter lab.
-
Most common SVG tags are supported and others can easily be added by writing a small subclass of `DrawableBasicElement` or `DrawableParentElement`.
+
Most common SVG tags are supported and others can easily be added by writing a small subclass of `DrawableBasicElement` or `DrawableParentElement`. [Nearly all SVG attributes](https://developer.mozilla.org/en-US/docs/Web/SVG) are supported via keyword args (e.g. Python keyword argument `fill_opacity=0.5` becomes SVG attribute `fill-opacity="0.5"`).
-
An interactive [Jupyter notebook](https://jupyter.org) widget, `drawSvg.widgets.DrawingWidget`, is included that can update drawings based on mouse events.
+
An interactive [Jupyter notebook](https://jupyter.org) widget, `drawsvg.widgets.DrawingWidget`, [is included](#interactive-widget) that can update drawings based on mouse events. The widget does not yet work in Jupyter lab.
+
+
[SVG quick reference docs](https://cduck.github.io/drawsvg/)
+
# Install
-
drawSvg is available on PyPI:
+
Drawsvg is available on PyPI:
+
```bash
+
$ python3 -m pip install "drawsvg~=2.0"
```
-
$ pip3 install drawSvg
-
```
+
+
To enable raster image support (PNG, MP4, and GIF), follow the [full-feature install instructions](#full-feature-install).
+
+
+
## Upgrading from version 1.x
+
+
Major breaking changes:
+
+
- camelCase method and argument names are now snake\_case and the package name is lowercase (except for arguments that correspond to camelCase SVG attributes).
+
- The default coordinate system y-axis now matches the SVG coordinate system (y increases down the screen, x increases right)
+
- How to fix `ModuleNotFoundError: No module named 'drawSvg'` (with a capital S)? Either pip install `"drawSvg~=1.9"` or update your code for drawsvg 2.x (for example, change `drawSvg` to `drawsvg` and `d.saveSvg` to `d.save_svg`).
+
# Examples
### Basic drawing elements
```python
-
import drawSvg as draw
+
import drawsvg as draw
d = draw.Drawing(200, 100, origin='center')
-
d.append(draw.Lines(-80, -45,
-
70, -49,
-
95, 49,
-
-90, 40,
+
# Draw an irregular polygon
+
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'))
+
# Draw a rectangle
+
r = draw.Rectangle(-80, -50, 40, 50, fill='#1248ff')
+
r.append_title("Our first rectangle") # Add a tooltip
+
d.append(r)
-
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
+
# Draw a circle
+
d.append(draw.Circle(-40, 10, 30,
+
fill='red', stroke_width=2, stroke='black'))
+
+
# Draw an arbitrary path (a triangle in this case)
+
p = draw.Path(stroke_width=2, stroke='lime', fill='black', fill_opacity=0.2)
+
p.M(-10, -20) # Start path at point (-10, -20)
+
p.C(30, 10, 30, -50, 70, -20) # Draw a curve to (70, -20)
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))
+
# Draw text
+
d.append(draw.Text('Basic text', 8, -10, -35, fill='blue')) # 8pt text at (-10, -35)
+
d.append(draw.Text('Path text', 8, path=p, text_anchor='start', line_height=1))
+
d.append(draw.Text(['Multi-line', 'text'], 8, path=p, text_anchor='end', center=True))
+
+
# Draw multiple circular arcs
+
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, 90, -60, cw=True,
+
stroke='green', stroke_width=3, fill='none'))
+
d.append(draw.Arc(60, 20, 20, -60, 90, cw=False,
+
stroke='blue', stroke_width=1, fill='black', fill_opacity=0.3))
+
+
# Draw arrows
+
arrow = draw.Marker(-0.1, -0.51, 0.9, 0.5, scale=4, orient='auto')
+
arrow.append(draw.Lines(-0.1, 0.5, -0.1, -0.5, 0.9, 0, fill='red', close=True))
+
p = draw.Path(stroke='red', stroke_width=2, fill='none',
+
marker_end=arrow) # Add an arrow to the end of a path
+
p.M(20, 40).L(20, 27).L(0, 20) # Chain multiple path commands
+
d.append(p)
+
d.append(draw.Line(30, 20, 0, 10,
+
stroke='red', stroke_width=2, fill='none',
+
marker_end=arrow)) # Add an arrow to the end of a line
-
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')
+
d.set_pixel_scale(2) # Set number of pixels per geometry unit
+
#d.set_render_size(400, 200) # Alternative to set_pixel_scale
+
d.save_svg('example.svg')
+
d.save_png('example.png')
-
# Display in iPython notebook
-
d.rasterize() # Display as PNG
+
# Display in Jupyter notebook
+
#d.rasterize() # Display as PNG
d # Display as SVG
```
-
![Example output image](https://raw.githubusercontent.com/cduck/drawSvg/master/examples/example1.png)
+
[![Example output image](https://raw.githubusercontent.com/cduck/drawsvg/master/examples/example1.png)](https://github.com/cduck/drawsvg/blob/master/examples/example1.svg)
-
### Gradients
+
### SVG-native animation with playback controls
```python
-
import drawSvg as draw
+
import drawsvg as draw
+
+
d = draw.Drawing(400, 200, origin='center',
+
animation_config=draw.types.SyncedAnimationConfig(
+
# Animation configuration
+
duration=8, # Seconds
+
show_playback_progress=True,
+
show_playback_controls=True))
+
d.append(draw.Rectangle(-200, -100, 400, 200, fill='#eee')) # Background
+
d.append(draw.Circle(0, 0, 40, fill='green')) # Center circle
+
+
# Animation
+
circle = draw.Circle(0, 0, 0, fill='gray') # Moving circle
+
circle.add_key_frame(0, cx=-100, cy=0, r=0)
+
circle.add_key_frame(2, cx=0, cy=-100, r=40)
+
circle.add_key_frame(4, cx=100, cy=0, r=0)
+
circle.add_key_frame(6, cx=0, cy=100, r=40)
+
circle.add_key_frame(8, cx=-100, cy=0, r=0)
+
d.append(circle)
+
r = draw.Rectangle(0, 0, 0, 0, fill='silver') # Moving square
+
r.add_key_frame(0, x=-100, y=0, width=0, height=0)
+
r.add_key_frame(2, x=0-20, y=-100-20, width=40, height=40)
+
r.add_key_frame(4, x=100, y=0, width=0, height=0)
+
r.add_key_frame(6, x=0-20, y=100-20, width=40, height=40)
+
r.add_key_frame(8, x=-100, y=0, width=0, height=0)
+
d.append(r)
+
+
# Changing text
+
draw.native_animation.animate_text_sequence(
+
d,
+
[0, 2, 4, 6],
+
['0', '1', '2', '3'],
+
30, 0, 1, fill='yellow', center=True)
+
+
# Save as a standalone animated SVG or HTML
+
d.save_svg('playback-controls.svg')
+
d.save_html('playback-controls.html')
+
+
# Display in Jupyter notebook
+
#d.display_image() # Display SVG as an image (will not be interactive)
+
#d.display_iframe() # Display as interactive SVG (alternative)
+
#d.as_gif('orbit.gif', fps=10) # Render as a GIF image, optionally save to file
+
#d.as_mp4('orbig.mp4', fps=60, verbose=True) # Render as an MP4 video, optionally save to file
+
#d.as_spritesheet('orbit-spritesheet.png', row_length=10, fps=3) # Render as a spritesheet
+
d.display_inline() # Display as interactive SVG
+
```
+
+
[![Example animated image](https://raw.githubusercontent.com/cduck/drawsvg/master/examples/playback-controls.svg?sanitize=true)](https://github.com/cduck/drawsvg/blob/master/examples/playback-controls.svg)
+
+
Note: GitHub blocks the playback controls.
+
Download the above SVG and open it in a web browser to try.
+
+
https://user-images.githubusercontent.com/2476062/221400434-1529d237-e9bf-4363-a143-0ece75cd349a.mp4
+
+
### Patterns and gradients
+
```python
+
import drawsvg as draw
d = draw.Drawing(1.5, 0.8, origin='center')
-
d.draw(draw.Rectangle(-0.75,-0.5,1.5,1, fill='#ddd'))
+
# Background pattern (not supported by Cairo, d.rasterize() will not show it)
+
pattern = draw.Pattern(width=0.13, height=0.23)
+
pattern.append(draw.Rectangle(0, 0, .1, .1, fill='yellow'))
+
pattern.append(draw.Rectangle(0, .1, .1, .1, fill='orange'))
+
d.draw(draw.Rectangle(-0.75, -0.5, 1.5, 1, fill=pattern, fill_opacity=0.4))
# 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)
+
gradient = draw.RadialGradient(0, 0.35, 0.7*10)
+
gradient.add_stop(0.5/0.7/10, 'green', 1)
+
gradient.add_stop(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.arc(0, 0.35, 0.7, -30, -120, cw=False)
+
p.arc(0, 0.35, 0.5, -120, -30, cw=True, include_l=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.arc(0, 0.35, 0.75, -130, -160, cw=False)
+
p.arc(0, 0.35, 0, -160, -130, cw=True, include_l=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,
+
gradient2 = draw.LinearGradient(0.1, 0.35, 0.1+0.6, 0.35+0.2)
+
gradient2.add_stop(0, 'green', 1)
+
gradient2.add_stop(1, 'red', 0)
+
d.append(draw.Rectangle(0.1, 0.15, 0.6, 0.2,
stroke='black', stroke_width=0.002,
fill=gradient2))
# Display
-
d.setRenderSize(w=600)
+
d.set_render_size(w=600)
d
```
-
![Example output image](https://raw.githubusercontent.com/cduck/drawSvg/master/examples/example2.png)
+
[![Example output image](https://raw.githubusercontent.com/cduck/drawsvg/master/examples/example2.svg?sanitize=true)](https://github.com/cduck/drawsvg/blob/master/examples/example2.svg)
### Duplicate geometry and clip paths
```python
-
import drawSvg as draw
+
import drawsvg as draw
d = draw.Drawing(1.4, 1.4, origin='center')
# Define clip path
clip = draw.ClipPath()
-
clip.append(draw.Rectangle(-.25,.25-1,1,1))
+
clip.append(draw.Rectangle(-.25, -.25, 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)
+
circle = draw.Circle(0, 0, 0.5,
+
stroke_width='0.01', stroke='black',
+
fill_opacity=0.3, clip_path=clip)
+
d.append(circle)
# Make a transparent copy, cropped again
g = draw.Group(opacity=0.5, clip_path=clip)
-
g.append(draw.Use('circle', 0.25,0.1))
+
# Here, circle is not directly appended to the drawing.
+
# drawsvg recognizes that `Use` references `circle` and automatically adds
+
# `circle` to the <defs></defs> section of the SVG.
+
g.append(draw.Use(circle, 0.25, -0.1))
d.append(g)
# Display
-
d.setRenderSize(400)
-
d.rasterize()
+
d.set_render_size(400)
+
#d.rasterize() # Display as PNG
+
d # Display as SVG
+
```
+
+
[![Example output image](https://raw.githubusercontent.com/cduck/drawsvg/master/examples/example3.png)](https://github.com/cduck/drawsvg/blob/master/examples/example3.svg)
+
+
### Organizing and duplicating drawing elements
+
```python
+
import drawsvg as draw
+
+
d = draw.Drawing(300, 100)
+
d.set_pixel_scale(2)
+
+
# Use groups to contain other elements
+
# Children elements of groups inherit the coordinate system (transform)
+
# and attribute values
+
group = draw.Group(fill='orange', transform='rotate(-20)')
+
group.append(draw.Rectangle(0, 10, 20, 40)) # This rectangle will be orange
+
group.append(draw.Circle(30, 40, 10)) # This circle will also be orange
+
group.append(draw.Circle(50, 40, 10, fill='green')) # This circle will not
+
d.append(group)
+
+
# Use the Use element to make duplicates of elements
+
# Each duplicate can be placed at an offset (x, y) location and any additional
+
# attributes (like fill color) are inherited if the element didn't specify them.
+
d.append(draw.Use(group, 80, 0, stroke='black', stroke_width=1))
+
d.append(draw.Use(group, 80, 20, stroke='blue', stroke_width=2))
+
d.append(draw.Use(group, 80, 40, stroke='red', stroke_width=3))
+
+
d.display_inline()
```
-
![Example output image](https://raw.githubusercontent.com/cduck/drawSvg/master/examples/example3.png)
+
[![Example output image](https://raw.githubusercontent.com/cduck/drawsvg/master/examples/example8.png)](https://github.com/cduck/drawsvg/blob/master/examples/example8.svg)
### Implementing other SVG tags
```python
-
import drawSvg as draw
+
import drawsvg as draw
# Subclass DrawingBasicElement if it cannot have child nodes
# Subclass DrawingParentElement otherwise
···
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"
+
# arguments: stroke_width=5 -> <a stroke-width="5" ...>...</a>
super().__init__(href=href, target=target, **kwargs)
d = draw.Drawing(1, 1.2, origin='center')
···
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'))
+
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.set_render_size(200)
d
```
-
![Example output image](https://raw.githubusercontent.com/cduck/drawSvg/master/examples/example4.png)
+
[![Example output image](https://raw.githubusercontent.com/cduck/drawsvg/master/examples/example4.png)](https://github.com/cduck/drawsvg/blob/master/examples/example4.svg)
+
+
### Animation with the SVG Animate Tag
+
```python
+
import drawsvg as draw
+
+
d = draw.Drawing(200, 200, origin='center')
+
+
# Animate the position and color of circle
+
c = draw.Circle(0, 0, 20, fill='red')
+
# See for supported attributes:
+
# https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animate
+
c.append_anim(draw.Animate('cy', '6s', '-80;80;-80',
+
repeatCount='indefinite'))
+
c.append_anim(draw.Animate('cx', '6s', '0;80;0;-80;0',
+
repeatCount='indefinite'))
+
c.append_anim(draw.Animate('fill', '6s', 'red;green;blue;yellow',
+
calc_mode='discrete',
+
repeatCount='indefinite'))
+
d.append(c)
+
+
# Animate a black circle around an ellipse
+
ellipse = draw.Path()
+
ellipse.M(-90, 0)
+
ellipse.A(90, 40, 360, True, True, 90, 0) # Ellipse path
+
ellipse.A(90, 40, 360, True, True, -90, 0)
+
ellipse.Z()
+
c2 = draw.Circle(0, 0, 10)
+
# See for supported attributes:
+
# https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animate_motion
+
c2.append_anim(draw.AnimateMotion(ellipse, '3s',
+
repeatCount='indefinite'))
+
# See for supported attributes:
+
# https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animate_transform
+
c2.append_anim(draw.AnimateTransform('scale', '3s', '1,2;2,1;1,2;2,1;1,2',
+
repeatCount='indefinite'))
+
d.append(c2)
+
+
d.save_svg('animated.svg') # Save to file
+
d # Display in Jupyter notebook
+
```
+
+
[![Example output image](https://raw.githubusercontent.com/cduck/drawsvg/master/examples/animated-fix-github.svg?sanitize=true)](https://github.com/cduck/drawsvg/blob/master/examples/animated.svg)
### Interactive Widget
```python
-
import drawSvg as draw
-
from drawSvg.widgets import DrawingWidget
-
import hyperbolic.poincare.shapes as hyper # pip3 install hyperbolic
+
import drawsvg as draw
+
from drawsvg.widgets import DrawingWidget
+
import hyperbolic.poincare as hyper # python3 -m pip install hyperbolic
+
from hyperbolic import euclid
# Create drawing
-
d = draw.Drawing(2, 2, origin='center')
-
d.setRenderSize(500)
+
d = draw.Drawing(2, 2, origin='center', context=draw.Context(invert_y=True))
+
d.set_render_size(500)
d.append(draw.Circle(0, 0, 1, fill='orange'))
group = draw.Group()
d.append(group)
···
for x1, y1 in points:
for x2, y2 in points:
if (x1, y1) == (x2, y2): continue
-
p1 = hyper.Point.fromEuclid(x1, y1)
-
p2 = hyper.Point.fromEuclid(x2, y2)
-
if p1.distanceTo(p2) <= 2:
-
line = hyper.Line.fromPoints(*p1, *p2, segment=True)
+
p1 = hyper.Point.from_euclid(x1, y1)
+
p2 = hyper.Point.from_euclid(x2, y2)
+
if p1.distance_to(p2) <= 2:
+
line = hyper.Line.from_points(*p1, *p2, segment=True)
group.draw(line, hwidth=0.2, fill='white')
for x, y in points:
-
p = hyper.Point.fromEuclid(x, y)
-
group.draw(hyper.Circle.fromCenterRadius(p, 0.1),
+
p = hyper.Point.from_euclid(x, y)
+
group.draw(hyper.Circle.from_center_radius(p, 0.1),
fill='green')
redraw(click_list)
···
widget
```
-
![Example output image](https://raw.githubusercontent.com/cduck/drawSvg/master/examples/example5.gif)
+
![Example output image](https://raw.githubusercontent.com/cduck/drawsvg/master/examples/example5.gif)
+
+
Note: The above example currently only works in `jupyter notebook`, not `jupyter lab`.
-
### Animation
+
### Frame-by-Frame Animation
```python
-
import drawSvg as draw
+
import drawsvg as draw
# Draw a frame of the animation
def draw_frame(t):
-
d = draw.Drawing(2, 6.05, origin=(-1,-1.05))
-
d.setRenderSize(h=300)
-
d.append(draw.Rectangle(-2, -2, 4, 8, fill='white'))
-
d.append(draw.Rectangle(-1, -1.05, 2, 0.05, fill='brown'))
+
d = draw.Drawing(2, 6.05, origin=(-1, -5))
+
d.set_render_size(h=300)
+
d.append(draw.Rectangle(-2, -6, 4, 8, fill='white'))
+
d.append(draw.Rectangle(-1, 1, 2, 0.05, fill='brown'))
t = (t + 1) % 2 - 1
-
y = 4 - t**2 * 4
+
y = t**2 * 4 - 4
d.append(draw.Circle(0, y, 1, fill='lime'))
return d
-
with draw.animate_jupyter(draw_frame, delay=0.05) as anim:
+
with draw.frame_animate_jupyter(draw_frame, delay=0.05) as anim:
# Or:
-
#with draw.animate_video('example6.gif', draw_frame, duration=0.05
-
# ) as anim:
+
#with draw.frame_animate_video('example6.gif', draw_frame, duration=0.05) as anim:
+
# Or:
+
#with draw.frame_animate_spritesheet('example6.png', draw_frame, row_length=10) as anim:
# Add each frame to the animation
for i in range(20):
anim.draw_frame(i/10)
···
anim.draw_frame(i/10)
```
-
![Example output image](https://raw.githubusercontent.com/cduck/drawSvg/master/examples/example6.gif)
+
GIF:
+
+
![Example output gif](https://raw.githubusercontent.com/cduck/drawsvg/master/examples/example6.gif)
+
+
Spritesheet (usable in most 2D game engines):
+
+
![Example output spritesheet](https://raw.githubusercontent.com/cduck/drawsvg/master/examples/example6.png)
+
+
### Asynchronous Frame-based Animation in Jupyter
+
```python
+
# Jupyter cell 1:
+
import drawsvg as draw
+
from drawsvg.widgets import AsyncAnimation
+
widget = AsyncAnimation(fps=10)
+
widget
+
# [Animation is displayed here (click to pause)]
+
+
# Jupyter cell 2:
+
global_variable = 'a'
+
@widget.set_draw_frame # Animation above is automatically updated
+
def draw_frame(secs=0):
+
# Draw something...
+
d = draw.Drawing(100, 40)
+
d.append(draw.Text(global_variable, 20, 0, 30))
+
d.append(draw.Text('{:0.1f}'.format(secs), 20, 30, 30))
+
return d
+
+
# Jupyter cell 3:
+
global_variable = 'b' # Animation above now displays 'b'
+
```
+
+
![Example output image](https://raw.githubusercontent.com/cduck/drawsvg/master/examples/example7.gif)
+
+
Note: The above example currently only works in `jupyter notebook`, not `jupyter lab`.
+
+
+
### Embed custom fonts
+
```python
+
import drawsvg as draw
+
+
d = draw.Drawing(400, 100, origin='center')
+
d.embed_google_font('Permanent Marker', text=set('Text with custom font'))
+
+
d.append(draw.Text('Text with custom font', 35, 0, 0, center=True,
+
font_family='Permanent Marker', font_style='italic'))
+
+
d.save_svg('font.svg')
+
d # Custom fonts work in most browsers but not in rasterize(), save_png(), or save_video()
+
```
+
+
[![Example output image](https://raw.githubusercontent.com/cduck/drawsvg/master/examples/font.svg?sanitize=true)](https://github.com/cduck/drawsvg/blob/master/examples/font.svg)
+
+
+
---
+
+
# Full-feature install
+
Drawsvg may be either be installed with no dependencies (only SVG and SVG-native animation will work):
+
```bash
+
$ python3 -m pip install "drawsvg~=2.0"
+
```
+
Or drawsvg may be installed with extra dependencies to support PNG, MP4, and GIF output:
+
```bash
+
$ python3 -m pip install "drawsvg[all]~=2.0"
+
```
+
+
An additional required package, [Cairo](https://www.cairographics.org/download/), cannot be installed with pip and must be installed separately. When Cairo is installed, drawsvg can output PNG and other image formats in addition to SVG. Install it with your preferred package manager. Examples:
+
+
**Ubuntu**
+
+
```bash
+
$ sudo apt install libcairo2
+
```
+
+
**macOS**
+
+
Using [homebrew](https://brew.sh/) (may require a Python version installed with `brew install python`):
+
+
```bash
+
$ brew install cairo
+
```
+
+
**Any platform**
+
+
Using [Anaconda](https://docs.conda.io/en/latest/miniconda.html) (may require Python and cairo installed in the same conda environment):
+
+
```bash
+
$ conda install -c anaconda cairo
+
```
+9
docs/img/01_circ.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="300" height="100" viewBox="0 0 300 100">
+
<defs>
+
</defs>
+
<circle cx="50" cy="50" r="40" />
+
<circle cx="150" cy="50" r="40" stroke="black" fill="none" />
+
<circle cx="250" cy="50" r="40" stroke="black" fill="none" stroke-width="15" />
+
</svg>
+9
docs/img/01_ellip.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="300" height="100" viewBox="0 0 300 100">
+
<defs>
+
</defs>
+
<ellipse cx="50" cy="50" rx="50" ry="30" />
+
<ellipse cx="160" cy="50" rx="50" ry="30" stroke="black" fill="none" />
+
<ellipse cx="250" cy="50" rx="30" ry="45" stroke="black" fill="none" />
+
</svg>
+7
docs/img/01_line.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="100" height="100" viewBox="0 0 100 100">
+
<defs>
+
</defs>
+
<path d="M30,30 L90,90" stroke="black" />
+
</svg>
+7
docs/img/01_multilines.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="100" height="100" viewBox="0 0 100 100">
+
<defs>
+
</defs>
+
<path d="M10,90 L10,10 L80,90 L80,10" fill="none" stroke="black" />
+
</svg>
+7
docs/img/01_multilines2.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="300" height="100" viewBox="0 0 300 100">
+
<defs>
+
</defs>
+
<path d="M30,80 L40,20 L50,80 L60,20 L70,80 L80,20 L90,80 L100,20 L110,80 L120,20 L130,80 L140,20 L150,80 L160,20 L170,80 L180,20 L190,80 L200,20 L210,80 L220,20" stroke="black" stroke-width="5" fill="none" />
+
</svg>
+7
docs/img/01_polygon.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="100" height="100" viewBox="0 0 100 100">
+
<defs>
+
</defs>
+
<path d="M48,16 L16,96 L96,48 L0,48 L88,96 Z" stroke="black" fill="none" />
+
</svg>
+10
docs/img/01_rect.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="500" height="200" viewBox="0 0 500 200">
+
<defs>
+
</defs>
+
<rect x="10" y="10" width="90" height="150" />
+
<rect x="120" y="10" width="60" height="120" fill="none" stroke="black" />
+
<rect x="210" y="10" width="75" height="90" fill="#0000ff" stroke="red" stroke-width="7" stroke-opacity="0.5" />
+
<rect x="300" y="10" width="105" height="60" fill="yellow" fill-opacity="0.5" stroke="green" stroke-width="2" stroke-dasharray="5,2" />
+
</svg>
+11
docs/img/01_rectround.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="500" height="200" viewBox="0 0 500 200">
+
<defs>
+
</defs>
+
<rect x="10" y="10" width="80" height="180" rx="10" ry="10" stroke="black" fill="none" />
+
<rect x="110" y="10" width="80" height="180" ry="20" stroke="black" fill="none" />
+
<rect x="210" y="10" width="80" height="180" rx="40" stroke="black" fill="none" />
+
<rect x="310" y="10" width="80" height="180" rx="30" ry="10" stroke="black" fill="none" />
+
<rect x="410" y="10" width="80" height="180" rx="10" ry="30" stroke="black" fill="none" />
+
</svg>
+9
docs/img/02_dash.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="110" height="40" viewBox="0 0 110 40">
+
<defs>
+
</defs>
+
<path d="M10,10 L100,10" stroke="black" stroke-width="2" stroke-dasharray="9,5" />
+
<path d="M10,20 L100,20" stroke="black" stroke-width="2" stroke-dasharray="5,3,9,2" />
+
<path d="M10,30 L100,30" stroke="black" stroke-width="2" stroke-dasharray="9,3,5" />
+
</svg>
+16
docs/img/02_foso.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="300" height="100" viewBox="0 0 300 100">
+
<defs>
+
</defs>
+
<path d="M0,10 L290,10" stroke="black" stroke-width="5" stroke-opacity="0.1" />
+
<rect x="0" y="70" width="50" height="50" fill="red" fill-opacity="0.1" />
+
<path d="M0,20 L290,20" stroke="black" stroke-width="5" stroke-opacity="0.30000000000000004" />
+
<rect x="60" y="70" width="50" height="50" fill="red" fill-opacity="0.30000000000000004" />
+
<path d="M0,30 L290,30" stroke="black" stroke-width="5" stroke-opacity="0.5" />
+
<rect x="120" y="70" width="50" height="50" fill="red" fill-opacity="0.5" />
+
<path d="M0,40 L290,40" stroke="black" stroke-width="5" stroke-opacity="0.7" />
+
<rect x="180" y="70" width="50" height="50" fill="red" fill-opacity="0.7" />
+
<path d="M0,50 L290,50" stroke="black" stroke-width="5" stroke-opacity="0.9" />
+
<rect x="240" y="70" width="50" height="50" fill="red" fill-opacity="0.9" />
+
</svg>
+11
docs/img/02_fsc.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="100" height="60" viewBox="0 0 100 60">
+
<defs>
+
</defs>
+
<path d="M10,10 L80,10" stroke="red" stroke-width="5" />
+
<path d="M10,20 L80,20" stroke="#9f9" stroke-width="5" />
+
<path d="M10,30 L80,30" stroke="#9999ff" stroke-width="5" />
+
<path d="M10,40 L80,40" stroke="rgb(255,128,64)" stroke-width="5" />
+
<path d="M10,50 L80,50" stroke="rgb(60%,20%,60%)" stroke-width="5" />
+
</svg>
+12
docs/img/02_join.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="300" height="100" viewBox="0 0 300 100">
+
<defs>
+
</defs>
+
<path d="M0,20 L300,20" stroke="gray" />
+
<g stroke-width="20" stroke="black" fill="none">
+
<path d="M10,80 L50,20 L90,80" stroke-linejoin="miter" />
+
<path d="M110,80 L150,20 L190,80" stroke-linejoin="round" />
+
<path d="M210,80 L250,20 L290,80" stroke-linejoin="bevel" />
+
</g>
+
</svg>
+11
docs/img/02_linecap.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="150" height="100" viewBox="0 0 150 100">
+
<defs>
+
</defs>
+
<path d="M10,15 L50,15" stroke="black" stroke-width="15" stroke-linecap="butt" />
+
<path d="M10,45 L50,45" stroke="black" stroke-width="15" stroke-linecap="round" />
+
<path d="M10,75 L50,75" stroke="black" stroke-width="15" stroke-linecap="square" />
+
<path d="M10,0 L10,100" stroke="#999" />
+
<path d="M50,0 L50,100" stroke="#999" />
+
</svg>
+12
docs/img/02_mlimit.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="300" height="100" viewBox="0 0 300 100">
+
<defs>
+
</defs>
+
<path d="M0,30 L300,30" stroke="gray" />
+
<g stroke-width="20" stroke="black" fill="none" stroke-linejoin="miter">
+
<path d="M10,90 L40,30 L70,90" />
+
<path d="M100,90 L130,30 L160,90" stroke-miterlimit="2.3" />
+
<path d="M190,90 L220,30 L250,90" stroke-miterlimit="1" />
+
</g>
+
</svg>
+26
docs/img/02_strokewdth.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="320" height="100" viewBox="0 0 320 100">
+
<defs>
+
</defs>
+
<path d="M15,10 L15,90" stroke="black" stroke-width="11" />
+
<path d="M30,10 L30,90" stroke="black" stroke-width="10" />
+
<path d="M45,10 L45,90" stroke="black" stroke-width="9" />
+
<path d="M60,10 L60,90" stroke="black" stroke-width="8" />
+
<path d="M75,10 L75,90" stroke="black" stroke-width="7" />
+
<path d="M90,10 L90,90" stroke="black" stroke-width="6" />
+
<path d="M105,10 L105,90" stroke="black" stroke-width="5" />
+
<path d="M120,10 L120,90" stroke="black" stroke-width="4" />
+
<path d="M135,10 L135,90" stroke="black" stroke-width="3" />
+
<path d="M150,10 L150,90" stroke="black" stroke-width="2" />
+
<path d="M165,10 L165,90" stroke="black" stroke-width="1" />
+
<path d="M180,10 L180,90" stroke="black" stroke-width="2" />
+
<path d="M195,10 L195,90" stroke="black" stroke-width="3" />
+
<path d="M210,10 L210,90" stroke="black" stroke-width="4" />
+
<path d="M225,10 L225,90" stroke="black" stroke-width="5" />
+
<path d="M240,10 L240,90" stroke="black" stroke-width="6" />
+
<path d="M255,10 L255,90" stroke="black" stroke-width="7" />
+
<path d="M270,10 L270,90" stroke="black" stroke-width="8" />
+
<path d="M285,10 L285,90" stroke="black" stroke-width="9" />
+
<path d="M300,10 L300,90" stroke="black" stroke-width="10" />
+
</svg>
+10
docs/img/03_pA.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="0 0 400 200" stroke-width="3" fill="none">
+
<defs>
+
</defs>
+
<path d="M125,75 A100,50,0,0,0,225,125" stroke="red" />
+
<path d="M125,75 A100,50,0,0,1,225,125" stroke="blue" />
+
<path d="M125,75 A100,50,0,1,0,225,125" stroke="rgb(0 80 255)" stroke-dasharray="5 3" />
+
<path d="M125,75 A100,50,0,1,1,225,125" stroke="rgb(255 80 0)" stroke-dasharray="5 3" />
+
</svg>
+48
docs/img/03_pC.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="650" height="100" viewBox="0 0 650 100">
+
<defs>
+
</defs>
+
<path d="M40,50 C10,10,140,10,110,50" stroke="black" fill="none" stroke-width="3" transform="translate(0,0)" />
+
<g stroke="gray" fill="gray" stroke-width="1" transform="translate(0,0)">
+
<circle cx="10" cy="10" r="2" />
+
<circle cx="140" cy="10" r="2" />
+
<path d="M40,50 L10,10" />
+
<path d="M110,50 L140,10" />
+
</g>
+
<path d="M40,50 C60,10,90,10,110,50" stroke="black" fill="none" stroke-width="3" transform="translate(100,0)" />
+
<g stroke="gray" fill="gray" stroke-width="1" transform="translate(100,0)">
+
<circle cx="60" cy="10" r="2" />
+
<circle cx="90" cy="10" r="2" />
+
<path d="M40,50 L60,10" />
+
<path d="M110,50 L90,10" />
+
</g>
+
<path d="M40,50 C110,10,40,10,110,50" stroke="black" fill="none" stroke-width="3" transform="translate(200,0)" />
+
<g stroke="gray" fill="gray" stroke-width="1" transform="translate(200,0)">
+
<circle cx="110" cy="10" r="2" />
+
<circle cx="40" cy="10" r="2" />
+
<path d="M40,50 L110,10" />
+
<path d="M110,50 L40,10" />
+
</g>
+
<path d="M40,50 C110,10,40,10,110,50" stroke="black" fill="none" stroke-width="3" transform="translate(300,0)" />
+
<g stroke="gray" fill="gray" stroke-width="1" transform="translate(300,0)">
+
<circle cx="110" cy="10" r="2" />
+
<circle cx="40" cy="10" r="2" />
+
<path d="M40,50 L110,10" />
+
<path d="M110,50 L40,10" />
+
</g>
+
<path d="M40,50 C60,10,90,90,110,50" stroke="black" fill="none" stroke-width="3" transform="translate(400,0)" />
+
<g stroke="gray" fill="gray" stroke-width="1" transform="translate(400,0)">
+
<circle cx="60" cy="10" r="2" />
+
<circle cx="90" cy="90" r="2" />
+
<path d="M40,50 L60,10" />
+
<path d="M110,50 L90,90" />
+
</g>
+
<path d="M40,50 C110,10,40,90,110,50" stroke="black" fill="none" stroke-width="3" transform="translate(500,0)" />
+
<g stroke="gray" fill="gray" stroke-width="1" transform="translate(500,0)">
+
<circle cx="110" cy="10" r="2" />
+
<circle cx="40" cy="90" r="2" />
+
<path d="M40,50 L110,10" />
+
<path d="M110,50 L40,90" />
+
</g>
+
</svg>
+8
docs/img/03_pHV.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="100" height="60" viewBox="0 0 100 60">
+
<defs>
+
</defs>
+
<path d="M10,10 H100 M10,20 H100 V50" stroke="black" fill="none" id="pathHV0" />
+
<use xlink:href="#pathHV0" />
+
</svg>
+11
docs/img/03_pL.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="100" height="70" viewBox="0 0 100 70">
+
<defs>
+
</defs>
+
<g stroke="black" fill="none">
+
<path d="M10,10 L100,10" />
+
<path d="M10,20 L100,20 L100,50" />
+
<path d="M40,60 L10,60 L40,42 M60,60 L90,60 L60,42" />
+
</g>
+
</svg>
+21
docs/img/03_pQ.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="700" height="130" viewBox="0 0 700 130">
+
<defs>
+
<path d="M30,75 Q240,30,300,120" stroke="black" fill="none" stroke-width="3" id="pathQ0" />
+
</defs>
+
<use xlink:href="#pathQ0" />
+
<use xlink:href="#pathQ0" x="300" y="0" />
+
<g stroke="gray" fill="gray">
+
<circle cx="330" cy="75" r="3" />
+
<circle cx="600" cy="120" r="3" />
+
<circle cx="540" cy="30" r="3" />
+
<path d="M330,75 L540,30" />
+
<path d="M540,30 L600,120" />
+
<path d="M330,75 L600,120" stroke-dasharray="5,5" />
+
<circle cx="435" cy="52.5" r="3" />
+
<circle cx="570" cy="75" r="3" />
+
<path d="M435,52.5 L570,75" />
+
<circle cx="502.5" cy="63.75" r="4" fill="none" />
+
</g>
+
</svg>
+16
docs/img/03_pS.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="250" height="150" viewBox="0 0 250 150">
+
<defs>
+
</defs>
+
<path d="M30,100 C50,30,70,50,100,100 S150,40,200,80" stroke="black" fill="none" stroke-width="3" />
+
<circle cx="30" cy="100" r="4" />
+
<circle cx="50" cy="30" r="2" stroke="gray" fill="gray" />
+
<path d="M30,100 L50,30" stroke="gray" />
+
<circle cx="100" cy="100" r="4" />
+
<circle cx="70" cy="50" r="2" stroke="gray" fill="gray" />
+
<path d="M100,100 L70,50" stroke="gray" />
+
<circle cx="200" cy="80" r="4" />
+
<circle cx="150" cy="40" r="2" stroke="gray" fill="gray" />
+
<path d="M200,80 L150,40" stroke="gray" />
+
</svg>
+8
docs/img/03_pT.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="100" viewBox="0 0 400 100">
+
<defs>
+
</defs>
+
<path d="M30,60 Q80,-10,100,60 Q130,25,200,40" stroke="black" fill="none" stroke-width="3" />
+
<path d="M30,60 Q80,-10,100,60 T200,40" stroke="black" fill="none" stroke-width="3" transform="translate(200,0)" />
+
</svg>
+8
docs/img/03_pZ.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="90" height="70" viewBox="0 0 90 70">
+
<defs>
+
</defs>
+
<path d="M10,10 h30 v50 h-30 Z M50,10 h30 v50 Z" stroke="black" fill="none" id="pathZ0" />
+
<use xlink:href="#pathZ0" />
+
</svg>
+16
docs/img/04_align.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="250" height="120" viewBox="0 0 250 120">
+
<defs>
+
</defs>
+
<path d="M75,100 L75,0" stroke="gray" />
+
<path d="M140,30 L250,30" stroke="gray" />
+
<path d="M140,60 L250,60" stroke="gray" />
+
<path d="M140,90 L250,90" stroke="gray" />
+
<text x="75" y="30" font-size="24" text-anchor="start">Start</text>
+
<text x="75" y="60" font-size="24" text-anchor="middle">Middle</text>
+
<text x="75" y="90" font-size="24" text-anchor="end">End</text>
+
<text x="150" y="30" font-size="24" dominant-baseline="auto">Auto</text>
+
<text x="150" y="60" font-size="24" dominant-baseline="middle">Middle</text>
+
<text x="150" y="90" font-size="24" dominant-baseline="hanging">Hanging</text>
+
</svg>
+13
docs/img/04_fill.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="550" height="370" viewBox="0 0 550 370">
+
<defs>
+
</defs>
+
<path d="M20,0 V370 M10,60 H500 M10,120 H500 M10,180 H500 M10,240 H500 M10,300 H500 M10,360 H500" stroke="gray" />
+
<text x="20" y="60" font-size="50">Simplest Text</text>
+
<text x="20" y="120" font-size="50" stroke="black">Outline / Filled</text>
+
<text x="20" y="180" font-size="50" stroke="black" stroke-width="5">Too big stroke</text>
+
<text x="20" y="240" font-size="50" stroke="black" stroke-width="0.5" fill="none">Outlined only</text>
+
<text x="20" y="300" font-size="50" stroke="black" fill="red">Outlined and colored</text>
+
<text x="20" y="360" font-size="50" fill="blue">Colored fill only</text>
+
</svg>
docs/img/04_fonts1.png

This is a binary file and will not be displayed.

docs/img/04_fonts2.png

This is a binary file and will not be displayed.

+14
docs/img/04_len.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="0 0 400 200">
+
<defs>
+
</defs>
+
<text x="20" y="30" font-size="20" textLength="250" lengthAdjust="spacing">Two words</text>
+
<text x="20" y="70" font-size="20" textLength="250" lengthAdjust="spacingAndGlyphs">Two words</text>
+
<text x="20" y="110" font-size="20">Two words (normal length)</text>
+
<text x="20" y="150" font-size="20" textLength="80" lengthAdjust="spacing">Two words</text>
+
<text x="20" y="190" font-size="20" textLength="80" lengthAdjust="spacingAndGlyphs">Two words</text>
+
<path d="M20,10 L20,195" stroke="gray" />
+
<path d="M270,80 L270,10" stroke="gray" />
+
<path d="M100,130 L100,195" stroke="gray" />
+
</svg>
+8
docs/img/04_multiline_text.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="100" viewBox="0 0 200 100">
+
<defs>
+
</defs>
+
<text x="50" y="20" font-size="14" text-anchor="middle"><tspan x="50" dy="0em">this is</tspan><tspan x="50" dy="1em">a</tspan><tspan x="50" dy="1em">multiline text</tspan><tspan x="50" dy="1em">given as a</tspan><tspan x="50" dy="1em">list</tspan></text>
+
<text x="150" y="20" font-size="14" text-anchor="middle"><tspan x="150" dy="0em">this is</tspan><tspan x="150" dy="1em">a</tspan><tspan x="150" dy="1em">multiline text</tspan><tspan x="150" dy="1em">given as a</tspan><tspan x="150" dy="1em">string</tspan></text>
+
</svg>
+8
docs/img/04_mutiline_text.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="100" viewBox="0 0 200 100">
+
<defs>
+
</defs>
+
<text x="50" y="20" font-size="14" text-anchor="middle"><tspan x="50" dy="0em">this is</tspan><tspan x="50" dy="1em">a</tspan><tspan x="50" dy="1em">multiline text</tspan><tspan x="50" dy="1em">given as a</tspan><tspan x="50" dy="1em">list</tspan></text>
+
<text x="150" y="20" font-size="14" text-anchor="middle"><tspan x="150" dy="0em">this is</tspan><tspan x="150" dy="1em">a</tspan><tspan x="150" dy="1em">multiline text</tspan><tspan x="150" dy="1em">given as a</tspan><tspan x="150" dy="1em">string</tspan></text>
+
</svg>
+31
docs/img/04_path.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="500" height="200" viewBox="0 0 500 200">
+
<defs>
+
<path d="M30,50 C50,20,70,20,120,50 S150,10,200,50" stroke="gray" fill="none" id="textpath0" />
+
<path d="M250,30 L300,30 A30,30,0,0,1,330,60 L330,110" stroke="gray" fill="none" id="textpath1" />
+
<path d="M30,110 L100,110 L100,160" stroke="gray" fill="none" id="textpath2" />
+
<path d="M150,110 A40,30,0,1,0,230,110 M250,110 L270,140" stroke="gray" fill="none" id="textpath3" />
+
<path d="M330,130 L330,160 A30,30,0,0,1,300,180 L200,180" stroke="gray" fill="none" id="textpath4" />
+
</defs>
+
<use xlink:href="#textpath0" />
+
<use xlink:href="#textpath1" />
+
<use xlink:href="#textpath2" />
+
<use xlink:href="#textpath3" />
+
<use xlink:href="#textpath4" />
+
<text font-size="14"><textPath xlink:href="#textpath0">
+
<tspan dy="0em">Following a cubic Bรฉzier curve</tspan>
+
</textPath></text>
+
<text font-size="14"><textPath xlink:href="#textpath1">
+
<tspan dy="0em">Going 'round the bend</tspan>
+
</textPath></text>
+
<text font-size="14"><textPath xlink:href="#textpath2">
+
<tspan dy="0em">Making a quick turn</tspan>
+
</textPath></text>
+
<text font-size="14"><textPath xlink:href="#textpath3">
+
<tspan dy="0em">Text along a broken path</tspan>
+
</textPath></text>
+
<text font-size="14" offset="50%" text-anchor="middle"><textPath xlink:href="#textpath4" startOffset="50%">
+
<tspan dy="0em">centered</tspan>
+
</textPath></text>
+
</svg>
+8
docs/img/04_rot.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="100" viewBox="0 0 200 100">
+
<defs>
+
</defs>
+
<text x="20" y="20" font-size="20" letter-spacing="20" rotate="90">Rotate</text>
+
<text x="20" y="80" font-size="20" letter-spacing="20" rotate="0 90 180 270">Rotate</text>
+
</svg>
+7
docs/img/04_rot2.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="80" viewBox="0 0 200 80">
+
<defs>
+
</defs>
+
<text x="20" y="50" font-size="20" letter-spacing="20"><tspan rotate="68">R</tspan><tspan rotate="50 20">OT</tspan><tspan rotate="291 32 130">ATE</tspan></text>
+
</svg>
+7
docs/img/04_tspan.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="50" viewBox="0 0 600 50">
+
<defs>
+
</defs>
+
<text x="10" y="40" font-size="24">Switch among <tspan font-style="italic">italic</tspan><tspan>, normal, and </tspan><tspan font-weight="bold">bold</tspan><tspan> text.</tspan></text>
+
</svg>
+7
docs/img/04_tspan2.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="300" height="160" viewBox="0 0 300 160">
+
<defs>
+
</defs>
+
<text x="10" y="30" font-size="24">F<tspan dy="5">a</tspan><tspan dy="31" dx="21">l</tspan><tspan dy="89" dx="54">l</tspan></text>
+
</svg>
+7
docs/img/04_tspan3.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="300" height="160" viewBox="0 0 300 160">
+
<defs>
+
</defs>
+
<text x="10" y="30" font-size="24" dx="0,0,21,54" dy="0,5,21,54">Fall</text>
+
</svg>
+16
docs/img/04_weight.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="200" viewBox="0 0 600 200">
+
<defs>
+
</defs>
+
<text x="20" y="35" font-size="30" font-weight="bold">bold</text>
+
<text x="20" y="75" font-size="30" font-style="italic">italic</text>
+
<text x="20" y="115" font-size="30" text-decoration="underline">under</text>
+
<text x="20" y="155" font-size="30" text-decoration="overline">over</text>
+
<text x="20" y="195" font-size="30" text-decoration="line-through">through</text>
+
<text x="200" y="35" font-size="30">normal word space</text>
+
<text x="200" y="75" font-size="30" word-spacing="10">more word space</text>
+
<text x="200" y="115" font-size="30" word-spacing="-5">less word space</text>
+
<text x="200" y="155" font-size="30" letter-spacing="8">wide letter space</text>
+
<text x="200" y="195" font-size="30" letter-spacing="-2">narrow letter space</text>
+
</svg>
+12
docs/img/05_clip.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="200" viewBox="0 0 200 200">
+
<defs>
+
<clipPath id="clip0">
+
<rect x="100" y="100" width="100" height="100" />
+
</clipPath>
+
</defs>
+
<rect x="100" y="100" width="100" height="100" stroke="gray" fill="none" />
+
<circle cx="100" cy="100" r="100" fill="none" stroke="gray" stroke-dasharray="5 5" />
+
<circle cx="100" cy="100" r="100" fill="cyan" clip-path="url(#clip0)" />
+
</svg>
+12
docs/img/05_clip2.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="200" viewBox="0 0 600 200">
+
<defs>
+
<path d="M150,150 L68,145 L32,65 L60,126 L230,120 L194,53 L48,124 L14,99 L221,155 L1,178 L228,68 L117,151 L52,81 L15,5 L13,166 L277,2 L195,175 L110,108 L14,135 L113,195 L224,126 L283,59 L176,59 L112,194 L235,74 L11,106 L284,164 L51,47 L151,30 L170,184 L256,108 L259,171 L97,77 L145,150 L255,129 L201,150 L17,122 L124,190 L206,106 L88,93 L280,179" stroke="black" stroke-width="2" fill="none" id="clip20" />
+
<clipPath id="clip21">
+
<circle cx="150" cy="100" r="75" />
+
</clipPath>
+
</defs>
+
<use xlink:href="#clip20" />
+
<use xlink:href="#clip20" x="300" y="0" clip-path="url(#clip21)" />
+
</svg>
+29
docs/img/05_clip3.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="300" height="300" viewBox="0 0 300 300">
+
<defs>
+
<g id="clip30">
+
<rect x="0" y="50" width="90" height="60" fill="#999" />
+
<circle cx="25" cy="25" r="25" fill="#666" />
+
<path d="M30,0 L80,0 L80,100 Z" fill="#ccc" />
+
</g>
+
<path d="M5,55 C25,5,45,-25,75,55 C85,85,20,105,40,55 Z" stroke="black" stroke-width="1" stroke-dasharray="3 2" fill="none" id="clip31" />
+
<clipPath id="clip32">
+
<use xlink:href="#clip31" x="0" y="0" />
+
</clipPath>
+
<text x="20" y="20" font-size="48" font-weight="bold" transform="rotate(60)" stroke="black" stroke-width="1" stroke-dasharray="3 2" fill="none" id="clip33">CLIP</text>
+
<clipPath id="clip34">
+
<use xlink:href="#clip33" x="0" y="0" />
+
</clipPath>
+
</defs>
+
<use xlink:href="#clip30" x="0" y="0" clip-path="url(#clip32)" />
+
<g transform="translate(100,0)">
+
<use xlink:href="#clip30" />
+
<use xlink:href="#clip31" x="0" y="0" />
+
</g>
+
<use xlink:href="#clip30" x="0" y="150" clip-path="url(#clip34)" />
+
<g transform="translate(100,150)">
+
<use xlink:href="#clip30" />
+
<use xlink:href="#clip33" x="0" y="0" />
+
</g>
+
</svg>
+11
docs/img/05_lingrad.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="80" viewBox="0 0 200 80">
+
<defs>
+
<linearGradient x1="150" y1="0" x2="0" y2="0" gradientUnits="userSpaceOnUse" id="grad10">
+
<stop offset="0" stop-color="green" />
+
<stop offset="1" stop-color="yellow" />
+
</linearGradient>
+
</defs>
+
<rect x="10" y="10" width="150" height="60" stroke="black" fill="url(#grad10)" />
+
</svg>
+15
docs/img/05_mask.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="100" viewBox="0 0 200 100">
+
<defs>
+
<linearGradient x1="0" y1="0" x2="1" y2="0" gradientUnits="objectBoundingBox" id="mask10">
+
<stop offset="0" stop-color="white" />
+
<stop offset="1" stop-color="black" />
+
</linearGradient>
+
<mask id="mask11">
+
<rect x="30" y="0" width="100" height="100" fill="url(#mask10)" />
+
</mask>
+
</defs>
+
<rect x="0" y="0" width="200" height="100" fill="cyan" stroke="blue" stroke-width="2" />
+
<rect x="0" y="0" width="200" height="100" fill="pink" stroke="red" stroke-width="2" mask="url(#mask11)" />
+
</svg>
+38
docs/img/05_mask2.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="250" height="150" viewBox="0 0 250 150">
+
<defs>
+
<mask maskContentUnits="objectBoundingBox" id="mask20">
+
<rect x="0" y="0" width="1" height="1" fill="#f00" />
+
</mask>
+
<mask maskContentUnits="objectBoundingBox" id="mask21">
+
<rect x="0" y="0" width="1" height="1" fill="#0f0" />
+
</mask>
+
<mask maskContentUnits="objectBoundingBox" id="mask22">
+
<rect x="0" y="0" width="1" height="1" fill="#00f" />
+
</mask>
+
<mask maskContentUnits="objectBoundingBox" id="mask23">
+
<rect x="0" y="0" width="1" height="1" fill="#fff" />
+
</mask>
+
</defs>
+
<rect x="10" y="10" width="50" height="50" fill="#f00" />
+
<rect x="70" y="10" width="50" height="50" fill="#0f0" />
+
<rect x="130" y="10" width="50" height="50" fill="#00f" />
+
<rect x="190" y="10" width="50" height="50" fill="#fff" stroke="black" />
+
<g mask="url(#mask20)">
+
<circle cx="35" cy="115" r="25" fill="black" />
+
<text x="35" y="80" font-size="14" text-anchor="middle">Red</text>
+
</g>
+
<g mask="url(#mask21)">
+
<circle cx="95" cy="115" r="25" fill="black" />
+
<text x="95" y="80" font-size="14" text-anchor="middle">Green</text>
+
</g>
+
<g mask="url(#mask22)">
+
<circle cx="155" cy="115" r="25" fill="black" />
+
<text x="155" y="80" font-size="14" text-anchor="middle">Blue</text>
+
</g>
+
<g mask="url(#mask23)">
+
<circle cx="215" cy="115" r="25" fill="black" />
+
<text x="215" y="80" font-size="14" text-anchor="middle">White</text>
+
</g>
+
</svg>
+34
docs/img/05_mask3.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="250" height="100" viewBox="0 0 250 100">
+
<defs>
+
<mask maskContentUnits="objectBoundingBox" id="mask30">
+
<rect x="0" y="0" width="1" height="1" fill-opacity="1.0" fill="white" />
+
</mask>
+
<mask maskContentUnits="objectBoundingBox" id="mask31">
+
<rect x="0" y="0" width="1" height="1" fill-opacity="0.75" fill="white" />
+
</mask>
+
<mask maskContentUnits="objectBoundingBox" id="mask32">
+
<rect x="0" y="0" width="1" height="1" fill-opacity="0.5" fill="white" />
+
</mask>
+
<mask maskContentUnits="objectBoundingBox" id="mask33">
+
<rect x="0" y="0" width="1" height="1" fill-opacity="0.25" fill="white" />
+
</mask>
+
</defs>
+
<g mask="url(#mask30)">
+
<circle cx="35" cy="35" r="25" />
+
<text x="35" y="80" font-size="14" text-anchor="middle">100%</text>
+
</g>
+
<g mask="url(#mask31)">
+
<circle cx="95" cy="35" r="25" />
+
<text x="95" y="80" font-size="14" text-anchor="middle">50%</text>
+
</g>
+
<g mask="url(#mask32)">
+
<circle cx="155" cy="35" r="25" />
+
<text x="155" y="80" font-size="14" text-anchor="middle">50%</text>
+
</g>
+
<g mask="url(#mask33)">
+
<circle cx="215" cy="35" r="25" />
+
<text x="215" y="80" font-size="14" text-anchor="middle">25%</text>
+
</g>
+
</svg>
+11
docs/img/05_radgrad.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="0 0 400 200">
+
<defs>
+
<radialGradient cx="200" cy="100" r="100" gradientUnits="userSpaceOnUse" id="grad20">
+
<stop offset="0" stop-color="green" stop-opacity="1" />
+
<stop offset="1" stop-color="orange" stop-opacity="1" />
+
</radialGradient>
+
</defs>
+
<rect x="0" y="0" width="100%" height="100%" fill="url(#grad20)" />
+
</svg>
+24
docs/img/06_group.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="240" height="120" viewBox="0 0 240 120">
+
<defs>
+
</defs>
+
<g id="house" fill="none" stroke="black">
+
<rect x="6" y="50" width="60" height="60" />
+
<path d="M6,50 L36,9 L66,50" />
+
<path d="M36,110 L36,80 L50,80 L50,110" />
+
</g>
+
<g id="man" fill="none" stroke="blue">
+
<circle cx="85" cy="56" r="10" />
+
<path d="M85,66 L85,80" />
+
<path d="M76,104 L85,80 L94,104" />
+
<path d="M76,70 L85,76 L94,70" />
+
</g>
+
<g id="woman" fill="none" stroke="red">
+
<circle cx="110" cy="56" r="10" />
+
<path d="M110,66 L110,80 L100,90 L120,90 L110,80" />
+
<path d="M104,104 L108,90" />
+
<path d="M112,90 L116,104" />
+
<path d="M101,70 L110,76 L119,70" />
+
</g>
+
</svg>
+7
docs/img/06_imag.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="200" viewBox="0 0 200 200">
+
<defs>
+
</defs>
+
<image x="0" y="0" width="200" height="200" xlink:href="data:image/png;base64,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" />
+
</svg>
+14
docs/img/06_use.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="220" height="170" viewBox="0 0 220 170">
+
<defs>
+
<g id="house" fill="none" stroke="black">
+
<rect x="6" y="50" width="60" height="60" />
+
<path d="M6,50 L36,9 L66,50" />
+
<path d="M36,110 L36,80 L50,80 L50,110" />
+
</g>
+
</defs>
+
<use xlink:href="#house" />
+
<use xlink:href="#house" x="100" y="50" />
+
<use xlink:href="#house" x="150" y="20" />
+
</svg>
+9
docs/img/07_cart1.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="100" height="100" viewBox="0 0 100 100">
+
<defs>
+
</defs>
+
<path d="M0,100 L0,0 L100,0" fill="none" stroke="green" stroke-width="2" />
+
<path d="M40,40 L100,40 L70,70 L40,70" fill="silver" stroke="black" stroke-width="2" />
+
<text x="5" y="95" font-size="12">downward y</text>
+
</svg>
+11
docs/img/07_cart2.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="100" height="100" viewBox="0 0 100 100">
+
<defs>
+
</defs>
+
<g transform="translate(0,100) scale(1,-1)">
+
<path d="M0,100 L0,0 L100,0" fill="none" stroke="green" stroke-width="2" />
+
<path d="M40,40 L100,40 L70,70 L40,70" fill="silver" stroke="black" stroke-width="2" />
+
<text x="5" y="95" font-size="12">upward y</text>
+
</g>
+
</svg>
+9
docs/img/07_cart3.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="100" height="100" viewBox="0 0 100 100" transform="scale(1,-1)">
+
<defs>
+
</defs>
+
<path d="M0,100 L0,0 L100,0" fill="none" stroke="green" stroke-width="2" />
+
<path d="M40,40 L100,40 L70,70 L40,70" fill="silver" stroke="black" stroke-width="2" />
+
<text x="5" y="95" font-size="12">upward y</text>
+
</svg>
+10
docs/img/07_rota.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="200" viewBox="0 0 200 200">
+
<defs>
+
</defs>
+
<rect x="0" y="0" width="200" height="200" stroke="gray" fill="none" />
+
<rect x="70" y="30" width="40" height="40" fill="silver" />
+
<rect x="70" y="30" width="40" height="40" fill="gray" transform="rotate(22.5)" />
+
<rect x="70" y="30" width="40" height="40" fill="black" transform="rotate(45)" />
+
</svg>
+17
docs/img/07_rota2.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="300" height="200" viewBox="0 0 300 200">
+
<defs>
+
<g id="arrow">
+
<path d="M110,100 L160,100" />
+
<path d="M160,100 L155,95 L155,105" />
+
</g>
+
</defs>
+
<circle cx="100" cy="100" r="3" fill="black" />
+
<use xlink:href="#arrow" x="0" y="0" stroke="black" fill="black" />
+
<g stroke="red" fill="red">
+
<use xlink:href="#arrow" x="0" y="0" transform="rotate (60,100,100)" />
+
<use xlink:href="#arrow" x="0" y="0" transform="rotate (-90,100,100)" />
+
<use xlink:href="#arrow" x="0" y="0" transform="rotate (-150,100,100)" />
+
</g>
+
</svg>
+12
docs/img/07_scalcent.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="300" height="200" viewBox="0 0 300 200">
+
<defs>
+
<rect x="70" y="80" width="60" height="40" stroke="black" fill="none" id="scalecenter0" />
+
</defs>
+
<circle cx="100" cy="100" r="4" fill="black" />
+
<use xlink:href="#scalecenter0" />
+
<use xlink:href="#scalecenter0" x="0" y="0" transform="translate(-100,-100) scale(2)" stroke-width="0.5" />
+
<use xlink:href="#scalecenter0" x="0" y="0" transform="translate(-150,-150) scale(2.5)" stroke-width="0.4" />
+
<use xlink:href="#scalecenter0" x="0" y="0" transform="translate(-200,-200) scale(3)" stroke-width="0.33" />
+
</svg>
+9
docs/img/07_scale.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="200" viewBox="0 0 200 200">
+
<defs>
+
<rect x="0" y="0" width="40" height="40" fill="none" stroke-width="2" id="scale10" />
+
</defs>
+
<use xlink:href="#scale10" x="10" y="10" stroke="black" />
+
<use xlink:href="#scale10" x="10" y="10" stroke="red" transform="scale(2)" />
+
</svg>
+9
docs/img/07_scale2.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="100" viewBox="0 0 200 100">
+
<defs>
+
<rect x="0" y="0" width="40" height="40" fill="none" stroke-width="2" id="scale20" />
+
</defs>
+
<use xlink:href="#scale20" x="10" y="10" stroke="black" />
+
<use xlink:href="#scale20" x="10" y="10" stroke="red" transform="scale(3,1.5)" />
+
</svg>
+23
docs/img/07_skew.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="100" viewBox="0 0 200 100" id-srefix="skew">
+
<defs>
+
</defs>
+
<g stroke="gray" stroke-dasharray="4 4">
+
<path d="M0,0 L200,0" />
+
<path d="M20,0 L20,90" />
+
<path d="M120,0 L120,90" />
+
</g>
+
<g transform="translate(20,0)">
+
<g transform="skewX(30)">
+
<path d="M50,0 L0,0 L0,50" stroke="black" fill="none" stroke-width="2" />
+
<text x="0" y="60" font-size="16">skewX</text>
+
</g>
+
</g>
+
<g transform="translate(120,0)">
+
<g transform="skewY(30)">
+
<path d="M50,0 L0,0 L0,50" stroke="black" fill="none" stroke-width="2" />
+
<text x="0" y="60" font-size="16">skewY</text>
+
</g>
+
</g>
+
</svg>
+8
docs/img/07_trans.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="200" viewBox="0 0 200 200">
+
<defs>
+
</defs>
+
<rect x="0" y="0" width="40" height="40" />
+
<rect x="0" y="0" width="40" height="40" fill="red" transform="translate(50,50)" />
+
</svg>
docs/img/example1.png

This is a binary file and will not be displayed.

+1307
docs/index.md
···
+
# Drawsvg Quick Reference
+
+
Repository: [https://github.com/cduck/drawsvg](https://github.com/cduck/drawsvg)
+
+
+
```python
+
import drawsvg as dw
+
```
+
+
+
## Canvas and Document Structure
+
+
```python
+
d = dw.Drawing(width, height, origin=(0, 0),
+
context: drawsvg.types.Context = None, animation_config=None,
+
id_prefix='d', **svg_args)
+
```
+
+
It is recommended to use a unique `id_prefix` for each svg if you embed multiple on a web page.
+
+
```python
+
d = dw.Drawing(400, 300, id_prefix='pic')
+
```
+
+
+
## Basic Shapes
+
+
### One Line
+
+
```python
+
dw.Line(sx, sy, ex, ey, **kwargs)
+
```
+
+
```python
+
line = dw.Line(30, 30, 90, 90, stroke='black')
+
d.append(line)
+
```
+
+
![svg](img/01_line.svg)
+
+
+
### Multiple Lines
+
+
This is SVG's `polyline` (but drawsvg renders as path with multiple L).
+
+
```python
+
dw.Lines(sx, sy, *points, close=False, **kwargs)
+
```
+
+
```python
+
lines = dw.Lines(10, 90, 10, 10, 80, 90, 80, 10,
+
fill='none', stroke='black')
+
d.append(lines)
+
```
+
+
![svg](img/01_multilines.svg)
+
+
+
```python
+
x = [30 + x*10 for x in range(20)]
+
y = [80, 20]*10
+
xy = [item for sublist in zip(x, y) for item in sublist]
+
d.append(dw.Lines(*xy, stroke='black', stroke_width=5, fill='none'))
+
```
+
+
![svg](img/01_multilines2.svg)
+
+
+
### Polygon
+
+
SVG `Polygon` is drawsvg `Lines` with `close=True`.
+
+
```python
+
polygon = dw.Lines(15, 10, 55, 10, 45, 20, 5, 20,
+
fill='red', stroke='black', close='true')
+
star = dw.Lines(48, 16, 16, 96, 96, 48, 0, 48, 88, 96,
+
stroke='black', fill='none', close='true')
+
d.append(star)
+
```
+
+
![svg](img/01_polygon.svg)
+
+
+
### Rectangle
+
+
```python
+
dw.Rectangle(x, y, width, height, **kwargs)
+
```
+
+
```python
+
# Black interior, no outline
+
d.append(dw.Rectangle(10, 10, 90, 150))
+
# No interior, black outline
+
d.append(dw.Rectangle(120, 10, 60, 120,
+
fill='none', stroke='black'))
+
# Blue interior, thick semi-transparent red outline
+
d.append(dw.Rectangle(210, 10, 75, 90,
+
fill='#0000ff', stroke='red',
+
stroke_width=7, stroke_opacity=0.5))
+
# Semi-transparent yellow interior, dashed green outline
+
d.append(dw.Rectangle(300, 10, 105, 60,
+
fill='yellow', fill_opacity=0.5,
+
stroke='green', stroke_width=2,
+
stroke_dasharray='5,2'))
+
```
+
+
![svg](img/01_rect.svg)
+
+
Rounded corners:
+
+
```python
+
# Define both rx and ry
+
d.append(dw.Rectangle(10, 10, 80, 180, rx='10', ry='10',
+
stroke='black', fill='none'))
+
# If only one is given, it applies to both
+
d.append(dw.Rectangle(110, 10, 80, 180, ry='20',
+
stroke='black', fill='none'))
+
d.append(dw.Rectangle(210, 10, 80, 180, rx='40',
+
stroke='black', fill='none'))
+
# Rx and ry unequal
+
d.append(dw.Rectangle(310, 10, 80, 180, rx='30', ry='10',
+
stroke='black', fill='none'))
+
d.append(dw.Rectangle(410, 10, 80, 180, rx='10', ry='30',
+
stroke='black', fill='none'))
+
```
+
+
![svg](img/01_rectround.svg)
+
+
+
### Circle
+
+
```python
+
dw.Circle(cx, cy, r, **kwargs)
+
```
+
+
cx and cy point to circle's center, r refer to its radius
+
+
```python
+
d.append(dw.Circle(50, 50, 40))
+
d.append(dw.Circle(150, 50, 40,
+
stroke='black', fill='none'))
+
d.append(dw.Circle(250, 50, 40,
+
stroke='black', fill='none',
+
stroke_width=15))
+
```
+
+
![svg](img/01_circ.svg)
+
+
+
### Ellipse
+
+
```python
+
dw.Ellipse(cx, cy, rx, ry, **kwarg)
+
```
+
(cx,cy) points to the center and (rx,ry) tells its radius
+
+
```python
+
d.append(dw.Ellipse(50, 50, 50, 30))
+
d.append(dw.Ellipse(160, 50, 50, 30,
+
stroke='black', fill='none'))
+
d.append(dw.Ellipse(250, 50, 30, 45,
+
stroke='black',fill='none'))
+
```
+
+
![svg](img/01_ellip.svg)
+
+
+
+
## Color and Painting Properties
+
+
For a full list, see [W3C specifications](https://www.w3.org/TR/SVG11/styling.html).
+
+
+
### fill and stroke\_color
+
+
Some color keyword names are:
+
aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, purple, red, silver, teal, white, and yellow.
+
+
Also supported is `#rrggbb`, `#rgb` (hexadecimal), or `rgb(R,G,B)` with 0-255 or with 0-100% for each value.
+
+
```python
+
c = ['red', '#9f9', '#9999ff', 'rgb(255,128,64)', 'rgb(60%,20%,60%)']
+
for i in range(5):
+
y = (i + 1)*10
+
d.append(dw.Line(10, y, 80, y,
+
stroke=c[i], stroke_width=5))
+
```
+
+
![svg](img/02_fsc.svg)
+
+
+
### fill\_opacity and stroke\_opacity
+
+
Value range from 0 = transparent to 1 = solid.
+
+
```python
+
for i in range(5):
+
y = (i + 1)*10
+
d.append(dw.Line(0, y, 290, y,
+
stroke='black', stroke_width=5,
+
stroke_opacity=i/5 + 0.1))
+
d.append(dw.Rectangle(i*60, 70, 50, 50,
+
fill='red', fill_opacity=i/5+0.1))
+
```
+
+
![svg](img/02_foso.svg)
+
+
+
### stroke\_dasharray
+
+
```python
+
# Nine-pixel dash, five-pixel gap
+
d.append(dw.Line(10, 10, 100, 10,
+
stroke='black', stroke_width=2,
+
stroke_dasharray='9,5'))
+
# Five-pixel dash, three-pixel gap, nine-pixel dash, two-pixel gap
+
d.append(dw.Line(10, 20, 100, 20,
+
stroke='black', stroke_width=2,
+
stroke_dasharray='5,3,9,2'))
+
# Odd number of entries alternates dashes and gaps
+
d.append(dw.Line(10, 30, 100, 30,
+
stroke='black', stroke_width=2,
+
stroke_dasharray='9,3,5'))
+
```
+
+
![svg](img/02_dash.svg)
+
+
+
### stroke\_width
+
+
```python
+
for i in range(20):
+
d.append(dw.Line((i+1)*15, 10, (i+1)*15, 90,
+
stroke='black', stroke_width=abs(10-i)+1))
+
```
+
+
![svg](img/02_strokewdth.svg)
+
+
+
### stroke\_linecap
+
+
`stroke_linecap` can be set to `butt`, `round`, or `square`.
+
Note that the latter two extend beyond the end coordinates.
+
+
```python
+
d.append(dw.Line(10, 15, 50, 15,
+
stroke='black', stroke_width=15,
+
stroke_linecap='butt'))
+
d.append(dw.Line(10, 45, 50, 45,
+
stroke='black', stroke_width=15,
+
stroke_linecap='round'))
+
d.append(dw.Line(10, 75, 50, 75,
+
stroke='black', stroke_width=15,
+
stroke_linecap='square'))
+
# Guide lines
+
d.append(dw.Lines(10, 0, 10, 100, stroke='#999'))
+
d.append(dw.Lines(50, 0, 50, 100, stroke='#999'))
+
```
+
+
![svg](img/02_linecap.svg)
+
+
+
### stroke\_linejoin
+
+
Define the way lines connect at a corner with `stroke-linejoin`: `miter` (pointed), `round`, or `bevel` (flat).
+
+
```python
+
d.append(dw.Line(0, 20, 300, 20, stroke='gray'))
+
g = dw.Group(stroke_width=20, stroke='black', fill='none')
+
g.append(dw.Lines(10, 80, 50, 20, 90, 80,
+
stroke_linejoin='miter'))
+
g.append(dw.Lines(110, 80, 150, 20, 190, 80,
+
stroke_linejoin='round'))
+
g.append(dw.Lines(210, 80, 250, 20, 290, 80,
+
stroke_linejoin='bevel'))
+
d.append(g)
+
```
+
+
![svg](img/02_join.svg)
+
+
+
### stroke\_miterlimit
+
+
When two line segments meet at a sharp angle and miter joins have been specified for `stroke-linejoin`,
+
it is possible for the miter to extend far beyond the thickness of the line stroking the path.
+
The `stroke-miterlimit` imposes a limit on the ratio of the miter length to the `stroke-width`.
+
When the limit is exceeded, the join is converted from a miter to a bevel.
+
(From [W3C doc](https://www.w3.org/TR/SVG11/painting.html#StrokeMiterlimitProperty))
+
+
```python
+
d.append(dw.Line(0, 30, 300, 30, stroke='gray'))
+
g = dw.Group(stroke_width=20, stroke='black',
+
fill='none', stroke_linejoin='miter')
+
g.append(dw.Lines(10, 90, 40, 30, 70, 90))
+
g.append(dw.Lines(100, 90, 130, 30, 160, 90,
+
stroke_miterlimit=2.3))
+
g.append(dw.Lines(190, 90, 220, 30, 250, 90,
+
stroke_miterlimit=1))
+
d.append(g)
+
```
+
+
![svg](img/02_mlimit.svg)
+
+
+
## Path
+
+
```python
+
path = dw.Path(**kwargs)
+
```
+
+
The following Path specifiers are also available as lowercase characters.
+
In that case, their movements are relative to current location.
+
+
+
### M: moveto
+
+
```python
+
path.M(x, y)
+
```
+
+
Move to `x, y` (and draw nothing).
+
+
+
### L: lineto
+
+
```python
+
path.L(x, y)
+
```
+
+
Draw a straight line to `x, y`.
+
+
```python
+
g = dw.Group(stroke='black', fill='none')
+
+
p = dw.Path()
+
p.M(10, 10).L(100, 10)
+
g.append(p)
+
+
p = dw.Path()
+
p.M(10, 20).L(100, 20).L(100, 50)
+
g.append(p)
+
+
p = dw.Path()
+
p.M(40, 60).L(10, 60).L(40, 42)
+
p.M(60, 60).L(90, 60).L(60, 42)
+
g.append(p)
+
+
d.append(g)
+
```
+
+
![svg](img/03_pL.svg)
+
+
+
### H: horizontal line
+
+
```python
+
path.H(x)
+
```
+
+
Draw a horizontal line to the new `x` location.
+
+
+
### V: vertical line
+
+
```python
+
path.V(y)
+
```
+
+
Draw a vertical line to the new `y` location.
+
+
```python
+
p = dw.Path(stroke='black', fill='none')
+
d.append(p.M(10, 10).H(100))
+
d.append(p.M(10, 20).H(100).V(50))
+
```
+
![svg](img/03_pHV.svg)
+
+
+
### Q: quadratic Bรฉzier curve (one control point)
+
+
```python
+
path.Q(x_ctl, y_ctl, x_end, y_end)
+
```
+
+
Draw a quadratic Bรฉzier curve from current location to `x_end, y_end` by means of `x_ctl, y_ctl`.
+
+
```python
+
# Curve only (left)
+
p = dw.Path(stroke='black', fill='none', stroke_width=3)
+
d.append(p.M(30, 75).Q(240, 30, 300, 120))
+
# With control point and construction lines
+
d.append(dw.Use(p, 300, 0))
+
g = dw.Group(stroke='gray', fill='gray')
+
g.append(dw.Circle(330, 75, 3))
+
g.append(dw.Circle(600, 120, 3))
+
g.append(dw.Circle(540, 30, 3))
+
g.append(dw.Line(330, 75, 540, 30))
+
g.append(dw.Line(540, 30, 600, 120))
+
g.append(dw.Line(330, 75, 600, 120, stroke_dasharray='5,5'))
+
g.append(dw.Circle(435, 52.5, 3))
+
g.append(dw.Circle(570, 75, 3))
+
g.append(dw.Line(435, 52.5, 570, 75))
+
g.append(dw.Circle(502.5, 63.75, 4, fill='none'))
+
d.append(g)
+
```
+
+
![svg](img/03_pQ.svg)
+
+
+
### T: smooth quadratic Bรฉzier curve (generated control point)
+
+
```python
+
path.T(x, y)
+
```
+
+
Draws a quadratic Bรฉzier curve from the current point to (x, y).
+
The control point is assumed to be the reflection of the control point on the previous command relative to the current point.
+
If there is no previous command or if the previous command was not a Q, q, T or t, assume the control point is coincident with the current point.
+
(From [W3C Doc](https://www.w3.org/TR/SVG11/paths.html#PathDataQuadraticBezierCommands))
+
+
```python
+
# Curve sequence (left)
+
p = dw.Path(stroke='black', fill='none', stroke_width=3)
+
d.append(p.M(30, 60).Q(80, -10, 100, 60).Q(130, 25, 200, 40))
+
# With smooth continuation (right)
+
p = dw.Path(stroke='black', fill='none', stroke_width=3,
+
transform='translate(200,0)')
+
d.append(p.M(30, 60).Q(80, -10, 100, 60).T(200, 40))
+
```
+
+
![svg](img/03_pT.svg)
+
+
+
### C: cubic Bรฉzier curve (two control points)
+
+
```python
+
path.C(x_ctl_1, y_ctl_1, x_ctl_2, y_ctl_2, x_end, y_end)
+
```
+
+
Draw a cubic Bรฉzier curve by means of two control points (one for start and one for end).
+
+
```python
+
pnt_1 = (40, 50)
+
pnt_2 = (110, 50)
+
ctl_1_x = (10, 60, 110, 110, 60, 110)
+
ctls_2 = ((140, 10), (90, 10), (40, 10), (40, 10), (90, 90), (40, 90))
+
+
for i in range(6):
+
trans = f'translate({i*100},0)'
+
p = dw.Path(stroke='black', fill='none',
+
stroke_width=3, transform=trans)
+
ctl_1 = (ctl_1_x[i], 10)
+
ctl_2 = ctls_2[i]
+
p.M(*pnt_1)
+
p.C(*ctl_1, *ctl_2, *pnt_2)
+
d.append(p)
+
g = dw.Group(stroke='gray', fill='gray',
+
stroke_width=1, transform=trans)
+
g.append(dw.Circle(*ctl_1, 2))
+
g.append(dw.Circle(*ctl_2, 2))
+
g.append(dw.Line(*pnt_1, *ctl_1))
+
g.append(dw.Line(*pnt_2, *ctl_2))
+
d.append(g)
+
```
+
+
![svg](img/03_pC.svg)
+
+
+
### S: smooth cubic Bรฉzier (one control point)
+
+
Similar to `T` in quadratic Bรฉzier curve. The first control point is calculated as reflection of the previous second control point.
+
+
```python
+
path.S(x_ctl_2, y_ctl_2, x_end, y_end)
+
```
+
+
```python
+
pnt_1 = (30, 100)
+
pnt_2 = (100, 100)
+
pnt_3 = (200, 80)
+
ctl_1 = (50, 30)
+
ctl_2 = (70, 50)
+
ctl_3 = (150, 40)
+
+
p = dw.Path(stroke='black', fill='none', stroke_width=3)
+
p.M(*pnt_1)
+
p.C(*ctl_1, *ctl_2, *pnt_2)
+
p.S(*ctl_3, *pnt_3)
+
d.append(p)
+
+
for pnt, ctl in zip((pnt_1, pnt_2, pnt_3), (ctl_1, ctl_2, ctl_3)):
+
d.append(dw.Circle(*pnt, 4))
+
d.append(dw.Circle(*ctl, 2, stroke='gray', fill='gray'))
+
d.append(dw.Line(*pnt, *ctl, stroke='gray'))
+
```
+
+
![svg](img/03_pS.svg)
+
+
+
### A: elliptical Arc
+
+
```python
+
path.A(rx, ry, rot, largeArc, sweep, ex, ey)
+
+
rx, ry: radius
+
rot: x-axis rotation
+
largeArc: True or False
+
sweep: True (positive) or False (negative) angle
+
ex, ey: end point
+
```
+
+
```python
+
p = dw.Path(stroke='red')
+
d.append(p.M(125, 75).A(100, 50, rot=0, large_arc=0, sweep=0, ex=225, ey=125))
+
p = dw.Path(stroke='blue')
+
d.append(p.M(125, 75).A(100, 50, rot=0, large_arc=0, sweep=1, ex=225, ey=125))
+
p = dw.Path(stroke='rgb(0 80 255)',stroke_dasharray='5 3')
+
d.append(p.M(125, 75).A(100, 50, rot=0, large_arc=1, sweep=0, ex=225, ey=125))
+
p = dw.Path(stroke='rgb(255 80 0)',stroke_dasharray='5 3')
+
d.append(p.M(125, 75).A(100, 50, rot=0, large_arc=1, sweep=1, ex=225, ey=125))
+
```
+
+
![svg](img/03_pA.svg)
+
+
+
### Z: closepath
+
+
```python
+
path.Z()
+
```
+
+
Close the path.
+
+
```python
+
p = dw.Path(stroke='black', fill='none')
+
d.append(p.M(10, 10).h(30).v(50).h(-30).Z())
+
d.append(p.M(50, 10).h(30).v(50).Z())
+
```
+
+
![svg](img/03_pZ.svg)
+
+
+
## Text
+
```python
+
dw.Text(text, fontSize, x=None, y=None, *, center=False,
+
line_height=1, line_offset=0, path=None,
+
start_offset=None, path_args=None, tspan_args=None,
+
cairo_fix=True, **kwargs)
+
```
+
+
### Fill and Outline
+
+
Default is black as fill color and no outline.
+
+
```python
+
# Reference lines
+
l = dw.Path(stroke='gray')
+
l.M(20, 0).V(370)
+
for i in range(1, 7):
+
l.M(10, i*60).H(500)
+
d.append(l)
+
+
d.append(dw.Text('Simplest Text', font_size=50, x=20, y=60))
+
d.append(dw.Text('Outline / Filled', font_size=50, x=20, y=120, stroke='black'))
+
d.append(dw.Text('Too big stroke', font_size=50, x=20, y=180, stroke='black', stroke_width=5))
+
d.append(dw.Text('Outlined only', font_size=50, x=20, y=240, stroke='black', stroke_width=0.5, fill='none'))
+
d.append(dw.Text('Outlined and colored', font_size=50, x=20, y=300, stroke='black', fill='red'))
+
d.append(dw.Text('Colored fill only', font_size=50, x=20, y=360, fill='blue'))
+
```
+
+
![svg](img/04_fill.svg)
+
+
+
### Weight, Style, Decoration, Spacing
+
+
```python
+
d.append(dw.Text('bold', font_size=30, x=20, y=35, font_weight='bold'))
+
d.append(dw.Text('italic', font_size=30, x=20, y=75, font_style='italic'))
+
d.append(dw.Text('under', font_size=30, x=20, y=115, text_decoration='underline'))
+
d.append(dw.Text('over', font_size=30, x=20, y=155, text_decoration='overline'))
+
d.append(dw.Text('through', font_size=30, x=20, y=195, text_decoration='line-through'))
+
d.append(dw.Text('normal word space', font_size=30, x=200, y=35))
+
d.append(dw.Text('more word space', font_size=30, x=200, y=75, word_spacing=10))
+
d.append(dw.Text('less word space', font_size=30, x=200, y=115, word_spacing=-5))
+
d.append(dw.Text('wide letter space', font_size=30, x=200, y=155, letter_spacing=8))
+
d.append(dw.Text('narrow letter space', font_size=30, x=200, y=195, letter_spacing=-2))
+
```
+
+
![svg](img/04_weight.svg)
+
+
+
### Text Alignment
+
+
Horizontal alignment (`text_anchor`) can be `'start'`, `'middle'` or `'end'`.
+
+
Vertical alignment (`dominant_baseline`) can be `'auto'`, `'middle'` or `'hanging'`
+
(and more, see [here](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/dominant-baseline)).
+
+
```python
+
d.append(dw.Line(75, 100, 75, 0, stroke='gray'))
+
d.append(dw.Line(140, 30, 250, 30, stroke='gray'))
+
d.append(dw.Line(140, 60, 250, 60, stroke='gray'))
+
d.append(dw.Line(140, 90, 250, 90, stroke='gray'))
+
d.append(dw.Text('Start', 24, 75, 30, text_anchor='start'))
+
d.append(dw.Text('Middle', 24, 75, 60, text_anchor='middle'))
+
d.append(dw.Text('End', 24, 75, 90, text_anchor='end'))
+
d.append(dw.Text('Auto', 24, 150, 30, dominant_baseline='auto'))
+
d.append(dw.Text('Middle', 24, 150, 60, dominant_baseline='middle'))
+
d.append(dw.Text('Hanging', 24, 150, 90, dominant_baseline='hanging'))
+
```
+
+
![svg](img/04_align.svg)
+
+
+
### TSpan
+
+
Continues a `Text` element.
+
+
```python
+
txt = dw.Text('Switch among ', 24, 10, 40)
+
txt.append(dw.TSpan('italic', font_style='italic'))
+
txt.append(dw.TSpan(', normal, and '))
+
txt.append(dw.TSpan('bold', font_weight='bold'))
+
txt.append(dw.TSpan(' text.'))
+
d.append(txt)
+
```
+
+
![svg](img/04_tspan.svg)
+
+
+
```python
+
txt = dw.Text('F', 24, 10, 30)
+
txt.append(dw.TSpan('a', dy=5))
+
txt.append(dw.TSpan('l', dy=31, dx=21))
+
txt.append(dw.TSpan('l', dy=89, dx=54))
+
d.append(txt)
+
```
+
+
![svg](img/04_tspan2.svg)
+
+
The same could be achieved by a list of dx/dy values:
+
+
```python
+
d.append(dw.Text('Fall', 24, 10, 30,
+
dx='0,0,21,54', dy='0,5,21,54'))
+
```
+
+
![svg](img/04_tspan3.svg)
+
+
+
### Rotate
+
+
Either one angle (degrees), or a list which is applied to all characters.
+
If the list is smaller than the number of characters, the last angle persists.
+
+
```python
+
d.append(dw.Text('Rotate', 20, 20, 20, letter_spacing=20, rotate='90'))
+
d.append(dw.Text('Rotate', 20, 20, 80, letter_spacing=20, rotate='0 90 180 270'))
+
```
+
+
![svg](img/04_rot.svg)
+
+
`TSpan` can also be used:
+
+
```python
+
import random
+
random.seed(1)
+
+
txt = dw.Text('', 20, 20, 50, letter_spacing=20)
+
txt.append(dw.TSpan('R', rotate=random.randrange(360)))
+
txt.append(dw.TSpan('OT', rotate='50 20'))
+
rotate = ' '.join([str(random.randrange(360)) for i in range(3)])
+
txt.append(dw.TSpan('ATE', rotate=rotate))
+
d.append(txt)
+
```
+
+
![svg](img/04_rot2.svg)
+
+
+
### Setting Text Length
+
+
```python
+
s = 'Two words'
+
d.append(dw.Text(s, 20, 20, 30, textLength=250, lengthAdjust='spacing'))
+
d.append(dw.Text(s, 20, 20, 70, textLength=250, lengthAdjust='spacingAndGlyphs'))
+
d.append(dw.Text(s+' (normal length)', 20, 20, 110))
+
d.append(dw.Text(s, 20, 20, 150, textLength=80, lengthAdjust='spacing'))
+
d.append(dw.Text(s, 20, 20, 190, textLength=80, lengthAdjust='spacingAndGlyphs'))
+
+
d.append(dw.Line(20, 10, 20, 195, stroke='gray'))
+
d.append(dw.Line(270, 80, 270, 10, stroke='gray'))
+
d.append(dw.Line(100, 130, 100, 195, stroke='gray'))
+
```
+
+
![svg](img/04_len.svg)
+
+
+
### Text on a Path
+
+
```python
+
curve_path = dw.Path(stroke='gray', fill='none')
+
curve_path.M(30, 50).C(50, 20, 70, 20, 120, 50).S(150, 10, 200, 50)
+
+
round_corner = dw.Path(stroke='gray', fill='none')
+
round_corner.M(250, 30).L(300, 30).A(30, 30, 0, 0, 1, 330, 60).L(330, 110)
+
+
sharp_corner = dw.Path(stroke='gray', fill='none')
+
sharp_corner.M(30, 110).L(100, 110).L(100, 160)
+
+
discontinuous = dw.Path(stroke='gray', fill='none')
+
discontinuous.M(150, 110).A(40, 30, 0, 1, 0, 230, 110).M(250, 110).L(270, 140)
+
+
center_curve = dw.Path(stroke='gray', fill='none')
+
center_curve.M(330, 130).L(330, 160).A(30, 30, 0, 0, 1, 300, 180).L(200, 180)
+
+
d.append(curve_path)
+
d.append(round_corner)
+
d.append(sharp_corner)
+
d.append(discontinuous)
+
d.append(center_curve)
+
+
t_cp = dw.Text('Following a cubic Bรฉzier curve', 14, path=curve_path)
+
t_rc = dw.Text("Going 'round the bend", 14, path=round_corner)
+
t_sc = dw.Text('Making a quick turn', 14, path=sharp_corner)
+
t_dc = dw.Text('Text along a broken path', 14, path=discontinuous)
+
t_ct = dw.Text('centered', 14, path=center_curve, offset='50%', text_anchor='middle')
+
+
d.append(t_cp)
+
d.append(t_rc)
+
d.append(t_sc)
+
d.append(t_dc)
+
d.append(t_ct)
+
```
+
+
![svg](img/04_path.svg)
+
+
+
### Multi Line Text
+
+
This is a particular feature of drawsvg: A list of strings as input for Text()
+
is rendered as multi-line text.
+
+
```python
+
tl = ['this is', 'a', 'multiline text', 'given as a', 'list']
+
d.append(dw.Text(tl, 14, 50, 20, text_anchor='middle'))
+
+
ts = 'this is\na\nmultiline text\ngiven as a\nstring'
+
d.append(dw.Text(ts, 14, 150, 20, text_anchor='middle'))
+
```
+
+
![svg](img/04_multiline_text.svg)
+
+
+
### Fonts
+
+
Specify fonts via `font_family`.
+
+
```python
+
d.append(dw.Text('Some text in Times New Roman.', 30, 10, 35, font_family='Times New Roman'))
+
d.append(dw.Text('Some text in Arial Black.', 30, 10, 75, font_family='Arial Black'))
+
d.append(dw.Text('Some text in Georgia.', 30, 10, 115, font_family='Georgia'))
+
```
+
+
![png](img/04_fonts1.png)
+
+
Specify a default font.
+
+
```python
+
d = dw.Drawing(600, 120, font_family='Times New Roman')
+
d.append(dw.Text('Some text in global setting (Times New Roman).', 30, 10, 35))
+
d.append(dw.Text('Some text in Arial Black.', 30, 10, 75, font_family='Arial Black'))
+
d.append(dw.Text('Some text in Georgia.', 30, 10, 115, font_family='Georgia'))
+
```
+
+
![png](img/04_fonts2.png)
+
+
+
+
## Gradient, Clip, Mask
+
+
### Linear Gradient
+
+
```python
+
gradient = dw.LinearGradient(x1, y1, x2, y2, gradientUnits='userSpaceOnUse', **kwargs)
+
gradient.add_stop(offset, color, opacity=None)
+
```
+
+
```python
+
grad = dw.LinearGradient(150, 0, 0, 0)
+
grad.add_stop(0, 'green')
+
grad.add_stop(1, 'yellow')
+
d.append(dw.Rectangle(10, 10, 150, 60,
+
stroke='black', fill=grad))
+
```
+
+
![svg](img/05_lingrad.svg)
+
+
+
### Radial Gradient
+
+
```python
+
gradient = dw.RadialGradient(cx, cy, r, **kwargs)
+
gradient.add_stop(offset, color, opacity=None)
+
```
+
+
```python
+
gradient = dw.RadialGradient(200, 100, 100)
+
gradient.add_stop(0, 'green', 1)
+
gradient.add_stop(1, 'orange', 1)
+
bg = dw.Rectangle(x=0, y=0, width='100%', height='100%', fill=gradient)
+
d.append(bg)
+
```
+
+
![svg](img/05_radgrad.svg)
+
+
+
### Clip
+
+
```python
+
clip_name = dw.ClipPath()
+
```
+
+
To add shape as Clip, use `.append()` method.
+
To apply Clip, fill `clip_path` argument with `clip_name`.
+
+
```python
+
# Show both shapes as they are
+
d.append(dw.Rectangle(100, 100, 100, 100,
+
stroke='gray', fill='none'))
+
d.append(dw.Circle(100, 100, 100,
+
fill='none', stroke='gray', stroke_dasharray='5 5'))
+
# Apply rect as clip to circle
+
clip = dw.ClipPath()
+
clip.append(dw.Rectangle(100, 100, 100, 100))
+
d.append(dw.Circle(100, 100, 100,
+
fill='cyan', clip_path=clip))
+
```
+
+
![svg](img/05_clip.svg)
+
+
Another example:
+
+
```python
+
# Draw a random path in the left half of the canvas
+
p = dw.Path(stroke='black', stroke_width=2, fill='none')
+
p.M(150, 150)
+
import random
+
random.seed(1)
+
for i in range(40):
+
p.L(random.randint(0, 300), random.randint(0, 200))
+
d.append(p)
+
+
# Circle as clipping shape
+
circ = dw.Circle(150, 100, 75)
+
c = dw.ClipPath()
+
c.append(circ)
+
+
# Repeat lines in the right half and apply clipping
+
d.append(dw.Use(p, 300, 0, clip_path=c))
+
```
+
+
![svg](img/05_clip2.svg)
+
+
Complex clip path:
+
+
```python
+
curve1 = dw.Path(stroke='black', stroke_width=1, stroke_dasharray='3 2', fill='none')
+
curve1.M(5, 55).C(25, 5, 45, -25, 75, 55).C(85, 85, 20, 105, 40, 55).Z()
+
+
curveClip = dw.ClipPath()
+
curveClip.append(dw.Use(curve1, 0, 0))
+
+
text1 = dw.Text('CLIP', 48, 20, 20, font_weight='bold', transform='rotate(60)',
+
stroke='black', stroke_width=1, stroke_dasharray='3 2', fill='none')
+
textClip = dw.ClipPath()
+
textClip.append(dw.Use(text1, 0, 0))
+
+
shapes = dw.Group()
+
shapes.append(dw.Rectangle(0, 50, 90, 60, fill='#999'))
+
shapes.append(dw.Circle(25, 25, 25, fill='#666'))
+
shapes.append(dw.Lines(30, 0, 80, 0, 80, 100, close='true', fill='#ccc'))
+
+
# draw shapes with clip path
+
d.append(dw.Use(shapes, 0, 0, clip_path=curveClip))
+
+
# show clip path
+
g = dw.Group(transform='translate(100,0)')
+
g.append(shapes)
+
g.append(dw.Use(curve1, 0, 0))
+
d.append(g)
+
+
# draw shapes with text as clip path
+
d.append(dw.Use(shapes, 0, 150, clip_path=textClip))
+
+
# show text clip path
+
g = dw.Group(transform='translate(100,150)')
+
g.append(shapes)
+
g.append(dw.Use(text1, 0, 0))
+
d.append(g)
+
```
+
+
![svg](img/05_clip3.svg)
+
+
+
### Mask
+
+
```python
+
mask_name = dw.Mask()
+
```
+
+
The transparency of the masking object is transfered to the masked object.
+
Opaque pixels of the mask produce opaque pixels of the masked object.
+
Transparent parts of the mask make the corresponding parts of the masked object invisible.
+
+
```python
+
gradient = dw.LinearGradient(*[0,0], *[1,0], gradientUnits='objectBoundingBox')
+
gradient.add_stop(0, 'white')
+
gradient.add_stop(1, 'black')
+
+
mask = dw.Mask()
+
box = dw.Rectangle(30, 0, 100, 100, fill=gradient)
+
mask.append(box)
+
+
# Initial shape
+
rect = dw.Rectangle(0, 0, 200, 100,
+
fill='cyan', stroke='blue', stroke_width=2)
+
d.append(rect)
+
+
# After mask
+
rect = dw.Rectangle(0, 0, 200, 100,
+
fill='pink', stroke='red', stroke_width=2,
+
mask=mask)
+
d.append(rect)
+
```
+
+
![svg](img/05_mask.svg)
+
+
Mask using opaque colors:
+
+
```python
+
# Define the masks
+
redmask = dw.Mask(maskContentUnits='objectBoundingBox')
+
redmask.append(dw.Rectangle(0, 0, 1, 1, fill='#f00'))
+
greenmask = dw.Mask(maskContentUnits='objectBoundingBox')
+
greenmask.append(dw.Rectangle(0, 0, 1, 1, fill='#0f0'))
+
bluemask = dw.Mask(maskContentUnits='objectBoundingBox')
+
bluemask.append(dw.Rectangle(0, 0, 1, 1, fill='#00f'))
+
whitemask = dw.Mask(maskContentUnits='objectBoundingBox')
+
whitemask.append(dw.Rectangle(0, 0, 1, 1, fill='#fff'))
+
+
# Display the colors
+
d.append(dw.Rectangle(10, 10, 50, 50, fill='#f00'))
+
d.append(dw.Rectangle(70, 10, 50, 50, fill='#0f0'))
+
d.append(dw.Rectangle(130, 10, 50, 50, fill='#00f'))
+
d.append(dw.Rectangle(190, 10, 50, 50, fill='#fff', stroke='black'))
+
+
# Mask
+
g = dw.Group(mask=redmask)
+
g.append(dw.Circle(35,115,25,fill='black'))
+
g.append(dw.Text('Red',14,35,80,text_anchor='middle'))
+
d.append(g)
+
g = dw.Group(mask=greenmask)
+
g.append(dw.Circle(95, 115, 25, fill='black'))
+
g.append(dw.Text('Green', 14, 95, 80, text_anchor='middle'))
+
d.append(g)
+
g = dw.Group(mask=bluemask)
+
g.append(dw.Circle(155, 115, 25, fill='black'))
+
g.append(dw.Text('Blue', 14, 155, 80, text_anchor='middle'))
+
d.append(g)
+
g = dw.Group(mask=whitemask)
+
g.append(dw.Circle(215, 115, 25, fill='black'))
+
g.append(dw.Text('White', 14, 215, 80, text_anchor='middle'))
+
d.append(g)
+
```
+
+
![svg](img/05_mask2.svg)
+
+
Mask alpha using opacity only:
+
+
```python
+
fullmask = dw.Mask(maskContentUnits='objectBoundingBox')
+
fullmask.append(dw.Rectangle(0, 0, 1, 1, fill_opacity=1.0, fill='white'))
+
three_fourths = dw.Mask(maskContentUnits='objectBoundingBox')
+
three_fourths.append(dw.Rectangle(0, 0, 1, 1, fill_opacity=0.75, fill='white'))
+
one_half = dw.Mask(maskContentUnits='objectBoundingBox')
+
one_half.append(dw.Rectangle(0, 0, 1, 1, fill_opacity=0.5, fill='white'))
+
one_fourth = dw.Mask(maskContentUnits='objectBoundingBox')
+
one_fourth.append(dw.Rectangle(0, 0, 1, 1, fill_opacity=0.25, fill='white'))
+
+
g = dw.Group(mask=fullmask)
+
g.append(dw.Circle(35, 35, 25))
+
g.append(dw.Text('100%', 14, 35, 80, text_anchor='middle'))
+
d.append(g)
+
g = dw.Group(mask=three_fourths)
+
g.append(dw.Circle(95, 35, 25))
+
g.append(dw.Text('50%', 14, 95, 80, text_anchor='middle'))
+
d.append(g)
+
g = dw.Group(mask=one_half)
+
g.append(dw.Circle(155, 35, 25))
+
g.append(dw.Text('50%', 14, 155, 80, text_anchor='middle'))
+
d.append(g)
+
g = dw.Group(mask=one_fourth)
+
g.append(dw.Circle(215, 35, 25))
+
g.append(dw.Text('25%', 14, 215, 80, text_anchor='middle'))
+
d.append(g)
+
```
+
+
![svg](img/05_mask3.svg)
+
+
+
## Group, Use, Defs, Image
+
+
### Group
+
+
```python
+
dw.Group(**kwargs)
+
```
+
+
Any style specified in the g tag will apply to all child elements in the group.
+
+
```python
+
g_house = dw.Group(id='house', fill='none', stroke='black')
+
g_house.append(dw.Rectangle(6, 50, 60, 60))
+
g_house.append(dw.Lines(6, 50, 36, 9, 66, 50))
+
g_house.append(dw.Lines(36, 110, 36, 80, 50, 80, 50, 110))
+
d.append(g_house)
+
+
g_man = dw.Group(id='man', fill='none', stroke='blue')
+
g_man.append(dw.Circle(85, 56, 10))
+
g_man.append(dw.Line(85, 66, 85, 80))
+
g_man.append(dw.Lines(76, 104, 85, 80, 94, 104))
+
g_man.append(dw.Lines(76, 70, 85, 76, 94, 70))
+
d.append(g_man)
+
+
g_woman = dw.Group(id='woman', fill='none', stroke='red')
+
g_woman.append(dw.Circle(110, 56, 10))
+
g_woman.append(dw.Lines(110, 66, 110, 80, 100, 90, 120, 90, 110, 80))
+
g_woman.append(dw.Line(104, 104, 108, 90))
+
g_woman.append(dw.Line(112, 90, 116, 104))
+
g_woman.append(dw.Lines(101, 70, 110, 76, 119, 70))
+
d.append(g_woman)
+
```
+
+
![svg](img/06_group.svg)
+
+
+
### Use
+
+
```python
+
dw.Use(other_elem, x, y, **kwargs)
+
```
+
+
```python
+
g_house = dw.Group(id='house', fill='none', stroke='black')
+
g_house.append(dw.Rectangle(6, 50, 60, 60))
+
g_house.append(dw.Lines(6, 50, 36, 9, 66, 50))
+
g_house.append(dw.Lines(36, 110, 36, 80, 50, 80, 50, 110))
+
d.append(g_house)
+
+
# Use id which is set
+
d.append(dw.Use('house', 100, 50))
+
# Or use variable name
+
d.append(dw.Use(g_house, 150, 20))
+
```
+
+
![svg](img/06_use.svg)
+
+
+
### Defs
+
+
Elements that are not appended to the drawing but are referenced by other elements will automatically be included in `<defs></defs>`.
+
([source](https://github.com/cduck/drawsvg/issues/46))
+
+
```python
+
d = dw.Drawing(200, 200, id_prefix='defs')
+
+
# Do not append `bond` to the drawing
+
bond = dw.Line(0, 0, 10, 10, stroke='black')
+
+
# `bond` is automatically added into <defs>
+
# A default `id` is generated if one isn't set
+
d.append(dw.Use(bond, 20, 50))
+
d.append(dw.Use(bond, 50, 50))
+
d.append(dw.Use(bond, 80, 50))
+
+
print(d.as_svg())
+
```
+
+
Output:
+
+
```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="200" viewBox="0 0 200 200">
+
<defs>
+
<path d="M0,0 L10,10" stroke="black" id="defs0" />
+
</defs>
+
<use xlink:href="#defs0" x="20" y="50" />
+
<use xlink:href="#defs0" x="50" y="50" />
+
<use xlink:href="#defs0" x="80" y="50" />
+
</svg>
+
```
+
+
+
### Image
+
+
```python
+
dw.Image(x, y, width, height, path=None, data=None,
+
embed=False, mimeType=None, **kwargs)
+
```
+
+
```python
+
d.append(dw.Image(0, 0, 200, 200, 'example1.png', embed=True))
+
```
+
+
![svg](img/06_imag.svg)
+
+
+
## Transformations
+
+
### Translate
+
+
```python
+
transform = 'translate(x,y)'
+
```
+
+
This attribute can be added to many objects. Simple example:
+
+
```python
+
d.append(dw.Rectangle(0, 0, 40, 40))
+
d.append(dw.Rectangle(0, 0, 40, 40, fill='red', transform='translate(50,50)'))
+
```
+
+
![svg](img/07_trans.svg)
+
+
+
### Scale
+
+
```python
+
transform = 'scale(x_mult[, y_mult])'
+
```
+
+
Note that scaling also affects stroke width.
+
+
```python
+
square = dw.Rectangle(0, 0, 40, 40, fill='none', stroke_width=2)
+
d.append(dw.Use(square, 10, 10, stroke='black'))
+
d.append(dw.Use(square, 10, 10, stroke='red', transform='scale(2)'))
+
```
+
+
![svg](img/07_scale.svg)
+
+
It is possible to specify x and y scale separately:
+
+
```python
+
square = dw.Rectangle(0, 0, 40, 40, fill='none', stroke_width=2)
+
d.append(dw.Use(square, 10, 10, stroke='black'))
+
d.append(dw.Use(square, 10, 10, stroke='red', transform='scale(3,1.5)'))
+
```
+
+
![svg](img/07_scale2.svg)
+
+
Scaling around a center point:
+
+
```python
+
# Center of scaling: (100, 100)
+
d.append(dw.Circle(100, 100, 4, fill='black'))
+
# Non-scaled rectangle
+
rect = dw.Rectangle(70, 80, 60, 40, stroke='black', fill='none')
+
d.append(rect)
+
# Scaled rectangles
+
d.append(dw.Use(rect, 0, 0, transform='translate(-100,-100) scale(2)', stroke_width=0.5))
+
d.append(dw.Use(rect, 0, 0, transform='translate(-150,-150) scale(2.5)', stroke_width=0.4))
+
d.append(dw.Use(rect, 0, 0, transform='translate(-200,-200) scale(3)', stroke_width=0.33))
+
```
+
+
![svg](img/07_scalcent.svg)
+
+
+
### Rotate
+
+
```python
+
transform = 'rotate(angle,cx=0,cy=0)'
+
```
+
+
`angle` counts clockwise in degrees.
+
`cx`/`cy` are the center of rotation.
+
+
```python
+
# Show frame border
+
d.append(dw.Rectangle(0, 0, 200, 200, stroke='gray', fill='none'))
+
# Rotation is around (0, 0)
+
d.append(dw.Rectangle(70, 30, 40, 40, fill='silver'))
+
d.append(dw.Rectangle(70, 30, 40, 40, fill='gray', transform='rotate(22.5)'))
+
d.append(dw.Rectangle(70, 30, 40, 40, fill='black', transform='rotate(45)'))
+
```
+
+
![svg](img/07_rota.svg)
+
+
```python
+
# Center of rotation
+
d.append(dw.Circle(100, 100, 3, fill='black'))
+
# Non-rotated arrow
+
arrow = dw.Group(id='arrow')
+
arrow.append(dw.Line(110, 100, 160, 100))
+
arrow.append(dw.Lines(160, 100, 155, 95, 155, 105))
+
d.append(dw.Use(arrow, 0, 0, stroke='black', fill='black'))
+
# Rotated arrows
+
g = dw.Group(stroke='red', fill='red')
+
g.append(dw.Use(arrow, 0, 0, transform='rotate (60,100,100)'))
+
g.append(dw.Use(arrow, 0, 0, transform='rotate (-90,100,100)'))
+
g.append(dw.Use(arrow, 0, 0, transform='rotate (-150,100,100)'))
+
d.append(g)
+
```
+
+
![svg](img/07_rota2.svg)
+
+
+
### Skew
+
+
```python
+
transform = 'skewX(angle)'
+
transform = 'skewY(angle)'
+
```
+
+
```python
+
g = dw.Group(stroke='gray', stroke_dasharray='4 4')
+
g.append(dw.Line(0, 0, 200, 0))
+
g.append(dw.Line(20, 0, 20, 90))
+
g.append(dw.Line(120, 0, 120, 90))
+
d.append(g)
+
+
h = dw.Group(transform='translate(20,0)')
+
h1 = dw.Group(transform='skewX(30)')
+
h1.append(dw.Lines(50, 0, 0, 0, 0, 50,
+
stroke='black', fill='none', stroke_width=2))
+
h1.append(dw.Text('skewX', 16, 0, 60))
+
h.append(h1)
+
d.append(h)
+
+
i = dw.Group(transform='translate(120,0)')
+
i1 = dw.Group(transform='skewY(30)')
+
i1.append(dw.Lines(50, 0, 0, 0, 0, 50,
+
stroke='black', fill='none', stroke_width=2))
+
i1.append(dw.Text('skewY', 16, 0, 60))
+
i.append(i1)
+
d.append(i)
+
```
+
+
![svg](img/07_skew.svg)
+
+
+
### Cartesian Coordinates
+
+
A drawing which can be translated to Cartesian coordinates
+
(where y-coordinates increase upward, not downward)
+
by setting the translate-y value to the drawing's height, and also applying `scale(1,-1)`.
+
+
Trapezoid with origin to top left (the default):
+
+
```python
+
d.append(dw.Lines(0, 100, 0, 0, 100, 0,
+
fill='none', stroke='green', stroke_width=2))
+
d.append(dw.Lines(40, 40, 100, 40, 70, 70, 40, 70,
+
fill='silver', stroke='black', stroke_width=2))
+
d.append(dw.Text('downward y', 12, 5, 95))
+
```
+
+
![svg](img/07_cart1.svg)
+
+
Translated origin to bottom left and upward-y:
+
+
```python
+
g = dw.Group(transform='translate(0,100) scale(1,-1)')
+
g.append(dw.Lines(0, 100, 0, 0, 100, 0,
+
fill='none', stroke='green', stroke_width=2))
+
g.append(dw.Lines(40, 40, 100, 40, 70, 70, 40, 70,
+
fill='silver', stroke='black', stroke_width=2))
+
g.append(dw.Text('upward y', 12, 5, 95))
+
d.append(g)
+
```
+
+
![svg](img/07_cart2.svg)
+
+
Alternatively, apply `scale(1,-1)` to the whole drawing:
+
+
```python
+
d = dw.Drawing(100, 100, id_prefix='cart3', transform='scale(1,-1)')
+
d.append(dw.Lines(0, 100, 0, 0, 100, 0,
+
fill='none', stroke='green', stroke_width=2))
+
d.append(dw.Lines(40, 40, 100, 40, 70, 70, 40, 70,
+
fill='silver', stroke='black', stroke_width=2))
+
d.append(dw.Text('upward y', 12, 5, 95))
+
```
+
+
![svg](img/07_cart3.svg)
+
+
+
## Credits
+
+
Written by joachim heintz 2023. Edited by Casey Duckering.
+
+
Most examples are based on J. David Eisenberg, SVG Essentials, O'Reilly 2002.
+
+
Thanks to [Ahmad Aufar Husaini](https://github.com/aufarah) for his fork (draw2Svg) and for providing some documentation [here](https://draw2svg.netlify.app/) (some examples are used in this Quick Reference).
+
+
Thanks to [Casey Duckering](https://github.com/cduck) for drawsvg and many helpful explanations on its [discussion page](https://github.com/cduck/drawsvg/discussions).
-72
drawSvg/__init__.py
···
-
'''
-
A library for creating SVG files or just drawings that can be displayed in
-
iPython notebooks
-
-
Example:
-
```
-
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
-
```
-
'''
-
-
from .defs import *
-
from .raster import Raster
-
from .drawing import Drawing
-
from .elements import *
-
from .video import (
-
render_svg_frames,
-
save_video,
-
)
-
from .animation import (
-
Animation,
-
animate_video,
-
animate_jupyter,
-
)
-
-
-
# Make all elements available in the elements module
-
from . import defs
-
from . import elements
-
def registerElement(name, elem):
-
setattr(elements, name, elem)
-
elementsDir = dir(elements)
-
for k in dir(defs):
-
if k.startswith('_'): continue
-
if k in elementsDir: continue
-
registerElement(k, getattr(defs, k))
-
-104
drawSvg/animation.py
···
-
import time
-
-
from . import video
-
-
-
class Animation:
-
def __init__(self, draw_func=None, callback=None):
-
self.frames = []
-
if draw_func is None:
-
draw_func = lambda d:d
-
self.draw_func = draw_func
-
if callback is None:
-
callback = lambda d:None
-
self.callback = callback
-
-
def append_frame(self, frame):
-
self.frames.append(frame)
-
self.callback(frame)
-
-
def draw_frame(self, *args, **kwargs):
-
frame = self.draw_func(*args, **kwargs)
-
self.append_frame(frame)
-
return frame
-
-
def save_video(self, file, **kwargs):
-
video.save_video(self.frames, file, **kwargs)
-
-
-
class AnimationContext:
-
def __init__(self, draw_func=None, out_file=None,
-
jupyter=False, pause=False, clear=True, delay=0, disable=False,
-
video_args=None, _patch_delay=0.05):
-
self.jupyter = jupyter
-
self.disable = disable
-
if self.jupyter and not self.disable:
-
from IPython import display
-
self._jupyter_clear_output = display.clear_output
-
self._jupyter_display = display.display
-
callback = self.draw_jupyter_frame
-
else:
-
callback = None
-
self.anim = Animation(draw_func, callback=callback)
-
self.out_file = out_file
-
self.pause = pause
-
self.clear = clear
-
self.delay = delay
-
if video_args is None:
-
video_args = {}
-
self.video_args = video_args
-
self._patch_delay = _patch_delay
-
-
def draw_jupyter_frame(self, frame):
-
if self.clear:
-
self._jupyter_clear_output(wait=True)
-
self._jupyter_display(frame)
-
if self.pause:
-
# Patch. Jupyter sometimes clears the input field otherwise.
-
time.sleep(self._patch_delay)
-
input('Next?')
-
elif self.delay != 0:
-
time.sleep(self.delay)
-
-
def __enter__(self):
-
return self.anim
-
-
def __exit__(self, exc_type, exc_value, exc_traceback):
-
if exc_value is None:
-
# No error
-
if self.out_file is not None and not self.disable:
-
self.anim.save_video(self.out_file, **self.video_args)
-
-
-
def animate_video(out_file, draw_func=None, jupyter=False, **video_args):
-
'''
-
Returns a context manager that stores frames and saves a video when the
-
context exits.
-
-
Example:
-
```
-
with animate_video('video.mp4') as draw_frame:
-
while True:
-
...
-
draw_frame(...)
-
```
-
'''
-
return AnimationContext(draw_func=draw_func, out_file=out_file,
-
jupyter=jupyter, video_args=video_args)
-
-
-
def animate_jupyter(draw_func=None, pause=False, clear=True, delay=0.1,
-
**kwargs):
-
'''
-
Returns a context manager that displays frames in a Jupyter notebook.
-
-
Example:
-
```
-
with animate_jupyter(delay=0.5) as draw_frame:
-
while True:
-
...
-
draw_frame(...)
-
```
-
'''
-
return AnimationContext(draw_func=draw_func, jupyter=True, pause=pause,
-
clear=clear, delay=delay, **kwargs)
-199
drawSvg/color.py
···
-
-
import math
-
import numpy as np
-
import pwkit.colormaps # pip3 install pwkit
-
-
-
# Most calculations from http://www.chilliant.com/rgb2hsv.html
-
-
-
def limit(v, low=0, high=1):
-
return max(min(v, high), low)
-
-
class Srgb:
-
LUMA_WEIGHTS = (0.299, 0.587, 0.114)
-
def __init__(self, r, g, b):
-
self.r = float(r)
-
self.g = float(g)
-
self.b = float(b)
-
def __iter__(self):
-
return iter((self.r, self.g, self.b))
-
def __repr__(self):
-
return 'RGB({}, {}, {})'.format(self.r, self.g, self.b)
-
def __str__(self):
-
return 'rgb({}%,{}%,{}%)'.format(self.r*100, self.g*100, self.b*100)
-
def luma(self, wts=None):
-
if wts is None: wts = self.LUMA_WEIGHTS
-
rw, gw, bw = wts
-
return rw*self.r + gw*self.g + bw*self.b
-
def toSrgb(self):
-
return self
-
@staticmethod
-
def fromHue(h):
-
h = h % 1
-
r = abs(h * 6 - 3) - 1
-
g = 2 - abs(h * 6 - 2)
-
b = 2 - abs(h * 6 - 4)
-
return Srgb(limit(r), limit(g), limit(b))
-
-
class Hsl:
-
def __init__(self, h, s, l):
-
self.h = float(h) % 1
-
self.s = float(s)
-
self.l = float(l)
-
def __iter__(self):
-
return iter((self.h, self.s, self.l))
-
def __repr__(self):
-
return 'HSL({}, {}, {})'.format(self.h, self.s, self.l)
-
def __str__(self):
-
r, g, b = self.toSrgb()
-
return 'rgb({}%,{}%,{}%)'.format(round(r*100), round(g*100), round(b*100))
-
def toSrgb(self):
-
hs = Srgb.fromHue(self.h)
-
c = (1 - abs(2 * self.l - 1)) * self.s
-
return Srgb(
-
(hs.r - 0.5) * c + self.l,
-
(hs.g - 0.5) * c + self.l,
-
(hs.b - 0.5) * c + self.l
-
)
-
-
class Hsv:
-
def __init__(self, h, s, v):
-
self.h = float(h) % 1
-
self.s = float(s)
-
self.v = float(v)
-
def __iter__(self):
-
return iter((self.h, self.s, self.v))
-
def __repr__(self):
-
return 'HSV({}, {}, {})'.format(self.h, self.s, self.v)
-
def __str__(self):
-
r, g, b = self.toSrgb()
-
return 'rgb({}%,{}%,{}%)'.format(round(r*100), round(g*100), round(b*100))
-
def toSrgb(self):
-
hs = Srgb.fromHue(self.h)
-
c = self.v * self.s
-
hp = self.h * 6
-
x = c * (1 - abs(hp % 2 - 1))
-
if hp < 1:
-
r1, g1, b1 = c, x, 0
-
elif hp < 2:
-
r1, g1, b1 = x, c, 0
-
elif hp < 3:
-
r1, g1, b1 = 0, c, x
-
elif hp < 4:
-
r1, g1, b1 = 0, x, c
-
elif hp < 5:
-
r1, g1, b1 = x, 0, c
-
else:
-
r1, g1, b1 = c, 0, x
-
m = self.v - c
-
return Srgb(r1+m, g1+m, b1+m)
-
-
class Sin:
-
def __init__(self, h, s, l):
-
self.h = float(h) % 1
-
self.s = float(s)
-
self.l = float(l)
-
def __iter__(self):
-
return iter((self.h, self.s, self.l))
-
def __repr__(self):
-
return 'Sin({}, {}, {})'.format(self.h, self.s, self.l)
-
def __str__(self):
-
r, g, b = self.toSrgb()
-
return 'rgb({}%,{}%,{}%)'.format(round(r*100), round(g*100), round(b*100))
-
def toSrgb(self):
-
h = self.h
-
scale = self.s / 2
-
shift = self.l #* (1-2*scale)
-
return Srgb(
-
shift + scale * math.cos(math.pi*2 * (h - 0/6)),
-
shift + scale * math.cos(math.pi*2 * (h - 2/6)),
-
shift + scale * math.cos(math.pi*2 * (h - 4/6)),
-
)
-
-
class Hcy:
-
HCY_WEIGHTS = Srgb.LUMA_WEIGHTS
-
def __init__(self, h, c, y):
-
self.h = float(h) % 1
-
self.c = float(c)
-
self.y = float(y)
-
def __iter__(self):
-
return iter((self.h, self.c, self.y))
-
def __repr__(self):
-
return 'HCY({}, {}, {})'.format(self.h, self.c, self.y)
-
def __str__(self):
-
r, g, b = self.toSrgb()
-
return 'rgb({}%,{}%,{}%)'.format(r*100, g*100, b*100)
-
def toSrgb(self):
-
hs = Srgb.fromHue(self.h)
-
y = hs.luma(wts=self.HCY_WEIGHTS)
-
c = self.c
-
if self.y < y:
-
c *= self.y / y
-
elif y < 1:
-
c *= (1 - self.y) / (1 - y)
-
return Srgb(
-
(hs.r - y) * c + self.y,
-
(hs.g - y) * c + self.y,
-
(hs.b - y) * c + self.y,
-
)
-
@staticmethod
-
def _rgbToHcv(srgb):
-
if srgb.g < srgb.b:
-
p = (srgb.b, srgb.g, -1., 2./3.)
-
else:
-
p = (srgb.g, srgb.b, 0., -1./3.)
-
if srgb.r < p[0]:
-
q = (p[0], p[1], p[3], srgb.r)
-
else:
-
q = (srgb.r, p[1], p[2], p[0])
-
c = q[0] - min(q[3], q[1])
-
h = abs((q[3] - q[1]) / (6*c + 1e-10) + q[2])
-
return (h, c, q[0])
-
@classmethod
-
def fromSrgb(cls, srgb):
-
hcv = list(cls._rgbToHcv(srgb))
-
rw, gw, bw = cls.HCY_WEIGHTS
-
y = rw*srgb.r + gw*srgb.g + bw*srgb.b
-
hs = Srgb.fromHue(hcv[0])
-
z = rw*hs.r + gw*hs.g + bw*hs.b
-
if y < z:
-
hcv[1] *= z / (y + 1e-10)
-
else:
-
hcv[1] *= (1 - z) / (1 - y + 1e-10)
-
return Hcy(hcv[0], hcv[1], y)
-
-
class Cielab:
-
REF_WHITE = (0.95047, 1., 1.08883)
-
def __init__(self, l, a, b):
-
self.l = float(l)
-
self.a = float(a)
-
self.b = float(b)
-
def __iter__(self):
-
return iter((self.l, self.a, self.b))
-
def __repr__(self):
-
return 'CIELAB({}, {}, {})'.format(self.l, self.a, self.b)
-
def __str__(self):
-
r, g, b = self.toSrgb()
-
return 'rgb({}%,{}%,{}%)'.format(round(r*100), round(g*100), round(b*100))
-
def toSrgb(self):
-
inArr = np.array((*self.l,), dtype=float)
-
xyz = pwkit.colormaps.cielab_to_xyz(inArr, self.REF_WHITE)
-
linSrgb = pwkit.colormaps.xyz_to_linsrgb(xyz)
-
r, g, b = pwkit.colormaps.linsrgb_to_srgb(linSrgb)
-
return Srgb(r, g, b)
-
@classmethod
-
def fromSrgb(cls, srgb, refWhite=None):
-
if refWhite is None: refWhite = cls.REF_WHITE
-
inArr = np.array((*srgb,), dtype=float)
-
linSrgb = pwkit.colormaps.srgb_to_linsrgb(inArr)
-
xyz = pwkit.colormaps.linsrgb_to_xyz(linSrgb)
-
l, a, b = pwkit.colormaps.xyz_to_cielab(xyz, refWhite)
-
return Cielab(l, a, b)
-
def toSrgb(self):
-
inArr = np.array((self.l, self.a, self.b))
-
xyz = pwkit.colormaps.cielab_to_xyz(inArr, self.REF_WHITE)
-
linSrgb = pwkit.colormaps.xyz_to_linsrgb(xyz)
-
r, g, b = pwkit.colormaps.linsrgb_to_srgb(linSrgb)
-
return Srgb(r, g, b)
-
-67
drawSvg/defs.py
···
-
-
from .elements import DrawingElement, DrawingParentElement
-
-
-
class DrawingDef(DrawingParentElement):
-
''' Parent class of SVG nodes that must be direct children of <defs> '''
-
def getSvgDefs(self):
-
return (self,)
-
def writeSvgDefs(self, idGen, isDuplicate, outputFile):
-
DrawingElement.writeSvgDefs(idGen, isDuplicate, outputFile)
-
-
class DrawingDefSub(DrawingParentElement):
-
''' Parent class of SVG nodes that are meant to be descendants of a Def '''
-
pass
-
-
class LinearGradient(DrawingDef):
-
''' A linear gradient to use as a fill or other color
-
-
Has <stop> nodes as children. '''
-
TAG_NAME = 'linearGradient'
-
def __init__(self, x1, y1, x2, y2, gradientUnits='userSpaceOnUse', **kwargs):
-
yShift = 0
-
if gradientUnits != 'userSpaceOnUse':
-
yShift = 1
-
try: y1 = yShift - y1
-
except TypeError: pass
-
try: y2 = yShift - y2
-
except TypeError: pass
-
super().__init__(x1=x1, y1=y1, x2=x2, y2=y2, gradientUnits=gradientUnits,
-
**kwargs)
-
def addStop(self, offset, color, opacity=None, **kwargs):
-
stop = GradientStop(offset=offset, stop_color=color,
-
stop_opacity=opacity, **kwargs)
-
self.append(stop)
-
-
class RadialGradient(DrawingDef):
-
''' A radial gradient to use as a fill or other color
-
-
Has <stop> nodes as children. '''
-
TAG_NAME = 'radialGradient'
-
def __init__(self, cx, cy, r, gradientUnits='userSpaceOnUse', fy=None, **kwargs):
-
yShift = 0
-
if gradientUnits != 'userSpaceOnUse':
-
yShift = 1
-
try: cy = yShift - cy
-
except TypeError: pass
-
try: fy = yShift - fy
-
except TypeError: pass
-
super().__init__(cx=cx, cy=cy, r=r, gradientUnits=gradientUnits,
-
fy=fy, **kwargs)
-
def addStop(self, offset, color, opacity=None, **kwargs):
-
stop = GradientStop(offset=offset, stop_color=color,
-
stop_opacity=opacity, **kwargs)
-
self.append(stop)
-
-
class GradientStop(DrawingDefSub):
-
''' A control point for a radial or linear gradient '''
-
TAG_NAME = 'stop'
-
hasContent = False
-
-
class ClipPath(DrawingDef):
-
''' A shape used to crop another element by not drawing outside of this
-
shape
-
-
Has regular drawing elements as children. '''
-
TAG_NAME = 'clipPath'
-
-146
drawSvg/drawing.py
···
-
-
from io import StringIO
-
-
from . import Raster
-
from . import elements as elementsModule
-
-
-
class Drawing:
-
''' A canvas to draw on
-
-
Supports iPython: If a Drawing is the last line of a cell, it will be
-
displayed as an SVG below. '''
-
def __init__(self, width, height, origin=(0,0), **svgArgs):
-
assert float(width) == width
-
assert float(height) == height
-
self.width = width
-
self.height = height
-
if origin == 'center':
-
self.viewBox = (-width/2, -height/2, width, height)
-
else:
-
origin = tuple(origin)
-
assert len(origin) == 2
-
self.viewBox = origin + (width, height)
-
self.viewBox = (self.viewBox[0], -self.viewBox[1]-self.viewBox[3],
-
self.viewBox[2], self.viewBox[3])
-
self.elements = []
-
self.otherDefs = []
-
self.pixelScale = 1
-
self.renderWidth = None
-
self.renderHeight = None
-
self.svgArgs = svgArgs
-
def setRenderSize(self, w=None, h=None):
-
self.renderWidth = w
-
self.renderHeight = h
-
return self
-
def setPixelScale(self, s=1):
-
self.renderWidth = None
-
self.renderHeight = None
-
self.pixelScale = s
-
return self
-
def calcRenderSize(self):
-
if self.renderWidth is None and self.renderHeight is None:
-
return (self.width * self.pixelScale,
-
self.height * self.pixelScale)
-
elif self.renderWidth is None:
-
s = self.renderHeight / self.height
-
return self.width * s, self.renderHeight
-
elif self.renderHeight is None:
-
s = self.renderWidth / self.width
-
return self.renderWidth, self.height * s
-
else:
-
return self.renderWidth, self.renderHeight
-
def draw(self, obj, **kwargs):
-
if not hasattr(obj, 'writeSvgElement'):
-
elements = obj.toDrawables(elements=elementsModule, **kwargs)
-
else:
-
assert len(kwargs) == 0
-
elements = (obj,)
-
self.extend(elements)
-
def append(self, element):
-
self.elements.append(element)
-
def extend(self, iterable):
-
self.elements.extend(iterable)
-
def insert(self, i, element):
-
self.elements.insert(i, element)
-
def remove(self, element):
-
self.elements.remove(element)
-
def clear(self):
-
self.elements.clear()
-
def index(self, *args, **kwargs):
-
self.elements.index(*args, **kwargs)
-
def count(self, element):
-
self.elements.count(element)
-
def reverse(self):
-
self.elements.reverse()
-
def drawDef(self, obj, **kwargs):
-
if not hasattr(obj, 'writeSvgElement'):
-
elements = obj.toDrawables(elements=elementsModule, **kwargs)
-
else:
-
assert len(kwargs) == 0
-
elements = (obj,)
-
self.otherDefs.extend(elements)
-
def appendDef(self, element):
-
self.otherDefs.append(element)
-
def asSvg(self, outputFile=None):
-
returnString = outputFile is None
-
if returnString:
-
outputFile = StringIO()
-
imgWidth, imgHeight = self.calcRenderSize()
-
startStr = '''<?xml version="1.0" encoding="UTF-8"?>
-
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
-
width="{}" height="{}" viewBox="{} {} {} {}"'''.format(
-
imgWidth, imgHeight, *self.viewBox)
-
endStr = '</svg>'
-
outputFile.write(startStr)
-
elementsModule.writeXmlNodeArgs(self.svgArgs, outputFile)
-
outputFile.write('>\n<defs>\n')
-
# Write definition elements
-
idIndex = 0
-
def idGen(base='d'):
-
nonlocal idIndex
-
idStr = base + str(idIndex)
-
idIndex += 1
-
return idStr
-
prevSet = set((id(defn) for defn in self.otherDefs))
-
def isDuplicate(obj):
-
nonlocal prevSet
-
dup = id(obj) in prevSet
-
prevSet.add(id(obj))
-
return dup
-
for element in self.otherDefs:
-
try:
-
element.writeSvgElement(outputFile)
-
outputFile.write('\n')
-
except AttributeError:
-
pass
-
for element in self.elements:
-
try:
-
element.writeSvgDefs(idGen, isDuplicate, outputFile)
-
except AttributeError:
-
pass
-
outputFile.write('</defs>\n')
-
# Write normal elements
-
for element in self.elements:
-
try:
-
element.writeSvgElement(outputFile)
-
outputFile.write('\n')
-
except AttributeError:
-
pass
-
outputFile.write(endStr)
-
if returnString:
-
return outputFile.getvalue()
-
def saveSvg(self, fname):
-
with open(fname, 'w') as f:
-
self.asSvg(outputFile=f)
-
def savePng(self, fname):
-
self.rasterize(toFile=fname)
-
def rasterize(self, toFile=None):
-
if toFile:
-
return Raster.fromSvgToFile(self.asSvg(), toFile)
-
else:
-
return Raster.fromSvg(self.asSvg())
-
def _repr_svg_(self):
-
''' Display in Jupyter notebook '''
-
return self.asSvg()
-
-355
drawSvg/elements.py
···
-
-
import sys
-
import math
-
import os.path
-
import base64
-
import warnings
-
import xml.sax.saxutils as xml
-
-
from . import defs
-
-
elementsModule = sys.modules[__name__]
-
-
# TODO: Support drawing ellipses without manually using Path
-
-
def writeXmlNodeArgs(args, outputFile):
-
for k, v in args.items():
-
if v is None: continue
-
k = k.replace('__', ':')
-
k = k.replace('_', '-')
-
if k[-1]=='-':
-
k = k[:-1]
-
if isinstance(v, defs.DrawingDef):
-
v = 'url(#{})'.format(v.id)
-
outputFile.write(' {}="{}"'.format(k,v))
-
-
-
class DrawingElement:
-
''' Base class for drawing elements
-
-
Subclasses must implement writeSvgElement '''
-
def writeSvgElement(self, outputFile):
-
raise NotImplementedError('Abstract base class')
-
def getSvgDefs(self):
-
return ()
-
def writeSvgDefs(self, idGen, isDuplicate, outputFile):
-
for defn in self.getSvgDefs():
-
if isDuplicate(defn): continue
-
defn.id = idGen()
-
defn.writeSvgElement(outputFile)
-
outputFile.write('\n')
-
def __eq__(self, other):
-
return self is other
-
-
class DrawingBasicElement(DrawingElement):
-
''' Base class for SVG drawing elements that are a single node with no
-
child nodes '''
-
TAG_NAME = '_'
-
hasContent = False
-
def __init__(self, **args):
-
self.args = args
-
@property
-
def id(self):
-
return self.args.get('id', None)
-
@id.setter
-
def id(self, newId):
-
self.args['id'] = newId
-
def writeSvgElement(self, outputFile):
-
outputFile.write('<')
-
outputFile.write(self.TAG_NAME)
-
writeXmlNodeArgs(self.args, outputFile)
-
if not self.hasContent:
-
outputFile.write(' />')
-
else:
-
outputFile.write('>')
-
self.writeContent(outputFile)
-
outputFile.write('</')
-
outputFile.write(self.TAG_NAME)
-
outputFile.write('>')
-
def writeContent(self, outputFile):
-
''' Override in a subclass to add data between the start and end
-
tags. This will not be called if hasContent is False. '''
-
raise RuntimeError('This element has no content')
-
def getSvgDefs(self):
-
return [v for v in self.args.values() if isinstance(v, defs.DrawingDef)]
-
def __eq__(self, other):
-
if isinstance(other, type(self)):
-
return (self.TAG_NAME == other.TAG_NAME and
-
self.args == other.args)
-
return False
-
-
class DrawingParentElement(DrawingBasicElement):
-
''' Base class for SVG elements that can have child nodes '''
-
hasContent = True
-
def __init__(self, children=(), **args):
-
super().__init__(**args)
-
self.children = list(children)
-
if len(self.children) > 0:
-
self.checkChildrenAllowed()
-
def checkChildrenAllowed(self):
-
if not self.hasContent:
-
raise RuntimeError('{} does not support children'.format(type(self)))
-
def draw(self, obj, **kwargs):
-
if not hasattr(obj, 'writeSvgElement'):
-
elements = obj.toDrawables(elements=elementsModule, **kwargs)
-
self.extend(elements)
-
else:
-
assert len(kwargs) == 0
-
self.append(obj)
-
def append(self, element):
-
self.checkChildrenAllowed()
-
self.children.append(element)
-
def extend(self, iterable):
-
self.checkChildrenAllowed()
-
self.children.extend(iterable)
-
def writeContent(self, outputFile):
-
outputFile.write('\n')
-
for child in self.children:
-
child.writeSvgElement(outputFile)
-
outputFile.write('\n')
-
def writeSvgDefs(self, idGen, isDuplicate, outputFile):
-
super().writeSvgDefs(idGen, isDuplicate, outputFile)
-
for child in self.children:
-
child.writeSvgDefs(idGen, isDuplicate, outputFile)
-
-
class NoElement(DrawingElement):
-
''' A drawing element that has no effect '''
-
def __init__(self): pass
-
def writeSvgElement(self, outputFile):
-
pass
-
def __eq__(self, other):
-
if isinstance(other, type(self)):
-
return True
-
return False
-
-
class Group(DrawingParentElement):
-
''' A group of drawing elements
-
-
Any transform will apply to its children and other attributes will be
-
inherited by its children. '''
-
TAG_NAME = 'g'
-
-
class Use(DrawingBasicElement):
-
''' A copy of another element
-
-
Specify the other element by its id: href='#otherElemId'. '''
-
TAG_NAME = 'use'
-
def __init__(self, otherElem, x, y, **kwargs):
-
y = -y
-
if isinstance(otherElem, str):
-
otherElemId = otherElem
-
else:
-
if otherElem.id is None:
-
raise ValueError('otherElem must have an id')
-
otherElemId = otherElem.id
-
href = '#{}'.format(otherElemId)
-
super().__init__(xlink__href=href, x=x, y=y, **kwargs)
-
-
class Image(DrawingBasicElement):
-
''' A linked or embedded raster image '''
-
TAG_NAME = 'image'
-
MIME_MAP = {
-
'.bm': 'image/bmp',
-
'.bmp': 'image/bmp',
-
'.gif': 'image/gif',
-
'.jpeg':'image/jpeg',
-
'.jpg': 'image/jpeg',
-
'.png': 'image/png',
-
'.tif': 'image/tiff',
-
'.tiff':'image/tiff',
-
'.pdf': 'application/pdf',
-
'.txt': 'text/plain',
-
}
-
MIME_DEFAULT = 'image/png'
-
def __init__(self, x, y, width, height, path=None, data=None, embed=False,
-
mimeType=None, **kwargs):
-
''' Specify either the path or data argument. If path is used and
-
embed is True, the image file is embedded in a data URI. '''
-
if path is None and data is None:
-
raise ValueError('Either path or data arguments must be given')
-
if mimeType is None and path is not None:
-
ext = os.path.splitext(path)[1].lower()
-
if ext in self.MIME_MAP:
-
mimeType = self.MIME_MAP[ext]
-
else:
-
mimeType = self.MIME_DEFAULT
-
warnings.warn('Unknown image file type "{}"'.format(ext), Warning)
-
if mimeType is None:
-
mimeType = self.MIME_DEFAULT
-
warnings.warn('Unspecified image type; assuming png'.format(ext), Warning)
-
if data is not None:
-
embed = True
-
if embed and data is None:
-
with open(path, 'rb') as f:
-
data = f.read()
-
if not embed:
-
uri = path
-
else:
-
encData = base64.b64encode(data).decode()
-
uri = 'data:{};base64,{}'.format(mimeType, encData)
-
super().__init__(x=x, y=-y-height, width=width, height=height,
-
xlink__href=uri, **kwargs)
-
-
class Text(DrawingBasicElement):
-
''' Text
-
-
Additional keyword arguments are output as additional arguments to the
-
SVG node e.g. fill="red", font_size=20, text_anchor="middle". '''
-
TAG_NAME = 'text'
-
hasContent = True
-
def __init__(self, text, fontSize, x, y, center=False, **kwargs):
-
if center:
-
if 'text_anchor' not in kwargs:
-
kwargs['text_anchor'] = 'middle'
-
try:
-
fontSize = float(fontSize)
-
translate = 'translate(0,{})'.format(fontSize*0.5*center)
-
if 'transform' in kwargs:
-
kwargs['transform'] = translate + ' ' + kwargs['transform']
-
else:
-
kwargs['transform'] = translate
-
except TypeError:
-
pass
-
super().__init__(x=x, y=-y, font_size=fontSize, **kwargs)
-
self.escapedText = xml.escape(text)
-
def writeContent(self, outputFile):
-
outputFile.write(self.escapedText)
-
-
class Rectangle(DrawingBasicElement):
-
''' A rectangle
-
-
Additional keyword arguments are output as additional arguments to the
-
SVG node e.g. fill="red", stroke="#ff4477", stroke_width=2. '''
-
TAG_NAME = 'rect'
-
def __init__(self, x, y, width, height, **kwargs):
-
super().__init__(x=x, y=-y-height, width=width, height=height,
-
**kwargs)
-
-
class Circle(DrawingBasicElement):
-
''' A circle
-
-
Additional keyword arguments are output as additional properties to the
-
SVG node e.g. fill="red", stroke="#ff4477", stroke_width=2. '''
-
TAG_NAME = 'circle'
-
def __init__(self, cx, cy, r, **kwargs):
-
super().__init__(cx=cx, cy=-cy, r=r, **kwargs)
-
-
class ArcLine(Circle):
-
''' An arc
-
-
In most cases, use Arc instead of ArcLine. ArcLine uses the
-
stroke-dasharray SVG property to make the edge of a circle look like
-
an arc.
-
-
Additional keyword arguments are output as additional arguments to the
-
SVG node e.g. fill="red", stroke="#ff4477", stroke_width=2. '''
-
def __init__(self, cx, cy, r, startDeg, endDeg, **kwargs):
-
if endDeg - startDeg == 360:
-
super().__init__(cx, cy, r, **kwargs)
-
return
-
startDeg, endDeg = (-endDeg) % 360, (-startDeg) % 360
-
arcDeg = (endDeg - startDeg) % 360
-
def arcLen(deg): return math.radians(deg) * r
-
wholeLen = 2 * math.pi * r
-
if endDeg == startDeg:
-
offset = 1
-
dashes = "0 {}".format(wholeLen+2)
-
#elif endDeg >= startDeg:
-
elif True:
-
startLen = arcLen(startDeg)
-
arcLen = arcLen(arcDeg)
-
offLen = wholeLen - arcLen
-
offset = -startLen
-
dashes = "{} {}".format(arcLen, offLen)
-
#else:
-
# firstLen = arcLen(endDeg)
-
# secondLen = arcLen(360-startDeg)
-
# gapLen = wholeLen - firstLen - secondLen
-
# offset = 0
-
# dashes = "{} {} {}".format(firstLen, gapLen, secondLen)
-
super().__init__(cx, cy, r, stroke_dasharray=dashes,
-
stroke_dashoffset=offset, **kwargs)
-
-
class Path(DrawingBasicElement):
-
''' An arbitrary path
-
-
Path Supports building an SVG path by calling instance methods
-
corresponding to path commands.
-
-
Additional keyword arguments are output as additional properties to the
-
SVG node e.g. fill="red", stroke="#ff4477", stroke_width=2. '''
-
TAG_NAME = 'path'
-
def __init__(self, d='', **kwargs):
-
super().__init__(d=d, **kwargs)
-
def append(self, commandStr, *args):
-
if len(self.args['d']) > 0:
-
commandStr = ' ' + commandStr
-
if len(args) > 0:
-
commandStr = commandStr + ','.join(map(str, args))
-
self.args['d'] += commandStr
-
def M(self, x, y): self.append('M', x, -y)
-
def m(self, dx, dy): self.append('m', dx, -dy)
-
def L(self, x, y): self.append('L', x, -y)
-
def l(self, dx, dy): self.append('l', dx, -dy)
-
def H(self, x, y): self.append('H', x)
-
def h(self, dx): self.append('h', dx)
-
def V(self, y): self.append('V', -y)
-
def v(self, dy): self.append('v', -dy)
-
def Z(self): self.append('Z')
-
def C(self, cx1, cy1, cx2, cy2, ex, ey):
-
self.append('C', cx1, -cy1, cx2, -cy2, ex, -ey)
-
def c(self, cx1, cy1, cx2, cy2, ex, ey):
-
self.append('c', cx1, -cy1, cx2, -cy2, ex, -ey)
-
def S(self, cx2, cy2, ex, ey): self.append('S', cx2, -cy2, ex, -ey)
-
def s(self, cx2, cy2, ex, ey): self.append('s', cx2, -cy2, ex, -ey)
-
def Q(self, cx, cy, ex, ey): self.append('Q', cx, -cy, ex, -ey)
-
def q(self, cx, cy, ex, ey): self.append('q', cx, -cy, ex, -ey)
-
def T(self, ex, ey): self.append('T', ex, -ey)
-
def t(self, ex, ey): self.append('t', ex, -ey)
-
def A(self, rx, ry, rot, largeArc, sweep, ex, ey):
-
self.append('A', rx, ry, rot, int(bool(largeArc)), int(bool(sweep)), ex, -ey)
-
def a(self, rx, ry, rot, largeArc, sweep, ex, ey):
-
self.append('a', rx, ry, rot, int(bool(largeArc)), int(bool(sweep)), ex, -ey)
-
def arc(self, cx, cy, r, startDeg, endDeg, cw=False, includeM=True, includeL=False):
-
''' Uses A() to draw a circular arc '''
-
largeArc = (endDeg - startDeg) % 360 > 180
-
startRad, endRad = startDeg*math.pi/180, endDeg*math.pi/180
-
sx, sy = r*math.cos(startRad), r*math.sin(startRad)
-
ex, ey = r*math.cos(endRad), r*math.sin(endRad)
-
if includeL:
-
self.L(cx+sx, cy+sy)
-
elif includeM:
-
self.M(cx+sx, cy+sy)
-
self.A(r, r, 0, largeArc ^ cw, cw, cx+ex, cy+ey)
-
-
class Lines(Path):
-
''' A sequence of connected lines (or a polygon)
-
-
Additional keyword arguments are output as additional properties to the
-
SVG node e.g. fill="red", stroke="#ff4477", stroke_width=2. '''
-
def __init__(self, sx, sy, *points, close=False, **kwargs):
-
super().__init__(d='', **kwargs)
-
self.M(sx, sy)
-
assert len(points) % 2 == 0
-
for i in range(len(points) // 2):
-
self.L(points[2*i], points[2*i+1])
-
if close:
-
self.Z()
-
-
class Line(Lines):
-
''' A line
-
-
Additional keyword arguments are output as additional properties to the
-
SVG node e.g. fill="red", stroke="#ff4477", stroke_width=2. '''
-
def __init__(self, sx, sy, ex, ey, **kwargs):
-
super().__init__(sx, sy, ex, ey, close=False, **kwargs)
-
-
class Arc(Path):
-
''' An arc
-
-
Additional keyword arguments are output as additional properties to the
-
SVG node e.g. fill="red", stroke="#ff4477", stroke_width=2. '''
-
def __init__(self, cx, cy, r, startDeg, endDeg, cw=False, **kwargs):
-
super().__init__(d='', **kwargs)
-
self.arc(cx, cy, r, startDeg, endDeg, cw=cw, includeM=True)
-
-7
drawSvg/missing.py
···
-
-
class MissingModule:
-
def __init__(self, errorMsg):
-
self.errorMsg = errorMsg
-
def __getattr__(self, name):
-
raise RuntimeError(self.errorMsg)
-
-43
drawSvg/raster.py
···
-
-
import io
-
-
try:
-
import cairosvg
-
except (ImportError, OSError):
-
import warnings
-
from .missing import MissingModule
-
msg = 'CairoSVG will need to be installed to rasterize images: Install with `pip3 install cairosvg`'
-
cairosvg = MissingModule(msg)
-
warnings.warn(msg, RuntimeWarning)
-
-
-
class Raster:
-
def __init__(self, pngData=None, pngFile=None):
-
self.pngData = pngData
-
self.pngFile = pngFile
-
def savePng(self, fname):
-
with open(fname, 'wb') as f:
-
f.write(self.pngData)
-
@staticmethod
-
def fromSvg(svgData):
-
pngData = cairosvg.svg2png(bytestring=svgData)
-
return Raster(pngData)
-
@staticmethod
-
def fromSvgToFile(svgData, outFile):
-
cairosvg.svg2png(bytestring=svgData, write_to=outFile)
-
return Raster(None, pngFile=outFile)
-
def _repr_png_(self):
-
if self.pngData:
-
return self.pngData
-
elif self.pngFile:
-
try:
-
with open(self.pngFile, 'rb') as f:
-
return f.read()
-
except TypeError:
-
pass
-
try:
-
self.pngFile.seek(0)
-
return self.pngFile.read()
-
except io.UnsupportedOperation:
-
pass
-
-54
drawSvg/video.py
···
-
import numpy as np
-
import imageio
-
-
from .drawing import Drawing
-
-
-
def render_svg_frames(frames, align_bottom=False, align_right=False,
-
bg=(255,)*4, **kwargs):
-
arr_frames = [imageio.imread(d.rasterize().pngData)
-
for d in frames]
-
max_width = max(map(lambda arr:arr.shape[1], arr_frames))
-
max_height = max(map(lambda arr:arr.shape[0], arr_frames))
-
-
def mod_frame(arr):
-
new_arr = np.zeros((max_height, max_width) + arr.shape[2:],
-
dtype=arr.dtype)
-
new_arr[:,:] = bg[:new_arr.shape[-1]]
-
if align_bottom:
-
slice0 = slice(-arr.shape[0], None)
-
else:
-
slice0 = slice(None, arr.shape[0])
-
if align_right:
-
slice1 = slice(-arr.shape[1], None)
-
else:
-
slice1 = slice(None, arr.shape[1])
-
new_arr[slice0, slice1] = arr
-
return new_arr
-
return list(map(mod_frame, arr_frames))
-
-
def save_video(frames, file, **kwargs):
-
'''
-
Save a series of drawings as a GIF or video.
-
-
Arguments:
-
frames: A list of `Drawing`s or a list of `numpy.array`s.
-
file: File name or file like object to write the video to. The
-
extension determines the output format.
-
align_bottom: If frames are different sizes, align the bottoms of each
-
frame in the video.
-
align_right: If frames are different sizes, align the right edge of each
-
frame in the video.
-
bg: If frames are different sizes, fill the background with this color.
-
(default is white: (255, 255, 255, 255))
-
duration: If writing a GIF, sets the duration of each frame.
-
fps: If writing a video, sets the frame rate in FPS.
-
**kwargs: Other arguments to imageio.mimsave().
-
-
'''
-
if isinstance(frames[0], Drawing):
-
frames = render_svg_frames(frames, **kwargs)
-
kwargs.pop('align_bottom', None)
-
kwargs.pop('align_right', None)
-
kwargs.pop('bg', None)
-
imageio.mimsave(file, frames, **kwargs)
-1
drawSvg/widgets/__init__.py
···
-
from .drawing_widget import DrawingWidget
-91
drawSvg/widgets/drawing_javascript.py
···
-
javascript = '''
-
require.undef('drawingview');
-
-
define('drawingview', ['@jupyter-widgets/base'], function(widgets) {
-
var DrawingView = widgets.DOMWidgetView.extend({
-
render: function() {
-
this.container = document.createElement('a');
-
this.image_changed();
-
this.container.appendChild(this.svg_view);
-
this.el.appendChild(this.container);
-
this.model.on('change:_image', this.image_changed, this);
-
this.model.on('change:_mousemove_blocked', this.block_changed,
-
this);
-
},
-
image_changed: function() {
-
this.container.innerHTML = this.model.get('_image');
-
this.svg_view = this.container.getElementsByTagName('svg')[0];
-
this.cursor_point = this.svg_view.createSVGPoint();
-
this.register_events();
-
},
-
last_move: null,
-
block_changed: function() {
-
var widget = this;
-
window.setTimeout(function() {
-
if (!widget.model.get('_mousemove_blocked') && widget.last_move) {
-
widget.send_mouse_event('mousemove', widget.last_move);
-
}
-
}, 0);
-
},
-
send_mouse_event: function(name, e) {
-
this.last_move = null;
-
if (this.model.get('disable')) {
-
return;
-
}
-
if (this.model.get('throttle')) {
-
this.model.set('_mousemove_blocked', true);
-
this.model.save_changes();
-
}
-
-
this.cursor_point.x = e.clientX;
-
this.cursor_point.y = e.clientY;
-
var svg_pt = this.cursor_point.matrixTransform(
-
this.svg_view.getScreenCTM().inverse());
-
-
this.send({
-
name: name,
-
x: svg_pt.x,
-
y: -svg_pt.y,
-
type: e.type,
-
button: e.button,
-
buttons: e.buttons,
-
shiftKey: e.shiftKey,
-
altKey: e.altKey,
-
ctrlKey: e.ctrlKey,
-
metaKey: e.metaKey,
-
clientX: e.clientX,
-
clientY: e.clientY,
-
movementX: e.movementX,
-
movementY: e.movementY,
-
timeStamp: e.timeStamp,
-
targetId: e.target ? e.target.id : null,
-
currentTargetId: e.currentTarget ? e.currentTarget.id : null,
-
relatedTargetId: e.relatedTarget ? e.relatedTarget.id : null,
-
});
-
},
-
register_events: function() {
-
var widget = this;
-
this.svg_view.addEventListener('mousedown', function(e) {
-
e.preventDefault();
-
widget.send_mouse_event('mousedown', e);
-
});
-
this.svg_view.addEventListener('mousemove', function(e) {
-
e.preventDefault();
-
if (widget.model.get('_mousemove_blocked')) {
-
widget.last_move = e;
-
} else {
-
widget.send_mouse_event('mousemove', e);
-
}
-
});
-
this.svg_view.addEventListener('mouseup', function(e) {
-
e.preventDefault();
-
widget.send_mouse_event('mouseup', e);
-
});
-
}
-
});
-
-
return {
-
DrawingView: DrawingView
-
};
-
});
-
'''
-115
drawSvg/widgets/drawing_widget.py
···
-
from ipywidgets import widgets
-
from traitlets import Unicode, Bool
-
-
-
# Register front end javascript
-
from IPython import display
-
from . import drawing_javascript
-
display.display(display.Javascript(drawing_javascript.javascript))
-
del drawing_javascript
-
-
-
class DrawingWidget(widgets.DOMWidget):
-
_view_name = Unicode('DrawingView').tag(sync=True)
-
_view_module = Unicode('drawingview').tag(sync=True)
-
_view_module_version = Unicode('0.1.0').tag(sync=True)
-
_image = Unicode().tag(sync=True)
-
_mousemove_blocked = Bool(False).tag(sync=True)
-
throttle = Bool(True).tag(sync=True)
-
disable = Bool(False).tag(sync=True)
-
-
def __init__(self, drawing, throttle=True, disable=False):
-
'''
-
DrawingWidget is an interactive Jupyter notebook widget. It works
-
similarly to displaying a Drawing as a cell output but DrawingWidget
-
can register callbacks for user mouse events. Within a callback modify
-
the drawing then call .refresh() to update the output in real time.
-
-
Arguments:
-
drawing: The initial Drawing to display. Call .refresh() after
-
modifying or just assign a new Drawing.
-
throttle: If True, limit the rate of mousemove events. For drawings
-
with many elements, this will significantly reduce lag.
-
disable: While True, mouse events will be disabled.
-
'''
-
super().__init__()
-
self.throttle = throttle
-
self.disable = disable
-
self.drawing = drawing
-
self.mousedown_callbacks = []
-
self.mousemove_callbacks = []
-
self.mouseup_callbacks = []
-
-
self.on_msg(self._receive_msg)
-
-
@property
-
def drawing(self):
-
return self._drawing
-
-
@drawing.setter
-
def drawing(self, drawing):
-
self._drawing = drawing
-
self.refresh()
-
-
def refresh(self):
-
'''
-
Redraw the displayed output with the current value of self.drawing.
-
'''
-
self._image = self.drawing.asSvg()
-
-
def _receive_msg(self, _, content, buffers):
-
if not isinstance(content, dict):
-
return
-
callbacks = {
-
'mousedown': self.mousedown_callbacks,
-
'mousemove': self.mousemove_callbacks,
-
'mouseup': self.mouseup_callbacks,
-
}.get(content.get('name'), ())
-
try:
-
if callbacks:
-
self._call_handlers(callbacks, content.get('x'),
-
content.get('y'), content)
-
finally:
-
self._mousemove_blocked = False
-
-
-
def mousedown(self, handler, remove=False):
-
'''
-
Register (or unregister) a handler for the mousedown event.
-
-
Arguments:
-
remove: If True, unregister, otherwise register.
-
'''
-
self.on_msg
-
self._register_handler(
-
self.mousedown_callbacks, handler, remove=remove)
-
-
def mousemove(self, handler, remove=False):
-
'''
-
Register (or unregister) a handler for the mousemove event.
-
-
Arguments:
-
remove: If True, unregister, otherwise register.
-
'''
-
self._register_handler(
-
self.mousemove_callbacks, handler, remove=remove)
-
-
def mouseup(self, handler, remove=False):
-
'''
-
Register (or unregister) a handler for the mouseup event.
-
-
Arguments:
-
remove: If True, unregister, otherwise register.
-
'''
-
self._register_handler(
-
self.mouseup_callbacks, handler, remove=remove)
-
-
def _register_handler(self, callback_list, handler, remove=False):
-
if remove:
-
callback_list.remove(handler)
-
else:
-
callback_list.append(handler)
-
-
def _call_handlers(self, callback_list, *args, **kwargs):
-
for callback in callback_list:
-
callback(self, *args, **kwargs)
+99
drawsvg/__init__.py
···
+
'''
+
A library for creating SVG files or just drawings that can be displayed in
+
Jupyter notebooks
+
+
Example:
+
```
+
import drawsvg as draw
+
+
d = draw.Drawing(200, 100, origin='center')
+
+
# Draw an irregular polygon
+
d.append(draw.Lines(-80, 45,
+
70, 49,
+
95, -49,
+
-90, -40,
+
close=False,
+
fill='#eeee00',
+
stroke='black'))
+
+
# Draw a rectangle
+
r = draw.Rectangle(-80, -50, 40, 50, fill='#1248ff')
+
r.append_title("Our first rectangle") # Add a tooltip
+
d.append(r)
+
+
# Draw a circle
+
d.append(draw.Circle(-40, 10, 30,
+
fill='red', stroke_width=2, stroke='black'))
+
+
# Draw an arbitrary path (a triangle in this case)
+
p = draw.Path(stroke_width=2, stroke='lime', fill='black', fill_opacity=0.2)
+
p.M(-10, -20) # Start path at point (-10, -20)
+
p.C(30, 10, 30, -50, 70, -20) # Draw a curve to (70, -20)
+
d.append(p)
+
+
# Draw text
+
d.append(draw.Text('Basic text', 8, -10, -35, fill='blue')) # 8pt text at (-10, -35)
+
d.append(draw.Text('Path text', 8, path=p, text_anchor='start', line_height=1))
+
d.append(draw.Text(['Multi-line', 'text'], 8, path=p, text_anchor='end', center=True))
+
+
# Draw multiple circular arcs
+
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))
+
+
# Draw arrows
+
arrow = draw.Marker(-0.1, -0.51, 0.9, 0.5, scale=4, orient='auto')
+
arrow.append(draw.Lines(-0.1, 0.5, -0.1, -0.5, 0.9, 0, fill='red', close=True))
+
p = draw.Path(stroke='red', stroke_width=2, fill='none',
+
marker_end=arrow) # Add an arrow to the end of a path
+
p.M(20, 40).L(20, 27).L(0, 20) # Chain multiple path commands
+
d.append(p)
+
d.append(draw.Line(30, 20, 0, 10,
+
stroke='red', stroke_width=2, fill='none',
+
marker_end=arrow)) # Add an arrow to the end of a line
+
+
d.set_pixel_scale(2) # Set number of pixels per geometry unit
+
#d.set_render_size(400, 200) # Alternative to set_pixel_scale
+
d.save_svg('example.svg')
+
d.save_png('example.png')
+
+
# Display in Jupyter notebook
+
d.rasterize() # Display as PNG
+
d # Display as SVG
+
```
+
'''
+
+
from .defs import *
+
from .raster import Raster
+
from .drawing import Drawing
+
from .types import (
+
Context,
+
DrawingElement,
+
DrawingBasicElement,
+
DrawingParentElement,
+
)
+
from .elements import *
+
from .video import (
+
render_svg_frames,
+
save_video,
+
)
+
from .frame_animation import (
+
FrameAnimation,
+
frame_animate_video,
+
frame_animate_jupyter,
+
frame_animate_spritesheet,
+
)
+
from .native_animation import (
+
SyncedAnimationConfig,
+
animate_element_sequence,
+
animate_text_sequence,
+
)
+
from .url_encode import (
+
bytes_as_data_uri,
+
svg_as_data_uri,
+
svg_as_utf8_data_uri,
+
)
+205
drawsvg/color.py
···
+
import math
+
+
try:
+
import numpy as np
+
import pwkit.colormaps
+
except ImportError as e:
+
raise ImportError(
+
'Optional dependencies not installed. '
+
'Install with `python3 -m pip install "drawsvg[all]"` '
+
'or `python3 -m pip install "drawsvg[color]"`. '
+
'See https://github.com/cduck/drawsvg#full-feature-install '
+
'for more details.'
+
) from e
+
+
+
# Most calculations from http://www.chilliant.com/rgb2hsv.html
+
+
+
def limit(v, low=0, high=1):
+
return max(min(v, high), low)
+
+
class Srgb:
+
LUMA_WEIGHTS = (0.299, 0.587, 0.114)
+
def __init__(self, r, g, b):
+
self.r = float(r)
+
self.g = float(g)
+
self.b = float(b)
+
def __iter__(self):
+
return iter((self.r, self.g, self.b))
+
def __repr__(self):
+
return 'RGB({}, {}, {})'.format(self.r, self.g, self.b)
+
def __str__(self):
+
return 'rgb({}%,{}%,{}%)'.format(self.r*100, self.g*100, self.b*100)
+
def luma(self, wts=None):
+
if wts is None: wts = self.LUMA_WEIGHTS
+
rw, gw, bw = wts
+
return rw*self.r + gw*self.g + bw*self.b
+
def to_srgb(self):
+
return self
+
@staticmethod
+
def from_hue(h):
+
h = h % 1
+
r = abs(h * 6 - 3) - 1
+
g = 2 - abs(h * 6 - 2)
+
b = 2 - abs(h * 6 - 4)
+
return Srgb(limit(r), limit(g), limit(b))
+
+
class Hsl:
+
def __init__(self, h, s, l):
+
self.h = float(h) % 1
+
self.s = float(s)
+
self.l = float(l)
+
def __iter__(self):
+
return iter((self.h, self.s, self.l))
+
def __repr__(self):
+
return 'HSL({}, {}, {})'.format(self.h, self.s, self.l)
+
def __str__(self):
+
r, g, b = self.to_srgb()
+
return 'rgb({}%,{}%,{}%)'.format(
+
round(r*100, 2), round(g*100, 2), round(b*100, 2))
+
def to_srgb(self):
+
hs = Srgb.from_hue(self.h)
+
c = (1 - abs(2 * self.l - 1)) * self.s
+
return Srgb(
+
(hs.r - 0.5) * c + self.l,
+
(hs.g - 0.5) * c + self.l,
+
(hs.b - 0.5) * c + self.l
+
)
+
+
class Hsv:
+
def __init__(self, h, s, v):
+
self.h = float(h) % 1
+
self.s = float(s)
+
self.v = float(v)
+
def __iter__(self):
+
return iter((self.h, self.s, self.v))
+
def __repr__(self):
+
return 'HSV({}, {}, {})'.format(self.h, self.s, self.v)
+
def __str__(self):
+
r, g, b = self.to_srgb()
+
return 'rgb({}%,{}%,{}%)'.format(
+
round(r*100, 2), round(g*100, 2), round(b*100, 2))
+
def to_srgb(self):
+
hs = Srgb.from_hue(self.h)
+
c = self.v * self.s
+
hp = self.h * 6
+
x = c * (1 - abs(hp % 2 - 1))
+
if hp < 1:
+
r1, g1, b1 = c, x, 0
+
elif hp < 2:
+
r1, g1, b1 = x, c, 0
+
elif hp < 3:
+
r1, g1, b1 = 0, c, x
+
elif hp < 4:
+
r1, g1, b1 = 0, x, c
+
elif hp < 5:
+
r1, g1, b1 = x, 0, c
+
else:
+
r1, g1, b1 = c, 0, x
+
m = self.v - c
+
return Srgb(r1+m, g1+m, b1+m)
+
+
class Sin:
+
def __init__(self, h, s, l):
+
self.h = float(h) % 1
+
self.s = float(s)
+
self.l = float(l)
+
def __iter__(self):
+
return iter((self.h, self.s, self.l))
+
def __repr__(self):
+
return 'Sin({}, {}, {})'.format(self.h, self.s, self.l)
+
def __str__(self):
+
r, g, b = self.to_srgb()
+
return 'rgb({}%,{}%,{}%)'.format(
+
round(r*100, 2), round(g*100, 2), round(b*100, 2))
+
def to_srgb(self):
+
h = self.h
+
scale = self.s / 2
+
shift = self.l #* (1-2*scale)
+
return Srgb(
+
shift + scale * math.cos(math.pi*2 * (h - 0/6)),
+
shift + scale * math.cos(math.pi*2 * (h - 2/6)),
+
shift + scale * math.cos(math.pi*2 * (h - 4/6)),
+
)
+
+
class Hcy:
+
HCY_WEIGHTS = Srgb.LUMA_WEIGHTS
+
def __init__(self, h, c, y):
+
self.h = float(h) % 1
+
self.c = float(c)
+
self.y = float(y)
+
def __iter__(self):
+
return iter((self.h, self.c, self.y))
+
def __repr__(self):
+
return 'HCY({}, {}, {})'.format(self.h, self.c, self.y)
+
def __str__(self):
+
r, g, b = self.to_srgb()
+
return 'rgb({}%,{}%,{}%)'.format(r*100, g*100, b*100)
+
def to_srgb(self):
+
hs = Srgb.from_hue(self.h)
+
y = hs.luma(wts=self.HCY_WEIGHTS)
+
c = self.c
+
if self.y < y:
+
c *= self.y / y
+
elif y < 1:
+
c *= (1 - self.y) / (1 - y)
+
return Srgb(
+
(hs.r - y) * c + self.y,
+
(hs.g - y) * c + self.y,
+
(hs.b - y) * c + self.y,
+
)
+
@staticmethod
+
def _rgb_to_hcv(srgb):
+
if srgb.g < srgb.b:
+
p = (srgb.b, srgb.g, -1., 2./3.)
+
else:
+
p = (srgb.g, srgb.b, 0., -1./3.)
+
if srgb.r < p[0]:
+
q = (p[0], p[1], p[3], srgb.r)
+
else:
+
q = (srgb.r, p[1], p[2], p[0])
+
c = q[0] - min(q[3], q[1])
+
h = abs((q[3] - q[1]) / (6*c + 1e-10) + q[2])
+
return (h, c, q[0])
+
@classmethod
+
def from_srgb(cls, srgb):
+
hcv = list(cls._rgb_to_hcv(srgb))
+
rw, gw, bw = cls.HCY_WEIGHTS
+
y = rw*srgb.r + gw*srgb.g + bw*srgb.b
+
hs = Srgb.from_hue(hcv[0])
+
z = rw*hs.r + gw*hs.g + bw*hs.b
+
if y < z:
+
hcv[1] *= z / (y + 1e-10)
+
else:
+
hcv[1] *= (1 - z) / (1 - y + 1e-10)
+
return Hcy(hcv[0], hcv[1], y)
+
+
class Cielab:
+
REF_WHITE = (0.95047, 1., 1.08883)
+
def __init__(self, l, a, b):
+
self.l = float(l)
+
self.a = float(a)
+
self.b = float(b)
+
def __iter__(self):
+
return iter((self.l, self.a, self.b))
+
def __repr__(self):
+
return 'CIELAB({}, {}, {})'.format(self.l, self.a, self.b)
+
def __str__(self):
+
r, g, b = self.to_srgb()
+
return 'rgb({}%,{}%,{}%)'.format(
+
round(r*100, 2), round(g*100, 2), round(b*100, 2))
+
def to_srgb(self):
+
in_arr = np.array((self.l, self.a, self.b))
+
xyz = pwkit.colormaps.cielab_to_xyz(in_arr, self.REF_WHITE)
+
lin_srgb = pwkit.colormaps.xyz_to_linsrgb(xyz)
+
r, g, b = pwkit.colormaps.linsrgb_to_srgb(lin_srgb)
+
return Srgb(r, g, b)
+
@classmethod
+
def from_srgb(cls, srgb, ref_white=None):
+
if ref_white is None: ref_white = cls.REF_WHITE
+
in_arr = np.array((*srgb,), dtype=float)
+
lin_srgb = pwkit.colormaps.srgb_to_linsrgb(in_arr)
+
xyz = pwkit.colormaps.linsrgb_to_xyz(lin_srgb)
+
l, a, b = pwkit.colormaps.xyz_to_cielab(xyz, ref_white)
+
return Cielab(l, a, b)
+116
drawsvg/defs.py
···
+
from .elements import DrawingElement, DrawingParentElement
+
+
+
class DrawingDef(DrawingParentElement):
+
'''Parent class of SVG nodes that must be direct children of <defs>.'''
+
def get_svg_defs(self):
+
return (self,)
+
+
class DrawingDefSub(DrawingParentElement):
+
'''Parent class of SVG nodes that are meant to be descendants of a Def.'''
+
pass
+
+
class LinearGradient(DrawingDef):
+
'''
+
A linear gradient to use as a fill or other color.
+
+
Has <stop> nodes as children, added with `.add_stop()`.
+
'''
+
TAG_NAME = 'linearGradient'
+
def __init__(self, x1, y1, x2, y2, gradientUnits='userSpaceOnUse',
+
**kwargs):
+
super().__init__(x1=x1, y1=y1, x2=x2, y2=y2,
+
gradientUnits=gradientUnits, **kwargs)
+
def add_stop(self, offset, color, opacity=None, **kwargs):
+
stop = GradientStop(offset=offset, stop_color=color,
+
stop_opacity=opacity, **kwargs)
+
self.append(stop)
+
return stop
+
+
class RadialGradient(DrawingDef):
+
'''
+
A radial gradient to use as a fill or other color.
+
+
Has <stop> nodes as children, added with `.add_stop()`.
+
'''
+
TAG_NAME = 'radialGradient'
+
def __init__(self, cx, cy, r, gradientUnits='userSpaceOnUse', fy=None,
+
**kwargs):
+
super().__init__(cx=cx, cy=cy, r=r, gradientUnits=gradientUnits,
+
fy=fy, **kwargs)
+
def add_stop(self, offset, color, opacity=None, **kwargs):
+
stop = GradientStop(offset=offset, stop_color=color,
+
stop_opacity=opacity, **kwargs)
+
self.append(stop)
+
return stop
+
+
class GradientStop(DrawingDefSub):
+
'''A control point for a radial or linear gradient.'''
+
TAG_NAME = 'stop'
+
has_content = False
+
+
class Pattern(DrawingDef):
+
'''
+
A repeating pattern of other drawing elements to use as a fill or other
+
color.
+
+
Width and height specify the repetition period. Append regular drawing
+
elements to create the pattern.
+
'''
+
TAG_NAME = 'pattern'
+
def __init__(self, width, height, x=None, y=None,
+
patternUnits='userSpaceOnUse', **kwargs):
+
super().__init__(width=width, height=height, x=x, y=y,
+
patternUnits=patternUnits, **kwargs)
+
+
class ClipPath(DrawingDef):
+
'''
+
A shape used to crop another element by not drawing outside of this shape.
+
+
Has regular drawing elements as children.
+
'''
+
TAG_NAME = 'clipPath'
+
+
class Mask(DrawingDef):
+
'''
+
A drawing where the gray value and transparency are used to control the
+
transparency of another shape.
+
+
Has regular drawing elements as children.
+
'''
+
TAG_NAME = 'mask'
+
+
class Filter(DrawingDef):
+
'''
+
A filter to apply to geometry.
+
+
For example a blur filter.
+
'''
+
TAG_NAME = 'filter'
+
+
class FilterItem(DrawingDefSub):
+
'''A child of Filter with any tag name.'''
+
def __init__(self, tag_name, **args):
+
super().__init__(**args)
+
self.TAG_NAME = tag_name
+
+
class Marker(DrawingDef):
+
'''
+
A small drawing that can be placed at the ends of (or along) a path.
+
+
This can be used for arrow heads or points on a graph for example.
+
By default, units are multiples of stroke width.
+
'''
+
TAG_NAME = 'marker'
+
def __init__(self, minx, miny, maxx, maxy, scale=1, orient='auto',
+
**kwargs):
+
width = maxx - minx
+
height = maxy - miny
+
kwargs = {
+
'markerWidth': width if scale == 1 else float(width) * scale,
+
'markerHeight': height if scale == 1 else float(height) * scale,
+
'viewBox': '{} {} {} {}'.format(minx, miny, width, height),
+
'orient': orient,
+
**kwargs,
+
}
+
super().__init__(**kwargs)
+445
drawsvg/drawing.py
···
+
import dataclasses
+
from io import StringIO
+
from collections import defaultdict
+
import random
+
import string
+
import xml.sax.saxutils as xml
+
+
from . import (
+
types, elements as elements_module, raster, video, jupyter,
+
native_animation, font_embed,
+
)
+
+
+
XML_HEADER = '<?xml version="1.0" encoding="UTF-8"?>\n'
+
SVG_START = ('<svg xmlns="http://www.w3.org/2000/svg" '
+
'xmlns:xlink="http://www.w3.org/1999/xlink"\n ')
+
SVG_END = '</svg>'
+
SVG_CSS_FMT = '<style>/*<![CDATA[*/{}/*]]>*/</style>'
+
SVG_JS_FMT = '<script>/*<![CDATA[*/{}/*]]>*/</script>'
+
+
+
class Drawing:
+
'''
+
A vector drawing.
+
+
Append shapes and other elements with `.append()`. The default coordinate
+
system origin is at the top-left corner with x-values increasing to the
+
right and y-values increasing downward.
+
+
Supports Jupyter: If a Drawing is the last line of a cell, it will be
+
displayed as an SVG below.
+
'''
+
def __init__(self, width, height, origin=(0,0), context: types.Context=None,
+
animation_config=None, id_prefix='d', **svg_args):
+
if context is None:
+
context = types.Context()
+
if animation_config is not None:
+
context = dataclasses.replace(
+
context, animation_config=animation_config)
+
self.width = width
+
self.height = height
+
if isinstance(origin, str):
+
if context.invert_y and origin.startswith('bottom-'):
+
origin = origin.replace('bottom-', 'top-')
+
elif context.invert_y and origin.startswith('top-'):
+
origin = origin.replace('top-', 'bottom-')
+
self.view_box = {
+
'center': (-width/2, -height/2, width, height),
+
'top-left': (0, 0, width, height),
+
'top-right': (-width, 0, width, height),
+
'bottom-left': (0, -height, width, height),
+
'bottom-right': (-width, -height, width, height),
+
}[origin]
+
else:
+
origin = tuple(origin)
+
if len(origin) != 2:
+
raise ValueError(
+
"origin must be the string 'center', 'top-left', ..., "
+
"'bottom-right' or a tuple (x, y)")
+
self.view_box = origin + (width, height)
+
self.elements = []
+
self.ordered_elements = defaultdict(list)
+
self.other_defs = []
+
self.css_list = []
+
self.js_list = []
+
self.pixel_scale = 1
+
self.render_width = None
+
self.render_height = None
+
self.context = context
+
self.id_prefix = str(id_prefix)
+
self.svg_args = {}
+
self._cached_context = None
+
self._cached_extra_prepost_with_context = None
+
for k, v in svg_args.items():
+
k = k.replace('__', ':')
+
k = k.replace('_', '-')
+
if k[-1] == '-':
+
k = k[:-1]
+
self.svg_args[k] = v
+
def set_render_size(self, w=None, h=None):
+
self.render_width = w
+
self.render_height = h
+
return self
+
def set_pixel_scale(self, s=1):
+
self.render_width = None
+
self.render_height = None
+
self.pixel_scale = s
+
return self
+
def calc_render_size(self):
+
if self.render_width is None and self.render_height is None:
+
return (self.width * self.pixel_scale,
+
self.height * self.pixel_scale)
+
elif self.render_width is None:
+
s = self.render_height / self.height
+
return self.width * s, self.render_height
+
elif self.render_height is None:
+
s = self.render_width / self.width
+
return self.render_width, self.height * s
+
else:
+
return self.render_width, self.render_height
+
def draw(self, obj, *, z=None, **kwargs):
+
'''Add any object that knows how to draw itself to the drawing.
+
+
This object must implement the `to_drawables(**kwargs)` method
+
that returns a `DrawingElement` or list of elements.
+
'''
+
if obj is None:
+
return
+
if not hasattr(obj, 'write_svg_element'):
+
elements = obj.to_drawables(**kwargs)
+
else:
+
if len(kwargs) > 0:
+
raise ValueError('unexpected kwargs')
+
elements = obj
+
if hasattr(elements, 'write_svg_element'):
+
self.append(elements, z=z)
+
else:
+
self.extend(elements, z=z)
+
def append(self, element, *, z=None):
+
'''Add any `DrawingElement` to the drawing.
+
+
Do not append a `DrawingDef` referenced by other elements. These are
+
included automatically. Use `.append_def()` for an unreferenced
+
`DrawingDef`.
+
'''
+
if z is not None:
+
self.ordered_elements[z].append(element)
+
else:
+
self.elements.append(element)
+
def extend(self, iterable, *, z=None):
+
if z is not None:
+
self.ordered_elements[z].extend(iterable)
+
else:
+
self.elements.extend(iterable)
+
def insert(self, i, element):
+
'''Inserts a top-level element at the given array index.'''
+
self.elements.insert(i, element)
+
def remove(self, element):
+
'''Removes a top-level element (except those with a z-index).'''
+
self.elements.remove(element)
+
def clear(self):
+
'''Clears all drawing elements, with or without a z-index, but keeps
+
defs-type elements added with `append_def()`.
+
'''
+
self.elements.clear()
+
self.ordered_elements.clear()
+
def index(self, *args, **kwargs):
+
'''Finds the array-index of a top-level element (except those with a
+
z-index).
+
'''
+
return self.elements.index(*args, **kwargs)
+
def count(self, element):
+
'''Counts the number of top-level elements (except those with a z-index
+
).
+
'''
+
return self.elements.count(element)
+
def reverse(self):
+
'''Reverses the order of all elements (except those with a z-index).'''
+
self.elements.reverse()
+
def draw_def(self, obj, **kwargs):
+
if not hasattr(obj, 'write_svg_element'):
+
elements = obj.to_drawables(**kwargs)
+
else:
+
if len(kwargs) > 0:
+
raise ValueError('unexpected kwargs')
+
elements = obj
+
if hasattr(elements, 'write_svg_element'):
+
self.append_def(elements)
+
else:
+
self.other_defs.extend(elements)
+
def append_def(self, element):
+
self.other_defs.append(element)
+
def append_title(self, text, **kwargs):
+
self.append(elements_module.Title(text, **kwargs))
+
def append_css(self, css_text):
+
self.css_list.append(css_text)
+
def embed_google_font(self, family, text=None, display='swap', **kwargs):
+
'''Download SVG-embeddable CSS from Google fonts.
+
+
Args:
+
family: Name of font family or list of font families.
+
text: The set of characters required from the font. Only a font
+
subset with these characters will be downloaded.
+
display: The font-display CSS value.
+
**kwargs: Other URL parameters sent to
+
https://fonts.googleapis.com/css?...
+
'''
+
self.append_css(font_embed.download_google_font_css(
+
family, text=text, display=display, **kwargs))
+
def append_javascript(self, js_text, onload=None):
+
if onload:
+
if self.svg_args.get('onload'):
+
self.svg_args['onload'] = f'{self.svg_args["onload"]};{onload}'
+
else:
+
self.svg_args['onload'] = onload
+
self.js_list.append(js_text)
+
def all_elements(self, context=None):
+
'''Return self.elements, self.ordered_elements, and extras as a single
+
list.
+
'''
+
extra_pre, extra_post = (
+
self._extra_prepost_with_context_avoid_recompute(
+
context=context))
+
output = list(extra_pre)
+
output.extend(self.elements)
+
for z in sorted(self.ordered_elements):
+
output.extend(self.ordered_elements[z])
+
output.extend(extra_post)
+
return output
+
def _extra_prepost_with_context_avoid_recompute(self, context=None):
+
if (self._cached_extra_prepost_with_context is not None
+
and self._cached_context == context):
+
return self._cached_extra_prepost_with_context
+
self._cached_context = context
+
self._cached_extra_prepost_with_context = (
+
self._extra_prepost_children_with_context(context))
+
return self._cached_extra_prepost_with_context
+
def _extra_prepost_children_with_context(self, context=None):
+
if context is None:
+
context = self.context
+
return context.extra_prepost_drawing_elements(self)
+
def all_css(self, context=None):
+
if context is None:
+
context = self.context
+
return list(context.extra_css(self)) + self.css_list
+
def all_javascript(self, context=None):
+
if context is None:
+
context = self.context
+
return list(context.extra_javascript(self)) + self.js_list
+
def as_svg(self, output_file=None, randomize_ids=False, header=XML_HEADER,
+
skip_js=False, skip_css=False, context=None):
+
if output_file is None:
+
with StringIO() as f:
+
self.as_svg(
+
f, randomize_ids=randomize_ids, header=header,
+
skip_js=skip_js, skip_css=skip_css, context=context)
+
return f.getvalue()
+
if context is None:
+
context = self.context
+
output_file.write(header)
+
img_width, img_height = self.calc_render_size()
+
svg_args = dict(
+
width=img_width, height=img_height,
+
viewBox=' '.join(map(str, self.view_box)))
+
svg_args.update(self.svg_args)
+
output_file.write(SVG_START)
+
context.write_svg_document_args(self, svg_args, output_file)
+
output_file.write('>\n')
+
css_list = self.all_css(context)
+
if css_list and not skip_css:
+
output_file.write(SVG_CSS_FMT.format(elements_module.escape_cdata(
+
'\n'.join(css_list))))
+
output_file.write('\n')
+
output_file.write('<defs>\n')
+
# Write definition elements
+
id_prefix = self.id_prefix
+
id_prefix = self._random_id() if randomize_ids else self.id_prefix
+
id_index = 0
+
def id_gen(base=''):
+
nonlocal id_index
+
id_str = f'{id_prefix}{base}{id_index}'
+
id_index += 1
+
return id_str
+
id_map = defaultdict(id_gen)
+
prev_set = set()
+
def is_duplicate(obj):
+
nonlocal prev_set
+
dup = id(obj) in prev_set
+
prev_set.add(id(obj))
+
return dup
+
for element in self.other_defs:
+
if hasattr(element, 'write_svg_element'):
+
local = types.LocalContext(
+
context, element, self, self.other_defs)
+
element.write_svg_element(
+
id_map, is_duplicate, output_file, local, False)
+
output_file.write('\n')
+
all_elements = self.all_elements(context=context)
+
for element in all_elements:
+
if hasattr(element, 'write_svg_defs'):
+
local = types.LocalContext(context, element, self, all_elements)
+
element.write_svg_defs(
+
id_map, is_duplicate, output_file, local, False)
+
output_file.write('</defs>\n')
+
# Generate ids for normal elements
+
prev_def_set = set(prev_set)
+
for element in all_elements:
+
if hasattr(element, 'write_svg_element'):
+
local = types.LocalContext(context, element, self, all_elements)
+
element.write_svg_element(
+
id_map, is_duplicate, output_file, local, True)
+
prev_set = prev_def_set
+
# Write normal elements
+
for element in all_elements:
+
if hasattr(element, 'write_svg_element'):
+
local = types.LocalContext(context, element, self, all_elements)
+
element.write_svg_element(
+
id_map, is_duplicate, output_file, local, False)
+
output_file.write('\n')
+
js_list = self.all_javascript(context)
+
if js_list and not skip_js:
+
output_file.write(SVG_JS_FMT.format(elements_module.escape_cdata(
+
'\n'.join(js_list))))
+
output_file.write('\n')
+
output_file.write(SVG_END)
+
def as_html(self, output_file=None, title=None, randomize_ids=False,
+
context=None, fix_embed_iframe=False):
+
if output_file is None:
+
with StringIO() as f:
+
self.as_html(
+
f, title=title, randomize_ids=randomize_ids,
+
context=context, fix_embed_iframe=fix_embed_iframe)
+
return f.getvalue()
+
output_file.write('<!DOCTYPE html>\n')
+
output_file.write('<head>\n')
+
output_file.write('<meta charset="utf-8">\n')
+
if title is not None:
+
output_file.write(f'<title>{xml.escape(title)}</title>\n')
+
# Prevent iframe scroll bar
+
if fix_embed_iframe:
+
fix = self.calc_render_size()[1] / 2
+
output_file.write(f'''<style>
+
html,body {{
+
margin: 0;
+
height: 100%;
+
}}
+
svg {{
+
margin-bottom: {-fix}px;
+
}}
+
</style>''')
+
output_file.write('</head>\n<body>\n')
+
self.as_svg(
+
output_file, randomize_ids=randomize_ids, header="",
+
skip_css=False, skip_js=False, context=context)
+
output_file.write('\n</body>\n</html>\n')
+
@staticmethod
+
def _random_id(length=8):
+
return (random.choice(string.ascii_letters)
+
+ ''.join(random.choices(
+
string.ascii_letters+string.digits, k=length-1)))
+
def save_svg(self, fname, encoding='utf-8', context=None):
+
with open(fname, 'w', encoding=encoding) as f:
+
self.as_svg(output_file=f, context=context)
+
def save_html(self, fname, title=None, encoding='utf-8', context=None):
+
with open(fname, 'w', encoding=encoding) as f:
+
self.as_html(output_file=f, title=title, context=context)
+
def save_png(self, fname, context=None):
+
self.rasterize(to_file=fname, context=context)
+
def rasterize(self, to_file=None, context=None):
+
if to_file is not None:
+
return raster.Raster.from_svg_to_file(
+
self.as_svg(context=context), to_file)
+
else:
+
return raster.Raster.from_svg(self.as_svg(context=context))
+
def as_animation_frames(self, fps=10, duration=None, context=None):
+
'''Returns a list of synced animation frames that can be converted to a
+
video.'''
+
if context is None:
+
context = self.context
+
config = context.animation_config
+
if duration is None and config is not None:
+
duration = config.duration
+
if duration is None:
+
raise ValueError('unknown animation duration, specify duration')
+
if config is None:
+
config = native_animation.SyncedAnimationConfig(duration)
+
frames = []
+
for i in range(int(duration * fps + 1)):
+
time = i / fps
+
frame_context = dataclasses.replace(
+
context,
+
animation_config=dataclasses.replace(
+
config,
+
freeze_frame_at=time,
+
show_playback_controls=False))
+
frames.append(self.display_inline(context=frame_context))
+
return frames
+
def save_video(self, fname, fps=10, duration=None, mime_type=None,
+
file_type=None, context=None, verbose=False):
+
self.as_video(
+
fname, fps=fps, duration=duration, mime_type=mime_type,
+
file_type=file_type, context=context, verbose=verbose)
+
def save_gif(self, fname, fps=10, duration=None, context=None,
+
verbose=False):
+
self.as_gif(
+
fname, fps=fps, duration=duration, context=context,
+
verbose=verbose)
+
def save_mp4(self, fname, fps=10, duration=None, context=None,
+
verbose=False):
+
self.as_mp4(
+
fname, fps=fps, duration=duration, context=context,
+
verbose=verbose)
+
def save_spritesheet(self, fname, fps=10, duration=None, context=None,
+
row_length=None, verbose=False):
+
self.as_spritesheet(
+
fname, fps=fps, duration=duration, context=context,
+
row_length=row_length, verbose=verbose)
+
def as_video(self, to_file=None, fps=10, duration=None,
+
mime_type=None, file_type=None, context=None, verbose=False):
+
if file_type is None and mime_type is None:
+
if to_file is None or '.' not in str(to_file):
+
file_type = 'mp4'
+
else:
+
file_type = str(to_file).split('.')[-1]
+
if file_type is None:
+
file_type = mime_type.split('/')[-1]
+
elif mime_type is None:
+
mime_type = f'video/{file_type}'
+
frames = self.as_animation_frames(
+
fps=fps, duration=duration, context=context)
+
return video.RasterVideo.from_frames(
+
frames, to_file=to_file, fps=fps, mime_type=mime_type,
+
file_type=file_type, verbose=verbose)
+
def as_gif(self, to_file=None, fps=10, duration=None, context=None,
+
verbose=False):
+
return self.as_video(
+
to_file=to_file, fps=fps, duration=duration, context=context,
+
mime_type='image/gif', file_type='gif', verbose=verbose)
+
def as_mp4(self, to_file=None, fps=10, duration=None, context=None,
+
verbose=False):
+
return self.as_video(
+
to_file=to_file, fps=fps, duration=duration, context=context,
+
mime_type='video/mp4', file_type='mp4', verbose=verbose)
+
def as_spritesheet(self, to_file=None, fps=10, duration=None, context=None,
+
row_length=None, verbose=False):
+
frames = self.as_animation_frames(
+
fps=fps, duration=duration, context=context)
+
sheet = video.render_spritesheet(
+
frames, row_length=row_length, verbose=verbose)
+
return raster.Raster.from_arr(sheet, out_file=to_file)
+
def _repr_svg_(self):
+
'''Display in Jupyter notebook.'''
+
return self.as_svg(randomize_ids=True)
+
def display_inline(self, context=None):
+
'''Display inline in the Jupyter web page.'''
+
return jupyter.JupyterSvgInline(self.as_svg(
+
randomize_ids=True, context=context))
+
def display_iframe(self, context=None):
+
'''Display within an iframe the Jupyter web page.'''
+
w, h = self.calc_render_size()
+
html = self.as_html(fix_embed_iframe=True, context=context)
+
return jupyter.JupyterSvgFrame(html, w, h, mime='text/html')
+
def display_image(self, context=None):
+
'''Display within an img in the Jupyter web page.'''
+
return jupyter.JupyterSvgImage(self.as_svg(context=context))
+623
drawsvg/elements.py
···
+
import math
+
import os.path
+
import warnings
+
import xml.sax.saxutils as xml
+
+
from . import url_encode
+
from .types import (
+
DrawingElement, DrawingBasicElement, DrawingParentElement, LocalContext
+
)
+
+
+
def escape_cdata(content):
+
return content.replace(']]>', ']]]]><![CDATA[>')
+
+
+
class NoElement(DrawingElement):
+
''' A drawing element that has no effect '''
+
def __init__(self):
+
pass
+
def write_svg_element(self, id_map, is_duplicate, output_file, dry_run,
+
lcontext, force_dup=False):
+
pass
+
def __eq__(self, other):
+
if isinstance(other, type(self)):
+
return True
+
return False
+
+
class Group(DrawingParentElement):
+
'''A group of drawing elements.
+
+
Any transform will apply to its children and other attributes will be
+
inherited by its children.
+
'''
+
TAG_NAME = 'g'
+
+
class Raw(DrawingElement):
+
'''Raw unescaped text to include in the SVG output.
+
+
Special XML characters like '<' and '&' in the content may have unexpected
+
effects or completely break the resulting SVG.
+
'''
+
has_content = True
+
def __init__(self, content, defs=()):
+
super().__init__()
+
self.content = content
+
self.defs = defs
+
self.id = None
+
def write_svg_element(self, id_map, is_duplicate, output_file, lcontext,
+
dry_run, force_dup=False):
+
if dry_run:
+
return
+
output_file.write(self.content)
+
def get_svg_defs(self):
+
return self.defs
+
def check_children_allowed(self):
+
raise RuntimeError('{} does not support children'.format(type(self)))
+
+
class Use(DrawingBasicElement):
+
'''A copy of another element, drawn at a given position
+
+
The referenced element becomes an SVG def shared between all Use elements
+
that reference it. Useful for drawings with many copies of similar shapes.
+
Additional arguments like `fill='red'` will be used as the default for this
+
copy of the shapes.
+
'''
+
TAG_NAME = 'use'
+
def __init__(self, other_elem, x, y, **kwargs):
+
if isinstance(other_elem, str) and not other_elem.startswith('#'):
+
other_elem = '#' + other_elem
+
super().__init__(xlink__href=other_elem, x=x, y=y, **kwargs)
+
+
class Animate(DrawingBasicElement):
+
'''Animation for a specific property of another element.
+
+
This should be added as a child of the element to animate. Otherwise the
+
referenced other element and this element must both be added to the drawing.
+
+
Useful SVG attributes:
+
- repeatCount: 0, 1, ..., 'indefinite'
+
'''
+
TAG_NAME = 'animate'
+
def __init__(self, attributeName, dur, from_or_values=None, to=None,
+
begin=None, other_elem=None, **kwargs):
+
if to is None:
+
values = from_or_values
+
from_ = None
+
else:
+
values = None
+
from_ = from_or_values
+
if isinstance(other_elem, str) and not other_elem.startswith('#'):
+
other_elem = '#' + other_elem
+
args = dict(
+
attributeName=attributeName, dur=dur, begin=begin, from_=from_,
+
to=to, values=values)
+
args.update(kwargs)
+
super().__init__(xlink__href=other_elem, **args)
+
+
def get_svg_defs(self):
+
return [v for k, v in self.args.items()
+
if isinstance(v, DrawingElement)
+
if k != 'xlink:href']
+
+
def get_linked_elems(self):
+
elem = self.args['xlink:href']
+
return (elem,) if elem is not None else ()
+
+
class _Mpath(DrawingBasicElement):
+
'''Used by AnimateMotion.'''
+
TAG_NAME = 'mpath'
+
def __init__(self, other_path, **kwargs):
+
super().__init__(xlink__href=other_path, **kwargs)
+
+
class AnimateMotion(Animate):
+
'''Animation for the motion of another element along a path.
+
+
This should be added as a child of the element to animate. Otherwise the
+
referenced other element and this element must both be added to the drawing.
+
'''
+
TAG_NAME = 'animateMotion'
+
def __init__(self, path, dur, from_or_values=None, to=None, begin=None,
+
other_elem=None, **kwargs):
+
use_mpath = False
+
if isinstance(path, DrawingElement):
+
use_mpath = True
+
path_elem = path
+
path = None
+
kwargs.setdefault('attributeName', None)
+
super().__init__(dur=dur, from_or_values=from_or_values, to=to,
+
begin=begin, path=path, other_elem=other_elem,
+
**kwargs)
+
if use_mpath:
+
self.children.append(_Mpath(path_elem))
+
+
class AnimateTransform(Animate):
+
'''Animation for the transform property of another element.
+
+
This should be added as a child of the element to animate. Otherwise the
+
referenced other element and this element must both be added to the drawing.
+
'''
+
TAG_NAME = 'animateTransform'
+
def __init__(self, type, dur, from_or_values, to=None, begin=None,
+
attributeName='transform', other_elem=None, **kwargs):
+
super().__init__(attributeName, dur=dur, from_or_values=from_or_values,
+
to=to, begin=begin, type=type, other_elem=other_elem,
+
**kwargs)
+
+
class Set(Animate):
+
'''Animation for a specific property of another element that sets the new
+
value without a transition.
+
+
This should be added as a child of the element to animate. Otherwise the
+
referenced other element and this element must both be added to the drawing.
+
'''
+
TAG_NAME = 'set'
+
def __init__(self, attributeName, dur, to=None, begin=None,
+
other_elem=None, **kwargs):
+
super().__init__(attributeName, dur=dur, from_or_values=None,
+
to=to, begin=begin, other_elem=other_elem, **kwargs)
+
+
class Discard(Animate):
+
'''Animation configuration specifying when it is safe to discard another
+
element.
+
+
Use this when an element will no longer be visible after an animation.
+
This should be added as a child of the element to animate. Otherwise the
+
referenced other element and this element must both be added to the drawing.
+
'''
+
TAG_NAME = 'discard'
+
def __init__(self, attributeName, begin=None, **kwargs):
+
kwargs.setdefault('attributeName', None)
+
kwargs.setdefault('to', None)
+
kwargs.setdefault('dur', None)
+
super().__init__(from_or_values=None, begin=begin, other_elem=None,
+
**kwargs)
+
+
class Image(DrawingBasicElement):
+
'''A linked or embedded image.'''
+
TAG_NAME = 'image'
+
MIME_MAP = {
+
'.bm': 'image/bmp',
+
'.bmp': 'image/bmp',
+
'.gif': 'image/gif',
+
'.jpeg':'image/jpeg',
+
'.jpg': 'image/jpeg',
+
'.png': 'image/png',
+
'.svg': 'image/svg+xml',
+
'.tif': 'image/tiff',
+
'.tiff':'image/tiff',
+
'.pdf': 'application/pdf',
+
'.txt': 'text/plain',
+
}
+
MIME_DEFAULT = 'image/png'
+
def __init__(self, x, y, width, height, path=None, data=None, embed=False,
+
mime_type=None, **kwargs):
+
'''
+
Specify either the path or data argument. If path is used and embed is
+
True, the image file is embedded in a data URI.
+
'''
+
if path is None and data is None:
+
raise ValueError('Either path or data arguments must be given')
+
if embed:
+
if mime_type is None and path is not None:
+
ext = os.path.splitext(path)[1].lower()
+
if ext in self.MIME_MAP:
+
mime_type = self.MIME_MAP[ext]
+
else:
+
mime_type = self.MIME_DEFAULT
+
warnings.warn('Unknown image file type "{}"'.format(ext),
+
Warning)
+
if mime_type is None:
+
mime_type = self.MIME_DEFAULT
+
warnings.warn('Unspecified image type; assuming png', Warning)
+
if data is not None:
+
embed = True
+
if embed and data is None:
+
with open(path, 'rb') as f:
+
data = f.read()
+
if not embed:
+
uri = path
+
else:
+
uri = url_encode.bytes_as_data_uri(data, mime=mime_type)
+
super().__init__(x=x, y=y, width=width, height=height, xlink__href=uri,
+
**kwargs)
+
+
class Text(DrawingParentElement):
+
'''A line or multiple lines of text, optionally placed along a path.
+
+
Additional keyword arguments are output as additional arguments to the SVG
+
node e.g. fill='red', font_size=20, letter_spacing=1.5.
+
+
Useful SVG attributes:
+
- text_anchor: start, middle, end
+
- dominant_baseline:
+
auto, central, middle, hanging, text-top, mathematical, ...
+
See https://developer.mozilla.org/en-US/docs/Web/SVG/Element/text
+
+
CairoSVG bug with letter spacing text on a path: The first two letters are
+
always spaced as if letter_spacing=1.
+
'''
+
TAG_NAME = 'text'
+
has_content = True
+
def __new__(cls, text='', *args, path=None, id=None, _skip_check=False,
+
**kwargs):
+
# Check for the special case of multi-line text on a path
+
# This is inconsistently implemented by renderers so we return a group
+
# of single-line text on paths instead.
+
if path is not None and not _skip_check:
+
text, _ = cls._handle_text_argument(text, True)
+
if len(text) > 1:
+
# Special case
+
g = Group(id=id)
+
for i, line in enumerate(text):
+
subtext = [None] * len(text)
+
subtext[i] = line
+
g.append(Text(subtext, *args, path=path, _skip_check=True,
+
**kwargs))
+
return g
+
return super().__new__(cls)
+
def __init__(self, text, font_size, x=None, y=None, *, center=False,
+
line_height=1, line_offset=0, path=None, start_offset=None,
+
path_args=None, tspan_args=None, cairo_fix=True,
+
_skip_check=False, **kwargs):
+
# Check argument requirements
+
if path is None:
+
if x is None or y is None:
+
raise TypeError(
+
"__init__() missing required arguments: 'x' and 'y' "
+
"are required unless 'path' is specified")
+
else:
+
if x is not None or y is not None:
+
raise TypeError(
+
"__init__() conflicting arguments: 'x' and 'y' "
+
"should not be used when 'path' is specified")
+
if path_args is None:
+
path_args = {}
+
if start_offset is not None:
+
path_args.setdefault('startOffset', start_offset)
+
if tspan_args is None:
+
tspan_args = {}
+
on_path = path is not None
+
+
text, single_line = self._handle_text_argument(
+
text, force_multi=on_path)
+
num_lines = len(text)
+
+
# Text alignment
+
if center:
+
kwargs.setdefault('text_anchor', 'middle')
+
if path is None and single_line:
+
kwargs.setdefault('dominant_baseline', 'central')
+
else:
+
line_offset += 0.5
+
line_offset -= line_height * (num_lines - 1) / 2
+
# Text alignment on a path
+
if on_path:
+
if kwargs.get('text_anchor') == 'start':
+
path_args.setdefault('startOffset', '0')
+
elif kwargs.get('text_anchor') == 'middle':
+
path_args.setdefault('startOffset', '50%')
+
elif kwargs.get('text_anchor') == 'end':
+
if cairo_fix and 'startOffset' not in path_args:
+
# Fix CairoSVG not drawing the last character with aligned
+
# right
+
tspan_args.setdefault('dx', -1)
+
path_args.setdefault('startOffset', '100%')
+
+
super().__init__(x=x, y=y, font_size=font_size, **kwargs)
+
self._text_path = None
+
if single_line:
+
self.escaped_text = xml.escape(text[0])
+
else:
+
# Add elements for each line of text
+
self.escaped_text = ''
+
if path is None:
+
# Text is an iterable
+
for i, line in enumerate(text):
+
dy = '{}em'.format(line_offset if i == 0 else line_height)
+
self.append_line(line, x=x, dy=dy, **tspan_args)
+
else:
+
self._text_path = _TextPath(path, **path_args)
+
assert sum(bool(line) for line in text) <= 1, (
+
'Logic error, __new__ should handle multi-line paths')
+
for i, line in enumerate(text):
+
if line is None or len(line) == 0:
+
continue
+
dy = '{}em'.format(line_offset + i*line_height)
+
tspan = TSpan(line, dy=dy, **tspan_args)
+
self._text_path.append(tspan)
+
self.append(self._text_path)
+
@staticmethod
+
def _handle_text_argument(text, force_multi=False):
+
# Handle multi-line text (contains '\n' or is a list of strings)
+
if isinstance(text, str):
+
single_line = '\n' not in text and not force_multi
+
if single_line:
+
text = (text,)
+
else:
+
text = tuple(text.splitlines())
+
else:
+
single_line = False
+
text = tuple(text)
+
return text, single_line
+
def write_content(self, id_map, is_duplicate, output_file, lcontext,
+
dry_run):
+
if dry_run:
+
return
+
output_file.write(self.escaped_text)
+
def write_children_content(self, id_map, is_duplicate, output_file,
+
lcontext, dry_run):
+
children = self.all_children(lcontext=lcontext)
+
for child in children:
+
local = LocalContext(lcontext.context, child, self, children)
+
child.write_svg_element(
+
id_map, is_duplicate, output_file, local, dry_run)
+
def append_line(self, line, **kwargs):
+
if self._text_path is not None:
+
raise ValueError('appendLine is not supported for text on a path')
+
self.append(TSpan(line, **kwargs))
+
+
class _TextPath(DrawingParentElement):
+
TAG_NAME = 'textPath'
+
has_content = True
+
def __init__(self, path, **kwargs):
+
super().__init__(xlink__href=path, **kwargs)
+
+
class _TextContainingElement(DrawingBasicElement):
+
''' A private parent class used for elements that only have plain text
+
content. '''
+
has_content = True
+
def __init__(self, text, **kwargs):
+
super().__init__(**kwargs)
+
self.escaped_text = xml.escape(text)
+
def write_content(self, id_map, is_duplicate, output_file, lcontext,
+
dry_run):
+
if dry_run:
+
return
+
output_file.write(self.escaped_text)
+
+
class TSpan(_TextContainingElement):
+
''' A line of text within the Text element. '''
+
TAG_NAME = 'tspan'
+
+
class Title(_TextContainingElement):
+
'''A title element.
+
+
This element can be appended with shape.append_title("Your title!"), which
+
can be useful for adding a tooltip or on-hover text display to an element.
+
'''
+
TAG_NAME = 'title'
+
+
class Rectangle(DrawingBasicElement):
+
'''A rectangle.
+
+
Additional keyword arguments are output as additional arguments to the SVG
+
node e.g. fill="red", stroke="#ff4477", stroke_width=2.
+
'''
+
TAG_NAME = 'rect'
+
def __init__(self, x, y, width, height, **kwargs):
+
super().__init__(x=x, y=y, width=width, height=height, **kwargs)
+
+
class Circle(DrawingBasicElement):
+
'''A circle.
+
+
Additional keyword arguments are output as additional arguments to the SVG
+
node e.g. fill="red", stroke="#ff4477", stroke_width=2.
+
'''
+
TAG_NAME = 'circle'
+
def __init__(self, cx, cy, r, **kwargs):
+
super().__init__(cx=cx, cy=cy, r=r, **kwargs)
+
+
class Ellipse(DrawingBasicElement):
+
'''An ellipse.
+
+
Additional keyword arguments are output as additional arguments to the SVG
+
node e.g. fill="red", stroke="#ff4477", stroke_width=2.
+
'''
+
TAG_NAME = 'ellipse'
+
def __init__(self, cx, cy, rx, ry, **kwargs):
+
super().__init__(cx=cx, cy=cy, rx=rx, ry=ry, **kwargs)
+
+
class ArcLine(Circle):
+
'''An arc.
+
+
In most cases, use Arc instead of ArcLine. ArcLine uses the
+
stroke-dasharray SVG property to make the edge of a circle look like an arc.
+
+
Additional keyword arguments are output as additional arguments to the SVG
+
node e.g. fill="red", stroke="#ff4477", stroke_width=2.
+
'''
+
def __init__(self, cx, cy, r, start_deg, end_deg, **kwargs):
+
if end_deg - start_deg == 360:
+
super().__init__(cx, cy, r, **kwargs)
+
return
+
start_deg, end_deg = (-end_deg) % 360, (-start_deg) % 360
+
arc_deg = (end_deg - start_deg) % 360
+
def arc_len(deg):
+
return math.radians(deg) * r
+
whole_len = 2 * math.pi * r
+
if end_deg == start_deg:
+
offset = 1
+
dashes = "0 {}".format(whole_len+2)
+
else:
+
start_len = arc_len(start_deg)
+
arc_len = arc_len(arc_deg)
+
off_len = whole_len - arc_len
+
offset = -start_len
+
dashes = "{} {}".format(arc_len, off_len)
+
super().__init__(cx, cy, r, stroke_dasharray=dashes,
+
stroke_dashoffset=offset, **kwargs)
+
+
class Path(DrawingBasicElement):
+
'''An arbitrary path.
+
+
Path Supports building an SVG path by calling instance methods corresponding
+
to path commands.
+
+
Complete descriptions of path commands:
+
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#path_commands
+
+
Additional keyword arguments are output as additional arguments to the SVG
+
node e.g. fill="red", stroke="#ff4477", stroke_width=2.
+
'''
+
TAG_NAME = 'path'
+
def __init__(self, d='', **kwargs):
+
super().__init__(d=d, **kwargs)
+
def append(self, command_str, *args):
+
if len(self.args['d']) > 0:
+
command_str = ' ' + command_str
+
if len(args) > 0:
+
command_str = command_str + ','.join(map(str, args))
+
self.args['d'] += command_str
+
return self
+
def M(self, x, y):
+
'''Start a new curve section from this point.'''
+
return self.append('M', x, y)
+
def m(self, dx, dy):
+
'''Start a new curve section from this point (relative coordinates).'''
+
return self.append('m', dx, dy)
+
def L(self, x, y):
+
'''Draw a line to this point.'''
+
return self.append('L', x, y)
+
def l(self, dx, dy):
+
'''Draw a line to this point (relative coordinates).'''
+
return self.append('l', dx, dy)
+
def H(self, x):
+
'''Draw a horizontal line to this x coordinate.'''
+
return self.append('H', x)
+
def h(self, dx):
+
'''Draw a horizontal line to this relative x coordinate.'''
+
return self.append('h', dx)
+
def V(self, y):
+
'''Draw a horizontal line to this y coordinate.'''
+
return self.append('V', y)
+
def v(self, dy):
+
'''Draw a horizontal line to this relative y coordinate.'''
+
return self.append('v', dy)
+
def Z(self):
+
'''Draw a line back to the previous m or M point.'''
+
return self.append('Z')
+
def C(self, cx1, cy1, cx2, cy2, ex, ey):
+
'''Draw a cubic Bezier curve.'''
+
return self.append('C', cx1, cy1, cx2, cy2, ex, ey)
+
def c(self, cx1, cy1, cx2, cy2, ex, ey):
+
'''Draw a cubic Bezier curve (relative coordinates).'''
+
return self.append('c', cx1, cy1, cx2, cy2, ex, ey)
+
def S(self, cx2, cy2, ex, ey):
+
'''Draw a cubic Bezier curve, transitioning smoothly from the previous.
+
'''
+
return self.append('S', cx2, cy2, ex, ey)
+
def s(self, cx2, cy2, ex, ey):
+
'''Draw a cubic Bezier curve, transitioning smoothly from the previous
+
(relative coordinates).
+
'''
+
return self.append('s', cx2, cy2, ex, ey)
+
def Q(self, cx, cy, ex, ey):
+
'''Draw a quadratic Bezier curve.'''
+
return self.append('Q', cx, cy, ex, ey)
+
def q(self, cx, cy, ex, ey):
+
'''Draw a quadratic Bezier curve (relative coordinates).'''
+
return self.append('q', cx, cy, ex, ey)
+
def T(self, ex, ey):
+
'''Draw a quadratic Bezier curve, transitioning soothly from the
+
previous.
+
'''
+
return self.append('T', ex, ey)
+
def t(self, ex, ey):
+
'''Draw a quadratic Bezier curve, transitioning soothly from the
+
previous (relative coordinates).
+
'''
+
return self.append('t', ex, ey)
+
def A(self, rx, ry, rot, large_arc, sweep, ex, ey):
+
'''Draw a circular or elliptical arc.
+
+
See
+
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#elliptical_arc_curve
+
'''
+
return self.append('A', rx, ry, rot, int(bool(large_arc)),
+
int(bool(sweep)), ex, ey)
+
def a(self, rx, ry, rot, large_arc, sweep, ex, ey):
+
'''Draw a circular or elliptical arc (relative coordinates).
+
+
See
+
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d#elliptical_arc_curve
+
'''
+
return self.append('a', rx, ry, rot, int(bool(large_arc)),
+
int(bool(sweep)), ex, ey)
+
def arc(self, cx, cy, r, start_deg, end_deg, cw=True, include_m=True,
+
include_l=False):
+
'''Draw a circular arc, controlled by center, radius, and start/end
+
degrees.
+
+
Angles rotate from the x-axis towards the positive y-axis.
+
'''
+
large_arc = ((end_deg - start_deg) % 360 <= 180) ^ cw
+
start_rad, end_rad = start_deg*math.pi/180, end_deg*math.pi/180
+
sx, sy = r*math.cos(start_rad), r*math.sin(start_rad)
+
ex, ey = r*math.cos(end_rad), r*math.sin(end_rad)
+
if include_l:
+
self.L(cx+sx, cy+sy)
+
elif include_m:
+
self.M(cx+sx, cy+sy)
+
return self.A(r, r, 0, large_arc, cw, cx+ex, cy+ey)
+
+
class Lines(Path):
+
'''A sequence of connected lines (or a polygon).
+
+
Additional keyword arguments are output as additional arguments to the SVG
+
node e.g. fill="red", stroke="#ff4477", stroke_width=2.
+
'''
+
def __init__(self, sx, sy, *points, close=False, **kwargs):
+
super().__init__(d='', **kwargs)
+
self.M(sx, sy)
+
if len(points) % 2 != 0:
+
raise TypeError(
+
'expected an even number of positional arguments x0, y0, '
+
'x1, y1, ...')
+
for i in range(len(points) // 2):
+
self.L(points[2*i], points[2*i+1])
+
if close:
+
self.Z()
+
+
class Line(Lines):
+
'''A simple line.
+
+
Additional keyword arguments are output as additional arguments to the SVG
+
node e.g. fill="red", stroke="#ff4477", stroke_width=2.
+
'''
+
def __init__(self, sx, sy, ex, ey, **kwargs):
+
super().__init__(sx, sy, ex, ey, close=False, **kwargs)
+
+
class Arc(Path):
+
'''A circular arc.
+
+
Additional keyword arguments are output as additional arguments to the SVG
+
node e.g. fill="red", stroke="#ff4477", stroke_width=2.
+
'''
+
def __init__(self, cx, cy, r, start_deg, end_deg, cw=False, **kwargs):
+
super().__init__(d='', **kwargs)
+
self.arc(cx, cy, r, start_deg, end_deg, cw=cw, include_m=True)
+
+
class ForeignObject(DrawingBasicElement):
+
'''A foreign object, i.e. for embedding HTML.
+
+
In the context of SVG embedded in an HTML document, the XHTML namespace
+
could be omitted in the foreignObject content, but it is mandatory in the
+
context of an SVG document.
+
+
This element works best when viewing the SVG in a browser. Drawing.rasterize()
+
and Drawing.save_png() are unable to display this element.
+
+
Additional keyword arguments are output as additional arguments to the SVG
+
node e.g. fill="red", stroke="#ff4477", stroke_width=2.
+
'''
+
TAG_NAME = 'foreignObject'
+
has_content = True
+
def __init__(self, content, **kwargs):
+
super().__init__(**kwargs)
+
self.content = content
+
def write_content(self, id_map, is_duplicate, output_file, lcontext,
+
dry_run):
+
if dry_run:
+
return
+
output_file.write(self.content)
+48
drawsvg/font_embed.py
···
+
import urllib.request, urllib.parse
+
import re
+
+
from . import url_encode
+
+
+
def download_url(url):
+
with urllib.request.urlopen(url) as r:
+
return r.read()
+
+
def download_url_to_data_uri(url, mime='application/octet-stream'):
+
data = download_url(url)
+
return url_encode.bytes_as_data_uri(data, strip_chars='', mime=mime)
+
+
def embed_css_resources(css):
+
'''Replace all URLs in the CSS string with downloaded data URIs.'''
+
regex = re.compile(r'url\((https?://[^)]*)\)')
+
def repl(match):
+
url = match[1]
+
uri = download_url_to_data_uri(url)
+
return f'url({uri})'
+
embedded, _ = regex.subn(repl, css)
+
return embedded
+
+
def download_google_font_css(family, text=None, display='swap', **kwargs):
+
'''Download SVG-embeddable CSS from Google fonts.
+
+
Args:
+
family: Name of font family or list of font families.
+
text: The set of characters required from the font. Only a font subset
+
with these characters will be downloaded.
+
display: The font-display CSS value.
+
**kwargs: Other URL parameters sent to
+
https://fonts.googleapis.com/css?...
+
'''
+
if not isinstance(family, str):
+
family = '|'.join(family) # Request a list of families
+
args = dict(family=family, display=display)
+
if text is not None:
+
if not isinstance(text, str):
+
text = ''.join(text)
+
args['text'] = text
+
args.update(kwargs)
+
params = urllib.parse.urlencode(args)
+
url = f'https://fonts.googleapis.com/css?{params}'
+
with urllib.request.urlopen(url) as r:
+
css = r.read().decode('utf-8')
+
return embed_css_resources(css)
+130
drawsvg/frame_animation.py
···
+
import time
+
+
from . import video
+
+
+
class FrameAnimation:
+
def __init__(self, draw_func=None, callback=None):
+
self.frames = []
+
if draw_func is None:
+
draw_func = lambda d:d
+
self.draw_func = draw_func
+
if callback is None:
+
callback = lambda d:None
+
self.callback = callback
+
+
def append_frame(self, frame):
+
self.frames.append(frame)
+
self.callback(frame)
+
+
def draw_frame(self, *args, **kwargs):
+
frame = self.draw_func(*args, **kwargs)
+
self.append_frame(frame)
+
return frame
+
+
def save_video(self, file, **kwargs):
+
video.save_video(self.frames, file, **kwargs)
+
+
def save_spritesheet(self, file, **kwargs):
+
video.save_spritesheet(self.frames, file, **kwargs)
+
+
+
class FrameAnimationContext:
+
def __init__(self, draw_func=None, out_file=None,
+
jupyter=False, spritesheet=False, pause=False,
+
clear=True, delay=0, disable=False, video_args=None,
+
_patch_delay=0.05):
+
self.jupyter = jupyter
+
self.spritesheet = spritesheet
+
self.disable = disable
+
if self.jupyter and not self.disable:
+
from IPython import display
+
self._jupyter_clear_output = display.clear_output
+
self._jupyter_display = display.display
+
callback = self.draw_jupyter_frame
+
else:
+
callback = None
+
self.anim = FrameAnimation(draw_func, callback=callback)
+
self.out_file = out_file
+
self.pause = pause
+
self.clear = clear
+
self.delay = delay
+
if video_args is None:
+
video_args = {}
+
self.video_args = video_args
+
self._patch_delay = _patch_delay
+
+
def draw_jupyter_frame(self, frame):
+
if self.clear:
+
self._jupyter_clear_output(wait=True)
+
self._jupyter_display(frame)
+
if self.pause:
+
# Patch. Jupyter sometimes clears the input field otherwise.
+
time.sleep(self._patch_delay)
+
input('Next?')
+
elif self.delay != 0:
+
time.sleep(self.delay)
+
+
def __enter__(self):
+
return self.anim
+
+
def __exit__(self, exc_type, exc_value, exc_traceback):
+
if exc_value is None:
+
# No error
+
if self.out_file is not None and not self.disable:
+
if self.spritesheet:
+
self.anim.save_spritesheet(self.out_file, **self.video_args)
+
else:
+
self.anim.save_video(self.out_file, **self.video_args)
+
+
+
def frame_animate_video(out_file, draw_func=None, jupyter=False, **video_args):
+
'''
+
Returns a context manager that stores frames and saves a video when the
+
context exits.
+
+
Example:
+
```
+
with frame_animate_video('video.mp4') as anim:
+
while True:
+
...
+
anim.draw_frame(...)
+
```
+
'''
+
return FrameAnimationContext(draw_func=draw_func, out_file=out_file,
+
jupyter=jupyter, video_args=video_args)
+
+
def frame_animate_spritesheet(out_file, draw_func=None, jupyter=False,
+
**video_args):
+
'''
+
Returns a context manager that stores frames and saves a spritesheet when
+
the context exits.
+
+
Example:
+
```
+
with frame_animate_spritesheet('sheet.png', row_length=10) as anim:
+
while True:
+
...
+
anim.draw_frame(...)
+
```
+
'''
+
return FrameAnimationContext(draw_func=draw_func, out_file=out_file,
+
jupyter=jupyter, spritesheet=True,
+
video_args=video_args)
+
+
+
def frame_animate_jupyter(draw_func=None, pause=False, clear=True, delay=0.1,
+
**kwargs):
+
'''
+
Returns a context manager that displays frames in a Jupyter notebook.
+
+
Example:
+
```
+
with frame_animate_jupyter(delay=0.5) as anim:
+
while True:
+
...
+
anim.draw_frame(...)
+
```
+
'''
+
return FrameAnimationContext(draw_func=draw_func, jupyter=True, pause=pause,
+
clear=clear, delay=delay, **kwargs)
+41
drawsvg/jupyter.py
···
+
import dataclasses
+
+
from . import url_encode
+
from . import raster
+
+
+
class _Rasterizable:
+
def rasterize(self, to_file=None):
+
if to_file is not None:
+
return raster.Raster.from_svg_to_file(self.svg, to_file)
+
else:
+
return raster.Raster.from_svg(self.svg)
+
+
@dataclasses.dataclass
+
class JupyterSvgInline(_Rasterizable):
+
'''Jupyter-displayable SVG displayed inline on the Jupyter web page.'''
+
svg: str
+
def _repr_html_(self):
+
return self.svg
+
+
@dataclasses.dataclass
+
class JupyterSvgImage(_Rasterizable):
+
'''Jupyter-displayable SVG displayed within an img tag on the Jupyter web
+
page.
+
'''
+
svg: str
+
def _repr_html_(self):
+
uri = url_encode.svg_as_utf8_data_uri(self.svg)
+
return '<img src="{}">'.format(uri)
+
+
@dataclasses.dataclass
+
class JupyterSvgFrame:
+
'''Jupyter-displayable SVG displayed within an HTML iframe.'''
+
svg: str
+
width: float
+
height: float
+
mime: str = 'image/svg+xml'
+
def _repr_html_(self):
+
uri = url_encode.svg_as_data_uri(self.svg, mime=self.mime)
+
return (f'<iframe src="{uri}" width="{self.width}" '
+
f'height="{self.height}" style="border:0" />')
+8
drawsvg/native_animation/__init__.py
···
+
from .synced_animation import (
+
SyncedAnimationConfig,
+
AnimatedAttributeTimeline,
+
AnimationHelperData,
+
animate_element_sequence,
+
animate_text_sequence,
+
)
+
from .playback_control_ui import draw_scrub
+120
drawsvg/native_animation/playback_control_js.py
···
+
SVG_ONLOAD = 'svgOnLoad(event)'
+
SVG_JS_CONTENT = '''
+
/* Animation playback controls generated by drawsvg */
+
/* https://github.com/cduck/drawsvg/ */
+
function svgOnLoad(event) {
+
/* Support standalone SVG or embedded in HTML or iframe */
+
if (event && event.target && event.target.ownerDocument) {
+
svgSetup(event.target.ownerDocument);
+
} else if (document && document.currentScript
+
&& document.currentScript.parentElement) {
+
svgSetup(document.currentScript.parentElement);
+
}
+
}
+
function svgSetup(doc) {
+
var svgRoot = doc.documentElement || doc;
+
var scrubCapture = doc.getElementById("scrub-capture");
+
/* Block multiple setups */
+
if (!scrubCapture || scrubCapture.getAttribute("svgSetupDone")) {
+
return;
+
}
+
scrubCapture.setAttribute("svgSetupDone", true);
+
var scrubContainer = doc.getElementById("scrub");
+
var scrubPlay = doc.getElementById("scrub-play");
+
var scrubPause = doc.getElementById("scrub-pause");
+
var scrubKnob = doc.getElementById("scrub-knob");
+
var scrubXMin = parseFloat(scrubCapture.dataset.xmin);
+
var scrubXMax = parseFloat(scrubCapture.dataset.xmax);
+
var scrubTotalDur = parseFloat(scrubCapture.dataset.totaldur);
+
var scrubStartDelay = parseFloat(scrubCapture.dataset.startdelay);
+
var scrubEndDelay = parseFloat(scrubCapture.dataset.enddelay);
+
var scrubPauseOnLoad = parseFloat(scrubCapture.dataset.pauseonload);
+
var paused = false;
+
var dragXOffset = 0;
+
var point = svgRoot.createSVGPoint();
+
+
function screenToSvgX(p) {
+
var matrix = scrubKnob.getScreenCTM().inverse();
+
point.x = p.x;
+
point.y = p.y;
+
return point.matrixTransform(matrix).x;
+
};
+
function screenToProgress(p) {
+
var matrix = scrubKnob.getScreenCTM().inverse();
+
point.x = p.x;
+
point.y = p.y;
+
var x = point.matrixTransform(matrix).x;
+
if (x <= scrubXMin) {
+
return scrubStartDelay / scrubTotalDur;
+
}
+
if (x >= scrubXMax) {
+
return (scrubTotalDur - scrubEndDelay) / scrubTotalDur;
+
}
+
return (scrubStartDelay/scrubTotalDur
+
+ (x - dragXOffset - scrubXMin)
+
/ (scrubXMax - scrubXMin)
+
* (scrubTotalDur - scrubStartDelay - scrubEndDelay)
+
/ scrubTotalDur);
+
};
+
function currentScrubX() {
+
return scrubKnob.cx.animVal.value;
+
};
+
function pause() {
+
svgRoot.pauseAnimations();
+
scrubPlay.setAttribute("visibility", "visible");
+
scrubPause.setAttribute("visibility", "hidden");
+
paused = true;
+
};
+
function play() {
+
svgRoot.unpauseAnimations();
+
scrubPause.setAttribute("visibility", "visible");
+
scrubPlay.setAttribute("visibility", "hidden");
+
paused = false;
+
};
+
function scrub(playbackFraction) {
+
var t = scrubTotalDur * playbackFraction;
+
/* Stop 10ms before end to avoid loop (>=1ms needed on FF) */
+
var limit = scrubTotalDur - 10e-3;
+
if (t < 0) t = 0;
+
else if (t > limit) t = limit;
+
svgRoot.setCurrentTime(t);
+
};
+
function mousedown(e) {
+
svgRoot.pauseAnimations();
+
if (e.target == scrubKnob) {
+
dragXOffset = screenToSvgX(e) - currentScrubX();
+
} else {
+
dragXOffset = 0;
+
}
+
scrub(screenToProgress(e));
+
/* Global document listeners */
+
document.addEventListener('mousemove', mousemove);
+
document.addEventListener('mouseup', mouseup);
+
e.preventDefault();
+
};
+
function mouseup(e) {
+
dragXOffset = 0;
+
document.removeEventListener('mousemove', mousemove);
+
document.removeEventListener('mouseup', mouseup);
+
if (!paused) {
+
svgRoot.unpauseAnimations();
+
}
+
e.preventDefault();
+
};
+
function mousemove(e) {
+
scrub(screenToProgress(e));
+
};
+
scrubPause.addEventListener("click", pause);
+
scrubPlay.addEventListener("click", play);
+
scrubCapture.addEventListener("mousedown", mousedown);
+
scrubContainer.setAttribute("visibility", "visible");
+
scrubKnob.setAttribute("visibility", "visible");
+
if (scrubPauseOnLoad) {
+
pause();
+
scrub(0);
+
} else {
+
play();
+
}
+
};
+
svgOnLoad();
+
'''
+87
drawsvg/native_animation/playback_control_ui.py
···
+
from .. import elements as draw
+
+
+
def draw_scrub(config: 'SyncedAnimationConfig', hidden=False) -> 'Group':
+
hpad = config.bar_hpad
+
bar_x = config.controls_x + hpad
+
bar_y = config.controls_center_y
+
bar_width = config.controls_width - 2*hpad
+
knob_rad = config.knob_rad
+
pause_width = config.pause_width
+
pause_corner_rad = config.pause_corner_rad
+
g = draw.Group(id='scrub', visibility='hidden' if hidden else None)
+
g.append(draw.Line(
+
bar_x, bar_y, bar_x+bar_width, bar_y,
+
stroke=config.bar_color,
+
stroke_width=config.bar_thickness,
+
stroke_linecap='round'))
+
progress = draw.Rectangle(
+
bar_x, bar_y, 0, 0.001,
+
stroke=config.bar_past_color,
+
stroke_width=config.bar_thickness,
+
stroke_linejoin='round')
+
g.append(progress)
+
g_capture = draw.Group(
+
id='scrub-capture',
+
data_xmin=bar_x,
+
data_xmax=bar_x+bar_width,
+
data_totaldur=config.total_duration,
+
data_startdelay=config.start_delay,
+
data_enddelay=config.end_delay,
+
data_pauseonload=int(bool(config.pause_on_load)))
+
g_capture.append(draw.Rectangle(
+
bar_x-config.bar_thickness/2, bar_y-config.controls_height/2,
+
bar_width+config.bar_thickness, config.controls_height,
+
fill='rgba(255,255,255,0)'))
+
knob = draw.Circle(
+
bar_x, bar_y, knob_rad, fill=config.knob_fill,
+
id='scrub-knob',
+
visibility='hidden')
+
g_capture.append(knob)
+
g.append(g_capture)
+
g_play = draw.Group(id='scrub-play', visibility='hidden')
+
g_play.append(draw.Rectangle(
+
bar_x - hpad/2 - knob_rad/2 - pause_width/2 + pause_corner_rad,
+
bar_y - pause_width/2 + pause_corner_rad,
+
pause_width - pause_corner_rad*2,
+
pause_width - pause_corner_rad*2,
+
fill=config.pause_color,
+
stroke=config.pause_color,
+
stroke_width=pause_corner_rad*2,
+
stroke_linejoin='round'))
+
g_play.append(draw.Path(fill=config.pause_icon_color)
+
.M(bar_x - hpad/2 - knob_rad/2 - pause_width/4,
+
bar_y - pause_width/4)
+
.v(pause_width/2)
+
.l(pause_width/4*2, -pause_width/4)
+
.Z())
+
g.append(g_play)
+
g_pause = draw.Group(id='scrub-pause', visibility='hidden')
+
g_pause.append(draw.Rectangle(
+
bar_x - hpad/2 - knob_rad/2 - pause_width/2 + pause_corner_rad,
+
bar_y - pause_width/2 + pause_corner_rad,
+
pause_width - pause_corner_rad*2,
+
pause_width - pause_corner_rad*2,
+
fill=config.pause_color,
+
stroke=config.pause_color,
+
stroke_width=pause_corner_rad*2,
+
stroke_linejoin='round'))
+
g_pause.append(draw.Rectangle(
+
bar_x - hpad/2 - knob_rad/2 - pause_width/16*3,
+
bar_y - pause_width/4,
+
pause_width/8,
+
pause_width/2,
+
fill=config.pause_icon_color))
+
g_pause.append(draw.Rectangle(
+
bar_x - hpad/2 - knob_rad/2 + pause_width/16,
+
bar_y - pause_width/4,
+
pause_width/8,
+
pause_width/2,
+
fill=config.pause_icon_color))
+
g.append(g_pause)
+
+
progress.add_key_frame(0, width=0)
+
progress.add_key_frame(config.duration, width=bar_width)
+
knob.add_key_frame(0, cx=bar_x)
+
knob.add_key_frame(config.duration, cx=bar_x+bar_width)
+
return g
+352
drawsvg/native_animation/synced_animation.py
···
+
from typing import Any, Callable, Dict, List, Optional, Union
+
+
import dataclasses
+
from collections import defaultdict
+
from numbers import Number
+
+
from .. import elements, types
+
from . import playback_control_ui, playback_control_js
+
+
+
@dataclasses.dataclass
+
class SyncedAnimationConfig:
+
# Animation settings
+
duration: float
+
start_delay: float = 0
+
end_delay: float = 0
+
repeat_count: Union[int, str] = 'indefinite'
+
fill: str = 'freeze'
+
freeze_frame_at: Optional[float] = None
+
+
# Playback controls
+
show_playback_progress: bool = False
+
show_playback_controls: bool = False # Adds JavaScript to the drawing
+
pause_on_load: bool = False
+
controls_width: Optional[float] = None
+
controls_height: float = 20
+
controls_x: Optional[float] = None
+
controls_center_y: Optional[float] = None
+
+
# Playback control style
+
bar_thickness: float = 4
+
bar_hpad: float = 32
+
bar_color: str = '#ccc'
+
bar_past_color: str = '#05f'
+
knob_rad: float = 6
+
knob_fill: str = '#05f'
+
pause_width: float = 16
+
pause_corner_rad: float = 4
+
pause_color: str = '#05f'
+
pause_icon_color: str = '#eee'
+
+
# Advanced configuration
+
controls_js: str = 'DEFAULT'
+
controls_js_onload: str = playback_control_js.SVG_ONLOAD
+
controls_draw_function: Callable[['SyncedAnimationConfig', bool], 'Group'
+
] = playback_control_ui.draw_scrub
+
+
@property
+
def total_duration(self):
+
return self.start_delay + self.duration + self.end_delay
+
+
def extra_css(self, d, context):
+
return []
+
+
def extra_javascript(self, d, context):
+
config = self._with_filled_defaults(d, context)
+
if self.show_playback_controls:
+
return [config.controls_js]
+
return []
+
+
def extra_onload_js(self, d, context):
+
config = self._with_filled_defaults(d, context)
+
if self.show_playback_controls:
+
return [config.controls_js_onload]
+
return []
+
+
def extra_drawing_elements(self, d, context):
+
config = self._with_filled_defaults(d, context)
+
if self.show_playback_progress or self.show_playback_controls:
+
# Control UI
+
controls = config.controls_draw_function(
+
config, hidden=not self.show_playback_progress)
+
return [controls]
+
return []
+
+
def _with_filled_defaults(self, d, context):
+
# By default place the controls along the bottom edge
+
width = d.view_box[2]
+
x = d.view_box[0]
+
if context.invert_y:
+
y = d.view_box[1] + self.controls_height/2
+
else:
+
y = d.view_box[1] + d.view_box[3] - self.controls_height/2
+
js = playback_control_js.SVG_JS_CONTENT
+
if self.controls_width is not None:
+
width = self.controls_width
+
x += (d.view_box[2] - width) / 2
+
if self.controls_x is not None:
+
x = self.controls_x
+
if self.controls_center_y is not None:
+
y = self.controls_center_y
+
if self.controls_js != 'DEFAULT':
+
js = self.controls_js
+
return dataclasses.replace(
+
self, controls_width=width, controls_x=x, controls_center_y=y,
+
controls_js=js)
+
+
def override_args(self, args, lcontext):
+
if (self.freeze_frame_at is not None
+
and hasattr(lcontext.element, 'animation_data')):
+
args = dict(args)
+
data = lcontext.element.animation_data
+
args.update(data.interpolate_at_time(self.freeze_frame_at))
+
return args
+
+
+
@dataclasses.dataclass
+
class AnimatedAttributeTimeline:
+
name: str
+
animate_attrs: Optional[Dict[str, Any]] = None
+
times: List[float] = dataclasses.field(default_factory=list)
+
values: List[Any] = dataclasses.field(default_factory=list)
+
+
def __post_init__(self):
+
self.name = types.normalize_attribute_name(self.name)
+
+
def append(self, time, value):
+
if self.times and time < self.times[-1]:
+
raise ValueError('out-of-order key frame times')
+
self.times.append(time)
+
self.values.append(value)
+
+
def extend(self, times, values):
+
if len(times) != len(values):
+
raise ValueError('times and values lists are mismatched lengths')
+
if len(self.times) and len(times) and times[0] < self.times[-1]:
+
raise ValueError('out-of-order key frame times')
+
if list(times) != sorted(times):
+
raise ValueError('out-of-order key frame times')
+
self.times.extend(times)
+
self.values.extend(values)
+
+
def interpolate_at_time(self, at_time):
+
return linear_interpolate_value(self.times, self.values, at_time)
+
+
def as_animate_element(self, config: Optional[SyncedAnimationConfig]=None):
+
if config is not None:
+
total_duration = (
+
config.start_delay + config.duration + config.end_delay)
+
start_delay = config.start_delay
+
repeat_count = config.repeat_count
+
fill = config.fill
+
else:
+
total_duration = self.times[-1]
+
start_delay = 0
+
repeat_count = 1
+
fill = 'freeze'
+
dur_str = f'{total_duration}s'
+
values = self.values
+
times = self.times
+
key_times = ';'.join(
+
str(max(0, min(1, round(
+
(start_delay + t) / total_duration, 3))))
+
for t in times
+
)
+
values_str = ';'.join(map(str, values))
+
if not key_times.startswith('0;'):
+
key_times = '0;' + key_times
+
values_str = f'{values[0]};' + values_str
+
if not key_times.endswith(';1'):
+
key_times = key_times + ';1'
+
values_str = values_str + f';{values[-1]}'
+
attrs = dict(
+
dur=dur_str,
+
values=values_str,
+
keyTimes=key_times,
+
repeatCount=repeat_count,
+
fill=fill)
+
attrs.update(self.animate_attrs or {})
+
anim = elements.Animate(self.name, **attrs)
+
return anim
+
+
+
class AnimationHelperData:
+
def __init__(self):
+
self.attr_timelines = {}
+
+
def add_key_frame(self, time, animation_args=None, **attr_values):
+
for attr, val in attr_values.items():
+
attr = types.normalize_attribute_name(attr)
+
timeline = self.attr_timelines.get(attr)
+
if timeline is None:
+
timeline = AnimatedAttributeTimeline(attr, animation_args)
+
self.attr_timelines[attr] = timeline
+
timeline.append(time, val)
+
+
def add_attribute_key_sequence(self, attr, times, values, *,
+
animation_args=None):
+
attr = types.normalize_attribute_name(attr)
+
timeline = self.attr_timelines.get(attr)
+
if timeline is None:
+
timeline = AnimatedAttributeTimeline(attr, animation_args)
+
self.attr_timelines[attr] = timeline
+
timeline.extend(times, values)
+
+
def interpolate_at_time(self, at_time):
+
return {
+
name: timeline.interpolate_at_time(at_time)
+
for name, timeline in self.attr_timelines.items()
+
}
+
+
def _timelines_adjusted_for_context(self, lcontext=None):
+
all_timelines = dict(self.attr_timelines)
+
if lcontext is not None and lcontext.context.invert_y:
+
# Invert cy, y1, y2, ...
+
for name, timeline in self.attr_timelines.items():
+
if name != 'y' and lcontext.context.is_attr_inverted(name):
+
inv_timeline = AnimatedAttributeTimeline(
+
timeline.name, timeline.animate_attrs,
+
timeline.times, [-v for v in timeline.values])
+
all_timelines[name] = inv_timeline
+
# Invert -y - height
+
y_attrs = None
+
if 'height' in all_timelines.keys():
+
height_timeline = all_timelines['height']
+
htimes = height_timeline.times
+
hvalues = height_timeline.values
+
y_attrs = height_timeline.animate_attrs
+
else:
+
height_timeline = None
+
htimes = [0]
+
hvalues = [lcontext.element.args.get('height', 0)]
+
if 'y' in all_timelines.keys():
+
y_timeline = all_timelines['y']
+
ytimes = y_timeline.times
+
yvalues = y_timeline.values
+
y_attrs = y_timeline.animate_attrs
+
else:
+
y_timeline = None
+
ytimes = [0]
+
yvalues = [lcontext.element.args.get('y', 0)]
+
if y_timeline is not None or height_timeline is not None:
+
ytimes, yvalues = _merge_timeline_inverted_y_values(
+
ytimes, yvalues, htimes, hvalues,
+
linear_interpolate_value, linear_interpolate_value)
+
if ytimes is not None:
+
y_timeline = AnimatedAttributeTimeline(
+
'y', y_attrs, ytimes, yvalues)
+
all_timelines['y'] = y_timeline
+
return all_timelines
+
+
def children_with_context(self, lcontext=None):
+
if (lcontext is not None
+
and lcontext.context.animation_config is not None
+
and lcontext.context.animation_config.freeze_frame_at
+
is not None):
+
return [] # Don't animate if frame is frozen
+
all_timelines = self._timelines_adjusted_for_context(lcontext)
+
return [
+
timeline.as_animate_element(lcontext.context.animation_config)
+
for timeline in all_timelines.values()
+
]
+
+
+
def linear_interpolate_value(times, values, at_time):
+
if len(times) == 0:
+
return 0
+
idx = sum(t <= at_time for t in times)
+
if idx >= len(times):
+
return values[-1]
+
elif idx <= 0:
+
return values[0]
+
elif at_time == times[idx-1]:
+
return values[idx-1]
+
elif isinstance(values[idx], Number) and isinstance(values[idx-1], Number):
+
fraction = (at_time-times[idx-1]) / (times[idx]-times[idx-1])
+
return values[idx-1] * (1-fraction) + (values[idx] * fraction)
+
else:
+
return values[idx-1]
+
+
def _merge_timeline_inverted_y_values(ytimes, yvalues, htimes, hvalues,
+
yinterpolate, hinterpolate):
+
if len(yvalues) == 1:
+
try:
+
return htimes, [-yvalues[0]-h for h in hvalues]
+
except TypeError:
+
return None, None
+
elif len(hvalues) == 1:
+
try:
+
return ytimes, [-y-hvalues[0] for y in yvalues]
+
except TypeError:
+
return None, None
+
elif ytimes == htimes:
+
try:
+
return ytimes, [-y-h for y, h in zip(yvalues, hvalues)]
+
except TypeError:
+
return None, None
+
try:
+
# Offset y-value by height if invert_y
+
# Merge key_times for y and height animations
+
new_times = []
+
new_values = []
+
hi = yi = 0
+
inf = float('inf')
+
ht = htimes[0] if len(htimes) else inf
+
yt = ytimes[0] if len(ytimes) else inf
+
while ht < inf and yt < inf:
+
if yt < ht:
+
h_val = hinterpolate(htimes, hvalues, yt)
+
new_times.append(yt)
+
new_values.append(-yvalues[yi] - h_val)
+
yi += 1
+
elif ht < yt:
+
y_val = yinterpolate(ytimes, yvalues, ht)
+
new_times.append(ht)
+
new_values.append(-y_val - hvalues[hi])
+
hi += 1
+
else:
+
new_times.append(yt)
+
new_values.append(-yvalues[yi] - hvalues[hi])
+
yi += 1
+
hi += 1
+
yt = ytimes[yi] if yi < len(ytimes) else inf
+
ht = htimes[hi] if hi < len(htimes) else inf
+
return new_times, new_values
+
except TypeError:
+
return None, None
+
+
def animate_element_sequence(times, element_sequence):
+
'''Animate a list of elements to appear one-at-a-time in sequence.
+
+
Elements should already be added to the drawing before using this.
+
'''
+
for i, elem in enumerate(element_sequence):
+
if elem is None:
+
continue # Draw nothing during this time
+
key_times = [times[i]]
+
values = ['visible']
+
if i > 0:
+
key_times.insert(0, times[i-1])
+
values.insert(0, 'hidden')
+
if i < len(element_sequence) - 1:
+
key_times.append(times[i+1])
+
values.append('hidden')
+
elem.add_attribute_key_sequence('visibility', key_times, values)
+
+
def animate_text_sequence(container, times: List[float], values: List[str],
+
*text_args, kwargs_list=None, **text_kwargs):
+
'''Animate a sequence of text to appear one-at-a-time in sequence.
+
+
Multiple `Text` elements will be appended to the given `container`.
+
'''
+
if kwargs_list is None:
+
kwargs_list = [None] * len(values)
+
new_elements = []
+
for val, current_kw in zip(values, kwargs_list):
+
kwargs = dict(text_kwargs)
+
if current_kw is not None:
+
kwargs.update(current_kw)
+
new_elements.append(elements.Text(val, *text_args, **kwargs))
+
animate_element_sequence(times, new_elements)
+
container.extend(new_elements)
+94
drawsvg/raster.py
···
+
import base64
+
import io
+
import warnings
+
+
from .url_encode import bytes_as_data_uri
+
+
def delay_import_cairo():
+
try:
+
import cairosvg
+
except OSError as e:
+
raise ImportError(
+
'Failed to load CairoSVG. '
+
'drawSvg will be unable to output PNG or other raster image '
+
'formats. '
+
'See https://github.com/cduck/drawsvg#full-feature-install '
+
'for more details.'
+
) from e
+
except ImportError as e:
+
raise ImportError(
+
'CairoSVG will need to be installed to rasterize images. '
+
'Install with `python3 -m pip install "drawsvg[all]"` '
+
'or `python3 -m pip install "drawsvg[raster]"`. '
+
'See https://github.com/cduck/drawsvg#full-feature-install '
+
'for more details.'
+
) from e
+
return cairosvg
+
+
def delay_import_imageio():
+
try:
+
import imageio
+
except ImportError as e:
+
raise ImportError(
+
'Optional dependencies not installed. '
+
'Install with `python3 -m pip install "drawsvg[all]"` '
+
'or `python3 -m pip install "drawsvg[raster]"`. '
+
'See https://github.com/cduck/drawsvg#full-feature-install '
+
'for more details.'
+
) from e
+
return imageio
+
+
+
class Raster:
+
def __init__(self, png_data=None, png_file=None):
+
self.png_data = png_data
+
self.png_file = png_file
+
def save_png(self, fname):
+
with open(fname, 'wb') as f:
+
f.write(self.png_data)
+
@staticmethod
+
def from_svg(svg_data):
+
cairosvg = delay_import_cairo()
+
png_data = cairosvg.svg2png(bytestring=svg_data)
+
return Raster(png_data)
+
@staticmethod
+
def from_svg_to_file(svg_data, out_file):
+
cairosvg = delay_import_cairo()
+
cairosvg.svg2png(bytestring=svg_data, write_to=out_file)
+
return Raster(None, png_file=out_file)
+
@staticmethod
+
def from_arr(arr, out_file=None):
+
imageio = delay_import_imageio()
+
if out_file is None:
+
with io.BytesIO() as f:
+
imageio.imwrite(f, arr, format='png')
+
f.seek(0)
+
return Raster(f.read())
+
else:
+
imageio.imwrite(out_file, arr, format='png')
+
return Raster(None, png_file=out_file)
+
def _repr_png_(self):
+
if self.png_data:
+
return self.png_data
+
elif self.png_file:
+
try:
+
with open(self.png_file, 'rb') as f:
+
return f.read()
+
except TypeError:
+
pass
+
try:
+
self.png_file.seek(0)
+
return self.png_file.read()
+
except io.UnsupportedOperation:
+
pass
+
def as_data_uri(self):
+
if self.png_data:
+
data = self.png_data
+
else:
+
try:
+
with open(self.png_file, 'rb') as f:
+
data = f.read()
+
except TypeError:
+
self.png_file.seek(0)
+
data = self.png_file.read()
+
return bytes_as_data_uri(data, mime='image/png')
+376
drawsvg/types.py
···
+
from typing import Optional, Sequence, Union
+
+
from collections import defaultdict
+
import dataclasses
+
+
from . import elements
+
from .native_animation import SyncedAnimationConfig, AnimationHelperData
+
+
+
@dataclasses.dataclass(frozen=True)
+
class Context:
+
'''Additional drawing configuration that can modify element's SVG output.'''
+
invert_y: bool = False
+
animation_config: Optional[SyncedAnimationConfig] = None
+
+
def extra_prepost_drawing_elements(self, d):
+
pre, post = [], []
+
if self.animation_config:
+
post.extend(self.animation_config.extra_drawing_elements(
+
d, context=self))
+
return pre, post
+
+
def extra_css(self, d):
+
if self.animation_config:
+
return self.animation_config.extra_css(d, context=self)
+
return []
+
+
def extra_javascript(self, d):
+
if self.animation_config:
+
return self.animation_config.extra_javascript(d, context=self)
+
return []
+
+
def extra_onload_js(self, d):
+
if self.animation_config:
+
return self.animation_config.extra_onload_js(d, context=self)
+
return []
+
+
def override_view_box(self, view_box):
+
if self.invert_y:
+
if isinstance(view_box, str):
+
view_box = tuple(map(float, view_box.split()))
+
return ' '.join(map(str, self.override_view_box(view_box)))
+
x, y, w, h = view_box
+
view_box = (x, -y-h, w, h)
+
return view_box
+
+
def is_attr_inverted(self, name):
+
return self.invert_y and name in ('y', 'cy', 'y1', 'y2')
+
+
def override_args(self, args):
+
args = dict(args)
+
if self.invert_y:
+
for y_like_arg in ('cy', 'y1', 'y2'):
+
if y_like_arg in args:
+
# Flip y for circle, ellipse, line, gradient, etc.
+
try:
+
args[y_like_arg] = -args[y_like_arg]
+
except TypeError:
+
pass
+
if 'y' in args:
+
# Flip y for most elements
+
try:
+
args['y'] = -args['y']
+
if 'height' in args:
+
args['y'] -= args['height']
+
except TypeError:
+
pass
+
if 'viewBox' in args:
+
# Flip y for SVG, marker, or other viewBox
+
try:
+
args['viewBox'] = self.override_view_box(args['viewBox'])
+
except (TypeError, ValueError):
+
pass
+
if 'd' in args:
+
# Flip y for paths
+
try:
+
new_commands = []
+
for cmd in args['d'].split():
+
name = cmd[:1]
+
vals = [float(s) if '.' in s else int(s)
+
for s in cmd[1:].split(',') if s]
+
if name in 'vV':
+
vals = [-y for y in vals]
+
elif name in 'hH':
+
pass
+
elif name in 'aA':
+
if len(vals) >= 7:
+
vals[6] = -vals[6]
+
vals[4] = int(bool(not vals[4]))
+
else:
+
vals[1::2] = [-y for y in vals[1::2]]
+
val_str = ','.join(map(str, vals))
+
new_commands.append(name + val_str)
+
args['d'] = ' '.join(new_commands)
+
except (TypeError, ValueError):
+
pass
+
if ('cx' in args and 'cy' in args and 'r' in args
+
and 'stroke-dashoffset' in args
+
and 'stroke-dasharray' in args):
+
# Flip ArcLine (drawn with stroke-dasharray)
+
try:
+
length = float(
+
args['stroke-dasharray'].split(maxsplit=1)[0])
+
offset = float(args['stroke-dashoffset'])
+
offset = length - offset
+
args['stroke-dashoffset'] = offset
+
except KeyError: pass
+
except (TypeError, ValueError, IndexError):
+
pass
+
raise
+
return args
+
+
def write_svg_document_args(self, d, args, output_file):
+
'''Called by Drawing during SVG output of the <svg> tag.'''
+
args['viewBox'] = self.override_view_box(args['viewBox'])
+
onload_list = self.extra_onload_js(d)
+
onload_list.extend(args.get('onload', '').split(';'))
+
onload = ';'.join(onload_list)
+
if onload:
+
args['onload'] = onload
+
self._write_tag_args(args, output_file)
+
+
def _write_tag_args(self, args, output_file, id_map=None):
+
'''Called by an element during SVG output of its tag.'''
+
for k, v in args.items():
+
if v is None: continue
+
if isinstance(v, DrawingElement):
+
mapped_id = v.id
+
if id_map is not None and id(v) in id_map:
+
mapped_id = id_map[id(v)]
+
if mapped_id is None:
+
continue
+
if k == 'xlink:href':
+
v = '#{}'.format(mapped_id)
+
else:
+
v = 'url(#{})'.format(mapped_id)
+
output_file.write(' {}="{}"'.format(k,v))
+
+
+
@dataclasses.dataclass(frozen=True)
+
class LocalContext:
+
context: Context
+
element: 'DrawingElement'
+
parent: Union['DrawingElement', 'Drawing']
+
siblings: Sequence['DrawingElement'] = ()
+
+
def write_tag_args(self, args, output_file, id_map=None):
+
'''Called by an element during SVG output of its tag.'''
+
if self.context.animation_config is not None:
+
args = self.context.animation_config.override_args(args, self)
+
self.context._write_tag_args(
+
self.context.override_args(args), output_file, id_map=id_map)
+
+
+
class DrawingElement:
+
'''Base class for drawing elements.
+
+
Subclasses must implement write_svg_element.
+
'''
+
def write_svg_element(self, id_map, is_duplicate, output_file, lcontext,
+
dry_run, force_dup=False):
+
raise NotImplementedError('Abstract base class')
+
def get_svg_defs(self):
+
return ()
+
def get_linked_elems(self):
+
return ()
+
def write_svg_defs(self, id_map, is_duplicate, output_file, lcontext,
+
dry_run):
+
for defn in self.get_svg_defs():
+
local = LocalContext(lcontext.context, defn, self, ())
+
if is_duplicate(defn):
+
continue
+
defn.write_svg_defs(
+
id_map, is_duplicate, output_file, local, dry_run)
+
if defn.id is None:
+
id_map[id(defn)]
+
defn.write_svg_element(
+
id_map, is_duplicate, output_file, local, dry_run,
+
force_dup=True)
+
if not dry_run:
+
output_file.write('\n')
+
def __eq__(self, other):
+
return self is other
+
+
+
class DrawingBasicElement(DrawingElement):
+
'''Base class for SVG drawing elements that are a single node with no child
+
nodes.
+
'''
+
TAG_NAME = '_'
+
has_content = False
+
def __init__(self, **args):
+
self.args = {}
+
for k, v in args.items():
+
self.args[normalize_attribute_name(k)] = v
+
self.children = []
+
self.ordered_children = defaultdict(list)
+
self.animation_data = AnimationHelperData()
+
self._cached_context = None
+
self._cached_extra_children_with_context = None
+
def check_children_allowed(self):
+
if not self.has_content:
+
raise RuntimeError(
+
'{} does not support children'.format(type(self)))
+
def _extra_children_with_context_avoid_recompute(self, lcontext=None):
+
if (self._cached_extra_children_with_context is not None
+
and self._cached_context == lcontext.context):
+
return self._cached_extra_children_with_context
+
self._cached_context = lcontext.context
+
self._cached_extra_children_with_context = (
+
self.extra_children_with_context(lcontext))
+
return self._cached_extra_children_with_context
+
def extra_children_with_context(self, lcontext=None):
+
return self.animation_data.children_with_context(lcontext)
+
def all_children(self, lcontext=None):
+
'''Return self.children and self.ordered_children as a single list.'''
+
output = list(self.children)
+
for z in sorted(self.ordered_children):
+
output.extend(self.ordered_children[z])
+
output.extend(
+
self._extra_children_with_context_avoid_recompute(lcontext))
+
return output
+
@property
+
def id(self):
+
return self.args.get('id', None)
+
def write_svg_element(self, id_map, is_duplicate, output_file, lcontext,
+
dry_run, force_dup=False):
+
children = self.all_children(lcontext=lcontext)
+
if dry_run:
+
if is_duplicate(self) and self.id is None:
+
id_map[id(self)]
+
for elem in self.get_linked_elems():
+
if elem.id is None:
+
id_map[id(elem.id)]
+
if self.has_content:
+
self.write_content(
+
id_map, is_duplicate, output_file, lcontext, dry_run)
+
if children is not None and len(children):
+
self.write_children_content(
+
id_map, is_duplicate, output_file, lcontext, dry_run)
+
return
+
if is_duplicate(self) and not force_dup:
+
mapped_id = self.id
+
if id_map is not None and id(self) in id_map:
+
mapped_id = id_map[id(self)]
+
output_file.write('<use xlink:href="#{}" />'.format(mapped_id))
+
return
+
output_file.write('<')
+
output_file.write(self.TAG_NAME)
+
override_args = self.args
+
if id(self) in id_map:
+
override_args = dict(override_args)
+
override_args['id'] = id_map[id(self)]
+
lcontext.write_tag_args(override_args, output_file, id_map)
+
if not self.has_content and (children is None or len(children) == 0):
+
output_file.write(' />')
+
else:
+
output_file.write('>')
+
if self.has_content:
+
self.write_content(
+
id_map, is_duplicate, output_file, lcontext, dry_run)
+
if children is not None and len(children):
+
self.write_children_content(
+
id_map, is_duplicate, output_file, lcontext, dry_run)
+
output_file.write('</')
+
output_file.write(self.TAG_NAME)
+
output_file.write('>')
+
def write_content(self, id_map, is_duplicate, output_file, lcontext,
+
dry_run):
+
'''Override in a subclass to add data between the start and end tags.
+
+
This will not be called if has_content is False.
+
'''
+
raise RuntimeError('This element has no content')
+
def write_children_content(self, id_map, is_duplicate, output_file,
+
lcontext, dry_run):
+
'''Override in a subclass to add data between the start and end tags.
+
+
This will not be called if has_content is False.
+
'''
+
children = self.all_children(lcontext=lcontext)
+
if dry_run:
+
for child in children:
+
local = LocalContext(lcontext.context, child, self, children)
+
child.write_svg_element(
+
id_map, is_duplicate, output_file, local, dry_run)
+
return
+
output_file.write('\n')
+
for child in children:
+
local = LocalContext(lcontext.context, child, self, children)
+
child.write_svg_element(
+
id_map, is_duplicate, output_file, local, dry_run)
+
output_file.write('\n')
+
def get_svg_defs(self):
+
return [v for v in self.args.values()
+
if isinstance(v, DrawingElement)]
+
def write_svg_defs(self, id_map, is_duplicate, output_file, lcontext,
+
dry_run):
+
super().write_svg_defs(
+
id_map, is_duplicate, output_file, lcontext, dry_run)
+
children = self.all_children(lcontext=lcontext)
+
for child in children:
+
local = LocalContext(lcontext.context, child, self, children)
+
child.write_svg_defs(
+
id_map, is_duplicate, output_file, local, dry_run)
+
def __eq__(self, other):
+
if isinstance(other, type(self)):
+
return (self.TAG_NAME == other.TAG_NAME and
+
self.args == other.args and
+
self.children == other.children and
+
self.ordered_children == other.ordered_children)
+
return False
+
def append_anim(self, animate_element):
+
self.children.append(animate_element)
+
def extend_anim(self, animate_iterable):
+
self.children.extend(animate_iterable)
+
def append_title(self, text, **kwargs):
+
self.children.append(elements.Title(text, **kwargs))
+
def add_key_frame(self, time, animation_args=None, **attr_values):
+
self._cached_extra_children_with_context = None
+
self.animation_data.add_key_frame(
+
time, animation_args=animation_args, **attr_values)
+
def add_attribute_key_sequence(self, attr, times, values, *,
+
animation_args=None):
+
self._cached_extra_children_with_context = None
+
self.animation_data.add_attribute_key_sequence(
+
attr, times, values, animation_args=animation_args)
+
+
+
class DrawingParentElement(DrawingBasicElement):
+
'''Base class for SVG elements that can have child nodes.'''
+
has_content = True
+
def __init__(self, children=(), ordered_children=None, **args):
+
super().__init__(**args)
+
self.children = list(children)
+
if ordered_children is not None and len(ordered_children):
+
self.ordered_children.update(
+
(z, list(v)) for z, v in ordered_children.items())
+
if self.children or self.ordered_children:
+
self.check_children_allowed()
+
def draw(self, obj, *, z=None, **kwargs):
+
if obj is None:
+
return
+
if not hasattr(obj, 'write_svg_element'):
+
elements = obj.to_drawables(**kwargs)
+
else:
+
if len(kwargs) > 0:
+
raise ValueError('unexpected kwargs')
+
elements = obj
+
if hasattr(elements, 'write_svg_element'):
+
self.append(elements, z=z)
+
else:
+
self.extend(elements, z=z)
+
def append(self, element, *, z=None):
+
self.check_children_allowed()
+
if z is not None:
+
self.ordered_children[z].append(element)
+
else:
+
self.children.append(element)
+
def extend(self, iterable, *, z=None):
+
self.check_children_allowed()
+
if z is not None:
+
self.ordered_children[z].extend(iterable)
+
else:
+
self.children.extend(iterable)
+
def write_content(self, id_map, is_duplicate, output_file, lcontext,
+
dry_run):
+
pass
+
+
+
def normalize_attribute_name(name):
+
name = name.replace('__', ':')
+
name = name.replace('_', '-')
+
if name[-1] == '-':
+
name = name[:-1]
+
return name
+41
drawsvg/url_encode.py
···
+
import base64
+
import urllib.parse
+
import re
+
+
+
STRIP_CHARS = ('\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f\x10\x11'
+
'\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f')
+
+
def bytes_as_data_uri(data, strip_chars=STRIP_CHARS, mime='image/svg+xml'):
+
'''Return a data URI with base64 encoding.'''
+
b64 = base64.b64encode(data)
+
return f'data:{mime};base64,{b64.decode(encoding="ascii")}'
+
+
def svg_as_data_uri(txt, strip_chars=STRIP_CHARS, mime='image/svg+xml'):
+
'''Return a data URI with base64 encoding, stripping unsafe chars for SVG.
+
'''
+
search = re.compile('|'.join(strip_chars))
+
data_safe = search.sub(lambda m: '', txt)
+
return bytes_as_data_uri(data_safe.encode(encoding='utf-8'), mime=mime)
+
+
def svg_as_utf8_data_uri(txt, unsafe_chars='"', strip_chars=STRIP_CHARS,
+
mime='image/svg+xml'):
+
'''Returns a data URI without base64 encoding.
+
+
The characters '#&%' are always escaped. '#' and '&' break parsing of
+
the data URI. If '%' is not escaped, plain text like '%50' will be
+
incorrectly decoded to 'P'. The characters in `strip_chars` cause the
+
SVG not to render even if they are escaped.
+
'''
+
unsafe_chars = (unsafe_chars or '') + '#&%'
+
replacements = {
+
char: urllib.parse.quote(char, safe='')
+
for char in unsafe_chars
+
}
+
replacements.update({
+
char: ''
+
for char in strip_chars
+
})
+
search = re.compile('|'.join(map(re.escape, replacements.keys())))
+
data_safe = search.sub(lambda m: replacements[m.group(0)], txt)
+
return f'data:{mime};utf8,{data_safe}'
+219
drawsvg/video.py
···
+
import base64
+
import shutil
+
import tempfile
+
+
def delay_import_np_imageio():
+
try:
+
import numpy as np
+
import imageio
+
except ImportError as e:
+
raise ImportError(
+
'Optional dependencies not installed. '
+
'Install with `python3 -m pip install "drawsvg[all]"` '
+
'or `python3 -m pip install "drawsvg[raster]"`. '
+
'See https://github.com/cduck/drawsvg#full-feature-install '
+
'for more details.'
+
) from e
+
return np, imageio
+
+
from .url_encode import bytes_as_data_uri
+
+
+
class RasterVideo:
+
def __init__(self, video_data=None, video_file=None, *, _file_handle=None,
+
mime_type='video/mp4'):
+
self.video_data = video_data
+
self.video_file = video_file
+
self._file_handle = _file_handle
+
self.mime_type = mime_type
+
def save_video(self, fname):
+
with open(fname, 'wb') as f:
+
if self.video_file is not None:
+
with open(self.video_file, 'rb') as source:
+
shutil.copyfileobj(source, f)
+
else:
+
f.write(self.video_data)
+
@staticmethod
+
def from_frames(svg_or_raster_frames, to_file=None, fps=10, *,
+
mime_type='video/mp4', file_type=None, _file_handle=None,
+
video_args=None, verbose=False):
+
if file_type is None:
+
file_type = mime_type.split('/')[-1]
+
if to_file is None:
+
# Create temp file for video
+
_file_handle = tempfile.NamedTemporaryFile(suffix='.'+file_type)
+
to_file = _file_handle.name
+
if video_args is None:
+
video_args = {}
+
if file_type == 'gif':
+
video_args.setdefault('duration', 1/fps)
+
else:
+
video_args.setdefault('fps', fps)
+
save_video(
+
svg_or_raster_frames, to_file, format=file_type,
+
verbose=verbose, **video_args)
+
return RasterVideo(
+
video_file=to_file, _file_handle=_file_handle,
+
mime_type=mime_type)
+
def _repr_png_(self):
+
if self.mime_type.startswith('image/'):
+
return self._as_bytes()
+
return None
+
def _repr_html_(self):
+
data_uri = self.as_data_uri()
+
if self.mime_type.startswith('video/'):
+
return (f'<video controls style="max-width:100%">'
+
f'<source src="{data_uri}" type="{self.mime_type}">'
+
f'Video unsupported.</video>')
+
return None
+
def _repr_mimebundle_(self, include=None, exclude=None):
+
b64 = base64.b64encode(self._as_bytes())
+
return {self.mime_type: b64}, {}
+
def as_data_uri(self):
+
return bytes_as_data_uri(self._as_bytes(), mime=self.mime_type)
+
def _as_bytes(self):
+
if self.video_data:
+
return self.video_data
+
else:
+
try:
+
with open(self.video_file, 'rb') as f:
+
return f.read()
+
except TypeError:
+
self.video_file.seek(0)
+
return self.video_file.read()
+
+
+
def render_svg_frames(frames, align_bottom=False, align_right=False,
+
bg=(255,)*4, verbose=False, **kwargs):
+
np, imageio = delay_import_np_imageio()
+
if verbose:
+
print(f'Rendering {len(frames)} frames: ', end='', flush=True)
+
arr_frames = []
+
for i, f in enumerate(frames):
+
if verbose:
+
print(f'{i} ', end='', flush=True)
+
if hasattr(f, 'rasterize'):
+
png_data = f.rasterize().png_data
+
elif hasattr(f, 'png_data'):
+
png_data = f.png_data
+
else:
+
png_data = f
+
im = imageio.imread(png_data)
+
arr_frames.append(im)
+
max_width = max(map(lambda arr:arr.shape[1], arr_frames))
+
max_height = max(map(lambda arr:arr.shape[0], arr_frames))
+
+
def mod_frame(arr):
+
new_arr = np.zeros((max_height, max_width) + arr.shape[2:],
+
dtype=arr.dtype)
+
new_arr[:,:] = bg[:new_arr.shape[-1]]
+
if align_bottom:
+
slice0 = slice(-arr.shape[0], None)
+
else:
+
slice0 = slice(None, arr.shape[0])
+
if align_right:
+
slice1 = slice(-arr.shape[1], None)
+
else:
+
slice1 = slice(None, arr.shape[1])
+
new_arr[slice0, slice1] = arr
+
return new_arr
+
return list(map(mod_frame, arr_frames))
+
+
def save_video(frames, file, verbose=False, **kwargs):
+
'''
+
Save a series of drawings as a GIF or video.
+
+
Arguments:
+
frames: A list of `Drawing`s or a list of `numpy.array`s.
+
file: File name or file like object to write the video to. The
+
extension determines the output format.
+
align_bottom: If frames are different sizes, align the bottoms of each
+
frame in the video.
+
align_right: If frames are different sizes, align the right edge of each
+
frame in the video.
+
bg: If frames are different sizes, fill the background with this color.
+
(default is white: (255, 255, 255, 255))
+
duration: If writing a GIF, sets the duration of each frame.
+
fps: If writing a video, sets the frame rate in FPS.
+
**kwargs: Other arguments to imageio.mimsave().
+
+
'''
+
np, imageio = delay_import_np_imageio()
+
if not isinstance(frames[0], np.ndarray):
+
frames = render_svg_frames(frames, verbose=verbose, **kwargs)
+
kwargs.pop('align_bottom', None)
+
kwargs.pop('align_right', None)
+
kwargs.pop('bg', None)
+
if verbose:
+
print()
+
print(f'Converting to video')
+
imageio.mimsave(file, frames, **kwargs)
+
+
def render_spritesheet(frames, row_length=None, verbose=False, **kwargs):
+
'''
+
Save a series of drawings as a bitmap spritesheet
+
+
Arguments:
+
frames: A list of `Drawing`s or a list of `numpy.array`s.
+
row_length: The length (in frames) of one row in the spritesheet.
+
If not provided, all frames go on one row.
+
align_bottom: If frames are different sizes, align the bottoms of each
+
frame in the video.
+
align_right: If frames are different sizes, align the right edge of each
+
frame in the video.
+
bg: If frames are different sizes, fill the background with this color.
+
(default is white: (255, 255, 255, 255))
+
**kwargs: Other arguments to imageio.imsave().
+
+
'''
+
np, _ = delay_import_np_imageio()
+
if not isinstance(frames[0], np.ndarray):
+
frames = render_svg_frames(frames, verbose=verbose, **kwargs)
+
kwargs.pop('align_bottom', None)
+
kwargs.pop('align_right', None)
+
bg = kwargs.pop('bg', (255, 255, 255, 255))
+
+
cols = row_length if row_length is not None else len(frames)
+
rows = (len(frames) - 1) // cols + 1
+
+
if rows * cols > len(frames): # Unfilled final row
+
empty_frame = np.zeros(frames[0].shape, dtype=frames[0].dtype)
+
empty_frame[..., :] = bg[:empty_frame.shape[-1]]
+
frames.extend([empty_frame] * (rows * cols - len(frames)))
+
+
block_arrangement = []
+
for row in range(rows):
+
next_row_end = (row+1)*cols
+
block_arrangement.append([
+
[frame] for frame in frames[row*cols:next_row_end]
+
])
+
+
spritesheet = np.block(block_arrangement)
+
return spritesheet
+
+
def save_spritesheet(frames, file, row_length=None, verbose=False, **kwargs):
+
'''
+
Save a series of drawings as a bitmap spritesheet
+
+
Arguments:
+
frames: A list of `Drawing`s or a list of `numpy.array`s.
+
file: File name or file like object to write the spritesheet to. The
+
extension determines the output format.
+
row_length: The length (in frames) of one row in the spritesheet.
+
If not provided, all frames go on one row.
+
align_bottom: If frames are different sizes, align the bottoms of each
+
frame in the video.
+
align_right: If frames are different sizes, align the right edge of each
+
frame in the video.
+
bg: If frames are different sizes, fill the background with this color.
+
(default is white: (255, 255, 255, 255))
+
**kwargs: Other arguments to imageio.imsave().
+
+
'''
+
_, imageio = delay_import_np_imageio()
+
spritesheet = render_spritesheet(
+
frames, row_length=row_length, verbose=verbose, **kwargs)
+
kwargs.pop('align_bottom', None)
+
kwargs.pop('align_right', None)
+
kwargs.pop('bg', None)
+
imageio.imsave(file, spritesheet, **kwargs)
+2
drawsvg/widgets/__init__.py
···
+
from .drawing_widget import DrawingWidget
+
from .async_animation import AsyncAnimation
+154
drawsvg/widgets/async_animation.py
···
+
import time
+
+
from ..drawing import Drawing
+
from .drawing_widget import DrawingWidget
+
+
+
class AsyncAnimation(DrawingWidget):
+
'''A Jupyter notebook widget for asynchronously displaying an animation.
+
+
Example:
+
# Jupyter cell 1:
+
widget = AsyncAnimation(fps=10)
+
widget
+
# [Animation is displayed here]
+
+
# Jupyter cell 2:
+
global_variable = 'a'
+
@widget.set_draw_frame # Animation above is automatically updated
+
def draw_frame(secs=0):
+
# Draw something...
+
d = draw.Drawing(300, 40)
+
d.append(draw.Text(global_variable, 20, 0, 10))
+
d.append(draw.Text(str(secs), 20, 30, 10))
+
return d
+
+
# Jupyter cell 3:
+
global_variable = 'b' # Animation above now displays 'b'
+
+
Attributes:
+
fps: The animation frame rate (frames per second).
+
draw_frame: A function that takes a single argument (animation time) and
+
returns a Drawing.
+
paused: While True, the animation will not run. Only the current frame
+
will be shown.
+
disable: While True, the widget will not be interactive and the
+
animation will not update.
+
click_pause: If True, clicking the drawing will pause or resume the
+
animation.
+
mousemove_pause: If True, moving the mouse up across the drawing will
+
pause the animation and moving the mouse down will resume it.
+
mousemove_y_threshold: Controls the sensitivity of mousemove_pause in
+
web browser pixels.
+
'''
+
+
def __init__(self, fps=10, draw_frame=None, *, paused=False, disable=False,
+
click_pause=True, mousemove_pause=False,
+
mousemove_y_threshold=10):
+
self._fps = fps
+
self._paused = paused
+
if draw_frame is None:
+
def draw_frame(secs):
+
return Drawing(0, 0)
+
self._draw_frame = draw_frame
+
self._last_secs = 0
+
self.click_pause = click_pause
+
self.mousemove_pause = mousemove_pause
+
self.mousemove_y_threshold = mousemove_y_threshold
+
self._start_time = 0
+
self._stop_time = 0
+
self._y_loc = 0
+
self._y_max = 0
+
self._y_min = 0
+
if self._paused:
+
frame_delay = -1
+
else:
+
frame_delay = 1000 // self._fps
+
self._start_time = time.monotonic()
+
initial_drawing = self.draw_frame(0)
+
super().__init__(initial_drawing, throttle=True, disable=disable,
+
frame_delay=frame_delay)
+
+
# Register callbacks
+
@self.mousedown
+
def mousedown(self, x, y, info):
+
if not self.click_pause:
+
return
+
self._y_min = self._y_max = self._y_loc
+
self.paused = not self.paused
+
+
@self.mousemove
+
def mousemove(self, x, y, info):
+
self._y_loc += info['movementY']
+
if not self.mousemove_pause:
+
self._y_min = self._y_max = self._y_loc
+
return
+
self._y_max = max(self._y_max, self._y_loc)
+
self._y_min = min(self._y_min, self._y_loc)
+
thresh = self.mousemove_y_threshold
+
invert = thresh < 0
+
thresh = max(0.01, abs(thresh))
+
down_triggered = self._y_loc - self._y_min >= thresh
+
up_triggered = self._y_max - self._y_loc >= thresh
+
if down_triggered:
+
self._y_min = self._y_loc
+
if up_triggered:
+
self._y_max = self._y_loc
+
if invert:
+
down_triggered, up_triggered = up_triggered, down_triggered
+
if down_triggered:
+
self.paused = False
+
if up_triggered:
+
self.paused = True
+
+
@self.timed
+
def timed(self, info):
+
secs = time.monotonic() - self._start_time
+
self.drawing = self.draw_frame(secs)
+
self._last_secs = secs
+
+
@self.on_exception
+
def on_exception(self, e):
+
self.paused = True
+
+
@property
+
def fps(self):
+
return self._fps
+
+
@fps.setter
+
def fps(self, new_fps):
+
self._fps = new_fps
+
if self.paused:
+
return
+
self.frame_delay = 1000 // self._fps
+
+
@property
+
def paused(self):
+
return self._paused
+
+
@paused.setter
+
def paused(self, new_paused):
+
if bool(self._paused) == bool(new_paused):
+
return
+
self._paused = new_paused
+
if self._paused:
+
self.frame_delay = -1
+
self._stop_time = time.monotonic()
+
else:
+
self._start_time += time.monotonic() - self._stop_time
+
self.frame_delay = 1000 // self._fps
+
+
@property
+
def draw_frame(self):
+
return self._draw_frame
+
+
@draw_frame.setter
+
def draw_frame(self, new_draw_frame):
+
self._draw_frame = new_draw_frame
+
if self.paused:
+
# Redraw if paused
+
self.drawing = self._draw_frame(self._last_secs)
+
+
def set_draw_frame(self, new_draw_frame):
+
self.draw_frame = new_draw_frame
+
return new_draw_frame
+155
drawsvg/widgets/drawing_javascript.py
···
+
javascript = '''
+
require.undef('drawingview');
+
+
define('drawingview', ['@jupyter-widgets/base'], function(widgets) {
+
class DrawingModel extends widgets.DOMWidgetModel {
+
defaults() {
+
return {
+
...super.defaults(),
+
_model_name: DrawingModel.model_name,
+
_model_module: DrawingModel.model_module,
+
_model_module_version: DrawingModel.model_module_version,
+
_view_name: DrawingModel.view_name,
+
_view_module: DrawingModel.view_module,
+
_view_module_version: DrawingModel.view_module_version,
+
};
+
}
+
static serializers = {
+
...widgets.DOMWidgetModel.serializers,
+
};
+
static model_name = 'DrawingModel';
+
static model_module = 'drawingview';
+
static model_module_version = '0.1.0';
+
static view_name = 'DrawingView';
+
static view_module = 'drawingview';
+
static view_module_version = '0.1.0';
+
}
+
+
class DrawingView extends widgets.DOMWidgetView {
+
render() {
+
this.container = document.createElement('a');
+
this.image_changed();
+
this.container.appendChild(this.svg_view);
+
this.el.appendChild(this.container);
+
this.model.on('change:_image', this.image_changed, this);
+
this.model.on('change:_mousemove_blocked', this.block_changed,
+
this);
+
this.model.on('change:frame_delay', this.delay_changed,
+
this);
+
this.model.on('change:_frame_blocked', this.delay_changed,
+
this);
+
this.model.on('change:disable', this.delay_changed,
+
this);
+
this.delay_changed();
+
}
+
image_changed() {
+
this.container.innerHTML = this.model.get('_image');
+
this.svg_view = this.container.getElementsByTagName('svg')[0];
+
this.cursor_point = this.svg_view.createSVGPoint();
+
this.register_events();
+
}
+
last_move = null;
+
last_mousemove_blocked = null;
+
last_timer = null;
+
block_changed() {
+
var widget = this;
+
window.setTimeout(function() {
+
if (widget.model.get('_mousemove_blocked')
+
!= widget.last_mousemove_blocked && widget.last_move) {
+
widget.send_mouse_event('mousemove', widget.last_move);
+
}
+
}, 0);
+
}
+
send_mouse_event(name, e) {
+
this.last_move = null;
+
if (this.model.get('disable')) {
+
return;
+
}
+
+
this.cursor_point.x = e.clientX;
+
this.cursor_point.y = e.clientY;
+
var svg_pt = this.cursor_point.matrixTransform(
+
this.svg_view.getScreenCTM().inverse());
+
+
var target_parents = [];
+
var target = e.target;
+
while(target && target != this.svg_view)
+
{
+
if (target.id) {
+
target_parents.push(target.id);
+
}
+
target = target.parentNode;
+
}
+
+
this.send({
+
name: name,
+
x: svg_pt.x,
+
y: -svg_pt.y,
+
type: e.type,
+
button: e.button,
+
buttons: e.buttons,
+
shiftKey: e.shiftKey,
+
altKey: e.altKey,
+
ctrlKey: e.ctrlKey,
+
metaKey: e.metaKey,
+
clientX: e.clientX,
+
clientY: e.clientY,
+
movementX: e.movementX,
+
movementY: e.movementY,
+
timeStamp: e.timeStamp,
+
targetId: e.target ? e.target.id : null,
+
targetParentIds: target_parents,
+
currentTargetId: e.currentTarget ? e.currentTarget.id : null,
+
relatedTargetId: e.relatedTarget ? e.relatedTarget.id : null,
+
});
+
}
+
delay_changed() {
+
var widget = this;
+
window.clearTimeout(widget.last_timer);
+
if (widget.model.get('disable')) {
+
return;
+
}
+
var delay = widget.model.get('frame_delay');
+
if (delay > 0) {
+
widget.last_timer = window.setTimeout(function() {
+
widget.send_timed_event('timed');
+
}, delay);
+
}
+
}
+
send_timed_event(name) {
+
if (this.model.get('disable')) {
+
return;
+
}
+
+
this.send({
+
name: name,
+
});
+
}
+
register_events() {
+
var widget = this;
+
this.svg_view.addEventListener('mousedown', function(e) {
+
e.preventDefault();
+
widget.send_mouse_event('mousedown', e);
+
});
+
this.svg_view.addEventListener('mousemove', function(e) {
+
e.preventDefault();
+
if (widget.model.get('_mousemove_blocked')
+
== widget.last_mousemove_blocked) {
+
widget.last_move = e;
+
} else {
+
widget.send_mouse_event('mousemove', e);
+
}
+
});
+
this.svg_view.addEventListener('mouseup', function(e) {
+
e.preventDefault();
+
widget.send_mouse_event('mouseup', e);
+
});
+
}
+
}
+
+
return {
+
DrawingModel: DrawingModel,
+
DrawingView: DrawingView
+
};
+
});
+
'''
+164
drawsvg/widgets/drawing_widget.py
···
+
from ipywidgets import widgets
+
from traitlets import Unicode, Bool, Int
+
+
+
# Register front end javascript
+
from IPython import display
+
from . import drawing_javascript
+
display.display(display.Javascript(drawing_javascript.javascript))
+
del drawing_javascript
+
+
+
class DrawingWidget(widgets.DOMWidget):
+
_model_name = Unicode('DrawingModel').tag(sync=True)
+
_model_module = Unicode('drawingview').tag(sync=True)
+
_model_module_version = Unicode('0.1.0').tag(sync=True)
+
_view_name = Unicode('DrawingView').tag(sync=True)
+
_view_module = Unicode('drawingview').tag(sync=True)
+
_view_module_version = Unicode('0.1.0').tag(sync=True)
+
_image = Unicode().tag(sync=True)
+
_mousemove_blocked = Int(0).tag(sync=True)
+
_frame_blocked = Int(0).tag(sync=True)
+
throttle = Bool(True).tag(sync=True)
+
disable = Bool(False).tag(sync=True)
+
frame_delay = Int(-1).tag(sync=True)
+
+
def __init__(self, drawing, throttle=True, disable=False, frame_delay=-1):
+
'''An interactive Jupyter notebook widget.
+
+
This works similarly to displaying a Drawing as a cell output but
+
DrawingWidget can register callbacks for user mouse events. Within a
+
callback modify the drawing then call .refresh() to update the output in
+
real time.
+
+
Arguments:
+
drawing: The initial Drawing to display. Call .refresh() after
+
modifying or just assign a new Drawing.
+
throttle: If True, limit the rate of mousemove events. For drawings
+
with many elements, this will significantly reduce lag.
+
disable: While True, mouse events will be disabled.
+
frame_delay: If greater than or equal to zero, a timed callback will
+
occur frame_delay milliseconds after the previous drawing
+
update.
+
'''
+
super().__init__()
+
self.throttle = throttle
+
self.disable = disable
+
self.frame_delay = frame_delay
+
self.drawing = drawing
+
self.mousedown_callbacks = []
+
self.mousemove_callbacks = []
+
self.mouseup_callbacks = []
+
self.timed_callbacks = []
+
self.exception_callbacks = []
+
+
self.on_msg(self._receive_msg)
+
+
@property
+
def drawing(self):
+
return self._drawing
+
+
@drawing.setter
+
def drawing(self, drawing):
+
self._drawing = drawing
+
self.refresh()
+
+
def refresh(self):
+
'''
+
Redraw the displayed output with the current value of self.drawing.
+
'''
+
self._image = self.drawing.as_svg()
+
+
def _receive_msg(self, _, content, buffers):
+
if not isinstance(content, dict):
+
return
+
name = content.get('name')
+
callbacks = {
+
'mousedown': self.mousedown_callbacks,
+
'mousemove': self.mousemove_callbacks,
+
'mouseup': self.mouseup_callbacks,
+
'timed': self.timed_callbacks,
+
}.get(name, ())
+
try:
+
if callbacks:
+
if name == 'timed':
+
self._call_handlers(callbacks, content)
+
else:
+
self._call_handlers(callbacks, content.get('x'),
+
content.get('y'), content)
+
except BaseException as e:
+
suppress = any(
+
handler(self, e)
+
for handler in self.exception_callbacks
+
)
+
if not suppress:
+
raise
+
finally:
+
if name == 'timed':
+
self._frame_blocked += 1
+
else:
+
self._mousemove_blocked += 1
+
+
+
def mousedown(self, handler, remove=False):
+
'''
+
Register (or unregister) a handler for the mousedown event.
+
+
Arguments:
+
remove: If True, unregister, otherwise register.
+
'''
+
self.on_msg
+
self._register_handler(
+
self.mousedown_callbacks, handler, remove=remove)
+
+
def mousemove(self, handler, remove=False):
+
'''
+
Register (or unregister) a handler for the mousemove event.
+
+
Arguments:
+
remove: If True, unregister, otherwise register.
+
'''
+
self._register_handler(
+
self.mousemove_callbacks, handler, remove=remove)
+
+
def mouseup(self, handler, remove=False):
+
'''
+
Register (or unregister) a handler for the mouseup event.
+
+
Arguments:
+
remove: If True, unregister, otherwise register.
+
'''
+
self._register_handler(
+
self.mouseup_callbacks, handler, remove=remove)
+
+
def timed(self, handler, remove=False):
+
'''
+
Register (or unregister) a handler for the timed event.
+
+
Arguments:
+
remove: If True, unregister, otherwise register.
+
'''
+
self._register_handler(
+
self.timed_callbacks, handler, remove=remove)
+
+
def on_exception(self, handler, remove=False):
+
'''
+
Register (or unregister) a handler for exceptions in other handlers.
+
+
If any handler returns True, the exception is suppressed.
+
+
Arguments:
+
remove: If True, unregister, otherwise register.
+
'''
+
self._register_handler(
+
self.exception_callbacks, handler, remove=remove)
+
+
def _register_handler(self, callback_list, handler, remove=False):
+
if remove:
+
callback_list.remove(handler)
+
else:
+
callback_list.append(handler)
+
+
def _call_handlers(self, callback_list, *args, **kwargs):
+
for callback in callback_list:
+
callback(self, *args, **kwargs)
+37
examples/animated-fix-github.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="200" viewBox="-100.0 -100.0 200 200">
+
<defs>
+
<path d="M-90,0 A90,40,360,1,1,90,0 A90,40,360,1,1,-90,0 Z" id="d0" />
+
</defs>
+
<g>
+
<circle cx="0" cy="0" r="19" fill="red">
+
<animateTransform type="scale" repeatCount="indefinite" attributeName="transform" dur="6s" values="1;1;1;1;1;1;0;0;0;0;0;0" />
+
</circle>
+
<animateTransform type="translate" repeatCount="indefinite" attributeName="transform" dur="6s" values="0,-80;80,0;0,80;-80,0;0,-80" />
+
</g>
+
<g>
+
<circle cx="0" cy="0" r="19.25" fill="green" transform="scale(0)">
+
<animateTransform type="scale" repeatCount="indefinite" attributeName="transform" dur="6s" begin="1.5s" values="1;1;1;1;1;1;0;0;0;0;0;0" />
+
</circle>
+
<animateTransform type="translate" repeatCount="indefinite" attributeName="transform" dur="6s" values="0,-80;80,0;0,80;-80,0;0,-80" />
+
</g>
+
<g>
+
<circle cx="0" cy="0" r="19.5" fill="blue" transform="scale(0)">
+
<animateTransform type="scale" repeatCount="indefinite" attributeName="transform" dur="6s" begin="3s" values="1;1;1;1;1;0;0;0;0;0;0;0" />
+
</circle>
+
<animateTransform type="translate" repeatCount="indefinite" attributeName="transform" dur="6s" values="0,-80;80,0;0,80;-80,0;0,-80" />
+
</g>
+
<g>
+
<circle cx="0" cy="0" r="20.1" fill="yellow" transform="scale(0)">
+
<animateTransform type="scale" repeatCount="indefinite" attributeName="transform" dur="6s" begin="4.5s" values="1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;" />
+
</circle>
+
<animateTransform type="translate" repeatCount="indefinite" attributeName="transform" dur="6s" values="0,-80;80,0;0,80;-80,0;0,-80" />
+
</g>
+
<circle cx="0" cy="0" r="10">
+
<animateMotion repeatCount="indefinite" dur="3s">
+
<mpath xlink:href="#d0" />
+
</animateMotion>
+
<animateTransform type="scale" repeatCount="indefinite" attributeName="transform" dur="3s" values="1,2;2,1;1,2;2,1;1,2" />
+
</circle>
+
</svg>
+18
examples/animated.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="200" viewBox="-100.0 -100.0 200 200">
+
<defs>
+
<path d="M-90,0 A90,40,360,1,1,90,0 A90,40,360,1,1,-90,0 Z" id="d0" />
+
</defs>
+
<circle cx="0" cy="0" r="20" fill="red">
+
<animate repeatCount="indefinite" attributeName="cy" dur="6s" values="-80;80;-80" />
+
<animate repeatCount="indefinite" attributeName="cx" dur="6s" values="0;80;0;-80;0" />
+
<animate calcMode="discrete" repeatCount="indefinite" attributeName="fill" dur="6s" values="red;green;blue;yellow" />
+
</circle>
+
<circle cx="0" cy="0" r="10">
+
<animateMotion repeatCount="indefinite" dur="3s">
+
<mpath xlink:href="#d0" />
+
</animateMotion>
+
<animateTransform type="scale" repeatCount="indefinite" attributeName="transform" dur="3s" values="1,2;2,1;1,2;2,1;1,2" />
+
</circle>
+
</svg>
examples/example1.png

This is a binary file and will not be displayed.

+22 -2
examples/example1.svg
···
<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>
+
<path d="M-10,-20 C30,10,30,-50,70,-20" stroke-width="2" stroke="lime" fill="black" fill-opacity="0.2" id="d0" />
+
<marker markerWidth="4.0" markerHeight="4.0" viewBox="-0.1 -0.5 1.0 1.0" orient="auto" id="d1">
+
<path d="M-0.1,0.5 L-0.1,-0.5 L0.9,0 Z" fill="red" />
+
</marker>
</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" />
+
<rect x="-80" y="-50" width="40" height="50" fill="#1248ff">
+
<title>Our first rectangle</title>
+
</rect>
<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" />
+
<use xlink:href="#d0" />
+
<text x="-10" y="-35" font-size="8" fill="blue" dy="0em">Basic text</text>
+
<text font-size="8" text-anchor="start"><textPath xlink:href="#d0" startOffset="0">
+
<tspan dy="0.4em">Path text</tspan>
+
</textPath></text>
+
<g>
+
<text font-size="8" text-anchor="end"><textPath xlink:href="#d0" startOffset="100%">
+
<tspan dy="0em" dx="-1">Multi-line</tspan>
+
</textPath></text>
+
<text font-size="8" text-anchor="end"><textPath xlink:href="#d0" startOffset="100%">
+
<tspan dy="1em" dx="-1">text</tspan>
+
</textPath></text>
+
</g>
<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" />
+
<path d="M20,40 L20,27 L0,20" stroke="red" stroke-width="2" fill="none" marker-end="url(#d1)" />
+
<path d="M30,20 L0,10" stroke="red" stroke-width="2" fill="none" marker-end="url(#d1)" />
</svg>
examples/example2.png

This is a binary file and will not be displayed.

+11 -7
examples/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">
+
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">
+
<pattern width="0.13" height="0.23" patternUnits="userSpaceOnUse" id="d0">
+
<rect x="0" y="0" width="0.1" height="0.1" fill="yellow" />
+
<rect x="0" y="0.1" width="0.1" height="0.1" fill="orange" />
+
</pattern>
+
<radialGradient cx="0" cy="0.35" r="7.0" gradientUnits="userSpaceOnUse" id="d1">
<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">
+
<linearGradient x1="0.1" y1="0.35" x2="0.7" y2="0.55" gradientUnits="userSpaceOnUse" id="d2">
<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)" />
+
<rect x="-0.75" y="-0.5" width="1.5" height="1" fill="url(#d0)" fill-opacity="0.4" />
+
<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(#d1)" 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(#d1)" stroke="red" stroke-width="0.002" />
+
<rect x="0.1" y="0.15" width="0.6" height="0.2" stroke="black" stroke-width="0.002" fill="url(#d2)" />
</svg>
examples/example6.png

This is a binary file and will not be displayed.

examples/example7.gif

This is a binary file and will not be displayed.

examples/example8.png

This is a binary file and will not be displayed.

+15
examples/example8.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="200" viewBox="0 0 300 100">
+
<defs>
+
<g fill="orange" transform="rotate(-20)" id="d0">
+
<rect x="0" y="10" width="20" height="40" />
+
<circle cx="30" cy="40" r="10" />
+
<circle cx="50" cy="40" r="10" fill="green" />
+
</g>
+
</defs>
+
<use xlink:href="#d0" />
+
<use xlink:href="#d0" x="80" y="0" stroke="black" stroke-width="1" />
+
<use xlink:href="#d0" x="80" y="20" stroke="blue" stroke-width="2" />
+
<use xlink:href="#d0" x="80" y="40" stroke="red" stroke-width="3" />
+
</svg>
+15
examples/font.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="100" viewBox="-200.0 -50.0 400 100">
+
<style>/*<![CDATA[*/@font-face {
+
font-family: 'Permanent Marker';
+
font-style: normal;
+
font-weight: 400;
+
font-display: swap;
+
src: url(data:application/octet-stream;base64,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) format('truetype');
+
}
+
/*]]>*/</style>
+
<defs>
+
</defs>
+
<text x="0" y="0" font-size="35" font-family="Permanent Marker" font-style="italic" text-anchor="middle" dominant-baseline="central">Text with custom font</text>
+
</svg>
+210
examples/logo.html
···
+
<!DOCTYPE html>
+
<head>
+
<meta charset="utf-8">
+
</head>
+
<body>
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+
width="400" height="100" viewBox="-200.0 -50.0 400 100" onload="svgOnLoad(event);">
+
<defs>
+
<linearGradient x1="-1250" y1="0" x2="1250" y2="0" gradientUnits="userSpaceOnUse" id="d0">
+
<stop offset="0" stop-color="#5544ee" stop-opacity="1" />
+
<stop offset="0.2" stop-color="#5544ee" stop-opacity="1" />
+
<stop offset="0.4" stop-color="#ee0055" stop-opacity="1" />
+
<stop offset="0.6" stop-color="#ee0055" stop-opacity="1" />
+
<stop offset="0.8" stop-color="#5544ee" stop-opacity="1" />
+
<stop offset="1" stop-color="#5544ee" stop-opacity="1" />
+
<animate attributeName="x1" dur="128s" values="-2250;-250" keyTimes="0;1" repeatCount="indefinite" fill="freeze" />
+
<animate attributeName="x2" dur="128s" values="250;2250" keyTimes="0;1" repeatCount="indefinite" fill="freeze" />
+
</linearGradient>
+
<path d="M85 438q-1 21 -1 42q0 55 8 113q10 80 45.5 156.5t93.5 131.5q51 49 111.5 81.5t115.5 45.5q56 14 111 18q26 2 50 2q25 0 49 -3q44 -5 79.5 -11t54.5 -12l19 -7q-1 7 -3 19.5t-5.5 50t-4.5 73.5v17q0 31 2 69q4 50 12.5 92.5t28 86t47.5 74t72.5 50t100.5 19.5 q63 0 115.5 -21t86.5 -54t60 -73t39.5 -80.5t22 -73.5t10.5 -54l2 -21q2 -16 4.5 -43.5t5.5 -111.5q2 -42 1 -84q0 -40 -1 -82q-3 -82 -17.5 -194t-41.5 -208q57 -27 89 -77t35 -105v-15q0 -47 -16 -95q-18 -54 -55 -93q-28 -30 -63 -47q-36 -17 -68 -20q-15 -1 -30 -1 q-18 -1 -35 1q-33 4 -60 13t-48.5 18t-33.5 15l-12 7q-159 -83 -371 -91q-19 -1 -38 0q-187 0 -324 69q-7 4 -19 11t-45 32t-60.5 54t-56 76t-41.5 99q-17 67 -21 141zM729 483q5 -75 82 -75q35 0 57.5 20.5t22.5 54.5t-22 60t-58 26q-35 -1 -60 -27q-22 -24 -22 -52v-7z" transform="scale(0.0341796875,-0.0341796875)" id="d1" />
+
<path d="M49 548q0 83 3.5 154.5t9.5 112.5l6 41q16 73 54 125t80.5 74.5t83 34.5t66.5 12h27h13q56 0 102 -16q52 -18 79.5 -45.5t46 -55.5t24.5 -47l5 -20q39 56 90.5 91.5t96.5 46t84.5 12.5t62.5 -2l23 -5q58 -15 99.5 -46t60.5 -70t27 -71t9 -65v-14q0 -41 -10 -79 q-12 -44 -30.5 -71t-37 -47t-32.5 -28l-13 -9q-49 -29 -103 -35q-20 -2 -38 -2q-30 0 -58 7q-42 11 -78.5 26.5t-56.5 28.5l-20 14q13 -99 13 -178q0 -34 -2 -64q-8 -102 -36 -168.5t-65.5 -108t-79 -61.5t-76 -27.5t-57.5 -6.5h-23h-9q-42 0 -80 9q-42 10 -71 27.5t-53 39 t-39 43t-25 39.5t-14 29l-4 11q-27 82 -41 181.5t-14 182.5z" transform="scale(0.0341796875,-0.0341796875)" id="d2" />
+
<path d="M59 496q0 22 5 74q5 58 17.5 111t43.5 119t76 116t121.5 88t173.5 47q31 3 60 3q48 0 88 -8q66 -13 103 -34t64 -50t35 -44.5t12 -26.5q2 8 7.5 22t23.5 42.5t42 51t67 40.5t94 18q62 0 112.5 -29t82 -75t54.5 -101.5t33.5 -111t16.5 -101.5t7 -75l1 -29q2 -42 2 -81 q0 -54 -4 -103q-7 -84 -22.5 -141t-37.5 -102t-47.5 -71.5t-51 -44.5t-49.5 -25t-42.5 -10.5t-29.5 -2.5l-11 1q-1 2 -11.5 0t-29.5 2t-41.5 8.5t-46.5 19.5t-45 34.5t-37.5 53t-22.5 75.5q-2 -9 -8 -23.5t-35 -50.5t-71 -62q-42 -25 -122 -40q-45 -8 -95 -8q-41 0 -86 5 q-8 0 -22.5 1.5t-55 12.5t-78 28.5t-83 54.5t-78 85.5t-55 127t-22.5 174.5q-2 17 -2 35zM686 483q5 -75 82 -75q35 0 57.5 20.5t22.5 54.5t-22 60t-58 26q-35 -1 -60 -27q-22 -24 -22 -52v-7z" transform="scale(0.0341796875,-0.0341796875)" id="d3" />
+
<path d="M35 735q-2 23 -2 44q0 43 8 79q12 56 35 90.5t52.5 60.5t59.5 38.5t55.5 19.5t41.5 9l16 1q69 0 125.5 -19.5t94 -50t66.5 -74t45 -83.5t26 -86.5t13.5 -76t4.5 -58.5q2 59 22 101t47 60t54 28t46 10l19 1q44 0 79.5 -13.5t56.5 -34t36.5 -45.5t21.5 -50t9.5 -45.5 t3.5 -33.5v-13q0 81 11 149.5t30 115.5t44 85t52 60t54 38t52 22.5t44 10t30 3.5l11 -1q63 -1 113.5 -20t80.5 -48.5t51 -65t29.5 -70.5t13 -64.5t3.5 -47.5v-18q-3 -96 -18 -187.5t-34.5 -153.5t-38 -110t-31.5 -70l-12 -23q-36 -66 -84.5 -113.5t-91.5 -68.5t-80.5 -32 t-59.5 -11l-23 -1q-92 0 -161.5 26.5t-105 61.5t-57 79t-26.5 71t-5 45q-3 -22 -10 -48t-31 -71t-59 -79.5t-99.5 -60.5t-146.5 -26q-64 0 -121.5 24.5t-94 59.5t-64.5 70t-40 59l-12 25q-60 115 -97 246.5t-45 205.5z" transform="scale(0.0341796875,-0.0341796875)" id="d4" />
+
<path d="M70 169v15v10q0 50 18 90q20 44 54.5 72t76.5 48.5t84 29.5t77 14.5t57 5.5l22 1q-75 18 -134.5 44t-95.5 53t-61.5 56.5t-36.5 55t-16.5 46t-5.5 32.5v12q3 71 34 128t77 91t101.5 59t110.5 35.5t100 16.5t73 6h28q112 -2 194 -16.5t131.5 -36t78.5 -53.5t39 -66 q8 -28 7 -61v-15q-2 -50 -27.5 -88t-64.5 -58t-85.5 -32.5t-92 -15t-83.5 -2.5t-62 2l-23 3q97 -12 174.5 -37.5t123 -56.5t78 -66t46 -67.5t20 -58.5t5.5 -42v-16q0 -73 -24.5 -133t-64 -97.5t-87.5 -65.5t-95.5 -41.5t-87 -21.5t-63.5 -10l-25 -1q-39 -2 -75 -2 q-80 0 -148 8q-98 11 -156.5 32.5t-100.5 48.5t-59.5 55t-27 51.5t-8.5 38.5z" transform="scale(0.0341796875,-0.0341796875)" id="d5" />
+
<path d="M23 773q0 65 18.5 118.5t70 97.5t132.5 60q30 6 58 6q36 1 70 -9q60 -16 101.5 -53.5t76 -85.5t55 -98t34.5 -92t19 -69l6 -27q2 21 7 55t33.5 118t70.5 144t125 98q52 24 113 24q37 0 77 -9q71 -16 115 -55.5t58.5 -90.5t15.5 -109v-8q0 -54 -12 -106q-13 -56 -27 -100 t-26 -72l-12 -27q-4 -10 -12.5 -28t-37 -69.5t-59.5 -98t-80 -104t-99.5 -98.5t-116.5 -69t-133 -28q-98 0 -189 46t-154.5 110t-119.5 148.5t-85 145t-50 115.5q-19 48 -31 101.5t-12 118.5z" transform="scale(0.0341796875,-0.0341796875)" id="d6" />
+
<path d="M85 390q-11 61 -11 117q0 90 28 167q21 57 51 108.5t58.5 83t54 55.5t40.5 34l16 10q129 77 288 86q26 1 51 1q131 0 246 -40q137 -47 228 -133q63 -59 106.5 -140.5t64 -164t30.5 -168.5q7 -64 7 -123q0 -18 -1 -36q-3 -74 -8 -133t-11 -92l-6 -34 q-21 -119 -64.5 -213.5t-99.5 -153.5t-122.5 -101t-135 -60t-135.5 -27q-46 -6 -88 -6q-18 0 -36 1q-59 4 -103.5 9t-70.5 11l-26 7q-76 27 -120 68t-54 81q-7 32 -7 62v12q2 36 11 58l10 22q17 37 48 61t64 32t64 11t50 1l20 -2q68 3 116.5 20t69 39t31.5 44t11 37v15 q-14 -4 -39 -8.5t-96 -8.5q-24 -1 -47 -1q-46 0 -88 5q-65 8 -143.5 41.5t-133.5 92.5q-89 94 -118 253zM641 444q5 -75 82 -75q35 0 57.5 20.5t22.5 54.5t-22 60t-58 26q-35 0 -60 -26q-22 -24 -22 -53v-7z" transform="scale(0.0341796875,-0.0341796875)" id="d7" />
+
<path d="" transform="scale(0.0341796875,-0.0341796875)" id="d8" />
+
<path d="M59 1126q-2 56 18.5 104.5t56 80t79 57t87.5 39.5t80.5 23.5t59.5 12.5l23 3q78 6 147 -1.5t117 -24.5t87.5 -39t63.5 -45.5t40.5 -43.5t23.5 -32l6 -13q39 -73 52 -152t2 -148t-33.5 -135.5t-52.5 -118t-57 -91.5t-45 -61l-19 -21h19t46.5 -4.5t66 -13.5t69.5 -29.5 t65 -49.5t45 -76.5t16 -106.5q-1 -45 -22 -82.5t-55 -63t-77.5 -44.5t-91.5 -30t-95.5 -18t-90.5 -9t-76.5 -2.5t-53.5 0.5l-19 1q-70 0 -130 8.5t-103 22.5t-78 32t-57.5 38t-39 40t-25 38t-13.5 32t-6 22l-1 8q-8 71 3.5 127t51.5 107t69 78t97 81q14 12 22 18.5t21 16.5 t23 19q30 25 53.5 53.5t39.5 60t15.5 64.5t-20.5 60t-48 40.5t-54 13t-49.5 -4t-37.5 -9.5l-14 -6q-51 -10 -90.5 4t-59.5 41.5t-32.5 56.5t-15.5 50z" transform="scale(0.0341796875,-0.0341796875)" id="d9" />
+
<clipPath id="d10">
+
<use xlink:href="#d1" x="-202.12158203125" y="17.5">
+
<animate attributeName="x" dur="128s" values="-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="19.324137087557;19.291309948237398;15.726205470907235;15.839487980635687;18.841995512517798;18.443879956274092;18.178921605760884;16.732545830356578;17.92377666271385;17.92720693456335;17.824816068448012;16.133531481019222;17.222678561165075;17.074127280821486;18.39204832494986;19.47927825179897;19.297581892372975;17.67670818971728;17.279416754903416;16.572962966597313;15.64369731757143;15.609779428363275;17.359575448389247;16.77386051141471;17.020059687602846;19.06715783131315;17.603011076584114;17.742041444105997;16.444493628460247;15.595432316563128;16.80057171504464;16.046789571945865;17.540895383348804;19.49473427277021;18.19791878938348;16.227373987292577;19.074286146331954;18.687039685686557;18.43760676757591;19.126374599590243;18.551541935332285;18.658990549847054;16.915147911366414;19.423906292288507;19.347603751592903;16.144738613216077;18.51601628660749;18.360603592949815;17.34562679096791;17.62142286449378;17.460055687400764;19.199328288378283;17.50336425052262;18.82609795916725;16.915696819474864;19.031403674325013;19.098802355026503;17.34404865952655;17.770820281680976;19.181321756767716;18.395091815488072;17.44643421944634;16.387244043964042;16.798668975075593;19.324137087557" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
<use xlink:href="#d2" x="-149.79248046875" y="17.5">
+
<animate attributeName="x" dur="128s" values="-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="18.298286552280974;16.164278741976503;19.13176198650438;16.572550051599265;19.145511343472187;16.73825249979784;19.32944684622463;18.324823225470418;17.51699526793327;17.570991024594207;18.105657595867164;17.85177884717794;16.747377298204;16.331273898151707;17.54756663342113;19.236617436535113;17.99306034690349;15.801501476296181;18.781599978848067;18.40379714990919;19.13061448380528;16.265610933216472;18.479130897109417;15.735035585594623;18.1116397097382;16.592398929348597;16.406466116979054;19.001964685792952;15.9250639305821;17.58945066143572;18.91577202873949;16.47932791187607;16.341915754782587;19.022327037465118;17.191670593558783;18.3678443956199;15.627492280506981;16.949427645212964;16.18752396850293;18.19106176565417;15.831612709618302;19.31824866138299;15.601378859307605;18.417694029767215;15.584579478892604;16.52276021622977;18.753417549609104;16.1284731547471;16.234955237003906;18.26598170405436;17.04226352541024;15.672643983189998;19.460006184811206;16.105680435026205;15.645075976973656;16.87680402214718;17.96095793329943;18.46983849250312;15.952459612072307;16.848855092786582;15.623243430506754;17.294613049969175;18.563879746352313;18.4597866548828;18.298286552280974" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
<use xlink:href="#d3" x="-107.95654296875" y="17.5">
+
<animate attributeName="x" dur="128s" values="-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="19.108080633400146;18.52264861470026;18.949783105301712;18.32138056020306;17.391118048393253;16.40211028139155;18.143313994628055;16.765223706913037;15.908196420198793;17.291287445676605;18.99905216539187;16.010145858475152;17.83982279244243;17.07181020042067;17.55921078705671;16.075317855907777;19.338924745948642;16.53638569300882;17.92431175621401;17.179022182622987;15.572132876499706;17.73180049535632;16.062277515845338;15.727123983292785;15.634224984994589;16.144660060724938;15.883487774532732;18.04030279015467;17.533036736142574;19.43386437619011;19.236521274787226;19.478100933038014;16.42989536267124;17.278789820478785;16.503123047364628;17.86494938219199;17.99665620301913;18.700829822537585;18.337993215317397;16.52643715395214;17.192067691050084;17.60475977512234;15.519299124214513;15.641997646831058;17.134905670888298;15.944699868919905;18.39507869147722;16.46346205776665;15.899092347019964;16.2270403132017;16.426101717508562;16.369414538873436;17.582945456191588;17.35761244457536;16.73890428350965;18.0670350307227;16.34979896797796;19.12625070692673;19.35246661931695;18.415724182310097;17.23493547515866;17.54600536886819;17.82430522416064;15.704938974423321;19.108080633400146" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
<use xlink:href="#d4" x="-58.70361328125" y="17.5">
+
<animate attributeName="x" dur="128s" values="-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="17.172065553929826;17.600258129279048;16.224900243300688;15.875147153833273;18.710620834821544;16.96473586617359;17.576838760395944;19.185801390367477;17.942041348412122;16.65832307012065;19.434084294213275;16.988906839736536;15.576220419238417;18.24124268925948;15.904647502452628;16.72368944282506;18.862446748475364;18.190287018579063;15.562888288857353;17.30569383680793;17.142697502294027;17.443451776008004;16.33298757343119;17.854980248616833;15.795157253901012;16.637437403921545;16.991608420187873;19.241081736194044;15.806192881440081;18.519936456991594;16.269436511867294;17.786210965317323;17.06712388145699;17.35289752899603;18.514322022942874;17.080170240631453;15.986917920534678;15.98708039888616;15.822042871014519;18.90028349561939;18.06396637532426;19.33867425358325;18.270610186618597;15.598675090616403;18.136638655375673;18.608847738599195;18.394073119932518;17.49179808171847;16.930338470923886;17.32814285580714;18.69488833471872;16.57576997531061;17.605214979225202;17.410238168226087;19.318787387845322;18.71739990767735;19.228215440872955;18.844022294289577;16.687054675658697;16.42650944460629;17.455157893793455;16.537621365444693;17.210615327823714;18.216560861444595;17.172065553929826" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
<use xlink:href="#d5" x="0.22216796875" y="17.5">
+
<animate attributeName="x" dur="128s" values="0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="19.17432090891334;17.843602321380402;18.7714130155966;15.883789243434663;16.924228911880682;19.490992085494522;16.086004191824927;17.167072173899555;15.767357581811265;15.844597426395598;19.082001408378922;19.45454811483057;18.092328311132544;16.014060019669373;16.685530078130782;16.426798573386762;18.182929303799767;18.224396029991347;17.255383347646312;17.595979073255904;15.948281054378096;17.663572996458;19.29975488689121;18.523109210244115;15.884617845083582;17.566005445975094;18.361459270541847;16.529042070197043;19.079586599991057;17.343763856511888;18.3129248390927;17.116653557850963;19.480532150225436;18.63126295102649;17.793761602906123;16.079060091574764;17.264733037914887;15.6175396385709;17.88065670082722;19.027270070813692;16.22169796803166;17.54068601291097;17.42983330112547;17.119658716133877;18.34184031877659;19.246679675941035;18.32157014751221;17.38999671817144;19.347909645664032;16.822910002269136;18.482451019047755;18.133940103096172;18.546435499777793;18.908292911225235;16.399995521690087;17.984996115800016;17.110895538815566;18.167887520270217;19.40893625858238;18.039323386996088;15.546423176316438;17.358195231067338;18.346305933794774;19.03288374740802;19.17432090891334" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
<use xlink:href="#d6" x="39.46044921875" y="17.5">
+
<animate attributeName="x" dur="128s" values="39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="18.100340133230947;18.76427960677236;15.56854958542948;19.272918260590053;18.417854534663782;17.92577580767231;19.121292120680188;19.038718908208274;15.901829522549633;18.762484865120747;18.568002963113543;16.298152186703973;18.476982770007467;17.844909774667702;16.265977695280423;18.716757583973582;16.051492512478955;17.949294825753448;17.23759193664957;16.514764443440473;17.764378493236507;17.36834751703482;16.319989978444156;19.36712314963816;15.791301469511914;15.512149152328888;17.441692517196124;18.848765989216805;18.133608253760478;18.518678354965495;17.44000181332036;18.19920941955595;16.839562548974325;16.567781057283934;17.511602832684538;15.610111265989747;15.819234359666222;18.515838961232788;16.194798551786665;18.501022903279;18.637503921917954;17.117963798501876;18.199972737584474;18.649689162774;18.956096402393687;16.039481015624983;16.150280579909165;17.026653676995508;17.358626773228572;16.679275487537627;15.541600951035464;17.729686691385268;19.367655038887854;16.965838731349027;17.651997680963095;17.029316620955058;17.271201914680454;18.981974830520677;16.733721777745423;18.096260968845616;17.435146143723358;17.654276690218072;19.158812502107267;15.806856043874703;18.100340133230947" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
<use xlink:href="#d7" x="83.89404296875" y="17.5">
+
<animate attributeName="x" dur="128s" values="83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="18.797481725009643;16.71667722862086;18.085233090770295;18.68336392379322;18.113637764536453;17.07186625951781;18.86281907321502;15.87179519261299;18.033257609922067;17.064503240471847;17.621863956694423;18.903764341757842;18.691459521790456;18.01536068585924;16.73231712105784;16.431655286240023;17.330152856150356;16.428438291628133;16.60994613353222;19.33101857042053;15.947864254575299;18.774465494972127;17.016856397885;16.958413517883915;16.773566048028542;15.809542248687551;17.32952080257834;16.16598872833203;17.26803588477986;16.667948647173095;19.078292820866682;19.186969808294943;17.267985939703188;18.058480895039562;19.21856883981926;16.804906581609988;15.898217555514494;16.45136751141817;16.25818481303237;18.213882620519925;16.99515331343813;16.92439166129364;18.680390540819793;16.432688333649505;18.734145478922123;18.031626653015802;17.101040476813708;18.794076465052875;16.869012990020046;19.01432525277344;19.20370368942909;17.510425286914632;18.25993322527753;19.29512844444772;18.470239621383687;18.504028192574403;18.977240613237264;19.24228357172442;18.514137267158166;19.41627674449577;16.666423873065096;17.989944804817238;18.18263173319903;16.96973709191637;18.797481725009643" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
<use xlink:href="#d8" x="131.47216796875" y="17.5">
+
<animate attributeName="x" dur="128s" values="131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="17.080711140751834;16.19909689599043;19.330849197546666;16.916014088414155;17.406536045303866;19.074260262355665;16.245800355520224;19.34267799195689;16.00822293662995;15.612118098267;16.903120016070773;16.936699184091;19.170577284050097;19.032777003417344;18.546243746631024;17.245711595069945;17.670745620739233;16.447078997643914;18.83411877139345;17.059644447003237;16.63861329760842;18.05122404994667;16.102314363712715;16.765402084652465;19.204710140778165;15.880181639513852;16.06879981708168;16.31738246474658;16.503920112292988;17.181590210211635;16.50070491698806;16.870759611949783;16.48592557630948;16.460353724003927;17.94241902371801;16.8458323460846;16.991156120537948;18.57126625214372;15.746753360697536;16.076169555707967;18.903294416161224;17.219109518635328;18.615213998315095;16.031173775225227;17.591960357928016;18.881495956075632;16.852166940218158;18.572714469152427;17.941504268568327;17.07829440318809;19.489404918376827;17.06921114821956;17.395173504645857;17.977942820530266;16.767355190057494;18.850555915282087;17.890144231633766;17.852002367688048;17.654345591803718;19.43973441409201;19.455720406809302;18.863165428982644;17.31830481199631;17.147159111082434;17.080711140751834" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
<use xlink:href="#d9" x="161.41357421875" y="17.5">
+
<animate attributeName="x" dur="128s" values="161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="17.599067165036384;15.684629436697106;15.933017326931099;19.48103060100494;16.012842062665598;19.2495377040299;18.21891658008112;19.160361116382305;15.80934600212031;16.723241372635748;18.691710970024175;15.535381801514223;15.923839916845994;16.902564830201552;16.192552924899463;16.087440861085202;18.17905744860602;15.86769170570407;19.3860173767723;18.097445829672445;15.699066515511975;19.094881340111694;16.466106118697553;17.425856057986543;17.735066325377616;16.054534187786356;17.50863562649949;15.741235287438158;16.29842816430794;19.174294144100713;18.788194352812756;17.591539631414015;18.22738288427816;19.00201258774057;16.059864800319716;17.468411654278867;16.027054087523638;15.966081954440476;15.932941808294345;16.3471435248239;15.712626296759733;16.3608679958098;17.016526709101825;17.99075155400056;18.934422862346082;19.116739330737822;18.370339170828384;17.528350763945213;19.16794945822426;16.15198700162683;15.92178177336796;18.771235410910514;18.008527128612236;16.341258113785702;17.009200698193677;16.689575235266634;17.22345328759322;17.21091887743478;17.092623332550826;18.69095955190943;18.746019425967766;17.74985102116934;17.39111954785501;16.63783697865552;17.599067165036384" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
</clipPath>
+
</defs>
+
<rect x="-500" y="-50" width="1000" height="100" fill="url(#d0)" clip-path="url(#d10)" />
+
<g id="scrub" visibility="hidden">
+
<path d="M-168.0,40.0 L168.0,40.0" stroke="#ccc" stroke-width="4" stroke-linecap="round" />
+
<rect x="-168.0" y="40.0" width="0" height="0.001" stroke="#05f" stroke-width="4" stroke-linejoin="round">
+
<animate attributeName="width" dur="128s" values="0;336" keyTimes="0;1" repeatCount="indefinite" fill="freeze" />
+
</rect>
+
<g id="scrub-capture" data-xmin="-168.0" data-xmax="168.0" data-totaldur="128" data-startdelay="0" data-enddelay="0" data-pauseonload="0">
+
<rect x="-170.0" y="30.0" width="340" height="20" fill="rgba(255,255,255,0)" />
+
<circle cx="-168.0" cy="40.0" r="6" fill="#05f" id="scrub-knob" visibility="hidden">
+
<animate attributeName="cx" dur="128s" values="-168.0;168.0" keyTimes="0;1" repeatCount="indefinite" fill="freeze" />
+
</circle>
+
</g>
+
<g id="scrub-play" visibility="hidden">
+
<rect x="-191.0" y="36.0" width="8" height="8" fill="#05f" stroke="#05f" stroke-width="8" stroke-linejoin="round" />
+
<path d="M-191.0,36.0 v8.0 l8.0,-4.0 Z" fill="#eee" />
+
</g>
+
<g id="scrub-pause" visibility="hidden">
+
<rect x="-191.0" y="36.0" width="8" height="8" fill="#05f" stroke="#05f" stroke-width="8" stroke-linejoin="round" />
+
<rect x="-190.0" y="36.0" width="2.0" height="8.0" fill="#eee" />
+
<rect x="-186.0" y="36.0" width="2.0" height="8.0" fill="#eee" />
+
</g>
+
</g>
+
<script>/*<![CDATA[*/
+
/* Animation playback controls generated by drawsvg */
+
/* https://github.com/cduck/drawsvg/ */
+
function svgOnLoad(event) {
+
/* Support standalone SVG or embedded in HTML or iframe */
+
if (event && event.target && event.target.ownerDocument) {
+
svgSetup(event.target.ownerDocument);
+
} else if (document && document.currentScript
+
&& document.currentScript.parentElement) {
+
svgSetup(document.currentScript.parentElement);
+
}
+
}
+
function svgSetup(doc) {
+
var svgRoot = doc.documentElement || doc;
+
var scrubCapture = doc.getElementById("scrub-capture");
+
/* Block multiple setups */
+
if (!scrubCapture || scrubCapture.getAttribute("svgSetupDone")) {
+
return;
+
}
+
scrubCapture.setAttribute("svgSetupDone", true);
+
var scrubContainer = doc.getElementById("scrub");
+
var scrubPlay = doc.getElementById("scrub-play");
+
var scrubPause = doc.getElementById("scrub-pause");
+
var scrubKnob = doc.getElementById("scrub-knob");
+
var scrubXMin = parseFloat(scrubCapture.dataset.xmin);
+
var scrubXMax = parseFloat(scrubCapture.dataset.xmax);
+
var scrubTotalDur = parseFloat(scrubCapture.dataset.totaldur);
+
var scrubStartDelay = parseFloat(scrubCapture.dataset.startdelay);
+
var scrubEndDelay = parseFloat(scrubCapture.dataset.enddelay);
+
var scrubPauseOnLoad = parseFloat(scrubCapture.dataset.pauseonload);
+
var paused = false;
+
var dragXOffset = 0;
+
var point = svgRoot.createSVGPoint();
+
+
function screenToSvgX(p) {
+
var matrix = scrubKnob.getScreenCTM().inverse();
+
point.x = p.x;
+
point.y = p.y;
+
return point.matrixTransform(matrix).x;
+
};
+
function screenToProgress(p) {
+
var matrix = scrubKnob.getScreenCTM().inverse();
+
point.x = p.x;
+
point.y = p.y;
+
var x = point.matrixTransform(matrix).x;
+
if (x <= scrubXMin) {
+
return scrubStartDelay / scrubTotalDur;
+
}
+
if (x >= scrubXMax) {
+
return (scrubTotalDur - scrubEndDelay) / scrubTotalDur;
+
}
+
return (scrubStartDelay/scrubTotalDur
+
+ (x - dragXOffset - scrubXMin)
+
/ (scrubXMax - scrubXMin)
+
* (scrubTotalDur - scrubStartDelay - scrubEndDelay)
+
/ scrubTotalDur);
+
};
+
function currentScrubX() {
+
return scrubKnob.cx.animVal.value;
+
};
+
function pause() {
+
svgRoot.pauseAnimations();
+
scrubPlay.setAttribute("visibility", "visible");
+
scrubPause.setAttribute("visibility", "hidden");
+
paused = true;
+
};
+
function play() {
+
svgRoot.unpauseAnimations();
+
scrubPause.setAttribute("visibility", "visible");
+
scrubPlay.setAttribute("visibility", "hidden");
+
paused = false;
+
};
+
function scrub(playbackFraction) {
+
var t = scrubTotalDur * playbackFraction;
+
/* Stop 10ms before end to avoid loop (>=1ms needed on FF) */
+
var limit = scrubTotalDur - 10e-3;
+
if (t < 0) t = 0;
+
else if (t > limit) t = limit;
+
svgRoot.setCurrentTime(t);
+
};
+
function mousedown(e) {
+
svgRoot.pauseAnimations();
+
if (e.target == scrubKnob) {
+
dragXOffset = screenToSvgX(e) - currentScrubX();
+
} else {
+
dragXOffset = 0;
+
}
+
scrub(screenToProgress(e));
+
/* Global document listeners */
+
document.addEventListener('mousemove', mousemove);
+
document.addEventListener('mouseup', mouseup);
+
e.preventDefault();
+
};
+
function mouseup(e) {
+
dragXOffset = 0;
+
document.removeEventListener('mousemove', mousemove);
+
document.removeEventListener('mouseup', mouseup);
+
if (!paused) {
+
svgRoot.unpauseAnimations();
+
}
+
e.preventDefault();
+
};
+
function mousemove(e) {
+
scrub(screenToProgress(e));
+
};
+
scrubPause.addEventListener("click", pause);
+
scrubPlay.addEventListener("click", play);
+
scrubCapture.addEventListener("mousedown", mousedown);
+
scrubContainer.setAttribute("visibility", "visible");
+
scrubKnob.setAttribute("visibility", "visible");
+
if (scrubPauseOnLoad) {
+
pause();
+
scrub(0);
+
} else {
+
play();
+
}
+
};
+
svgOnLoad();
+
/*]]>*/</script>
+
</svg>
+
</body>
+
</html>
+362
examples/logo.ipynb
···
+
{
+
"cells": [
+
{
+
"cell_type": "code",
+
"execution_count": 1,
+
"id": "84fdd538",
+
"metadata": {},
+
"outputs": [
+
{
+
"data": {
+
"text/html": [
+
"<iframe src=\"data:text/html;base64,PCFET0NUWVBFIGh0bWw+CjxoZWFkPgo8bWV0YSBjaGFyc2V0PSJ1dGYtOCI+CjxzdHlsZT4KaHRtbCxib2R5IHsKICBtYXJnaW46IDA7CiAgaGVpZ2h0OiAxMDAlOwp9CnN2ZyB7CiAgbWFyZ2luLWJvdHRvbTogLTUwLjBweDsKfQo8L3N0eWxlPjwvaGVhZD4KPGJvZHk+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKICAgIHdpZHRoPSI0MDAiIGhlaWdodD0iMTAwIiB2aWV3Qm94PSItMjAwLjAgLTUwLjAgNDAwIDEwMCIgb25sb2FkPSJzdmdPbkxvYWQoZXZlbnQpOyI+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgeDE9Ii0xMjUwIiB5MT0iMCIgeDI9IjEyNTAiIHkyPSIwIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgaWQ9ImQwIj4KPHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjNTU0NGVlIiBzdG9wLW9wYWNpdHk9IjEiIC8+CjxzdG9wIG9mZnNldD0iMC4yIiBzdG9wLWNvbG9yPSIjNTU0NGVlIiBzdG9wLW9wYWNpdHk9IjEiIC8+CjxzdG9wIG9mZnNldD0iMC40IiBzdG9wLWNvbG9yPSIjZWUwMDU1IiBzdG9wLW9wYWNpdHk9IjEiIC8+CjxzdG9wIG9mZnNldD0iMC42IiBzdG9wLWNvbG9yPSIjZWUwMDU1IiBzdG9wLW9wYWNpdHk9IjEiIC8+CjxzdG9wIG9mZnNldD0iMC44IiBzdG9wLWNvbG9yPSIjNTU0NGVlIiBzdG9wLW9wYWNpdHk9IjEiIC8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzU1NDRlZSIgc3RvcC1vcGFjaXR5PSIxIiAvPgo8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ4MSIgZHVyPSIxMjhzIiB2YWx1ZXM9Ii0yMjUwOy0yNTAiIGtleVRpbWVzPSIwOzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBmaWxsPSJmcmVlemUiIC8+CjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9IngyIiBkdXI9IjEyOHMiIHZhbHVlcz0iMjUwOzIyNTAiIGtleVRpbWVzPSIwOzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBmaWxsPSJmcmVlemUiIC8+CjwvbGluZWFyR3JhZGllbnQ+CjxwYXRoIGQ9Ik04NSA0MzhxLTEgMjEgLTEgNDJxMCA1NSA4IDExM3ExMCA4MCA0NS41IDE1Ni41dDkzLjUgMTMxLjVxNTEgNDkgMTExLjUgODEuNXQxMTUuNSA0NS41cTU2IDE0IDExMSAxOHEyNiAyIDUwIDJxMjUgMCA0OSAtM3E0NCAtNSA3OS41IC0xMXQ1NC41IC0xMmwxOSAtN3EtMSA3IC0zIDE5LjV0LTUuNSA1MHQtNC41IDczLjV2MTdxMCAzMSAyIDY5cTQgNTAgMTIuNSA5Mi41dDI4IDg2dDQ3LjUgNzR0NzIuNSA1MHQxMDAuNSAxOS41IHE2MyAwIDExNS41IC0yMXQ4Ni41IC01NHQ2MCAtNzN0MzkuNSAtODAuNXQyMiAtNzMuNXQxMC41IC01NGwyIC0yMXEyIC0xNiA0LjUgLTQzLjV0NS41IC0xMTEuNXEyIC00MiAxIC04NHEwIC00MCAtMSAtODJxLTMgLTgyIC0xNy41IC0xOTR0LTQxLjUgLTIwOHE1NyAtMjcgODkgLTc3dDM1IC0xMDV2LTE1cTAgLTQ3IC0xNiAtOTVxLTE4IC01NCAtNTUgLTkzcS0yOCAtMzAgLTYzIC00N3EtMzYgLTE3IC02OCAtMjBxLTE1IC0xIC0zMCAtMSBxLTE4IC0xIC0zNSAxcS0zMyA0IC02MCAxM3QtNDguNSAxOHQtMzMuNSAxNWwtMTIgN3EtMTU5IC04MyAtMzcxIC05MXEtMTkgLTEgLTM4IDBxLTE4NyAwIC0zMjQgNjlxLTcgNCAtMTkgMTF0LTQ1IDMydC02MC41IDU0dC01NiA3NnQtNDEuNSA5OXEtMTcgNjcgLTIxIDE0MXpNNzI5IDQ4M3E1IC03NSA4MiAtNzVxMzUgMCA1Ny41IDIwLjV0MjIuNSA1NC41dC0yMiA2MHQtNTggMjZxLTM1IC0xIC02MCAtMjdxLTIyIC0yNCAtMjIgLTUydi03eiIgdHJhbnNmb3JtPSJzY2FsZSgwLjAzNDE3OTY4NzUsLTAuMDM0MTc5Njg3NSkiIGlkPSJkMSIgLz4KPHBhdGggZD0iTTQ5IDU0OHEwIDgzIDMuNSAxNTQuNXQ5LjUgMTEyLjVsNiA0MXExNiA3MyA1NCAxMjV0ODAuNSA3NC41dDgzIDM0LjV0NjYuNSAxMmgyN2gxM3E1NiAwIDEwMiAtMTZxNTIgLTE4IDc5LjUgLTQ1LjV0NDYgLTU1LjV0MjQuNSAtNDdsNSAtMjBxMzkgNTYgOTAuNSA5MS41dDk2LjUgNDZ0ODQuNSAxMi41dDYyLjUgLTJsMjMgLTVxNTggLTE1IDk5LjUgLTQ2dDYwLjUgLTcwdDI3IC03MXQ5IC02NXYtMTRxMCAtNDEgLTEwIC03OSBxLTEyIC00NCAtMzAuNSAtNzF0LTM3IC00N3QtMzIuNSAtMjhsLTEzIC05cS00OSAtMjkgLTEwMyAtMzVxLTIwIC0yIC0zOCAtMnEtMzAgMCAtNTggN3EtNDIgMTEgLTc4LjUgMjYuNXQtNTYuNSAyOC41bC0yMCAxNHExMyAtOTkgMTMgLTE3OHEwIC0zNCAtMiAtNjRxLTggLTEwMiAtMzYgLTE2OC41dC02NS41IC0xMDh0LTc5IC02MS41dC03NiAtMjcuNXQtNTcuNSAtNi41aC0yM2gtOXEtNDIgMCAtODAgOXEtNDIgMTAgLTcxIDI3LjV0LTUzIDM5IHQtMzkgNDN0LTI1IDM5LjV0LTE0IDI5bC00IDExcS0yNyA4MiAtNDEgMTgxLjV0LTE0IDE4Mi41eiIgdHJhbnNmb3JtPSJzY2FsZSgwLjAzNDE3OTY4NzUsLTAuMDM0MTc5Njg3NSkiIGlkPSJkMiIgLz4KPHBhdGggZD0iTTU5IDQ5NnEwIDIyIDUgNzRxNSA1OCAxNy41IDExMXQ0My41IDExOXQ3NiAxMTZ0MTIxLjUgODh0MTczLjUgNDdxMzEgMyA2MCAzcTQ4IDAgODggLThxNjYgLTEzIDEwMyAtMzR0NjQgLTUwdDM1IC00NC41dDEyIC0yNi41cTIgOCA3LjUgMjJ0MjMuNSA0Mi41dDQyIDUxdDY3IDQwLjV0OTQgMThxNjIgMCAxMTIuNSAtMjl0ODIgLTc1dDU0LjUgLTEwMS41dDMzLjUgLTExMXQxNi41IC0xMDEuNXQ3IC03NWwxIC0yOXEyIC00MiAyIC04MSBxMCAtNTQgLTQgLTEwM3EtNyAtODQgLTIyLjUgLTE0MXQtMzcuNSAtMTAydC00Ny41IC03MS41dC01MSAtNDQuNXQtNDkuNSAtMjV0LTQyLjUgLTEwLjV0LTI5LjUgLTIuNWwtMTEgMXEtMSAyIC0xMS41IDB0LTI5LjUgMnQtNDEuNSA4LjV0LTQ2LjUgMTkuNXQtNDUgMzQuNXQtMzcuNSA1M3QtMjIuNSA3NS41cS0yIC05IC04IC0yMy41dC0zNSAtNTAuNXQtNzEgLTYycS00MiAtMjUgLTEyMiAtNDBxLTQ1IC04IC05NSAtOHEtNDEgMCAtODYgNSBxLTggMCAtMjIuNSAxLjV0LTU1IDEyLjV0LTc4IDI4LjV0LTgzIDU0LjV0LTc4IDg1LjV0LTU1IDEyN3QtMjIuNSAxNzQuNXEtMiAxNyAtMiAzNXpNNjg2IDQ4M3E1IC03NSA4MiAtNzVxMzUgMCA1Ny41IDIwLjV0MjIuNSA1NC41dC0yMiA2MHQtNTggMjZxLTM1IC0xIC02MCAtMjdxLTIyIC0yNCAtMjIgLTUydi03eiIgdHJhbnNmb3JtPSJzY2FsZSgwLjAzNDE3OTY4NzUsLTAuMDM0MTc5Njg3NSkiIGlkPSJkMyIgLz4KPHBhdGggZD0iTTM1IDczNXEtMiAyMyAtMiA0NHEwIDQzIDggNzlxMTIgNTYgMzUgOTAuNXQ1Mi41IDYwLjV0NTkuNSAzOC41dDU1LjUgMTkuNXQ0MS41IDlsMTYgMXE2OSAwIDEyNS41IC0xOS41dDk0IC01MHQ2Ni41IC03NHQ0NSAtODMuNXQyNiAtODYuNXQxMy41IC03NnQ0LjUgLTU4LjVxMiA1OSAyMiAxMDF0NDcgNjB0NTQgMjh0NDYgMTBsMTkgMXE0NCAwIDc5LjUgLTEzLjV0NTYuNSAtMzR0MzYuNSAtNDUuNXQyMS41IC01MHQ5LjUgLTQ1LjUgdDMuNSAtMzMuNXYtMTNxMCA4MSAxMSAxNDkuNXQzMCAxMTUuNXQ0NCA4NXQ1MiA2MHQ1NCAzOHQ1MiAyMi41dDQ0IDEwdDMwIDMuNWwxMSAtMXE2MyAtMSAxMTMuNSAtMjB0ODAuNSAtNDguNXQ1MSAtNjV0MjkuNSAtNzAuNXQxMyAtNjQuNXQzLjUgLTQ3LjV2LTE4cS0zIC05NiAtMTggLTE4Ny41dC0zNC41IC0xNTMuNXQtMzggLTExMHQtMzEuNSAtNzBsLTEyIC0yM3EtMzYgLTY2IC04NC41IC0xMTMuNXQtOTEuNSAtNjguNXQtODAuNSAtMzIgdC01OS41IC0xMWwtMjMgLTFxLTkyIDAgLTE2MS41IDI2LjV0LTEwNSA2MS41dC01NyA3OXQtMjYuNSA3MXQtNSA0NXEtMyAtMjIgLTEwIC00OHQtMzEgLTcxdC01OSAtNzkuNXQtOTkuNSAtNjAuNXQtMTQ2LjUgLTI2cS02NCAwIC0xMjEuNSAyNC41dC05NCA1OS41dC02NC41IDcwdC00MCA1OWwtMTIgMjVxLTYwIDExNSAtOTcgMjQ2LjV0LTQ1IDIwNS41eiIgdHJhbnNmb3JtPSJzY2FsZSgwLjAzNDE3OTY4NzUsLTAuMDM0MTc5Njg3NSkiIGlkPSJkNCIgLz4KPHBhdGggZD0iTTcwIDE2OXYxNXYxMHEwIDUwIDE4IDkwcTIwIDQ0IDU0LjUgNzJ0NzYuNSA0OC41dDg0IDI5LjV0NzcgMTQuNXQ1NyA1LjVsMjIgMXEtNzUgMTggLTEzNC41IDQ0dC05NS41IDUzdC02MS41IDU2LjV0LTM2LjUgNTV0LTE2LjUgNDZ0LTUuNSAzMi41djEycTMgNzEgMzQgMTI4dDc3IDkxdDEwMS41IDU5dDExMC41IDM1LjV0MTAwIDE2LjV0NzMgNmgyOHExMTIgLTIgMTk0IC0xNi41dDEzMS41IC0zNnQ3OC41IC01My41dDM5IC02NiBxOCAtMjggNyAtNjF2LTE1cS0yIC01MCAtMjcuNSAtODh0LTY0LjUgLTU4dC04NS41IC0zMi41dC05MiAtMTV0LTgzLjUgLTIuNXQtNjIgMmwtMjMgM3E5NyAtMTIgMTc0LjUgLTM3LjV0MTIzIC01Ni41dDc4IC02NnQ0NiAtNjcuNXQyMCAtNTguNXQ1LjUgLTQydi0xNnEwIC03MyAtMjQuNSAtMTMzdC02NCAtOTcuNXQtODcuNSAtNjUuNXQtOTUuNSAtNDEuNXQtODcgLTIxLjV0LTYzLjUgLTEwbC0yNSAtMXEtMzkgLTIgLTc1IC0yIHEtODAgMCAtMTQ4IDhxLTk4IDExIC0xNTYuNSAzMi41dC0xMDAuNSA0OC41dC01OS41IDU1dC0yNyA1MS41dC04LjUgMzguNXoiIHRyYW5zZm9ybT0ic2NhbGUoMC4wMzQxNzk2ODc1LC0wLjAzNDE3OTY4NzUpIiBpZD0iZDUiIC8+CjxwYXRoIGQ9Ik0yMyA3NzNxMCA2NSAxOC41IDExOC41dDcwIDk3LjV0MTMyLjUgNjBxMzAgNiA1OCA2cTM2IDEgNzAgLTlxNjAgLTE2IDEwMS41IC01My41dDc2IC04NS41dDU1IC05OHQzNC41IC05MnQxOSAtNjlsNiAtMjdxMiAyMSA3IDU1dDMzLjUgMTE4dDcwLjUgMTQ0dDEyNSA5OHE1MiAyNCAxMTMgMjRxMzcgMCA3NyAtOXE3MSAtMTYgMTE1IC01NS41dDU4LjUgLTkwLjV0MTUuNSAtMTA5di04cTAgLTU0IC0xMiAtMTA2cS0xMyAtNTYgLTI3IC0xMDAgdC0yNiAtNzJsLTEyIC0yN3EtNCAtMTAgLTEyLjUgLTI4dC0zNyAtNjkuNXQtNTkuNSAtOTh0LTgwIC0xMDR0LTk5LjUgLTk4LjV0LTExNi41IC02OXQtMTMzIC0yOHEtOTggMCAtMTg5IDQ2dC0xNTQuNSAxMTB0LTExOS41IDE0OC41dC04NSAxNDV0LTUwIDExNS41cS0xOSA0OCAtMzEgMTAxLjV0LTEyIDExOC41eiIgdHJhbnNmb3JtPSJzY2FsZSgwLjAzNDE3OTY4NzUsLTAuMDM0MTc5Njg3NSkiIGlkPSJkNiIgLz4KPHBhdGggZD0iTTg1IDM5MHEtMTEgNjEgLTExIDExN3EwIDkwIDI4IDE2N3EyMSA1NyA1MSAxMDguNXQ1OC41IDgzdDU0IDU1LjV0NDAuNSAzNGwxNiAxMHExMjkgNzcgMjg4IDg2cTI2IDEgNTEgMXExMzEgMCAyNDYgLTQwcTEzNyAtNDcgMjI4IC0xMzNxNjMgLTU5IDEwNi41IC0xNDAuNXQ2NCAtMTY0dDMwLjUgLTE2OC41cTcgLTY0IDcgLTEyM3EwIC0xOCAtMSAtMzZxLTMgLTc0IC04IC0xMzN0LTExIC05MmwtNiAtMzQgcS0yMSAtMTE5IC02NC41IC0yMTMuNXQtOTkuNSAtMTUzLjV0LTEyMi41IC0xMDF0LTEzNSAtNjB0LTEzNS41IC0yN3EtNDYgLTYgLTg4IC02cS0xOCAwIC0zNiAxcS01OSA0IC0xMDMuNSA5dC03MC41IDExbC0yNiA3cS03NiAyNyAtMTIwIDY4dC01NCA4MXEtNyAzMiAtNyA2MnYxMnEyIDM2IDExIDU4bDEwIDIycTE3IDM3IDQ4IDYxdDY0IDMydDY0IDExdDUwIDFsMjAgLTJxNjggMyAxMTYuNSAyMHQ2OSAzOXQzMS41IDQ0dDExIDM3djE1IHEtMTQgLTQgLTM5IC04LjV0LTk2IC04LjVxLTI0IC0xIC00NyAtMXEtNDYgMCAtODggNXEtNjUgOCAtMTQzLjUgNDEuNXQtMTMzLjUgOTIuNXEtODkgOTQgLTExOCAyNTN6TTY0MSA0NDRxNSAtNzUgODIgLTc1cTM1IDAgNTcuNSAyMC41dDIyLjUgNTQuNXQtMjIgNjB0LTU4IDI2cS0zNSAwIC02MCAtMjZxLTIyIC0yNCAtMjIgLTUzdi03eiIgdHJhbnNmb3JtPSJzY2FsZSgwLjAzNDE3OTY4NzUsLTAuMDM0MTc5Njg3NSkiIGlkPSJkNyIgLz4KPHBhdGggZD0iIiB0cmFuc2Zvcm09InNjYWxlKDAuMDM0MTc5Njg3NSwtMC4wMzQxNzk2ODc1KSIgaWQ9ImQ4IiAvPgo8cGF0aCBkPSJNNTkgMTEyNnEtMiA1NiAxOC41IDEwNC41dDU2IDgwdDc5IDU3dDg3LjUgMzkuNXQ4MC41IDIzLjV0NTkuNSAxMi41bDIzIDNxNzggNiAxNDcgLTEuNXQxMTcgLTI0LjV0ODcuNSAtMzl0NjMuNSAtNDUuNXQ0MC41IC00My41dDIzLjUgLTMybDYgLTEzcTM5IC03MyA1MiAtMTUydDIgLTE0OHQtMzMuNSAtMTM1LjV0LTUyLjUgLTExOHQtNTcgLTkxLjV0LTQ1IC02MWwtMTkgLTIxaDE5dDQ2LjUgLTQuNXQ2NiAtMTMuNXQ2OS41IC0yOS41IHQ2NSAtNDkuNXQ0NSAtNzYuNXQxNiAtMTA2LjVxLTEgLTQ1IC0yMiAtODIuNXQtNTUgLTYzdC03Ny41IC00NC41dC05MS41IC0zMHQtOTUuNSAtMTh0LTkwLjUgLTl0LTc2LjUgLTIuNXQtNTMuNSAwLjVsLTE5IDFxLTcwIDAgLTEzMCA4LjV0LTEwMyAyMi41dC03OCAzMnQtNTcuNSAzOHQtMzkgNDB0LTI1IDM4dC0xMy41IDMydC02IDIybC0xIDhxLTggNzEgMy41IDEyN3Q1MS41IDEwN3Q2OSA3OHQ5NyA4MXExNCAxMiAyMiAxOC41dDIxIDE2LjUgdDIzIDE5cTMwIDI1IDUzLjUgNTMuNXQzOS41IDYwdDE1LjUgNjQuNXQtMjAuNSA2MHQtNDggNDAuNXQtNTQgMTN0LTQ5LjUgLTR0LTM3LjUgLTkuNWwtMTQgLTZxLTUxIC0xMCAtOTAuNSA0dC01OS41IDQxLjV0LTMyLjUgNTYuNXQtMTUuNSA1MHoiIHRyYW5zZm9ybT0ic2NhbGUoMC4wMzQxNzk2ODc1LC0wLjAzNDE3OTY4NzUpIiBpZD0iZDkiIC8+CjxjbGlwUGF0aCBpZD0iZDEwIj4KPHVzZSB4bGluazpocmVmPSIjZDEiIHg9Ii0yMDIuMTIxNTgyMDMxMjUiIHk9IjE3LjUiPgo8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ4IiBkdXI9IjEyOHMiIHZhbHVlcz0iLTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1Oy0yMDIuMTIxNTgyMDMxMjU7LTIwMi4xMjE1ODIwMzEyNTstMjAyLjEyMTU4MjAzMTI1IiBrZXlUaW1lcz0iMDswLjAxNjswLjAzMTswLjA0NzswLjA2MjswLjA3ODswLjA5NDswLjEwOTswLjEyNTswLjE0MTswLjE1NjswLjE3MjswLjE4ODswLjIwMzswLjIxOTswLjIzNDswLjI1OzAuMjY2OzAuMjgxOzAuMjk3OzAuMzEyOzAuMzI4OzAuMzQ0OzAuMzU5OzAuMzc1OzAuMzkxOzAuNDA2OzAuNDIyOzAuNDM4OzAuNDUzOzAuNDY5OzAuNDg0OzAuNTswLjUxNjswLjUzMTswLjU0NzswLjU2MjswLjU3ODswLjU5NDswLjYwOTswLjYyNTswLjY0MTswLjY1NjswLjY3MjswLjY4ODswLjcwMzswLjcxOTswLjczNDswLjc1OzAuNzY2OzAuNzgxOzAuNzk3OzAuODEyOzAuODI4OzAuODQ0OzAuODU5OzAuODc1OzAuODkxOzAuOTA2OzAuOTIyOzAuOTM4OzAuOTUzOzAuOTY5OzAuOTg0OzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBmaWxsPSJmcmVlemUiIGNhbGNNb2RlPSJzcGxpbmUiIGtleVNwbGluZXM9IjAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxIiAvPgo8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ5IiBkdXI9IjEyOHMiIHZhbHVlcz0iMTkuMzI0MTM3MDg3NTU3OzE5LjI5MTMwOTk0ODIzNzM5ODsxNS43MjYyMDU0NzA5MDcyMzU7MTUuODM5NDg3OTgwNjM1Njg3OzE4Ljg0MTk5NTUxMjUxNzc5ODsxOC40NDM4Nzk5NTYyNzQwOTI7MTguMTc4OTIxNjA1NzYwODg0OzE2LjczMjU0NTgzMDM1NjU3ODsxNy45MjM3NzY2NjI3MTM4NTsxNy45MjcyMDY5MzQ1NjMzNTsxNy44MjQ4MTYwNjg0NDgwMTI7MTYuMTMzNTMxNDgxMDE5MjIyOzE3LjIyMjY3ODU2MTE2NTA3NTsxNy4wNzQxMjcyODA4MjE0ODY7MTguMzkyMDQ4MzI0OTQ5ODY7MTkuNDc5Mjc4MjUxNzk4OTc7MTkuMjk3NTgxODkyMzcyOTc1OzE3LjY3NjcwODE4OTcxNzI4OzE3LjI3OTQxNjc1NDkwMzQxNjsxNi41NzI5NjI5NjY1OTczMTM7MTUuNjQzNjk3MzE3NTcxNDM7MTUuNjA5Nzc5NDI4MzYzMjc1OzE3LjM1OTU3NTQ0ODM4OTI0NzsxNi43NzM4NjA1MTE0MTQ3MTsxNy4wMjAwNTk2ODc2MDI4NDY7MTkuMDY3MTU3ODMxMzEzMTU7MTcuNjAzMDExMDc2NTg0MTE0OzE3Ljc0MjA0MTQ0NDEwNTk5NzsxNi40NDQ0OTM2Mjg0NjAyNDc7MTUuNTk1NDMyMzE2NTYzMTI4OzE2LjgwMDU3MTcxNTA0NDY0OzE2LjA0Njc4OTU3MTk0NTg2NTsxNy41NDA4OTUzODMzNDg4MDQ7MTkuNDk0NzM0MjcyNzcwMjE7MTguMTk3OTE4Nzg5MzgzNDg7MTYuMjI3MzczOTg3MjkyNTc3OzE5LjA3NDI4NjE0NjMzMTk1NDsxOC42ODcwMzk2ODU2ODY1NTc7MTguNDM3NjA2NzY3NTc1OTE7MTkuMTI2Mzc0NTk5NTkwMjQzOzE4LjU1MTU0MTkzNTMzMjI4NTsxOC42NTg5OTA1NDk4NDcwNTQ7MTYuOTE1MTQ3OTExMzY2NDE0OzE5LjQyMzkwNjI5MjI4ODUwNzsxOS4zNDc2MDM3NTE1OTI5MDM7MTYuMTQ0NzM4NjEzMjE2MDc3OzE4LjUxNjAxNjI4NjYwNzQ5OzE4LjM2MDYwMzU5Mjk0OTgxNTsxNy4zNDU2MjY3OTA5Njc5MTsxNy42MjE0MjI4NjQ0OTM3ODsxNy40NjAwNTU2ODc0MDA3NjQ7MTkuMTk5MzI4Mjg4Mzc4MjgzOzE3LjUwMzM2NDI1MDUyMjYyOzE4LjgyNjA5Nzk1OTE2NzI1OzE2LjkxNTY5NjgxOTQ3NDg2NDsxOS4wMzE0MDM2NzQzMjUwMTM7MTkuMDk4ODAyMzU1MDI2NTAzOzE3LjM0NDA0ODY1OTUyNjU1OzE3Ljc3MDgyMDI4MTY4MDk3NjsxOS4xODEzMjE3NTY3Njc3MTY7MTguMzk1MDkxODE1NDg4MDcyOzE3LjQ0NjQzNDIxOTQ0NjM0OzE2LjM4NzI0NDA0Mzk2NDA0MjsxNi43OTg2Njg5NzUwNzU1OTM7MTkuMzI0MTM3MDg3NTU3IiBrZXlUaW1lcz0iMDswLjAxNjswLjAzMTswLjA0NzswLjA2MjswLjA3ODswLjA5NDswLjEwOTswLjEyNTswLjE0MTswLjE1NjswLjE3MjswLjE4ODswLjIwMzswLjIxOTswLjIzNDswLjI1OzAuMjY2OzAuMjgxOzAuMjk3OzAuMzEyOzAuMzI4OzAuMzQ0OzAuMzU5OzAuMzc1OzAuMzkxOzAuNDA2OzAuNDIyOzAuNDM4OzAuNDUzOzAuNDY5OzAuNDg0OzAuNTswLjUxNjswLjUzMTswLjU0NzswLjU2MjswLjU3ODswLjU5NDswLjYwOTswLjYyNTswLjY0MTswLjY1NjswLjY3MjswLjY4ODswLjcwMzswLjcxOTswLjczNDswLjc1OzAuNzY2OzAuNzgxOzAuNzk3OzAuODEyOzAuODI4OzAuODQ0OzAuODU5OzAuODc1OzAuODkxOzAuOTA2OzAuOTIyOzAuOTM4OzAuOTUzOzAuOTY5OzAuOTg0OzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBmaWxsPSJmcmVlemUiIGNhbGNNb2RlPSJzcGxpbmUiIGtleVNwbGluZXM9IjAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxIiAvPgo8L3VzZT4KPHVzZSB4bGluazpocmVmPSIjZDIiIHg9Ii0xNDkuNzkyNDgwNDY4NzUiIHk9IjE3LjUiPgo8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ4IiBkdXI9IjEyOHMiIHZhbHVlcz0iLTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1Oy0xNDkuNzkyNDgwNDY4NzU7LTE0OS43OTI0ODA0Njg3NTstMTQ5Ljc5MjQ4MDQ2ODc1IiBrZXlUaW1lcz0iMDswLjAxNjswLjAzMTswLjA0NzswLjA2MjswLjA3ODswLjA5NDswLjEwOTswLjEyNTswLjE0MTswLjE1NjswLjE3MjswLjE4ODswLjIwMzswLjIxOTswLjIzNDswLjI1OzAuMjY2OzAuMjgxOzAuMjk3OzAuMzEyOzAuMzI4OzAuMzQ0OzAuMzU5OzAuMzc1OzAuMzkxOzAuNDA2OzAuNDIyOzAuNDM4OzAuNDUzOzAuNDY5OzAuNDg0OzAuNTswLjUxNjswLjUzMTswLjU0NzswLjU2MjswLjU3ODswLjU5NDswLjYwOTswLjYyNTswLjY0MTswLjY1NjswLjY3MjswLjY4ODswLjcwMzswLjcxOTswLjczNDswLjc1OzAuNzY2OzAuNzgxOzAuNzk3OzAuODEyOzAuODI4OzAuODQ0OzAuODU5OzAuODc1OzAuODkxOzAuOTA2OzAuOTIyOzAuOTM4OzAuOTUzOzAuOTY5OzAuOTg0OzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBmaWxsPSJmcmVlemUiIGNhbGNNb2RlPSJzcGxpbmUiIGtleVNwbGluZXM9IjAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxIiAvPgo8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ5IiBkdXI9IjEyOHMiIHZhbHVlcz0iMTguMjk4Mjg2NTUyMjgwOTc0OzE2LjE2NDI3ODc0MTk3NjUwMzsxOS4xMzE3NjE5ODY1MDQzODsxNi41NzI1NTAwNTE1OTkyNjU7MTkuMTQ1NTExMzQzNDcyMTg3OzE2LjczODI1MjQ5OTc5Nzg0OzE5LjMyOTQ0Njg0NjIyNDYzOzE4LjMyNDgyMzIyNTQ3MDQxODsxNy41MTY5OTUyNjc5MzMyNzsxNy41NzA5OTEwMjQ1OTQyMDc7MTguMTA1NjU3NTk1ODY3MTY0OzE3Ljg1MTc3ODg0NzE3Nzk0OzE2Ljc0NzM3NzI5ODIwNDsxNi4zMzEyNzM4OTgxNTE3MDc7MTcuNTQ3NTY2NjMzNDIxMTM7MTkuMjM2NjE3NDM2NTM1MTEzOzE3Ljk5MzA2MDM0NjkwMzQ5OzE1LjgwMTUwMTQ3NjI5NjE4MTsxOC43ODE1OTk5Nzg4NDgwNjc7MTguNDAzNzk3MTQ5OTA5MTk7MTkuMTMwNjE0NDgzODA1Mjg7MTYuMjY1NjEwOTMzMjE2NDcyOzE4LjQ3OTEzMDg5NzEwOTQxNzsxNS43MzUwMzU1ODU1OTQ2MjM7MTguMTExNjM5NzA5NzM4MjsxNi41OTIzOTg5MjkzNDg1OTc7MTYuNDA2NDY2MTE2OTc5MDU0OzE5LjAwMTk2NDY4NTc5Mjk1MjsxNS45MjUwNjM5MzA1ODIxOzE3LjU4OTQ1MDY2MTQzNTcyOzE4LjkxNTc3MjAyODczOTQ5OzE2LjQ3OTMyNzkxMTg3NjA3OzE2LjM0MTkxNTc1NDc4MjU4NzsxOS4wMjIzMjcwMzc0NjUxMTg7MTcuMTkxNjcwNTkzNTU4NzgzOzE4LjM2Nzg0NDM5NTYxOTk7MTUuNjI3NDkyMjgwNTA2OTgxOzE2Ljk0OTQyNzY0NTIxMjk2NDsxNi4xODc1MjM5Njg1MDI5MzsxOC4xOTEwNjE3NjU2NTQxNzsxNS44MzE2MTI3MDk2MTgzMDI7MTkuMzE4MjQ4NjYxMzgyOTk7MTUuNjAxMzc4ODU5MzA3NjA1OzE4LjQxNzY5NDAyOTc2NzIxNTsxNS41ODQ1Nzk0Nzg4OTI2MDQ7MTYuNTIyNzYwMjE2MjI5Nzc7MTguNzUzNDE3NTQ5NjA5MTA0OzE2LjEyODQ3MzE1NDc0NzE7MTYuMjM0OTU1MjM3MDAzOTA2OzE4LjI2NTk4MTcwNDA1NDM2OzE3LjA0MjI2MzUyNTQxMDI0OzE1LjY3MjY0Mzk4MzE4OTk5ODsxOS40NjAwMDYxODQ4MTEyMDY7MTYuMTA1NjgwNDM1MDI2MjA1OzE1LjY0NTA3NTk3Njk3MzY1NjsxNi44NzY4MDQwMjIxNDcxODsxNy45NjA5NTc5MzMyOTk0MzsxOC40Njk4Mzg0OTI1MDMxMjsxNS45NTI0NTk2MTIwNzIzMDc7MTYuODQ4ODU1MDkyNzg2NTgyOzE1LjYyMzI0MzQzMDUwNjc1NDsxNy4yOTQ2MTMwNDk5NjkxNzU7MTguNTYzODc5NzQ2MzUyMzEzOzE4LjQ1OTc4NjY1NDg4Mjg7MTguMjk4Mjg2NTUyMjgwOTc0IiBrZXlUaW1lcz0iMDswLjAxNjswLjAzMTswLjA0NzswLjA2MjswLjA3ODswLjA5NDswLjEwOTswLjEyNTswLjE0MTswLjE1NjswLjE3MjswLjE4ODswLjIwMzswLjIxOTswLjIzNDswLjI1OzAuMjY2OzAuMjgxOzAuMjk3OzAuMzEyOzAuMzI4OzAuMzQ0OzAuMzU5OzAuMzc1OzAuMzkxOzAuNDA2OzAuNDIyOzAuNDM4OzAuNDUzOzAuNDY5OzAuNDg0OzAuNTswLjUxNjswLjUzMTswLjU0NzswLjU2MjswLjU3ODswLjU5NDswLjYwOTswLjYyNTswLjY0MTswLjY1NjswLjY3MjswLjY4ODswLjcwMzswLjcxOTswLjczNDswLjc1OzAuNzY2OzAuNzgxOzAuNzk3OzAuODEyOzAuODI4OzAuODQ0OzAuODU5OzAuODc1OzAuODkxOzAuOTA2OzAuOTIyOzAuOTM4OzAuOTUzOzAuOTY5OzAuOTg0OzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBmaWxsPSJmcmVlemUiIGNhbGNNb2RlPSJzcGxpbmUiIGtleVNwbGluZXM9IjAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxIiAvPgo8L3VzZT4KPHVzZSB4bGluazpocmVmPSIjZDMiIHg9Ii0xMDcuOTU2NTQyOTY4NzUiIHk9IjE3LjUiPgo8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ4IiBkdXI9IjEyOHMiIHZhbHVlcz0iLTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1Oy0xMDcuOTU2NTQyOTY4NzU7LTEwNy45NTY1NDI5Njg3NTstMTA3Ljk1NjU0Mjk2ODc1IiBrZXlUaW1lcz0iMDswLjAxNjswLjAzMTswLjA0NzswLjA2MjswLjA3ODswLjA5NDswLjEwOTswLjEyNTswLjE0MTswLjE1NjswLjE3MjswLjE4ODswLjIwMzswLjIxOTswLjIzNDswLjI1OzAuMjY2OzAuMjgxOzAuMjk3OzAuMzEyOzAuMzI4OzAuMzQ0OzAuMzU5OzAuMzc1OzAuMzkxOzAuNDA2OzAuNDIyOzAuNDM4OzAuNDUzOzAuNDY5OzAuNDg0OzAuNTswLjUxNjswLjUzMTswLjU0NzswLjU2MjswLjU3ODswLjU5NDswLjYwOTswLjYyNTswLjY0MTswLjY1NjswLjY3MjswLjY4ODswLjcwMzswLjcxOTswLjczNDswLjc1OzAuNzY2OzAuNzgxOzAuNzk3OzAuODEyOzAuODI4OzAuODQ0OzAuODU5OzAuODc1OzAuODkxOzAuOTA2OzAuOTIyOzAuOTM4OzAuOTUzOzAuOTY5OzAuOTg0OzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBmaWxsPSJmcmVlemUiIGNhbGNNb2RlPSJzcGxpbmUiIGtleVNwbGluZXM9IjAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxIiAvPgo8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ5IiBkdXI9IjEyOHMiIHZhbHVlcz0iMTkuMTA4MDgwNjMzNDAwMTQ2OzE4LjUyMjY0ODYxNDcwMDI2OzE4Ljk0OTc4MzEwNTMwMTcxMjsxOC4zMjEzODA1NjAyMDMwNjsxNy4zOTExMTgwNDgzOTMyNTM7MTYuNDAyMTEwMjgxMzkxNTU7MTguMTQzMzEzOTk0NjI4MDU1OzE2Ljc2NTIyMzcwNjkxMzAzNzsxNS45MDgxOTY0MjAxOTg3OTM7MTcuMjkxMjg3NDQ1Njc2NjA1OzE4Ljk5OTA1MjE2NTM5MTg3OzE2LjAxMDE0NTg1ODQ3NTE1MjsxNy44Mzk4MjI3OTI0NDI0MzsxNy4wNzE4MTAyMDA0MjA2NzsxNy41NTkyMTA3ODcwNTY3MTsxNi4wNzUzMTc4NTU5MDc3Nzc7MTkuMzM4OTI0NzQ1OTQ4NjQyOzE2LjUzNjM4NTY5MzAwODgyOzE3LjkyNDMxMTc1NjIxNDAxOzE3LjE3OTAyMjE4MjYyMjk4NzsxNS41NzIxMzI4NzY0OTk3MDY7MTcuNzMxODAwNDk1MzU2MzI7MTYuMDYyMjc3NTE1ODQ1MzM4OzE1LjcyNzEyMzk4MzI5Mjc4NTsxNS42MzQyMjQ5ODQ5OTQ1ODk7MTYuMTQ0NjYwMDYwNzI0OTM4OzE1Ljg4MzQ4Nzc3NDUzMjczMjsxOC4wNDAzMDI3OTAxNTQ2NzsxNy41MzMwMzY3MzYxNDI1NzQ7MTkuNDMzODY0Mzc2MTkwMTE7MTkuMjM2NTIxMjc0Nzg3MjI2OzE5LjQ3ODEwMDkzMzAzODAxNDsxNi40Mjk4OTUzNjI2NzEyNDsxNy4yNzg3ODk4MjA0Nzg3ODU7MTYuNTAzMTIzMDQ3MzY0NjI4OzE3Ljg2NDk0OTM4MjE5MTk5OzE3Ljk5NjY1NjIwMzAxOTEzOzE4LjcwMDgyOTgyMjUzNzU4NTsxOC4zMzc5OTMyMTUzMTczOTc7MTYuNTI2NDM3MTUzOTUyMTQ7MTcuMTkyMDY3NjkxMDUwMDg0OzE3LjYwNDc1OTc3NTEyMjM0OzE1LjUxOTI5OTEyNDIxNDUxMzsxNS42NDE5OTc2NDY4MzEwNTg7MTcuMTM0OTA1NjcwODg4Mjk4OzE1Ljk0NDY5OTg2ODkxOTkwNTsxOC4zOTUwNzg2OTE0NzcyMjsxNi40NjM0NjIwNTc3NjY2NTsxNS44OTkwOTIzNDcwMTk5NjQ7MTYuMjI3MDQwMzEzMjAxNzsxNi40MjYxMDE3MTc1MDg1NjI7MTYuMzY5NDE0NTM4ODczNDM2OzE3LjU4Mjk0NTQ1NjE5MTU4ODsxNy4zNTc2MTI0NDQ1NzUzNjsxNi43Mzg5MDQyODM1MDk2NTsxOC4wNjcwMzUwMzA3MjI3OzE2LjM0OTc5ODk2Nzk3Nzk2OzE5LjEyNjI1MDcwNjkyNjczOzE5LjM1MjQ2NjYxOTMxNjk1OzE4LjQxNTcyNDE4MjMxMDA5NzsxNy4yMzQ5MzU0NzUxNTg2NjsxNy41NDYwMDUzNjg4NjgxOTsxNy44MjQzMDUyMjQxNjA2NDsxNS43MDQ5Mzg5NzQ0MjMzMjE7MTkuMTA4MDgwNjMzNDAwMTQ2IiBrZXlUaW1lcz0iMDswLjAxNjswLjAzMTswLjA0NzswLjA2MjswLjA3ODswLjA5NDswLjEwOTswLjEyNTswLjE0MTswLjE1NjswLjE3MjswLjE4ODswLjIwMzswLjIxOTswLjIzNDswLjI1OzAuMjY2OzAuMjgxOzAuMjk3OzAuMzEyOzAuMzI4OzAuMzQ0OzAuMzU5OzAuMzc1OzAuMzkxOzAuNDA2OzAuNDIyOzAuNDM4OzAuNDUzOzAuNDY5OzAuNDg0OzAuNTswLjUxNjswLjUzMTswLjU0NzswLjU2MjswLjU3ODswLjU5NDswLjYwOTswLjYyNTswLjY0MTswLjY1NjswLjY3MjswLjY4ODswLjcwMzswLjcxOTswLjczNDswLjc1OzAuNzY2OzAuNzgxOzAuNzk3OzAuODEyOzAuODI4OzAuODQ0OzAuODU5OzAuODc1OzAuODkxOzAuOTA2OzAuOTIyOzAuOTM4OzAuOTUzOzAuOTY5OzAuOTg0OzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBmaWxsPSJmcmVlemUiIGNhbGNNb2RlPSJzcGxpbmUiIGtleVNwbGluZXM9IjAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxIiAvPgo8L3VzZT4KPHVzZSB4bGluazpocmVmPSIjZDQiIHg9Ii01OC43MDM2MTMyODEyNSIgeT0iMTcuNSI+CjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9IngiIGR1cj0iMTI4cyIgdmFsdWVzPSItNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1Oy01OC43MDM2MTMyODEyNTstNTguNzAzNjEzMjgxMjU7LTU4LjcwMzYxMzI4MTI1IiBrZXlUaW1lcz0iMDswLjAxNjswLjAzMTswLjA0NzswLjA2MjswLjA3ODswLjA5NDswLjEwOTswLjEyNTswLjE0MTswLjE1NjswLjE3MjswLjE4ODswLjIwMzswLjIxOTswLjIzNDswLjI1OzAuMjY2OzAuMjgxOzAuMjk3OzAuMzEyOzAuMzI4OzAuMzQ0OzAuMzU5OzAuMzc1OzAuMzkxOzAuNDA2OzAuNDIyOzAuNDM4OzAuNDUzOzAuNDY5OzAuNDg0OzAuNTswLjUxNjswLjUzMTswLjU0NzswLjU2MjswLjU3ODswLjU5NDswLjYwOTswLjYyNTswLjY0MTswLjY1NjswLjY3MjswLjY4ODswLjcwMzswLjcxOTswLjczNDswLjc1OzAuNzY2OzAuNzgxOzAuNzk3OzAuODEyOzAuODI4OzAuODQ0OzAuODU5OzAuODc1OzAuODkxOzAuOTA2OzAuOTIyOzAuOTM4OzAuOTUzOzAuOTY5OzAuOTg0OzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBmaWxsPSJmcmVlemUiIGNhbGNNb2RlPSJzcGxpbmUiIGtleVNwbGluZXM9IjAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxIiAvPgo8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ5IiBkdXI9IjEyOHMiIHZhbHVlcz0iMTcuMTcyMDY1NTUzOTI5ODI2OzE3LjYwMDI1ODEyOTI3OTA0ODsxNi4yMjQ5MDAyNDMzMDA2ODg7MTUuODc1MTQ3MTUzODMzMjczOzE4LjcxMDYyMDgzNDgyMTU0NDsxNi45NjQ3MzU4NjYxNzM1OTsxNy41NzY4Mzg3NjAzOTU5NDQ7MTkuMTg1ODAxMzkwMzY3NDc3OzE3Ljk0MjA0MTM0ODQxMjEyMjsxNi42NTgzMjMwNzAxMjA2NTsxOS40MzQwODQyOTQyMTMyNzU7MTYuOTg4OTA2ODM5NzM2NTM2OzE1LjU3NjIyMDQxOTIzODQxNzsxOC4yNDEyNDI2ODkyNTk0ODsxNS45MDQ2NDc1MDI0NTI2Mjg7MTYuNzIzNjg5NDQyODI1MDY7MTguODYyNDQ2NzQ4NDc1MzY0OzE4LjE5MDI4NzAxODU3OTA2MzsxNS41NjI4ODgyODg4NTczNTM7MTcuMzA1NjkzODM2ODA3OTM7MTcuMTQyNjk3NTAyMjk0MDI3OzE3LjQ0MzQ1MTc3NjAwODAwNDsxNi4zMzI5ODc1NzM0MzExOTsxNy44NTQ5ODAyNDg2MTY4MzM7MTUuNzk1MTU3MjUzOTAxMDEyOzE2LjYzNzQzNzQwMzkyMTU0NTsxNi45OTE2MDg0MjAxODc4NzM7MTkuMjQxMDgxNzM2MTk0MDQ0OzE1LjgwNjE5Mjg4MTQ0MDA4MTsxOC41MTk5MzY0NTY5OTE1OTQ7MTYuMjY5NDM2NTExODY3Mjk0OzE3Ljc4NjIxMDk2NTMxNzMyMzsxNy4wNjcxMjM4ODE0NTY5OTsxNy4zNTI4OTc1Mjg5OTYwMzsxOC41MTQzMjIwMjI5NDI4NzQ7MTcuMDgwMTcwMjQwNjMxNDUzOzE1Ljk4NjkxNzkyMDUzNDY3ODsxNS45ODcwODAzOTg4ODYxNjsxNS44MjIwNDI4NzEwMTQ1MTk7MTguOTAwMjgzNDk1NjE5Mzk7MTguMDYzOTY2Mzc1MzI0MjY7MTkuMzM4Njc0MjUzNTgzMjU7MTguMjcwNjEwMTg2NjE4NTk3OzE1LjU5ODY3NTA5MDYxNjQwMzsxOC4xMzY2Mzg2NTUzNzU2NzM7MTguNjA4ODQ3NzM4NTk5MTk1OzE4LjM5NDA3MzExOTkzMjUxODsxNy40OTE3OTgwODE3MTg0NzsxNi45MzAzMzg0NzA5MjM4ODY7MTcuMzI4MTQyODU1ODA3MTQ7MTguNjk0ODg4MzM0NzE4NzI7MTYuNTc1NzY5OTc1MzEwNjE7MTcuNjA1MjE0OTc5MjI1MjAyOzE3LjQxMDIzODE2ODIyNjA4NzsxOS4zMTg3ODczODc4NDUzMjI7MTguNzE3Mzk5OTA3Njc3MzU7MTkuMjI4MjE1NDQwODcyOTU1OzE4Ljg0NDAyMjI5NDI4OTU3NzsxNi42ODcwNTQ2NzU2NTg2OTc7MTYuNDI2NTA5NDQ0NjA2Mjk7MTcuNDU1MTU3ODkzNzkzNDU1OzE2LjUzNzYyMTM2NTQ0NDY5MzsxNy4yMTA2MTUzMjc4MjM3MTQ7MTguMjE2NTYwODYxNDQ0NTk1OzE3LjE3MjA2NTU1MzkyOTgyNiIga2V5VGltZXM9IjA7MC4wMTY7MC4wMzE7MC4wNDc7MC4wNjI7MC4wNzg7MC4wOTQ7MC4xMDk7MC4xMjU7MC4xNDE7MC4xNTY7MC4xNzI7MC4xODg7MC4yMDM7MC4yMTk7MC4yMzQ7MC4yNTswLjI2NjswLjI4MTswLjI5NzswLjMxMjswLjMyODswLjM0NDswLjM1OTswLjM3NTswLjM5MTswLjQwNjswLjQyMjswLjQzODswLjQ1MzswLjQ2OTswLjQ4NDswLjU7MC41MTY7MC41MzE7MC41NDc7MC41NjI7MC41Nzg7MC41OTQ7MC42MDk7MC42MjU7MC42NDE7MC42NTY7MC42NzI7MC42ODg7MC43MDM7MC43MTk7MC43MzQ7MC43NTswLjc2NjswLjc4MTswLjc5NzswLjgxMjswLjgyODswLjg0NDswLjg1OTswLjg3NTswLjg5MTswLjkwNjswLjkyMjswLjkzODswLjk1MzswLjk2OTswLjk4NDsxIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgZmlsbD0iZnJlZXplIiBjYWxjTW9kZT0ic3BsaW5lIiBrZXlTcGxpbmVzPSIwLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMSIgLz4KPC91c2U+Cjx1c2UgeGxpbms6aHJlZj0iI2Q1IiB4PSIwLjIyMjE2Nzk2ODc1IiB5PSIxNy41Ij4KPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0ieCIgZHVyPSIxMjhzIiB2YWx1ZXM9IjAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NTswLjIyMjE2Nzk2ODc1OzAuMjIyMTY3OTY4NzU7MC4yMjIxNjc5Njg3NSIga2V5VGltZXM9IjA7MC4wMTY7MC4wMzE7MC4wNDc7MC4wNjI7MC4wNzg7MC4wOTQ7MC4xMDk7MC4xMjU7MC4xNDE7MC4xNTY7MC4xNzI7MC4xODg7MC4yMDM7MC4yMTk7MC4yMzQ7MC4yNTswLjI2NjswLjI4MTswLjI5NzswLjMxMjswLjMyODswLjM0NDswLjM1OTswLjM3NTswLjM5MTswLjQwNjswLjQyMjswLjQzODswLjQ1MzswLjQ2OTswLjQ4NDswLjU7MC41MTY7MC41MzE7MC41NDc7MC41NjI7MC41Nzg7MC41OTQ7MC42MDk7MC42MjU7MC42NDE7MC42NTY7MC42NzI7MC42ODg7MC43MDM7MC43MTk7MC43MzQ7MC43NTswLjc2NjswLjc4MTswLjc5NzswLjgxMjswLjgyODswLjg0NDswLjg1OTswLjg3NTswLjg5MTswLjkwNjswLjkyMjswLjkzODswLjk1MzswLjk2OTswLjk4NDsxIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgZmlsbD0iZnJlZXplIiBjYWxjTW9kZT0ic3BsaW5lIiBrZXlTcGxpbmVzPSIwLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMSIgLz4KPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0ieSIgZHVyPSIxMjhzIiB2YWx1ZXM9IjE5LjE3NDMyMDkwODkxMzM0OzE3Ljg0MzYwMjMyMTM4MDQwMjsxOC43NzE0MTMwMTU1OTY2OzE1Ljg4Mzc4OTI0MzQzNDY2MzsxNi45MjQyMjg5MTE4ODA2ODI7MTkuNDkwOTkyMDg1NDk0NTIyOzE2LjA4NjAwNDE5MTgyNDkyNzsxNy4xNjcwNzIxNzM4OTk1NTU7MTUuNzY3MzU3NTgxODExMjY1OzE1Ljg0NDU5NzQyNjM5NTU5ODsxOS4wODIwMDE0MDgzNzg5MjI7MTkuNDU0NTQ4MTE0ODMwNTc7MTguMDkyMzI4MzExMTMyNTQ0OzE2LjAxNDA2MDAxOTY2OTM3MzsxNi42ODU1MzAwNzgxMzA3ODI7MTYuNDI2Nzk4NTczMzg2NzYyOzE4LjE4MjkyOTMwMzc5OTc2NzsxOC4yMjQzOTYwMjk5OTEzNDc7MTcuMjU1MzgzMzQ3NjQ2MzEyOzE3LjU5NTk3OTA3MzI1NTkwNDsxNS45NDgyODEwNTQzNzgwOTY7MTcuNjYzNTcyOTk2NDU4OzE5LjI5OTc1NDg4Njg5MTIxOzE4LjUyMzEwOTIxMDI0NDExNTsxNS44ODQ2MTc4NDUwODM1ODI7MTcuNTY2MDA1NDQ1OTc1MDk0OzE4LjM2MTQ1OTI3MDU0MTg0NzsxNi41MjkwNDIwNzAxOTcwNDM7MTkuMDc5NTg2NTk5OTkxMDU3OzE3LjM0Mzc2Mzg1NjUxMTg4ODsxOC4zMTI5MjQ4MzkwOTI3OzE3LjExNjY1MzU1Nzg1MDk2MzsxOS40ODA1MzIxNTAyMjU0MzY7MTguNjMxMjYyOTUxMDI2NDk7MTcuNzkzNzYxNjAyOTA2MTIzOzE2LjA3OTA2MDA5MTU3NDc2NDsxNy4yNjQ3MzMwMzc5MTQ4ODc7MTUuNjE3NTM5NjM4NTcwOTsxNy44ODA2NTY3MDA4MjcyMjsxOS4wMjcyNzAwNzA4MTM2OTI7MTYuMjIxNjk3OTY4MDMxNjY7MTcuNTQwNjg2MDEyOTEwOTc7MTcuNDI5ODMzMzAxMTI1NDc7MTcuMTE5NjU4NzE2MTMzODc3OzE4LjM0MTg0MDMxODc3NjU5OzE5LjI0NjY3OTY3NTk0MTAzNTsxOC4zMjE1NzAxNDc1MTIyMTsxNy4zODk5OTY3MTgxNzE0NDsxOS4zNDc5MDk2NDU2NjQwMzI7MTYuODIyOTEwMDAyMjY5MTM2OzE4LjQ4MjQ1MTAxOTA0Nzc1NTsxOC4xMzM5NDAxMDMwOTYxNzI7MTguNTQ2NDM1NDk5Nzc3NzkzOzE4LjkwODI5MjkxMTIyNTIzNTsxNi4zOTk5OTU1MjE2OTAwODc7MTcuOTg0OTk2MTE1ODAwMDE2OzE3LjExMDg5NTUzODgxNTU2NjsxOC4xNjc4ODc1MjAyNzAyMTc7MTkuNDA4OTM2MjU4NTgyMzg7MTguMDM5MzIzMzg2OTk2MDg4OzE1LjU0NjQyMzE3NjMxNjQzODsxNy4zNTgxOTUyMzEwNjczMzg7MTguMzQ2MzA1OTMzNzk0Nzc0OzE5LjAzMjg4Mzc0NzQwODAyOzE5LjE3NDMyMDkwODkxMzM0IiBrZXlUaW1lcz0iMDswLjAxNjswLjAzMTswLjA0NzswLjA2MjswLjA3ODswLjA5NDswLjEwOTswLjEyNTswLjE0MTswLjE1NjswLjE3MjswLjE4ODswLjIwMzswLjIxOTswLjIzNDswLjI1OzAuMjY2OzAuMjgxOzAuMjk3OzAuMzEyOzAuMzI4OzAuMzQ0OzAuMzU5OzAuMzc1OzAuMzkxOzAuNDA2OzAuNDIyOzAuNDM4OzAuNDUzOzAuNDY5OzAuNDg0OzAuNTswLjUxNjswLjUzMTswLjU0NzswLjU2MjswLjU3ODswLjU5NDswLjYwOTswLjYyNTswLjY0MTswLjY1NjswLjY3MjswLjY4ODswLjcwMzswLjcxOTswLjczNDswLjc1OzAuNzY2OzAuNzgxOzAuNzk3OzAuODEyOzAuODI4OzAuODQ0OzAuODU5OzAuODc1OzAuODkxOzAuOTA2OzAuOTIyOzAuOTM4OzAuOTUzOzAuOTY5OzAuOTg0OzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBmaWxsPSJmcmVlemUiIGNhbGNNb2RlPSJzcGxpbmUiIGtleVNwbGluZXM9IjAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxIiAvPgo8L3VzZT4KPHVzZSB4bGluazpocmVmPSIjZDYiIHg9IjM5LjQ2MDQ0OTIxODc1IiB5PSIxNy41Ij4KPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0ieCIgZHVyPSIxMjhzIiB2YWx1ZXM9IjM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1OzM5LjQ2MDQ0OTIxODc1IiBrZXlUaW1lcz0iMDswLjAxNjswLjAzMTswLjA0NzswLjA2MjswLjA3ODswLjA5NDswLjEwOTswLjEyNTswLjE0MTswLjE1NjswLjE3MjswLjE4ODswLjIwMzswLjIxOTswLjIzNDswLjI1OzAuMjY2OzAuMjgxOzAuMjk3OzAuMzEyOzAuMzI4OzAuMzQ0OzAuMzU5OzAuMzc1OzAuMzkxOzAuNDA2OzAuNDIyOzAuNDM4OzAuNDUzOzAuNDY5OzAuNDg0OzAuNTswLjUxNjswLjUzMTswLjU0NzswLjU2MjswLjU3ODswLjU5NDswLjYwOTswLjYyNTswLjY0MTswLjY1NjswLjY3MjswLjY4ODswLjcwMzswLjcxOTswLjczNDswLjc1OzAuNzY2OzAuNzgxOzAuNzk3OzAuODEyOzAuODI4OzAuODQ0OzAuODU5OzAuODc1OzAuODkxOzAuOTA2OzAuOTIyOzAuOTM4OzAuOTUzOzAuOTY5OzAuOTg0OzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBmaWxsPSJmcmVlemUiIGNhbGNNb2RlPSJzcGxpbmUiIGtleVNwbGluZXM9IjAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxIiAvPgo8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ5IiBkdXI9IjEyOHMiIHZhbHVlcz0iMTguMTAwMzQwMTMzMjMwOTQ3OzE4Ljc2NDI3OTYwNjc3MjM2OzE1LjU2ODU0OTU4NTQyOTQ4OzE5LjI3MjkxODI2MDU5MDA1MzsxOC40MTc4NTQ1MzQ2NjM3ODI7MTcuOTI1Nzc1ODA3NjcyMzE7MTkuMTIxMjkyMTIwNjgwMTg4OzE5LjAzODcxODkwODIwODI3NDsxNS45MDE4Mjk1MjI1NDk2MzM7MTguNzYyNDg0ODY1MTIwNzQ3OzE4LjU2ODAwMjk2MzExMzU0MzsxNi4yOTgxNTIxODY3MDM5NzM7MTguNDc2OTgyNzcwMDA3NDY3OzE3Ljg0NDkwOTc3NDY2NzcwMjsxNi4yNjU5Nzc2OTUyODA0MjM7MTguNzE2NzU3NTgzOTczNTgyOzE2LjA1MTQ5MjUxMjQ3ODk1NTsxNy45NDkyOTQ4MjU3NTM0NDg7MTcuMjM3NTkxOTM2NjQ5NTc7MTYuNTE0NzY0NDQzNDQwNDczOzE3Ljc2NDM3ODQ5MzIzNjUwNzsxNy4zNjgzNDc1MTcwMzQ4MjsxNi4zMTk5ODk5Nzg0NDQxNTY7MTkuMzY3MTIzMTQ5NjM4MTY7MTUuNzkxMzAxNDY5NTExOTE0OzE1LjUxMjE0OTE1MjMyODg4ODsxNy40NDE2OTI1MTcxOTYxMjQ7MTguODQ4NzY1OTg5MjE2ODA1OzE4LjEzMzYwODI1Mzc2MDQ3ODsxOC41MTg2NzgzNTQ5NjU0OTU7MTcuNDQwMDAxODEzMzIwMzY7MTguMTk5MjA5NDE5NTU1OTU7MTYuODM5NTYyNTQ4OTc0MzI1OzE2LjU2Nzc4MTA1NzI4MzkzNDsxNy41MTE2MDI4MzI2ODQ1Mzg7MTUuNjEwMTExMjY1OTg5NzQ3OzE1LjgxOTIzNDM1OTY2NjIyMjsxOC41MTU4Mzg5NjEyMzI3ODg7MTYuMTk0Nzk4NTUxNzg2NjY1OzE4LjUwMTAyMjkwMzI3OTsxOC42Mzc1MDM5MjE5MTc5NTQ7MTcuMTE3OTYzNzk4NTAxODc2OzE4LjE5OTk3MjczNzU4NDQ3NDsxOC42NDk2ODkxNjI3NzQ7MTguOTU2MDk2NDAyMzkzNjg3OzE2LjAzOTQ4MTAxNTYyNDk4MzsxNi4xNTAyODA1Nzk5MDkxNjU7MTcuMDI2NjUzNjc2OTk1NTA4OzE3LjM1ODYyNjc3MzIyODU3MjsxNi42NzkyNzU0ODc1Mzc2Mjc7MTUuNTQxNjAwOTUxMDM1NDY0OzE3LjcyOTY4NjY5MTM4NTI2ODsxOS4zNjc2NTUwMzg4ODc4NTQ7MTYuOTY1ODM4NzMxMzQ5MDI3OzE3LjY1MTk5NzY4MDk2MzA5NTsxNy4wMjkzMTY2MjA5NTUwNTg7MTcuMjcxMjAxOTE0NjgwNDU0OzE4Ljk4MTk3NDgzMDUyMDY3NzsxNi43MzM3MjE3Nzc3NDU0MjM7MTguMDk2MjYwOTY4ODQ1NjE2OzE3LjQzNTE0NjE0MzcyMzM1ODsxNy42NTQyNzY2OTAyMTgwNzI7MTkuMTU4ODEyNTAyMTA3MjY3OzE1LjgwNjg1NjA0Mzg3NDcwMzsxOC4xMDAzNDAxMzMyMzA5NDciIGtleVRpbWVzPSIwOzAuMDE2OzAuMDMxOzAuMDQ3OzAuMDYyOzAuMDc4OzAuMDk0OzAuMTA5OzAuMTI1OzAuMTQxOzAuMTU2OzAuMTcyOzAuMTg4OzAuMjAzOzAuMjE5OzAuMjM0OzAuMjU7MC4yNjY7MC4yODE7MC4yOTc7MC4zMTI7MC4zMjg7MC4zNDQ7MC4zNTk7MC4zNzU7MC4zOTE7MC40MDY7MC40MjI7MC40Mzg7MC40NTM7MC40Njk7MC40ODQ7MC41OzAuNTE2OzAuNTMxOzAuNTQ3OzAuNTYyOzAuNTc4OzAuNTk0OzAuNjA5OzAuNjI1OzAuNjQxOzAuNjU2OzAuNjcyOzAuNjg4OzAuNzAzOzAuNzE5OzAuNzM0OzAuNzU7MC43NjY7MC43ODE7MC43OTc7MC44MTI7MC44Mjg7MC44NDQ7MC44NTk7MC44NzU7MC44OTE7MC45MDY7MC45MjI7MC45Mzg7MC45NTM7MC45Njk7MC45ODQ7MSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGZpbGw9ImZyZWV6ZSIgY2FsY01vZGU9InNwbGluZSIga2V5U3BsaW5lcz0iMC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDEiIC8+CjwvdXNlPgo8dXNlIHhsaW5rOmhyZWY9IiNkNyIgeD0iODMuODk0MDQyOTY4NzUiIHk9IjE3LjUiPgo8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ4IiBkdXI9IjEyOHMiIHZhbHVlcz0iODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzU7ODMuODk0MDQyOTY4NzUiIGtleVRpbWVzPSIwOzAuMDE2OzAuMDMxOzAuMDQ3OzAuMDYyOzAuMDc4OzAuMDk0OzAuMTA5OzAuMTI1OzAuMTQxOzAuMTU2OzAuMTcyOzAuMTg4OzAuMjAzOzAuMjE5OzAuMjM0OzAuMjU7MC4yNjY7MC4yODE7MC4yOTc7MC4zMTI7MC4zMjg7MC4zNDQ7MC4zNTk7MC4zNzU7MC4zOTE7MC40MDY7MC40MjI7MC40Mzg7MC40NTM7MC40Njk7MC40ODQ7MC41OzAuNTE2OzAuNTMxOzAuNTQ3OzAuNTYyOzAuNTc4OzAuNTk0OzAuNjA5OzAuNjI1OzAuNjQxOzAuNjU2OzAuNjcyOzAuNjg4OzAuNzAzOzAuNzE5OzAuNzM0OzAuNzU7MC43NjY7MC43ODE7MC43OTc7MC44MTI7MC44Mjg7MC44NDQ7MC44NTk7MC44NzU7MC44OTE7MC45MDY7MC45MjI7MC45Mzg7MC45NTM7MC45Njk7MC45ODQ7MSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGZpbGw9ImZyZWV6ZSIgY2FsY01vZGU9InNwbGluZSIga2V5U3BsaW5lcz0iMC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDEiIC8+CjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9InkiIGR1cj0iMTI4cyIgdmFsdWVzPSIxOC43OTc0ODE3MjUwMDk2NDM7MTYuNzE2Njc3MjI4NjIwODY7MTguMDg1MjMzMDkwNzcwMjk1OzE4LjY4MzM2MzkyMzc5MzIyOzE4LjExMzYzNzc2NDUzNjQ1MzsxNy4wNzE4NjYyNTk1MTc4MTsxOC44NjI4MTkwNzMyMTUwMjsxNS44NzE3OTUxOTI2MTI5OTsxOC4wMzMyNTc2MDk5MjIwNjc7MTcuMDY0NTAzMjQwNDcxODQ3OzE3LjYyMTg2Mzk1NjY5NDQyMzsxOC45MDM3NjQzNDE3NTc4NDI7MTguNjkxNDU5NTIxNzkwNDU2OzE4LjAxNTM2MDY4NTg1OTI0OzE2LjczMjMxNzEyMTA1Nzg0OzE2LjQzMTY1NTI4NjI0MDAyMzsxNy4zMzAxNTI4NTYxNTAzNTY7MTYuNDI4NDM4MjkxNjI4MTMzOzE2LjYwOTk0NjEzMzUzMjIyOzE5LjMzMTAxODU3MDQyMDUzOzE1Ljk0Nzg2NDI1NDU3NTI5OTsxOC43NzQ0NjU0OTQ5NzIxMjc7MTcuMDE2ODU2Mzk3ODg1OzE2Ljk1ODQxMzUxNzg4MzkxNTsxNi43NzM1NjYwNDgwMjg1NDI7MTUuODA5NTQyMjQ4Njg3NTUxOzE3LjMyOTUyMDgwMjU3ODM0OzE2LjE2NTk4ODcyODMzMjAzOzE3LjI2ODAzNTg4NDc3OTg2OzE2LjY2Nzk0ODY0NzE3MzA5NTsxOS4wNzgyOTI4MjA4NjY2ODI7MTkuMTg2OTY5ODA4Mjk0OTQzOzE3LjI2Nzk4NTkzOTcwMzE4ODsxOC4wNTg0ODA4OTUwMzk1NjI7MTkuMjE4NTY4ODM5ODE5MjY7MTYuODA0OTA2NTgxNjA5OTg4OzE1Ljg5ODIxNzU1NTUxNDQ5NDsxNi40NTEzNjc1MTE0MTgxNzsxNi4yNTgxODQ4MTMwMzIzNzsxOC4yMTM4ODI2MjA1MTk5MjU7MTYuOTk1MTUzMzEzNDM4MTM7MTYuOTI0MzkxNjYxMjkzNjQ7MTguNjgwMzkwNTQwODE5NzkzOzE2LjQzMjY4ODMzMzY0OTUwNTsxOC43MzQxNDU0Nzg5MjIxMjM7MTguMDMxNjI2NjUzMDE1ODAyOzE3LjEwMTA0MDQ3NjgxMzcwODsxOC43OTQwNzY0NjUwNTI4NzU7MTYuODY5MDEyOTkwMDIwMDQ2OzE5LjAxNDMyNTI1Mjc3MzQ0OzE5LjIwMzcwMzY4OTQyOTA5OzE3LjUxMDQyNTI4NjkxNDYzMjsxOC4yNTk5MzMyMjUyNzc1MzsxOS4yOTUxMjg0NDQ0NDc3MjsxOC40NzAyMzk2MjEzODM2ODc7MTguNTA0MDI4MTkyNTc0NDAzOzE4Ljk3NzI0MDYxMzIzNzI2NDsxOS4yNDIyODM1NzE3MjQ0MjsxOC41MTQxMzcyNjcxNTgxNjY7MTkuNDE2Mjc2NzQ0NDk1Nzc7MTYuNjY2NDIzODczMDY1MDk2OzE3Ljk4OTk0NDgwNDgxNzIzODsxOC4xODI2MzE3MzMxOTkwMzsxNi45Njk3MzcwOTE5MTYzNzsxOC43OTc0ODE3MjUwMDk2NDMiIGtleVRpbWVzPSIwOzAuMDE2OzAuMDMxOzAuMDQ3OzAuMDYyOzAuMDc4OzAuMDk0OzAuMTA5OzAuMTI1OzAuMTQxOzAuMTU2OzAuMTcyOzAuMTg4OzAuMjAzOzAuMjE5OzAuMjM0OzAuMjU7MC4yNjY7MC4yODE7MC4yOTc7MC4zMTI7MC4zMjg7MC4zNDQ7MC4zNTk7MC4zNzU7MC4zOTE7MC40MDY7MC40MjI7MC40Mzg7MC40NTM7MC40Njk7MC40ODQ7MC41OzAuNTE2OzAuNTMxOzAuNTQ3OzAuNTYyOzAuNTc4OzAuNTk0OzAuNjA5OzAuNjI1OzAuNjQxOzAuNjU2OzAuNjcyOzAuNjg4OzAuNzAzOzAuNzE5OzAuNzM0OzAuNzU7MC43NjY7MC43ODE7MC43OTc7MC44MTI7MC44Mjg7MC44NDQ7MC44NTk7MC44NzU7MC44OTE7MC45MDY7MC45MjI7MC45Mzg7MC45NTM7MC45Njk7MC45ODQ7MSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGZpbGw9ImZyZWV6ZSIgY2FsY01vZGU9InNwbGluZSIga2V5U3BsaW5lcz0iMC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDEiIC8+CjwvdXNlPgo8dXNlIHhsaW5rOmhyZWY9IiNkOCIgeD0iMTMxLjQ3MjE2Nzk2ODc1IiB5PSIxNy41Ij4KPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0ieCIgZHVyPSIxMjhzIiB2YWx1ZXM9IjEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzU7MTMxLjQ3MjE2Nzk2ODc1OzEzMS40NzIxNjc5Njg3NTsxMzEuNDcyMTY3OTY4NzUiIGtleVRpbWVzPSIwOzAuMDE2OzAuMDMxOzAuMDQ3OzAuMDYyOzAuMDc4OzAuMDk0OzAuMTA5OzAuMTI1OzAuMTQxOzAuMTU2OzAuMTcyOzAuMTg4OzAuMjAzOzAuMjE5OzAuMjM0OzAuMjU7MC4yNjY7MC4yODE7MC4yOTc7MC4zMTI7MC4zMjg7MC4zNDQ7MC4zNTk7MC4zNzU7MC4zOTE7MC40MDY7MC40MjI7MC40Mzg7MC40NTM7MC40Njk7MC40ODQ7MC41OzAuNTE2OzAuNTMxOzAuNTQ3OzAuNTYyOzAuNTc4OzAuNTk0OzAuNjA5OzAuNjI1OzAuNjQxOzAuNjU2OzAuNjcyOzAuNjg4OzAuNzAzOzAuNzE5OzAuNzM0OzAuNzU7MC43NjY7MC43ODE7MC43OTc7MC44MTI7MC44Mjg7MC44NDQ7MC44NTk7MC44NzU7MC44OTE7MC45MDY7MC45MjI7MC45Mzg7MC45NTM7MC45Njk7MC45ODQ7MSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGZpbGw9ImZyZWV6ZSIgY2FsY01vZGU9InNwbGluZSIga2V5U3BsaW5lcz0iMC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDE7MC41IDAgMC41IDEiIC8+CjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9InkiIGR1cj0iMTI4cyIgdmFsdWVzPSIxNy4wODA3MTExNDA3NTE4MzQ7MTYuMTk5MDk2ODk1OTkwNDM7MTkuMzMwODQ5MTk3NTQ2NjY2OzE2LjkxNjAxNDA4ODQxNDE1NTsxNy40MDY1MzYwNDUzMDM4NjY7MTkuMDc0MjYwMjYyMzU1NjY1OzE2LjI0NTgwMDM1NTUyMDIyNDsxOS4zNDI2Nzc5OTE5NTY4OTsxNi4wMDgyMjI5MzY2Mjk5NTsxNS42MTIxMTgwOTgyNjc7MTYuOTAzMTIwMDE2MDcwNzczOzE2LjkzNjY5OTE4NDA5MTsxOS4xNzA1NzcyODQwNTAwOTc7MTkuMDMyNzc3MDAzNDE3MzQ0OzE4LjU0NjI0Mzc0NjYzMTAyNDsxNy4yNDU3MTE1OTUwNjk5NDU7MTcuNjcwNzQ1NjIwNzM5MjMzOzE2LjQ0NzA3ODk5NzY0MzkxNDsxOC44MzQxMTg3NzEzOTM0NTsxNy4wNTk2NDQ0NDcwMDMyMzc7MTYuNjM4NjEzMjk3NjA4NDI7MTguMDUxMjI0MDQ5OTQ2Njc7MTYuMTAyMzE0MzYzNzEyNzE1OzE2Ljc2NTQwMjA4NDY1MjQ2NTsxOS4yMDQ3MTAxNDA3NzgxNjU7MTUuODgwMTgxNjM5NTEzODUyOzE2LjA2ODc5OTgxNzA4MTY4OzE2LjMxNzM4MjQ2NDc0NjU4OzE2LjUwMzkyMDExMjI5Mjk4ODsxNy4xODE1OTAyMTAyMTE2MzU7MTYuNTAwNzA0OTE2OTg4MDY7MTYuODcwNzU5NjExOTQ5NzgzOzE2LjQ4NTkyNTU3NjMwOTQ4OzE2LjQ2MDM1MzcyNDAwMzkyNzsxNy45NDI0MTkwMjM3MTgwMTsxNi44NDU4MzIzNDYwODQ2OzE2Ljk5MTE1NjEyMDUzNzk0ODsxOC41NzEyNjYyNTIxNDM3MjsxNS43NDY3NTMzNjA2OTc1MzY7MTYuMDc2MTY5NTU1NzA3OTY3OzE4LjkwMzI5NDQxNjE2MTIyNDsxNy4yMTkxMDk1MTg2MzUzMjg7MTguNjE1MjEzOTk4MzE1MDk1OzE2LjAzMTE3Mzc3NTIyNTIyNzsxNy41OTE5NjAzNTc5MjgwMTY7MTguODgxNDk1OTU2MDc1NjMyOzE2Ljg1MjE2Njk0MDIxODE1ODsxOC41NzI3MTQ0NjkxNTI0Mjc7MTcuOTQxNTA0MjY4NTY4MzI3OzE3LjA3ODI5NDQwMzE4ODA5OzE5LjQ4OTQwNDkxODM3NjgyNzsxNy4wNjkyMTExNDgyMTk1NjsxNy4zOTUxNzM1MDQ2NDU4NTc7MTcuOTc3OTQyODIwNTMwMjY2OzE2Ljc2NzM1NTE5MDA1NzQ5NDsxOC44NTA1NTU5MTUyODIwODc7MTcuODkwMTQ0MjMxNjMzNzY2OzE3Ljg1MjAwMjM2NzY4ODA0ODsxNy42NTQzNDU1OTE4MDM3MTg7MTkuNDM5NzM0NDE0MDkyMDE7MTkuNDU1NzIwNDA2ODA5MzAyOzE4Ljg2MzE2NTQyODk4MjY0NDsxNy4zMTgzMDQ4MTE5OTYzMTsxNy4xNDcxNTkxMTEwODI0MzQ7MTcuMDgwNzExMTQwNzUxODM0IiBrZXlUaW1lcz0iMDswLjAxNjswLjAzMTswLjA0NzswLjA2MjswLjA3ODswLjA5NDswLjEwOTswLjEyNTswLjE0MTswLjE1NjswLjE3MjswLjE4ODswLjIwMzswLjIxOTswLjIzNDswLjI1OzAuMjY2OzAuMjgxOzAuMjk3OzAuMzEyOzAuMzI4OzAuMzQ0OzAuMzU5OzAuMzc1OzAuMzkxOzAuNDA2OzAuNDIyOzAuNDM4OzAuNDUzOzAuNDY5OzAuNDg0OzAuNTswLjUxNjswLjUzMTswLjU0NzswLjU2MjswLjU3ODswLjU5NDswLjYwOTswLjYyNTswLjY0MTswLjY1NjswLjY3MjswLjY4ODswLjcwMzswLjcxOTswLjczNDswLjc1OzAuNzY2OzAuNzgxOzAuNzk3OzAuODEyOzAuODI4OzAuODQ0OzAuODU5OzAuODc1OzAuODkxOzAuOTA2OzAuOTIyOzAuOTM4OzAuOTUzOzAuOTY5OzAuOTg0OzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBmaWxsPSJmcmVlemUiIGNhbGNNb2RlPSJzcGxpbmUiIGtleVNwbGluZXM9IjAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxIiAvPgo8L3VzZT4KPHVzZSB4bGluazpocmVmPSIjZDkiIHg9IjE2MS40MTM1NzQyMTg3NSIgeT0iMTcuNSI+CjxhbmltYXRlIGF0dHJpYnV0ZU5hbWU9IngiIGR1cj0iMTI4cyIgdmFsdWVzPSIxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1OzE2MS40MTM1NzQyMTg3NTsxNjEuNDEzNTc0MjE4NzU7MTYxLjQxMzU3NDIxODc1IiBrZXlUaW1lcz0iMDswLjAxNjswLjAzMTswLjA0NzswLjA2MjswLjA3ODswLjA5NDswLjEwOTswLjEyNTswLjE0MTswLjE1NjswLjE3MjswLjE4ODswLjIwMzswLjIxOTswLjIzNDswLjI1OzAuMjY2OzAuMjgxOzAuMjk3OzAuMzEyOzAuMzI4OzAuMzQ0OzAuMzU5OzAuMzc1OzAuMzkxOzAuNDA2OzAuNDIyOzAuNDM4OzAuNDUzOzAuNDY5OzAuNDg0OzAuNTswLjUxNjswLjUzMTswLjU0NzswLjU2MjswLjU3ODswLjU5NDswLjYwOTswLjYyNTswLjY0MTswLjY1NjswLjY3MjswLjY4ODswLjcwMzswLjcxOTswLjczNDswLjc1OzAuNzY2OzAuNzgxOzAuNzk3OzAuODEyOzAuODI4OzAuODQ0OzAuODU5OzAuODc1OzAuODkxOzAuOTA2OzAuOTIyOzAuOTM4OzAuOTUzOzAuOTY5OzAuOTg0OzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBmaWxsPSJmcmVlemUiIGNhbGNNb2RlPSJzcGxpbmUiIGtleVNwbGluZXM9IjAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxOzAuNSAwIDAuNSAxIiAvPgo8YW5pbWF0ZSBhdHRyaWJ1dGVOYW1lPSJ5IiBkdXI9IjEyOHMiIHZhbHVlcz0iMTcuNTk5MDY3MTY1MDM2Mzg0OzE1LjY4NDYyOTQzNjY5NzEwNjsxNS45MzMwMTczMjY5MzEwOTk7MTkuNDgxMDMwNjAxMDA0OTQ7MTYuMDEyODQyMDYyNjY1NTk4OzE5LjI0OTUzNzcwNDAyOTk7MTguMjE4OTE2NTgwMDgxMTI7MTkuMTYwMzYxMTE2MzgyMzA1OzE1LjgwOTM0NjAwMjEyMDMxOzE2LjcyMzI0MTM3MjYzNTc0ODsxOC42OTE3MTA5NzAwMjQxNzU7MTUuNTM1MzgxODAxNTE0MjIzOzE1LjkyMzgzOTkxNjg0NTk5NDsxNi45MDI1NjQ4MzAyMDE1NTI7MTYuMTkyNTUyOTI0ODk5NDYzOzE2LjA4NzQ0MDg2MTA4NTIwMjsxOC4xNzkwNTc0NDg2MDYwMjsxNS44Njc2OTE3MDU3MDQwNzsxOS4zODYwMTczNzY3NzIzOzE4LjA5NzQ0NTgyOTY3MjQ0NTsxNS42OTkwNjY1MTU1MTE5NzU7MTkuMDk0ODgxMzQwMTExNjk0OzE2LjQ2NjEwNjExODY5NzU1MzsxNy40MjU4NTYwNTc5ODY1NDM7MTcuNzM1MDY2MzI1Mzc3NjE2OzE2LjA1NDUzNDE4Nzc4NjM1NjsxNy41MDg2MzU2MjY0OTk0OTsxNS43NDEyMzUyODc0MzgxNTg7MTYuMjk4NDI4MTY0MzA3OTQ7MTkuMTc0Mjk0MTQ0MTAwNzEzOzE4Ljc4ODE5NDM1MjgxMjc1NjsxNy41OTE1Mzk2MzE0MTQwMTU7MTguMjI3MzgyODg0Mjc4MTY7MTkuMDAyMDEyNTg3NzQwNTc7MTYuMDU5ODY0ODAwMzE5NzE2OzE3LjQ2ODQxMTY1NDI3ODg2NzsxNi4wMjcwNTQwODc1MjM2Mzg7MTUuOTY2MDgxOTU0NDQwNDc2OzE1LjkzMjk0MTgwODI5NDM0NTsxNi4zNDcxNDM1MjQ4MjM5OzE1LjcxMjYyNjI5Njc1OTczMzsxNi4zNjA4Njc5OTU4MDk4OzE3LjAxNjUyNjcwOTEwMTgyNTsxNy45OTA3NTE1NTQwMDA1NjsxOC45MzQ0MjI4NjIzNDYwODI7MTkuMTE2NzM5MzMwNzM3ODIyOzE4LjM3MDMzOTE3MDgyODM4NDsxNy41MjgzNTA3NjM5NDUyMTM7MTkuMTY3OTQ5NDU4MjI0MjY7MTYuMTUxOTg3MDAxNjI2ODM7MTUuOTIxNzgxNzczMzY3OTY7MTguNzcxMjM1NDEwOTEwNTE0OzE4LjAwODUyNzEyODYxMjIzNjsxNi4zNDEyNTgxMTM3ODU3MDI7MTcuMDA5MjAwNjk4MTkzNjc3OzE2LjY4OTU3NTIzNTI2NjYzNDsxNy4yMjM0NTMyODc1OTMyMjsxNy4yMTA5MTg4Nzc0MzQ3ODsxNy4wOTI2MjMzMzI1NTA4MjY7MTguNjkwOTU5NTUxOTA5NDM7MTguNzQ2MDE5NDI1OTY3NzY2OzE3Ljc0OTg1MTAyMTE2OTM0OzE3LjM5MTExOTU0Nzg1NTAxOzE2LjYzNzgzNjk3ODY1NTUyOzE3LjU5OTA2NzE2NTAzNjM4NCIga2V5VGltZXM9IjA7MC4wMTY7MC4wMzE7MC4wNDc7MC4wNjI7MC4wNzg7MC4wOTQ7MC4xMDk7MC4xMjU7MC4xNDE7MC4xNTY7MC4xNzI7MC4xODg7MC4yMDM7MC4yMTk7MC4yMzQ7MC4yNTswLjI2NjswLjI4MTswLjI5NzswLjMxMjswLjMyODswLjM0NDswLjM1OTswLjM3NTswLjM5MTswLjQwNjswLjQyMjswLjQzODswLjQ1MzswLjQ2OTswLjQ4NDswLjU7MC41MTY7MC41MzE7MC41NDc7MC41NjI7MC41Nzg7MC41OTQ7MC42MDk7MC42MjU7MC42NDE7MC42NTY7MC42NzI7MC42ODg7MC43MDM7MC43MTk7MC43MzQ7MC43NTswLjc2NjswLjc4MTswLjc5NzswLjgxMjswLjgyODswLjg0NDswLjg1OTswLjg3NTswLjg5MTswLjkwNjswLjkyMjswLjkzODswLjk1MzswLjk2OTswLjk4NDsxIiByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgZmlsbD0iZnJlZXplIiBjYWxjTW9kZT0ic3BsaW5lIiBrZXlTcGxpbmVzPSIwLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMTswLjUgMCAwLjUgMSIgLz4KPC91c2U+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPHJlY3QgeD0iLTUwMCIgeT0iLTUwIiB3aWR0aD0iMTAwMCIgaGVpZ2h0PSIxMDAiIGZpbGw9InVybCgjZDApIiBjbGlwLXBhdGg9InVybCgjZDEwKSIgLz4KPGcgaWQ9InNjcnViIiB2aXNpYmlsaXR5PSJoaWRkZW4iPgo8cGF0aCBkPSJNLTE2OC4wLDQwLjAgTDE2OC4wLDQwLjAiIHN0cm9rZT0iI2NjYyIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbGluZWNhcD0icm91bmQiIC8+CjxyZWN0IHg9Ii0xNjguMCIgeT0iNDAuMCIgd2lkdGg9IjAiIGhlaWdodD0iMC4wMDEiIHN0cm9rZT0iIzA1ZiIgc3Ryb2tlLXdpZHRoPSI0IiBzdHJva2UtbGluZWpvaW49InJvdW5kIj4KPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0id2lkdGgiIGR1cj0iMTI4cyIgdmFsdWVzPSIwOzMzNiIga2V5VGltZXM9IjA7MSIgcmVwZWF0Q291bnQ9ImluZGVmaW5pdGUiIGZpbGw9ImZyZWV6ZSIgLz4KPC9yZWN0Pgo8ZyBpZD0ic2NydWItY2FwdHVyZSIgZGF0YS14bWluPSItMTY4LjAiIGRhdGEteG1heD0iMTY4LjAiIGRhdGEtdG90YWxkdXI9IjEyOCIgZGF0YS1zdGFydGRlbGF5PSIwIiBkYXRhLWVuZGRlbGF5PSIwIiBkYXRhLXBhdXNlb25sb2FkPSIwIj4KPHJlY3QgeD0iLTE3MC4wIiB5PSIzMC4wIiB3aWR0aD0iMzQwIiBoZWlnaHQ9IjIwIiBmaWxsPSJyZ2JhKDI1NSwyNTUsMjU1LDApIiAvPgo8Y2lyY2xlIGN4PSItMTY4LjAiIGN5PSI0MC4wIiByPSI2IiBmaWxsPSIjMDVmIiBpZD0ic2NydWIta25vYiIgdmlzaWJpbGl0eT0iaGlkZGVuIj4KPGFuaW1hdGUgYXR0cmlidXRlTmFtZT0iY3giIGR1cj0iMTI4cyIgdmFsdWVzPSItMTY4LjA7MTY4LjAiIGtleVRpbWVzPSIwOzEiIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiBmaWxsPSJmcmVlemUiIC8+CjwvY2lyY2xlPgo8L2c+CjxnIGlkPSJzY3J1Yi1wbGF5IiB2aXNpYmlsaXR5PSJoaWRkZW4iPgo8cmVjdCB4PSItMTkxLjAiIHk9IjM2LjAiIHdpZHRoPSI4IiBoZWlnaHQ9IjgiIGZpbGw9IiMwNWYiIHN0cm9rZT0iIzA1ZiIgc3Ryb2tlLXdpZHRoPSI4IiBzdHJva2UtbGluZWpvaW49InJvdW5kIiAvPgo8cGF0aCBkPSJNLTE5MS4wLDM2LjAgdjguMCBsOC4wLC00LjAgWiIgZmlsbD0iI2VlZSIgLz4KPC9nPgo8ZyBpZD0ic2NydWItcGF1c2UiIHZpc2liaWxpdHk9ImhpZGRlbiI+CjxyZWN0IHg9Ii0xOTEuMCIgeT0iMzYuMCIgd2lkdGg9IjgiIGhlaWdodD0iOCIgZmlsbD0iIzA1ZiIgc3Ryb2tlPSIjMDVmIiBzdHJva2Utd2lkdGg9IjgiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIC8+CjxyZWN0IHg9Ii0xOTAuMCIgeT0iMzYuMCIgd2lkdGg9IjIuMCIgaGVpZ2h0PSI4LjAiIGZpbGw9IiNlZWUiIC8+CjxyZWN0IHg9Ii0xODYuMCIgeT0iMzYuMCIgd2lkdGg9IjIuMCIgaGVpZ2h0PSI4LjAiIGZpbGw9IiNlZWUiIC8+CjwvZz4KPC9nPgo8c2NyaXB0Pi8qPCFbQ0RBVEFbKi8KLyogQW5pbWF0aW9uIHBsYXliYWNrIGNvbnRyb2xzIGdlbmVyYXRlZCBieSBkcmF3c3ZnICovCi8qIGh0dHBzOi8vZ2l0aHViLmNvbS9jZHVjay9kcmF3c3ZnLyAqLwpmdW5jdGlvbiBzdmdPbkxvYWQoZXZlbnQpIHsKICAgIC8qIFN1cHBvcnQgc3RhbmRhbG9uZSBTVkcgb3IgZW1iZWRkZWQgaW4gSFRNTCBvciBpZnJhbWUgKi8KICAgIGlmIChldmVudCAmJiBldmVudC50YXJnZXQgJiYgZXZlbnQudGFyZ2V0Lm93bmVyRG9jdW1lbnQpIHsKICAgICAgICBzdmdTZXR1cChldmVudC50YXJnZXQub3duZXJEb2N1bWVudCk7CiAgICB9IGVsc2UgaWYgKGRvY3VtZW50ICYmIGRvY3VtZW50LmN1cnJlbnRTY3JpcHQKICAgICAgICAgICAgICAgJiYgZG9jdW1lbnQuY3VycmVudFNjcmlwdC5wYXJlbnRFbGVtZW50KSB7CiAgICAgICAgc3ZnU2V0dXAoZG9jdW1lbnQuY3VycmVudFNjcmlwdC5wYXJlbnRFbGVtZW50KTsKICAgIH0KfQpmdW5jdGlvbiBzdmdTZXR1cChkb2MpIHsKICAgIHZhciBzdmdSb290ID0gZG9jLmRvY3VtZW50RWxlbWVudCB8fCBkb2M7CiAgICB2YXIgc2NydWJDYXB0dXJlID0gZG9jLmdldEVsZW1lbnRCeUlkKCJzY3J1Yi1jYXB0dXJlIik7CiAgICAvKiBCbG9jayBtdWx0aXBsZSBzZXR1cHMgKi8KICAgIGlmICghc2NydWJDYXB0dXJlIHx8IHNjcnViQ2FwdHVyZS5nZXRBdHRyaWJ1dGUoInN2Z1NldHVwRG9uZSIpKSB7CiAgICAgICAgcmV0dXJuOwogICAgfQogICAgc2NydWJDYXB0dXJlLnNldEF0dHJpYnV0ZSgic3ZnU2V0dXBEb25lIiwgdHJ1ZSk7CiAgICB2YXIgc2NydWJDb250YWluZXIgPSBkb2MuZ2V0RWxlbWVudEJ5SWQoInNjcnViIik7CiAgICB2YXIgc2NydWJQbGF5ID0gZG9jLmdldEVsZW1lbnRCeUlkKCJzY3J1Yi1wbGF5Iik7CiAgICB2YXIgc2NydWJQYXVzZSA9IGRvYy5nZXRFbGVtZW50QnlJZCgic2NydWItcGF1c2UiKTsKICAgIHZhciBzY3J1Yktub2IgPSBkb2MuZ2V0RWxlbWVudEJ5SWQoInNjcnViLWtub2IiKTsKICAgIHZhciBzY3J1YlhNaW4gPSBwYXJzZUZsb2F0KHNjcnViQ2FwdHVyZS5kYXRhc2V0LnhtaW4pOwogICAgdmFyIHNjcnViWE1heCA9IHBhcnNlRmxvYXQoc2NydWJDYXB0dXJlLmRhdGFzZXQueG1heCk7CiAgICB2YXIgc2NydWJUb3RhbER1ciA9IHBhcnNlRmxvYXQoc2NydWJDYXB0dXJlLmRhdGFzZXQudG90YWxkdXIpOwogICAgdmFyIHNjcnViU3RhcnREZWxheSA9IHBhcnNlRmxvYXQoc2NydWJDYXB0dXJlLmRhdGFzZXQuc3RhcnRkZWxheSk7CiAgICB2YXIgc2NydWJFbmREZWxheSA9IHBhcnNlRmxvYXQoc2NydWJDYXB0dXJlLmRhdGFzZXQuZW5kZGVsYXkpOwogICAgdmFyIHNjcnViUGF1c2VPbkxvYWQgPSBwYXJzZUZsb2F0KHNjcnViQ2FwdHVyZS5kYXRhc2V0LnBhdXNlb25sb2FkKTsKICAgIHZhciBwYXVzZWQgPSBmYWxzZTsKICAgIHZhciBkcmFnWE9mZnNldCA9IDA7CiAgICB2YXIgcG9pbnQgPSBzdmdSb290LmNyZWF0ZVNWR1BvaW50KCk7CgogICAgZnVuY3Rpb24gc2NyZWVuVG9TdmdYKHApIHsKICAgICAgICB2YXIgbWF0cml4ID0gc2NydWJLbm9iLmdldFNjcmVlbkNUTSgpLmludmVyc2UoKTsKICAgICAgICBwb2ludC54ID0gcC54OwogICAgICAgIHBvaW50LnkgPSBwLnk7CiAgICAgICAgcmV0dXJuIHBvaW50Lm1hdHJpeFRyYW5zZm9ybShtYXRyaXgpLng7CiAgICB9OwogICAgZnVuY3Rpb24gc2NyZWVuVG9Qcm9ncmVzcyhwKSB7CiAgICAgICAgdmFyIG1hdHJpeCA9IHNjcnViS25vYi5nZXRTY3JlZW5DVE0oKS5pbnZlcnNlKCk7CiAgICAgICAgcG9pbnQueCA9IHAueDsKICAgICAgICBwb2ludC55ID0gcC55OwogICAgICAgIHZhciB4ID0gcG9pbnQubWF0cml4VHJhbnNmb3JtKG1hdHJpeCkueDsKICAgICAgICBpZiAoeCA8PSBzY3J1YlhNaW4pIHsKICAgICAgICAgICAgcmV0dXJuIHNjcnViU3RhcnREZWxheSAvIHNjcnViVG90YWxEdXI7CiAgICAgICAgfQogICAgICAgIGlmICh4ID49IHNjcnViWE1heCkgewogICAgICAgICAgICByZXR1cm4gKHNjcnViVG90YWxEdXIgLSBzY3J1YkVuZERlbGF5KSAvIHNjcnViVG90YWxEdXI7CiAgICAgICAgfQogICAgICAgIHJldHVybiAoc2NydWJTdGFydERlbGF5L3NjcnViVG90YWxEdXIKICAgICAgICAgICAgICAgICsgKHggLSBkcmFnWE9mZnNldCAtIHNjcnViWE1pbikKICAgICAgICAgICAgICAgICAgLyAoc2NydWJYTWF4IC0gc2NydWJYTWluKQogICAgICAgICAgICAgICAgICAqIChzY3J1YlRvdGFsRHVyIC0gc2NydWJTdGFydERlbGF5IC0gc2NydWJFbmREZWxheSkKICAgICAgICAgICAgICAgICAgLyBzY3J1YlRvdGFsRHVyKTsKICAgIH07CiAgICBmdW5jdGlvbiBjdXJyZW50U2NydWJYKCkgewogICAgICAgIHJldHVybiBzY3J1Yktub2IuY3guYW5pbVZhbC52YWx1ZTsKICAgIH07CiAgICBmdW5jdGlvbiBwYXVzZSgpIHsKICAgICAgICBzdmdSb290LnBhdXNlQW5pbWF0aW9ucygpOwogICAgICAgIHNjcnViUGxheS5zZXRBdHRyaWJ1dGUoInZpc2liaWxpdHkiLCAidmlzaWJsZSIpOwogICAgICAgIHNjcnViUGF1c2Uuc2V0QXR0cmlidXRlKCJ2aXNpYmlsaXR5IiwgImhpZGRlbiIpOwogICAgICAgIHBhdXNlZCA9IHRydWU7CiAgICB9OwogICAgZnVuY3Rpb24gcGxheSgpIHsKICAgICAgICBzdmdSb290LnVucGF1c2VBbmltYXRpb25zKCk7CiAgICAgICAgc2NydWJQYXVzZS5zZXRBdHRyaWJ1dGUoInZpc2liaWxpdHkiLCAidmlzaWJsZSIpOwogICAgICAgIHNjcnViUGxheS5zZXRBdHRyaWJ1dGUoInZpc2liaWxpdHkiLCAiaGlkZGVuIik7CiAgICAgICAgcGF1c2VkID0gZmFsc2U7CiAgICB9OwogICAgZnVuY3Rpb24gc2NydWIocGxheWJhY2tGcmFjdGlvbikgewogICAgICAgIHZhciB0ID0gc2NydWJUb3RhbER1ciAqIHBsYXliYWNrRnJhY3Rpb247CiAgICAgICAgLyogU3RvcCAxMG1zIGJlZm9yZSBlbmQgdG8gYXZvaWQgbG9vcCAoPj0xbXMgbmVlZGVkIG9uIEZGKSAqLwogICAgICAgIHZhciBsaW1pdCA9IHNjcnViVG90YWxEdXIgLSAxMGUtMzsKICAgICAgICBpZiAodCA8IDApIHQgPSAwOwogICAgICAgIGVsc2UgaWYgKHQgPiBsaW1pdCkgdCA9IGxpbWl0OwogICAgICAgIHN2Z1Jvb3Quc2V0Q3VycmVudFRpbWUodCk7CiAgICB9OwogICAgZnVuY3Rpb24gbW91c2Vkb3duKGUpIHsKICAgICAgICBzdmdSb290LnBhdXNlQW5pbWF0aW9ucygpOwogICAgICAgIGlmIChlLnRhcmdldCA9PSBzY3J1Yktub2IpIHsKICAgICAgICAgICAgZHJhZ1hPZmZzZXQgPSBzY3JlZW5Ub1N2Z1goZSkgLSBjdXJyZW50U2NydWJYKCk7CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgZHJhZ1hPZmZzZXQgPSAwOwogICAgICAgIH0KICAgICAgICBzY3J1YihzY3JlZW5Ub1Byb2dyZXNzKGUpKTsKICAgICAgICAvKiBHbG9iYWwgZG9jdW1lbnQgbGlzdGVuZXJzICovCiAgICAgICAgZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignbW91c2Vtb3ZlJywgbW91c2Vtb3ZlKTsKICAgICAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdtb3VzZXVwJywgbW91c2V1cCk7CiAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOwogICAgfTsKICAgIGZ1bmN0aW9uIG1vdXNldXAoZSkgewogICAgICAgIGRyYWdYT2Zmc2V0ID0gMDsKICAgICAgICBkb2N1bWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCdtb3VzZW1vdmUnLCBtb3VzZW1vdmUpOwogICAgICAgIGRvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoJ21vdXNldXAnLCBtb3VzZXVwKTsKICAgICAgICBpZiAoIXBhdXNlZCkgewogICAgICAgICAgICBzdmdSb290LnVucGF1c2VBbmltYXRpb25zKCk7CiAgICAgICAgfQogICAgICAgIGUucHJldmVudERlZmF1bHQoKTsKICAgIH07CiAgICBmdW5jdGlvbiBtb3VzZW1vdmUoZSkgewogICAgICAgIHNjcnViKHNjcmVlblRvUHJvZ3Jlc3MoZSkpOwogICAgfTsKICAgIHNjcnViUGF1c2UuYWRkRXZlbnRMaXN0ZW5lcigiY2xpY2siLCBwYXVzZSk7CiAgICBzY3J1YlBsYXkuYWRkRXZlbnRMaXN0ZW5lcigiY2xpY2siLCBwbGF5KTsKICAgIHNjcnViQ2FwdHVyZS5hZGRFdmVudExpc3RlbmVyKCJtb3VzZWRvd24iLCBtb3VzZWRvd24pOwogICAgc2NydWJDb250YWluZXIuc2V0QXR0cmlidXRlKCJ2aXNpYmlsaXR5IiwgInZpc2libGUiKTsKICAgIHNjcnViS25vYi5zZXRBdHRyaWJ1dGUoInZpc2liaWxpdHkiLCAidmlzaWJsZSIpOwogICAgaWYgKHNjcnViUGF1c2VPbkxvYWQpIHsKICAgICAgICBwYXVzZSgpOwogICAgICAgIHNjcnViKDApOwogICAgfSBlbHNlIHsKICAgICAgICBwbGF5KCk7CiAgICB9Cn07CnN2Z09uTG9hZCgpOwovKl1dPiovPC9zY3JpcHQ+Cjwvc3ZnPgo8L2JvZHk+CjwvaHRtbD4K\" width=\"400\" height=\"100\" style=\"border:0\" />"
+
],
+
"text/plain": [
+
"JupyterSvgFrame(svg='<!DOCTYPE html>\\n<head>\\n<meta charset=\"utf-8\">\\n<style>\\nhtml,body {\\n margin: 0;\\n height: 100%;\\n}\\nsvg {\\n margin-bottom: -50.0px;\\n}\\n</style></head>\\n<body>\\n<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\\n width=\"400\" height=\"100\" viewBox=\"-200.0 -50.0 400 100\" onload=\"svgOnLoad(event);\">\\n<defs>\\n<linearGradient x1=\"-1250\" y1=\"0\" x2=\"1250\" y2=\"0\" gradientUnits=\"userSpaceOnUse\" id=\"d0\">\\n<stop offset=\"0\" stop-color=\"#5544ee\" stop-opacity=\"1\" />\\n<stop offset=\"0.2\" stop-color=\"#5544ee\" stop-opacity=\"1\" />\\n<stop offset=\"0.4\" stop-color=\"#ee0055\" stop-opacity=\"1\" />\\n<stop offset=\"0.6\" stop-color=\"#ee0055\" stop-opacity=\"1\" />\\n<stop offset=\"0.8\" stop-color=\"#5544ee\" stop-opacity=\"1\" />\\n<stop offset=\"1\" stop-color=\"#5544ee\" stop-opacity=\"1\" />\\n<animate attributeName=\"x1\" dur=\"128s\" values=\"-2250;-250\" keyTimes=\"0;1\" repeatCount=\"indefinite\" fill=\"freeze\" />\\n<animate attributeName=\"x2\" dur=\"128s\" values=\"250;2250\" keyTimes=\"0;1\" repeatCount=\"indefinite\" fill=\"freeze\" />\\n</linearGradient>\\n<path d=\"M85 438q-1 21 -1 42q0 55 8 113q10 80 45.5 156.5t93.5 131.5q51 49 111.5 81.5t115.5 45.5q56 14 111 18q26 2 50 2q25 0 49 -3q44 -5 79.5 -11t54.5 -12l19 -7q-1 7 -3 19.5t-5.5 50t-4.5 73.5v17q0 31 2 69q4 50 12.5 92.5t28 86t47.5 74t72.5 50t100.5 19.5 q63 0 115.5 -21t86.5 -54t60 -73t39.5 -80.5t22 -73.5t10.5 -54l2 -21q2 -16 4.5 -43.5t5.5 -111.5q2 -42 1 -84q0 -40 -1 -82q-3 -82 -17.5 -194t-41.5 -208q57 -27 89 -77t35 -105v-15q0 -47 -16 -95q-18 -54 -55 -93q-28 -30 -63 -47q-36 -17 -68 -20q-15 -1 -30 -1 q-18 -1 -35 1q-33 4 -60 13t-48.5 18t-33.5 15l-12 7q-159 -83 -371 -91q-19 -1 -38 0q-187 0 -324 69q-7 4 -19 11t-45 32t-60.5 54t-56 76t-41.5 99q-17 67 -21 141zM729 483q5 -75 82 -75q35 0 57.5 20.5t22.5 54.5t-22 60t-58 26q-35 -1 -60 -27q-22 -24 -22 -52v-7z\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"d1\" />\\n<path d=\"M49 548q0 83 3.5 154.5t9.5 112.5l6 41q16 73 54 125t80.5 74.5t83 34.5t66.5 12h27h13q56 0 102 -16q52 -18 79.5 -45.5t46 -55.5t24.5 -47l5 -20q39 56 90.5 91.5t96.5 46t84.5 12.5t62.5 -2l23 -5q58 -15 99.5 -46t60.5 -70t27 -71t9 -65v-14q0 -41 -10 -79 q-12 -44 -30.5 -71t-37 -47t-32.5 -28l-13 -9q-49 -29 -103 -35q-20 -2 -38 -2q-30 0 -58 7q-42 11 -78.5 26.5t-56.5 28.5l-20 14q13 -99 13 -178q0 -34 -2 -64q-8 -102 -36 -168.5t-65.5 -108t-79 -61.5t-76 -27.5t-57.5 -6.5h-23h-9q-42 0 -80 9q-42 10 -71 27.5t-53 39 t-39 43t-25 39.5t-14 29l-4 11q-27 82 -41 181.5t-14 182.5z\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"d2\" />\\n<path d=\"M59 496q0 22 5 74q5 58 17.5 111t43.5 119t76 116t121.5 88t173.5 47q31 3 60 3q48 0 88 -8q66 -13 103 -34t64 -50t35 -44.5t12 -26.5q2 8 7.5 22t23.5 42.5t42 51t67 40.5t94 18q62 0 112.5 -29t82 -75t54.5 -101.5t33.5 -111t16.5 -101.5t7 -75l1 -29q2 -42 2 -81 q0 -54 -4 -103q-7 -84 -22.5 -141t-37.5 -102t-47.5 -71.5t-51 -44.5t-49.5 -25t-42.5 -10.5t-29.5 -2.5l-11 1q-1 2 -11.5 0t-29.5 2t-41.5 8.5t-46.5 19.5t-45 34.5t-37.5 53t-22.5 75.5q-2 -9 -8 -23.5t-35 -50.5t-71 -62q-42 -25 -122 -40q-45 -8 -95 -8q-41 0 -86 5 q-8 0 -22.5 1.5t-55 12.5t-78 28.5t-83 54.5t-78 85.5t-55 127t-22.5 174.5q-2 17 -2 35zM686 483q5 -75 82 -75q35 0 57.5 20.5t22.5 54.5t-22 60t-58 26q-35 -1 -60 -27q-22 -24 -22 -52v-7z\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"d3\" />\\n<path d=\"M35 735q-2 23 -2 44q0 43 8 79q12 56 35 90.5t52.5 60.5t59.5 38.5t55.5 19.5t41.5 9l16 1q69 0 125.5 -19.5t94 -50t66.5 -74t45 -83.5t26 -86.5t13.5 -76t4.5 -58.5q2 59 22 101t47 60t54 28t46 10l19 1q44 0 79.5 -13.5t56.5 -34t36.5 -45.5t21.5 -50t9.5 -45.5 t3.5 -33.5v-13q0 81 11 149.5t30 115.5t44 85t52 60t54 38t52 22.5t44 10t30 3.5l11 -1q63 -1 113.5 -20t80.5 -48.5t51 -65t29.5 -70.5t13 -64.5t3.5 -47.5v-18q-3 -96 -18 -187.5t-34.5 -153.5t-38 -110t-31.5 -70l-12 -23q-36 -66 -84.5 -113.5t-91.5 -68.5t-80.5 -32 t-59.5 -11l-23 -1q-92 0 -161.5 26.5t-105 61.5t-57 79t-26.5 71t-5 45q-3 -22 -10 -48t-31 -71t-59 -79.5t-99.5 -60.5t-146.5 -26q-64 0 -121.5 24.5t-94 59.5t-64.5 70t-40 59l-12 25q-60 115 -97 246.5t-45 205.5z\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"d4\" />\\n<path d=\"M70 169v15v10q0 50 18 90q20 44 54.5 72t76.5 48.5t84 29.5t77 14.5t57 5.5l22 1q-75 18 -134.5 44t-95.5 53t-61.5 56.5t-36.5 55t-16.5 46t-5.5 32.5v12q3 71 34 128t77 91t101.5 59t110.5 35.5t100 16.5t73 6h28q112 -2 194 -16.5t131.5 -36t78.5 -53.5t39 -66 q8 -28 7 -61v-15q-2 -50 -27.5 -88t-64.5 -58t-85.5 -32.5t-92 -15t-83.5 -2.5t-62 2l-23 3q97 -12 174.5 -37.5t123 -56.5t78 -66t46 -67.5t20 -58.5t5.5 -42v-16q0 -73 -24.5 -133t-64 -97.5t-87.5 -65.5t-95.5 -41.5t-87 -21.5t-63.5 -10l-25 -1q-39 -2 -75 -2 q-80 0 -148 8q-98 11 -156.5 32.5t-100.5 48.5t-59.5 55t-27 51.5t-8.5 38.5z\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"d5\" />\\n<path d=\"M23 773q0 65 18.5 118.5t70 97.5t132.5 60q30 6 58 6q36 1 70 -9q60 -16 101.5 -53.5t76 -85.5t55 -98t34.5 -92t19 -69l6 -27q2 21 7 55t33.5 118t70.5 144t125 98q52 24 113 24q37 0 77 -9q71 -16 115 -55.5t58.5 -90.5t15.5 -109v-8q0 -54 -12 -106q-13 -56 -27 -100 t-26 -72l-12 -27q-4 -10 -12.5 -28t-37 -69.5t-59.5 -98t-80 -104t-99.5 -98.5t-116.5 -69t-133 -28q-98 0 -189 46t-154.5 110t-119.5 148.5t-85 145t-50 115.5q-19 48 -31 101.5t-12 118.5z\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"d6\" />\\n<path d=\"M85 390q-11 61 -11 117q0 90 28 167q21 57 51 108.5t58.5 83t54 55.5t40.5 34l16 10q129 77 288 86q26 1 51 1q131 0 246 -40q137 -47 228 -133q63 -59 106.5 -140.5t64 -164t30.5 -168.5q7 -64 7 -123q0 -18 -1 -36q-3 -74 -8 -133t-11 -92l-6 -34 q-21 -119 -64.5 -213.5t-99.5 -153.5t-122.5 -101t-135 -60t-135.5 -27q-46 -6 -88 -6q-18 0 -36 1q-59 4 -103.5 9t-70.5 11l-26 7q-76 27 -120 68t-54 81q-7 32 -7 62v12q2 36 11 58l10 22q17 37 48 61t64 32t64 11t50 1l20 -2q68 3 116.5 20t69 39t31.5 44t11 37v15 q-14 -4 -39 -8.5t-96 -8.5q-24 -1 -47 -1q-46 0 -88 5q-65 8 -143.5 41.5t-133.5 92.5q-89 94 -118 253zM641 444q5 -75 82 -75q35 0 57.5 20.5t22.5 54.5t-22 60t-58 26q-35 0 -60 -26q-22 -24 -22 -53v-7z\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"d7\" />\\n<path d=\"\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"d8\" />\\n<path d=\"M59 1126q-2 56 18.5 104.5t56 80t79 57t87.5 39.5t80.5 23.5t59.5 12.5l23 3q78 6 147 -1.5t117 -24.5t87.5 -39t63.5 -45.5t40.5 -43.5t23.5 -32l6 -13q39 -73 52 -152t2 -148t-33.5 -135.5t-52.5 -118t-57 -91.5t-45 -61l-19 -21h19t46.5 -4.5t66 -13.5t69.5 -29.5 t65 -49.5t45 -76.5t16 -106.5q-1 -45 -22 -82.5t-55 -63t-77.5 -44.5t-91.5 -30t-95.5 -18t-90.5 -9t-76.5 -2.5t-53.5 0.5l-19 1q-70 0 -130 8.5t-103 22.5t-78 32t-57.5 38t-39 40t-25 38t-13.5 32t-6 22l-1 8q-8 71 3.5 127t51.5 107t69 78t97 81q14 12 22 18.5t21 16.5 t23 19q30 25 53.5 53.5t39.5 60t15.5 64.5t-20.5 60t-48 40.5t-54 13t-49.5 -4t-37.5 -9.5l-14 -6q-51 -10 -90.5 4t-59.5 41.5t-32.5 56.5t-15.5 50z\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"d9\" />\\n<clipPath id=\"d10\">\\n<use xlink:href=\"#d1\" x=\"-202.12158203125\" y=\"17.5\">\\n<animate attributeName=\"x\" dur=\"128s\" values=\"-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n<animate attributeName=\"y\" dur=\"128s\" values=\"19.324137087557;19.291309948237398;15.726205470907235;15.839487980635687;18.841995512517798;18.443879956274092;18.178921605760884;16.732545830356578;17.92377666271385;17.92720693456335;17.824816068448012;16.133531481019222;17.222678561165075;17.074127280821486;18.39204832494986;19.47927825179897;19.297581892372975;17.67670818971728;17.279416754903416;16.572962966597313;15.64369731757143;15.609779428363275;17.359575448389247;16.77386051141471;17.020059687602846;19.06715783131315;17.603011076584114;17.742041444105997;16.444493628460247;15.595432316563128;16.80057171504464;16.046789571945865;17.540895383348804;19.49473427277021;18.19791878938348;16.227373987292577;19.074286146331954;18.687039685686557;18.43760676757591;19.126374599590243;18.551541935332285;18.658990549847054;16.915147911366414;19.423906292288507;19.347603751592903;16.144738613216077;18.51601628660749;18.360603592949815;17.34562679096791;17.62142286449378;17.460055687400764;19.199328288378283;17.50336425052262;18.82609795916725;16.915696819474864;19.031403674325013;19.098802355026503;17.34404865952655;17.770820281680976;19.181321756767716;18.395091815488072;17.44643421944634;16.387244043964042;16.798668975075593;19.324137087557\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n</use>\\n<use xlink:href=\"#d2\" x=\"-149.79248046875\" y=\"17.5\">\\n<animate attributeName=\"x\" dur=\"128s\" values=\"-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n<animate attributeName=\"y\" dur=\"128s\" values=\"18.298286552280974;16.164278741976503;19.13176198650438;16.572550051599265;19.145511343472187;16.73825249979784;19.32944684622463;18.324823225470418;17.51699526793327;17.570991024594207;18.105657595867164;17.85177884717794;16.747377298204;16.331273898151707;17.54756663342113;19.236617436535113;17.99306034690349;15.801501476296181;18.781599978848067;18.40379714990919;19.13061448380528;16.265610933216472;18.479130897109417;15.735035585594623;18.1116397097382;16.592398929348597;16.406466116979054;19.001964685792952;15.9250639305821;17.58945066143572;18.91577202873949;16.47932791187607;16.341915754782587;19.022327037465118;17.191670593558783;18.3678443956199;15.627492280506981;16.949427645212964;16.18752396850293;18.19106176565417;15.831612709618302;19.31824866138299;15.601378859307605;18.417694029767215;15.584579478892604;16.52276021622977;18.753417549609104;16.1284731547471;16.234955237003906;18.26598170405436;17.04226352541024;15.672643983189998;19.460006184811206;16.105680435026205;15.645075976973656;16.87680402214718;17.96095793329943;18.46983849250312;15.952459612072307;16.848855092786582;15.623243430506754;17.294613049969175;18.563879746352313;18.4597866548828;18.298286552280974\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n</use>\\n<use xlink:href=\"#d3\" x=\"-107.95654296875\" y=\"17.5\">\\n<animate attributeName=\"x\" dur=\"128s\" values=\"-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n<animate attributeName=\"y\" dur=\"128s\" values=\"19.108080633400146;18.52264861470026;18.949783105301712;18.32138056020306;17.391118048393253;16.40211028139155;18.143313994628055;16.765223706913037;15.908196420198793;17.291287445676605;18.99905216539187;16.010145858475152;17.83982279244243;17.07181020042067;17.55921078705671;16.075317855907777;19.338924745948642;16.53638569300882;17.92431175621401;17.179022182622987;15.572132876499706;17.73180049535632;16.062277515845338;15.727123983292785;15.634224984994589;16.144660060724938;15.883487774532732;18.04030279015467;17.533036736142574;19.43386437619011;19.236521274787226;19.478100933038014;16.42989536267124;17.278789820478785;16.503123047364628;17.86494938219199;17.99665620301913;18.700829822537585;18.337993215317397;16.52643715395214;17.192067691050084;17.60475977512234;15.519299124214513;15.641997646831058;17.134905670888298;15.944699868919905;18.39507869147722;16.46346205776665;15.899092347019964;16.2270403132017;16.426101717508562;16.369414538873436;17.582945456191588;17.35761244457536;16.73890428350965;18.0670350307227;16.34979896797796;19.12625070692673;19.35246661931695;18.415724182310097;17.23493547515866;17.54600536886819;17.82430522416064;15.704938974423321;19.108080633400146\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n</use>\\n<use xlink:href=\"#d4\" x=\"-58.70361328125\" y=\"17.5\">\\n<animate attributeName=\"x\" dur=\"128s\" values=\"-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n<animate attributeName=\"y\" dur=\"128s\" values=\"17.172065553929826;17.600258129279048;16.224900243300688;15.875147153833273;18.710620834821544;16.96473586617359;17.576838760395944;19.185801390367477;17.942041348412122;16.65832307012065;19.434084294213275;16.988906839736536;15.576220419238417;18.24124268925948;15.904647502452628;16.72368944282506;18.862446748475364;18.190287018579063;15.562888288857353;17.30569383680793;17.142697502294027;17.443451776008004;16.33298757343119;17.854980248616833;15.795157253901012;16.637437403921545;16.991608420187873;19.241081736194044;15.806192881440081;18.519936456991594;16.269436511867294;17.786210965317323;17.06712388145699;17.35289752899603;18.514322022942874;17.080170240631453;15.986917920534678;15.98708039888616;15.822042871014519;18.90028349561939;18.06396637532426;19.33867425358325;18.270610186618597;15.598675090616403;18.136638655375673;18.608847738599195;18.394073119932518;17.49179808171847;16.930338470923886;17.32814285580714;18.69488833471872;16.57576997531061;17.605214979225202;17.410238168226087;19.318787387845322;18.71739990767735;19.228215440872955;18.844022294289577;16.687054675658697;16.42650944460629;17.455157893793455;16.537621365444693;17.210615327823714;18.216560861444595;17.172065553929826\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n</use>\\n<use xlink:href=\"#d5\" x=\"0.22216796875\" y=\"17.5\">\\n<animate attributeName=\"x\" dur=\"128s\" values=\"0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n<animate attributeName=\"y\" dur=\"128s\" values=\"19.17432090891334;17.843602321380402;18.7714130155966;15.883789243434663;16.924228911880682;19.490992085494522;16.086004191824927;17.167072173899555;15.767357581811265;15.844597426395598;19.082001408378922;19.45454811483057;18.092328311132544;16.014060019669373;16.685530078130782;16.426798573386762;18.182929303799767;18.224396029991347;17.255383347646312;17.595979073255904;15.948281054378096;17.663572996458;19.29975488689121;18.523109210244115;15.884617845083582;17.566005445975094;18.361459270541847;16.529042070197043;19.079586599991057;17.343763856511888;18.3129248390927;17.116653557850963;19.480532150225436;18.63126295102649;17.793761602906123;16.079060091574764;17.264733037914887;15.6175396385709;17.88065670082722;19.027270070813692;16.22169796803166;17.54068601291097;17.42983330112547;17.119658716133877;18.34184031877659;19.246679675941035;18.32157014751221;17.38999671817144;19.347909645664032;16.822910002269136;18.482451019047755;18.133940103096172;18.546435499777793;18.908292911225235;16.399995521690087;17.984996115800016;17.110895538815566;18.167887520270217;19.40893625858238;18.039323386996088;15.546423176316438;17.358195231067338;18.346305933794774;19.03288374740802;19.17432090891334\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n</use>\\n<use xlink:href=\"#d6\" x=\"39.46044921875\" y=\"17.5\">\\n<animate attributeName=\"x\" dur=\"128s\" values=\"39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n<animate attributeName=\"y\" dur=\"128s\" values=\"18.100340133230947;18.76427960677236;15.56854958542948;19.272918260590053;18.417854534663782;17.92577580767231;19.121292120680188;19.038718908208274;15.901829522549633;18.762484865120747;18.568002963113543;16.298152186703973;18.476982770007467;17.844909774667702;16.265977695280423;18.716757583973582;16.051492512478955;17.949294825753448;17.23759193664957;16.514764443440473;17.764378493236507;17.36834751703482;16.319989978444156;19.36712314963816;15.791301469511914;15.512149152328888;17.441692517196124;18.848765989216805;18.133608253760478;18.518678354965495;17.44000181332036;18.19920941955595;16.839562548974325;16.567781057283934;17.511602832684538;15.610111265989747;15.819234359666222;18.515838961232788;16.194798551786665;18.501022903279;18.637503921917954;17.117963798501876;18.199972737584474;18.649689162774;18.956096402393687;16.039481015624983;16.150280579909165;17.026653676995508;17.358626773228572;16.679275487537627;15.541600951035464;17.729686691385268;19.367655038887854;16.965838731349027;17.651997680963095;17.029316620955058;17.271201914680454;18.981974830520677;16.733721777745423;18.096260968845616;17.435146143723358;17.654276690218072;19.158812502107267;15.806856043874703;18.100340133230947\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n</use>\\n<use xlink:href=\"#d7\" x=\"83.89404296875\" y=\"17.5\">\\n<animate attributeName=\"x\" dur=\"128s\" values=\"83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n<animate attributeName=\"y\" dur=\"128s\" values=\"18.797481725009643;16.71667722862086;18.085233090770295;18.68336392379322;18.113637764536453;17.07186625951781;18.86281907321502;15.87179519261299;18.033257609922067;17.064503240471847;17.621863956694423;18.903764341757842;18.691459521790456;18.01536068585924;16.73231712105784;16.431655286240023;17.330152856150356;16.428438291628133;16.60994613353222;19.33101857042053;15.947864254575299;18.774465494972127;17.016856397885;16.958413517883915;16.773566048028542;15.809542248687551;17.32952080257834;16.16598872833203;17.26803588477986;16.667948647173095;19.078292820866682;19.186969808294943;17.267985939703188;18.058480895039562;19.21856883981926;16.804906581609988;15.898217555514494;16.45136751141817;16.25818481303237;18.213882620519925;16.99515331343813;16.92439166129364;18.680390540819793;16.432688333649505;18.734145478922123;18.031626653015802;17.101040476813708;18.794076465052875;16.869012990020046;19.01432525277344;19.20370368942909;17.510425286914632;18.25993322527753;19.29512844444772;18.470239621383687;18.504028192574403;18.977240613237264;19.24228357172442;18.514137267158166;19.41627674449577;16.666423873065096;17.989944804817238;18.18263173319903;16.96973709191637;18.797481725009643\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n</use>\\n<use xlink:href=\"#d8\" x=\"131.47216796875\" y=\"17.5\">\\n<animate attributeName=\"x\" dur=\"128s\" values=\"131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n<animate attributeName=\"y\" dur=\"128s\" values=\"17.080711140751834;16.19909689599043;19.330849197546666;16.916014088414155;17.406536045303866;19.074260262355665;16.245800355520224;19.34267799195689;16.00822293662995;15.612118098267;16.903120016070773;16.936699184091;19.170577284050097;19.032777003417344;18.546243746631024;17.245711595069945;17.670745620739233;16.447078997643914;18.83411877139345;17.059644447003237;16.63861329760842;18.05122404994667;16.102314363712715;16.765402084652465;19.204710140778165;15.880181639513852;16.06879981708168;16.31738246474658;16.503920112292988;17.181590210211635;16.50070491698806;16.870759611949783;16.48592557630948;16.460353724003927;17.94241902371801;16.8458323460846;16.991156120537948;18.57126625214372;15.746753360697536;16.076169555707967;18.903294416161224;17.219109518635328;18.615213998315095;16.031173775225227;17.591960357928016;18.881495956075632;16.852166940218158;18.572714469152427;17.941504268568327;17.07829440318809;19.489404918376827;17.06921114821956;17.395173504645857;17.977942820530266;16.767355190057494;18.850555915282087;17.890144231633766;17.852002367688048;17.654345591803718;19.43973441409201;19.455720406809302;18.863165428982644;17.31830481199631;17.147159111082434;17.080711140751834\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n</use>\\n<use xlink:href=\"#d9\" x=\"161.41357421875\" y=\"17.5\">\\n<animate attributeName=\"x\" dur=\"128s\" values=\"161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n<animate attributeName=\"y\" dur=\"128s\" values=\"17.599067165036384;15.684629436697106;15.933017326931099;19.48103060100494;16.012842062665598;19.2495377040299;18.21891658008112;19.160361116382305;15.80934600212031;16.723241372635748;18.691710970024175;15.535381801514223;15.923839916845994;16.902564830201552;16.192552924899463;16.087440861085202;18.17905744860602;15.86769170570407;19.3860173767723;18.097445829672445;15.699066515511975;19.094881340111694;16.466106118697553;17.425856057986543;17.735066325377616;16.054534187786356;17.50863562649949;15.741235287438158;16.29842816430794;19.174294144100713;18.788194352812756;17.591539631414015;18.22738288427816;19.00201258774057;16.059864800319716;17.468411654278867;16.027054087523638;15.966081954440476;15.932941808294345;16.3471435248239;15.712626296759733;16.3608679958098;17.016526709101825;17.99075155400056;18.934422862346082;19.116739330737822;18.370339170828384;17.528350763945213;19.16794945822426;16.15198700162683;15.92178177336796;18.771235410910514;18.008527128612236;16.341258113785702;17.009200698193677;16.689575235266634;17.22345328759322;17.21091887743478;17.092623332550826;18.69095955190943;18.746019425967766;17.74985102116934;17.39111954785501;16.63783697865552;17.599067165036384\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\\n</use>\\n</clipPath>\\n</defs>\\n<rect x=\"-500\" y=\"-50\" width=\"1000\" height=\"100\" fill=\"url(#d0)\" clip-path=\"url(#d10)\" />\\n<g id=\"scrub\" visibility=\"hidden\">\\n<path d=\"M-168.0,40.0 L168.0,40.0\" stroke=\"#ccc\" stroke-width=\"4\" stroke-linecap=\"round\" />\\n<rect x=\"-168.0\" y=\"40.0\" width=\"0\" height=\"0.001\" stroke=\"#05f\" stroke-width=\"4\" stroke-linejoin=\"round\">\\n<animate attributeName=\"width\" dur=\"128s\" values=\"0;336\" keyTimes=\"0;1\" repeatCount=\"indefinite\" fill=\"freeze\" />\\n</rect>\\n<g id=\"scrub-capture\" data-xmin=\"-168.0\" data-xmax=\"168.0\" data-totaldur=\"128\" data-startdelay=\"0\" data-enddelay=\"0\" data-pauseonload=\"0\">\\n<rect x=\"-170.0\" y=\"30.0\" width=\"340\" height=\"20\" fill=\"rgba(255,255,255,0)\" />\\n<circle cx=\"-168.0\" cy=\"40.0\" r=\"6\" fill=\"#05f\" id=\"scrub-knob\" visibility=\"hidden\">\\n<animate attributeName=\"cx\" dur=\"128s\" values=\"-168.0;168.0\" keyTimes=\"0;1\" repeatCount=\"indefinite\" fill=\"freeze\" />\\n</circle>\\n</g>\\n<g id=\"scrub-play\" visibility=\"hidden\">\\n<rect x=\"-191.0\" y=\"36.0\" width=\"8\" height=\"8\" fill=\"#05f\" stroke=\"#05f\" stroke-width=\"8\" stroke-linejoin=\"round\" />\\n<path d=\"M-191.0,36.0 v8.0 l8.0,-4.0 Z\" fill=\"#eee\" />\\n</g>\\n<g id=\"scrub-pause\" visibility=\"hidden\">\\n<rect x=\"-191.0\" y=\"36.0\" width=\"8\" height=\"8\" fill=\"#05f\" stroke=\"#05f\" stroke-width=\"8\" stroke-linejoin=\"round\" />\\n<rect x=\"-190.0\" y=\"36.0\" width=\"2.0\" height=\"8.0\" fill=\"#eee\" />\\n<rect x=\"-186.0\" y=\"36.0\" width=\"2.0\" height=\"8.0\" fill=\"#eee\" />\\n</g>\\n</g>\\n<script>/*<![CDATA[*/\\n/* Animation playback controls generated by drawsvg */\\n/* https://github.com/cduck/drawsvg/ */\\nfunction svgOnLoad(event) {\\n /* Support standalone SVG or embedded in HTML or iframe */\\n if (event && event.target && event.target.ownerDocument) {\\n svgSetup(event.target.ownerDocument);\\n } else if (document && document.currentScript\\n && document.currentScript.parentElement) {\\n svgSetup(document.currentScript.parentElement);\\n }\\n}\\nfunction svgSetup(doc) {\\n var svgRoot = doc.documentElement || doc;\\n var scrubCapture = doc.getElementById(\"scrub-capture\");\\n /* Block multiple setups */\\n if (!scrubCapture || scrubCapture.getAttribute(\"svgSetupDone\")) {\\n return;\\n }\\n scrubCapture.setAttribute(\"svgSetupDone\", true);\\n var scrubContainer = doc.getElementById(\"scrub\");\\n var scrubPlay = doc.getElementById(\"scrub-play\");\\n var scrubPause = doc.getElementById(\"scrub-pause\");\\n var scrubKnob = doc.getElementById(\"scrub-knob\");\\n var scrubXMin = parseFloat(scrubCapture.dataset.xmin);\\n var scrubXMax = parseFloat(scrubCapture.dataset.xmax);\\n var scrubTotalDur = parseFloat(scrubCapture.dataset.totaldur);\\n var scrubStartDelay = parseFloat(scrubCapture.dataset.startdelay);\\n var scrubEndDelay = parseFloat(scrubCapture.dataset.enddelay);\\n var scrubPauseOnLoad = parseFloat(scrubCapture.dataset.pauseonload);\\n var paused = false;\\n var dragXOffset = 0;\\n var point = svgRoot.createSVGPoint();\\n\\n function screenToSvgX(p) {\\n var matrix = scrubKnob.getScreenCTM().inverse();\\n point.x = p.x;\\n point.y = p.y;\\n return point.matrixTransform(matrix).x;\\n };\\n function screenToProgress(p) {\\n var matrix = scrubKnob.getScreenCTM().inverse();\\n point.x = p.x;\\n point.y = p.y;\\n var x = point.matrixTransform(matrix).x;\\n if (x <= scrubXMin) {\\n return scrubStartDelay / scrubTotalDur;\\n }\\n if (x >= scrubXMax) {\\n return (scrubTotalDur - scrubEndDelay) / scrubTotalDur;\\n }\\n return (scrubStartDelay/scrubTotalDur\\n + (x - dragXOffset - scrubXMin)\\n / (scrubXMax - scrubXMin)\\n * (scrubTotalDur - scrubStartDelay - scrubEndDelay)\\n / scrubTotalDur);\\n };\\n function currentScrubX() {\\n return scrubKnob.cx.animVal.value;\\n };\\n function pause() {\\n svgRoot.pauseAnimations();\\n scrubPlay.setAttribute(\"visibility\", \"visible\");\\n scrubPause.setAttribute(\"visibility\", \"hidden\");\\n paused = true;\\n };\\n function play() {\\n svgRoot.unpauseAnimations();\\n scrubPause.setAttribute(\"visibility\", \"visible\");\\n scrubPlay.setAttribute(\"visibility\", \"hidden\");\\n paused = false;\\n };\\n function scrub(playbackFraction) {\\n var t = scrubTotalDur * playbackFraction;\\n /* Stop 10ms before end to avoid loop (>=1ms needed on FF) */\\n var limit = scrubTotalDur - 10e-3;\\n if (t < 0) t = 0;\\n else if (t > limit) t = limit;\\n svgRoot.setCurrentTime(t);\\n };\\n function mousedown(e) {\\n svgRoot.pauseAnimations();\\n if (e.target == scrubKnob) {\\n dragXOffset = screenToSvgX(e) - currentScrubX();\\n } else {\\n dragXOffset = 0;\\n }\\n scrub(screenToProgress(e));\\n /* Global document listeners */\\n document.addEventListener(\\'mousemove\\', mousemove);\\n document.addEventListener(\\'mouseup\\', mouseup);\\n e.preventDefault();\\n };\\n function mouseup(e) {\\n dragXOffset = 0;\\n document.removeEventListener(\\'mousemove\\', mousemove);\\n document.removeEventListener(\\'mouseup\\', mouseup);\\n if (!paused) {\\n svgRoot.unpauseAnimations();\\n }\\n e.preventDefault();\\n };\\n function mousemove(e) {\\n scrub(screenToProgress(e));\\n };\\n scrubPause.addEventListener(\"click\", pause);\\n scrubPlay.addEventListener(\"click\", play);\\n scrubCapture.addEventListener(\"mousedown\", mousedown);\\n scrubContainer.setAttribute(\"visibility\", \"visible\");\\n scrubKnob.setAttribute(\"visibility\", \"visible\");\\n if (scrubPauseOnLoad) {\\n pause();\\n scrub(0);\\n } else {\\n play();\\n }\\n};\\nsvgOnLoad();\\n/*]]>*/</script>\\n</svg>\\n</body>\\n</html>\\n', width=400, height=100, mime='text/html')"
+
]
+
},
+
"execution_count": 1,
+
"metadata": {},
+
"output_type": "execute_result"
+
}
+
],
+
"source": [
+
"import random\n",
+
"import drawsvg as draw\n",
+
"\n",
+
"# Define character paths\n",
+
"# Font: https://www.fontsquirrel.com/fonts/sniglet\n",
+
"# Converted to SVG paths with https://www.fontsquirrel.com/tools/webfont-generator\n",
+
"char_set = {\n",
+
" 'd': (1531, 'M85 438q-1 21 -1 42q0 55 8 113q10 80 45.5 156.5t93.5 131.5q51 49 111.5 81.5t115.5 45.5q56 14 111 18q26 2 50 2q25 0 49 -3q44 -5 79.5 -11t54.5 -12l19 -7q-1 7 -3 19.5t-5.5 50t-4.5 73.5v17q0 31 2 69q4 50 12.5 92.5t28 86t47.5 74t72.5 50t100.5 19.5 q63 0 115.5 -21t86.5 -54t60 -73t39.5 -80.5t22 -73.5t10.5 -54l2 -21q2 -16 4.5 -43.5t5.5 -111.5q2 -42 1 -84q0 -40 -1 -82q-3 -82 -17.5 -194t-41.5 -208q57 -27 89 -77t35 -105v-15q0 -47 -16 -95q-18 -54 -55 -93q-28 -30 -63 -47q-36 -17 -68 -20q-15 -1 -30 -1 q-18 -1 -35 1q-33 4 -60 13t-48.5 18t-33.5 15l-12 7q-159 -83 -371 -91q-19 -1 -38 0q-187 0 -324 69q-7 4 -19 11t-45 32t-60.5 54t-56 76t-41.5 99q-17 67 -21 141zM729 483q5 -75 82 -75q35 0 57.5 20.5t22.5 54.5t-22 60t-58 26q-35 -1 -60 -27q-22 -24 -22 -52v-7z'),\n",
+
" 'r': (1224, 'M49 548q0 83 3.5 154.5t9.5 112.5l6 41q16 73 54 125t80.5 74.5t83 34.5t66.5 12h27h13q56 0 102 -16q52 -18 79.5 -45.5t46 -55.5t24.5 -47l5 -20q39 56 90.5 91.5t96.5 46t84.5 12.5t62.5 -2l23 -5q58 -15 99.5 -46t60.5 -70t27 -71t9 -65v-14q0 -41 -10 -79 q-12 -44 -30.5 -71t-37 -47t-32.5 -28l-13 -9q-49 -29 -103 -35q-20 -2 -38 -2q-30 0 -58 7q-42 11 -78.5 26.5t-56.5 28.5l-20 14q13 -99 13 -178q0 -34 -2 -64q-8 -102 -36 -168.5t-65.5 -108t-79 -61.5t-76 -27.5t-57.5 -6.5h-23h-9q-42 0 -80 9q-42 10 -71 27.5t-53 39 t-39 43t-25 39.5t-14 29l-4 11q-27 82 -41 181.5t-14 182.5z'),\n",
+
" 'a': (1441, 'M59 496q0 22 5 74q5 58 17.5 111t43.5 119t76 116t121.5 88t173.5 47q31 3 60 3q48 0 88 -8q66 -13 103 -34t64 -50t35 -44.5t12 -26.5q2 8 7.5 22t23.5 42.5t42 51t67 40.5t94 18q62 0 112.5 -29t82 -75t54.5 -101.5t33.5 -111t16.5 -101.5t7 -75l1 -29q2 -42 2 -81 q0 -54 -4 -103q-7 -84 -22.5 -141t-37.5 -102t-47.5 -71.5t-51 -44.5t-49.5 -25t-42.5 -10.5t-29.5 -2.5l-11 1q-1 2 -11.5 0t-29.5 2t-41.5 8.5t-46.5 19.5t-45 34.5t-37.5 53t-22.5 75.5q-2 -9 -8 -23.5t-35 -50.5t-71 -62q-42 -25 -122 -40q-45 -8 -95 -8q-41 0 -86 5 q-8 0 -22.5 1.5t-55 12.5t-78 28.5t-83 54.5t-78 85.5t-55 127t-22.5 174.5q-2 17 -2 35zM686 483q5 -75 82 -75q35 0 57.5 20.5t22.5 54.5t-22 60t-58 26q-35 -1 -60 -27q-22 -24 -22 -52v-7z'),\n",
+
" 'w': (1724, 'M35 735q-2 23 -2 44q0 43 8 79q12 56 35 90.5t52.5 60.5t59.5 38.5t55.5 19.5t41.5 9l16 1q69 0 125.5 -19.5t94 -50t66.5 -74t45 -83.5t26 -86.5t13.5 -76t4.5 -58.5q2 59 22 101t47 60t54 28t46 10l19 1q44 0 79.5 -13.5t56.5 -34t36.5 -45.5t21.5 -50t9.5 -45.5 t3.5 -33.5v-13q0 81 11 149.5t30 115.5t44 85t52 60t54 38t52 22.5t44 10t30 3.5l11 -1q63 -1 113.5 -20t80.5 -48.5t51 -65t29.5 -70.5t13 -64.5t3.5 -47.5v-18q-3 -96 -18 -187.5t-34.5 -153.5t-38 -110t-31.5 -70l-12 -23q-36 -66 -84.5 -113.5t-91.5 -68.5t-80.5 -32 t-59.5 -11l-23 -1q-92 0 -161.5 26.5t-105 61.5t-57 79t-26.5 71t-5 45q-3 -22 -10 -48t-31 -71t-59 -79.5t-99.5 -60.5t-146.5 -26q-64 0 -121.5 24.5t-94 59.5t-64.5 70t-40 59l-12 25q-60 115 -97 246.5t-45 205.5z'),\n",
+
" 's': (1148, 'M70 169v15v10q0 50 18 90q20 44 54.5 72t76.5 48.5t84 29.5t77 14.5t57 5.5l22 1q-75 18 -134.5 44t-95.5 53t-61.5 56.5t-36.5 55t-16.5 46t-5.5 32.5v12q3 71 34 128t77 91t101.5 59t110.5 35.5t100 16.5t73 6h28q112 -2 194 -16.5t131.5 -36t78.5 -53.5t39 -66 q8 -28 7 -61v-15q-2 -50 -27.5 -88t-64.5 -58t-85.5 -32.5t-92 -15t-83.5 -2.5t-62 2l-23 3q97 -12 174.5 -37.5t123 -56.5t78 -66t46 -67.5t20 -58.5t5.5 -42v-16q0 -73 -24.5 -133t-64 -97.5t-87.5 -65.5t-95.5 -41.5t-87 -21.5t-63.5 -10l-25 -1q-39 -2 -75 -2 q-80 0 -148 8q-98 11 -156.5 32.5t-100.5 48.5t-59.5 55t-27 51.5t-8.5 38.5z'),\n",
+
" 'v': (1300, 'M23 773q0 65 18.5 118.5t70 97.5t132.5 60q30 6 58 6q36 1 70 -9q60 -16 101.5 -53.5t76 -85.5t55 -98t34.5 -92t19 -69l6 -27q2 21 7 55t33.5 118t70.5 144t125 98q52 24 113 24q37 0 77 -9q71 -16 115 -55.5t58.5 -90.5t15.5 -109v-8q0 -54 -12 -106q-13 -56 -27 -100 t-26 -72l-12 -27q-4 -10 -12.5 -28t-37 -69.5t-59.5 -98t-80 -104t-99.5 -98.5t-116.5 -69t-133 -28q-98 0 -189 46t-154.5 110t-119.5 148.5t-85 145t-50 115.5q-19 48 -31 101.5t-12 118.5z'),\n",
+
" 'g': (1392, 'M85 390q-11 61 -11 117q0 90 28 167q21 57 51 108.5t58.5 83t54 55.5t40.5 34l16 10q129 77 288 86q26 1 51 1q131 0 246 -40q137 -47 228 -133q63 -59 106.5 -140.5t64 -164t30.5 -168.5q7 -64 7 -123q0 -18 -1 -36q-3 -74 -8 -133t-11 -92l-6 -34 q-21 -119 -64.5 -213.5t-99.5 -153.5t-122.5 -101t-135 -60t-135.5 -27q-46 -6 -88 -6q-18 0 -36 1q-59 4 -103.5 9t-70.5 11l-26 7q-76 27 -120 68t-54 81q-7 32 -7 62v12q2 36 11 58l10 22q17 37 48 61t64 32t64 11t50 1l20 -2q68 3 116.5 20t69 39t31.5 44t11 37v15 q-14 -4 -39 -8.5t-96 -8.5q-24 -1 -47 -1q-46 0 -88 5q-65 8 -143.5 41.5t-133.5 92.5q-89 94 -118 253zM641 444q5 -75 82 -75q35 0 57.5 20.5t22.5 54.5t-22 60t-58 26q-35 0 -60 -26q-22 -24 -22 -53v-7z'),\n",
+
" ' ': (876, ''),\n",
+
" '2': (1191, 'M59 1126q-2 56 18.5 104.5t56 80t79 57t87.5 39.5t80.5 23.5t59.5 12.5l23 3q78 6 147 -1.5t117 -24.5t87.5 -39t63.5 -45.5t40.5 -43.5t23.5 -32l6 -13q39 -73 52 -152t2 -148t-33.5 -135.5t-52.5 -118t-57 -91.5t-45 -61l-19 -21h19t46.5 -4.5t66 -13.5t69.5 -29.5 t65 -49.5t45 -76.5t16 -106.5q-1 -45 -22 -82.5t-55 -63t-77.5 -44.5t-91.5 -30t-95.5 -18t-90.5 -9t-76.5 -2.5t-53.5 0.5l-19 1q-70 0 -130 8.5t-103 22.5t-78 32t-57.5 38t-39 40t-25 38t-13.5 32t-6 22l-1 8q-8 71 3.5 127t51.5 107t69 78t97 81q14 12 22 18.5t21 16.5 t23 19q30 25 53.5 53.5t39.5 60t15.5 64.5t-20.5 60t-48 40.5t-54 13t-49.5 -4t-37.5 -9.5l-14 -6q-51 -10 -90.5 4t-59.5 41.5t-32.5 56.5t-15.5 50z'),\n",
+
"}\n",
+
"# Create drawing\n",
+
"d = draw.Drawing(400, 100, origin='center',\n",
+
" animation_config=draw.types.SyncedAnimationConfig(\n",
+
" # Animation configuration\n",
+
" duration=128, # Seconds\n",
+
" show_playback_progress=False,\n",
+
" show_playback_controls=True,\n",
+
" pause_on_load=False,\n",
+
" ),\n",
+
")\n",
+
"msg = 'drawsvg 2'\n",
+
"size = 70/2048\n",
+
"msg_width = size * sum(char_set[char][0] for char in msg)\n",
+
"random.seed(2)\n",
+
"# Draw letters\n",
+
"clip = draw.ClipPath()\n",
+
"x = -msg_width/2\n",
+
"y = size/4*2048\n",
+
"for char in msg:\n",
+
" char_width, char_path = char_set[char]\n",
+
" p = draw.Path(char_path, transform=f'scale({size},-{size})')\n",
+
" use = draw.Use(p, x, y)\n",
+
" y_start = random.uniform(-2, 2)\n",
+
" use.add_key_frame(0, x=x, y=y+y_start, animation_args=dict(\n",
+
" calcMode='spline',\n",
+
" keySplines=('0.5 0 0.5 1;'*64)[:-1]))\n",
+
" for i in range(2,128,2):\n",
+
" use.add_key_frame(i, x=x, y=y+random.uniform(-2, 2))\n",
+
" use.add_key_frame(128, x=x, y=y+y_start)\n",
+
" clip.append(use)\n",
+
" x += char_width * size\n",
+
"# Draw gradient fill in the letters\n",
+
"fill1 = '#5544ee'\n",
+
"fill2 = '#ee0055'\n",
+
"fill = draw.LinearGradient(-1250, 0, 1250, 0)\n",
+
"fill.add_stop(0, fill1, 1)\n",
+
"fill.add_stop(1/5, fill1, 1)\n",
+
"fill.add_stop(2/5, fill2, 1)\n",
+
"fill.add_stop(3/5, fill2, 1)\n",
+
"fill.add_stop(4/5, fill1, 1)\n",
+
"fill.add_stop(1, fill1, 1)\n",
+
"fill.add_key_frame(0, x1=-2250, x2=250)\n",
+
"fill.add_key_frame(128, x1=-250, x2=2250)\n",
+
"d.append(draw.Rectangle(-500, -50, 1000, 100, fill=fill, clip_path=clip))\n",
+
"# Output\n",
+
"d.save_svg('logo.svg') # Save as SVG file\n",
+
"d.save_html('logo.html') # Save as HTML file\n",
+
"d.display_iframe() # Display as interactive SVG"
+
]
+
},
+
{
+
"cell_type": "code",
+
"execution_count": 2,
+
"id": "fb25c6e5",
+
"metadata": {},
+
"outputs": [
+
{
+
"data": {
+
"image/svg+xml": [
+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
+
"<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n",
+
" width=\"400\" height=\"100\" viewBox=\"-200.0 -50.0 400 100\" onload=\"svgOnLoad(event);\">\n",
+
"<defs>\n",
+
"<linearGradient x1=\"-1250\" y1=\"0\" x2=\"1250\" y2=\"0\" gradientUnits=\"userSpaceOnUse\" id=\"WErAiHgL0\">\n",
+
"<stop offset=\"0\" stop-color=\"#5544ee\" stop-opacity=\"1\" />\n",
+
"<stop offset=\"0.2\" stop-color=\"#5544ee\" stop-opacity=\"1\" />\n",
+
"<stop offset=\"0.4\" stop-color=\"#ee0055\" stop-opacity=\"1\" />\n",
+
"<stop offset=\"0.6\" stop-color=\"#ee0055\" stop-opacity=\"1\" />\n",
+
"<stop offset=\"0.8\" stop-color=\"#5544ee\" stop-opacity=\"1\" />\n",
+
"<stop offset=\"1\" stop-color=\"#5544ee\" stop-opacity=\"1\" />\n",
+
"<animate attributeName=\"x1\" dur=\"128s\" values=\"-2250;-250\" keyTimes=\"0;1\" repeatCount=\"indefinite\" fill=\"freeze\" />\n",
+
"<animate attributeName=\"x2\" dur=\"128s\" values=\"250;2250\" keyTimes=\"0;1\" repeatCount=\"indefinite\" fill=\"freeze\" />\n",
+
"</linearGradient>\n",
+
"<path d=\"M85 438q-1 21 -1 42q0 55 8 113q10 80 45.5 156.5t93.5 131.5q51 49 111.5 81.5t115.5 45.5q56 14 111 18q26 2 50 2q25 0 49 -3q44 -5 79.5 -11t54.5 -12l19 -7q-1 7 -3 19.5t-5.5 50t-4.5 73.5v17q0 31 2 69q4 50 12.5 92.5t28 86t47.5 74t72.5 50t100.5 19.5 q63 0 115.5 -21t86.5 -54t60 -73t39.5 -80.5t22 -73.5t10.5 -54l2 -21q2 -16 4.5 -43.5t5.5 -111.5q2 -42 1 -84q0 -40 -1 -82q-3 -82 -17.5 -194t-41.5 -208q57 -27 89 -77t35 -105v-15q0 -47 -16 -95q-18 -54 -55 -93q-28 -30 -63 -47q-36 -17 -68 -20q-15 -1 -30 -1 q-18 -1 -35 1q-33 4 -60 13t-48.5 18t-33.5 15l-12 7q-159 -83 -371 -91q-19 -1 -38 0q-187 0 -324 69q-7 4 -19 11t-45 32t-60.5 54t-56 76t-41.5 99q-17 67 -21 141zM729 483q5 -75 82 -75q35 0 57.5 20.5t22.5 54.5t-22 60t-58 26q-35 -1 -60 -27q-22 -24 -22 -52v-7z\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"WErAiHgL1\" />\n",
+
"<path d=\"M49 548q0 83 3.5 154.5t9.5 112.5l6 41q16 73 54 125t80.5 74.5t83 34.5t66.5 12h27h13q56 0 102 -16q52 -18 79.5 -45.5t46 -55.5t24.5 -47l5 -20q39 56 90.5 91.5t96.5 46t84.5 12.5t62.5 -2l23 -5q58 -15 99.5 -46t60.5 -70t27 -71t9 -65v-14q0 -41 -10 -79 q-12 -44 -30.5 -71t-37 -47t-32.5 -28l-13 -9q-49 -29 -103 -35q-20 -2 -38 -2q-30 0 -58 7q-42 11 -78.5 26.5t-56.5 28.5l-20 14q13 -99 13 -178q0 -34 -2 -64q-8 -102 -36 -168.5t-65.5 -108t-79 -61.5t-76 -27.5t-57.5 -6.5h-23h-9q-42 0 -80 9q-42 10 -71 27.5t-53 39 t-39 43t-25 39.5t-14 29l-4 11q-27 82 -41 181.5t-14 182.5z\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"WErAiHgL2\" />\n",
+
"<path d=\"M59 496q0 22 5 74q5 58 17.5 111t43.5 119t76 116t121.5 88t173.5 47q31 3 60 3q48 0 88 -8q66 -13 103 -34t64 -50t35 -44.5t12 -26.5q2 8 7.5 22t23.5 42.5t42 51t67 40.5t94 18q62 0 112.5 -29t82 -75t54.5 -101.5t33.5 -111t16.5 -101.5t7 -75l1 -29q2 -42 2 -81 q0 -54 -4 -103q-7 -84 -22.5 -141t-37.5 -102t-47.5 -71.5t-51 -44.5t-49.5 -25t-42.5 -10.5t-29.5 -2.5l-11 1q-1 2 -11.5 0t-29.5 2t-41.5 8.5t-46.5 19.5t-45 34.5t-37.5 53t-22.5 75.5q-2 -9 -8 -23.5t-35 -50.5t-71 -62q-42 -25 -122 -40q-45 -8 -95 -8q-41 0 -86 5 q-8 0 -22.5 1.5t-55 12.5t-78 28.5t-83 54.5t-78 85.5t-55 127t-22.5 174.5q-2 17 -2 35zM686 483q5 -75 82 -75q35 0 57.5 20.5t22.5 54.5t-22 60t-58 26q-35 -1 -60 -27q-22 -24 -22 -52v-7z\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"WErAiHgL3\" />\n",
+
"<path d=\"M35 735q-2 23 -2 44q0 43 8 79q12 56 35 90.5t52.5 60.5t59.5 38.5t55.5 19.5t41.5 9l16 1q69 0 125.5 -19.5t94 -50t66.5 -74t45 -83.5t26 -86.5t13.5 -76t4.5 -58.5q2 59 22 101t47 60t54 28t46 10l19 1q44 0 79.5 -13.5t56.5 -34t36.5 -45.5t21.5 -50t9.5 -45.5 t3.5 -33.5v-13q0 81 11 149.5t30 115.5t44 85t52 60t54 38t52 22.5t44 10t30 3.5l11 -1q63 -1 113.5 -20t80.5 -48.5t51 -65t29.5 -70.5t13 -64.5t3.5 -47.5v-18q-3 -96 -18 -187.5t-34.5 -153.5t-38 -110t-31.5 -70l-12 -23q-36 -66 -84.5 -113.5t-91.5 -68.5t-80.5 -32 t-59.5 -11l-23 -1q-92 0 -161.5 26.5t-105 61.5t-57 79t-26.5 71t-5 45q-3 -22 -10 -48t-31 -71t-59 -79.5t-99.5 -60.5t-146.5 -26q-64 0 -121.5 24.5t-94 59.5t-64.5 70t-40 59l-12 25q-60 115 -97 246.5t-45 205.5z\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"WErAiHgL4\" />\n",
+
"<path d=\"M70 169v15v10q0 50 18 90q20 44 54.5 72t76.5 48.5t84 29.5t77 14.5t57 5.5l22 1q-75 18 -134.5 44t-95.5 53t-61.5 56.5t-36.5 55t-16.5 46t-5.5 32.5v12q3 71 34 128t77 91t101.5 59t110.5 35.5t100 16.5t73 6h28q112 -2 194 -16.5t131.5 -36t78.5 -53.5t39 -66 q8 -28 7 -61v-15q-2 -50 -27.5 -88t-64.5 -58t-85.5 -32.5t-92 -15t-83.5 -2.5t-62 2l-23 3q97 -12 174.5 -37.5t123 -56.5t78 -66t46 -67.5t20 -58.5t5.5 -42v-16q0 -73 -24.5 -133t-64 -97.5t-87.5 -65.5t-95.5 -41.5t-87 -21.5t-63.5 -10l-25 -1q-39 -2 -75 -2 q-80 0 -148 8q-98 11 -156.5 32.5t-100.5 48.5t-59.5 55t-27 51.5t-8.5 38.5z\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"WErAiHgL5\" />\n",
+
"<path d=\"M23 773q0 65 18.5 118.5t70 97.5t132.5 60q30 6 58 6q36 1 70 -9q60 -16 101.5 -53.5t76 -85.5t55 -98t34.5 -92t19 -69l6 -27q2 21 7 55t33.5 118t70.5 144t125 98q52 24 113 24q37 0 77 -9q71 -16 115 -55.5t58.5 -90.5t15.5 -109v-8q0 -54 -12 -106q-13 -56 -27 -100 t-26 -72l-12 -27q-4 -10 -12.5 -28t-37 -69.5t-59.5 -98t-80 -104t-99.5 -98.5t-116.5 -69t-133 -28q-98 0 -189 46t-154.5 110t-119.5 148.5t-85 145t-50 115.5q-19 48 -31 101.5t-12 118.5z\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"WErAiHgL6\" />\n",
+
"<path d=\"M85 390q-11 61 -11 117q0 90 28 167q21 57 51 108.5t58.5 83t54 55.5t40.5 34l16 10q129 77 288 86q26 1 51 1q131 0 246 -40q137 -47 228 -133q63 -59 106.5 -140.5t64 -164t30.5 -168.5q7 -64 7 -123q0 -18 -1 -36q-3 -74 -8 -133t-11 -92l-6 -34 q-21 -119 -64.5 -213.5t-99.5 -153.5t-122.5 -101t-135 -60t-135.5 -27q-46 -6 -88 -6q-18 0 -36 1q-59 4 -103.5 9t-70.5 11l-26 7q-76 27 -120 68t-54 81q-7 32 -7 62v12q2 36 11 58l10 22q17 37 48 61t64 32t64 11t50 1l20 -2q68 3 116.5 20t69 39t31.5 44t11 37v15 q-14 -4 -39 -8.5t-96 -8.5q-24 -1 -47 -1q-46 0 -88 5q-65 8 -143.5 41.5t-133.5 92.5q-89 94 -118 253zM641 444q5 -75 82 -75q35 0 57.5 20.5t22.5 54.5t-22 60t-58 26q-35 0 -60 -26q-22 -24 -22 -53v-7z\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"WErAiHgL7\" />\n",
+
"<path d=\"\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"WErAiHgL8\" />\n",
+
"<path d=\"M59 1126q-2 56 18.5 104.5t56 80t79 57t87.5 39.5t80.5 23.5t59.5 12.5l23 3q78 6 147 -1.5t117 -24.5t87.5 -39t63.5 -45.5t40.5 -43.5t23.5 -32l6 -13q39 -73 52 -152t2 -148t-33.5 -135.5t-52.5 -118t-57 -91.5t-45 -61l-19 -21h19t46.5 -4.5t66 -13.5t69.5 -29.5 t65 -49.5t45 -76.5t16 -106.5q-1 -45 -22 -82.5t-55 -63t-77.5 -44.5t-91.5 -30t-95.5 -18t-90.5 -9t-76.5 -2.5t-53.5 0.5l-19 1q-70 0 -130 8.5t-103 22.5t-78 32t-57.5 38t-39 40t-25 38t-13.5 32t-6 22l-1 8q-8 71 3.5 127t51.5 107t69 78t97 81q14 12 22 18.5t21 16.5 t23 19q30 25 53.5 53.5t39.5 60t15.5 64.5t-20.5 60t-48 40.5t-54 13t-49.5 -4t-37.5 -9.5l-14 -6q-51 -10 -90.5 4t-59.5 41.5t-32.5 56.5t-15.5 50z\" transform=\"scale(0.0341796875,-0.0341796875)\" id=\"WErAiHgL9\" />\n",
+
"<clipPath id=\"WErAiHgL10\">\n",
+
"<use xlink:href=\"#WErAiHgL1\" x=\"-202.12158203125\" y=\"17.5\">\n",
+
"<animate attributeName=\"x\" dur=\"128s\" values=\"-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"<animate attributeName=\"y\" dur=\"128s\" values=\"19.324137087557;19.291309948237398;15.726205470907235;15.839487980635687;18.841995512517798;18.443879956274092;18.178921605760884;16.732545830356578;17.92377666271385;17.92720693456335;17.824816068448012;16.133531481019222;17.222678561165075;17.074127280821486;18.39204832494986;19.47927825179897;19.297581892372975;17.67670818971728;17.279416754903416;16.572962966597313;15.64369731757143;15.609779428363275;17.359575448389247;16.77386051141471;17.020059687602846;19.06715783131315;17.603011076584114;17.742041444105997;16.444493628460247;15.595432316563128;16.80057171504464;16.046789571945865;17.540895383348804;19.49473427277021;18.19791878938348;16.227373987292577;19.074286146331954;18.687039685686557;18.43760676757591;19.126374599590243;18.551541935332285;18.658990549847054;16.915147911366414;19.423906292288507;19.347603751592903;16.144738613216077;18.51601628660749;18.360603592949815;17.34562679096791;17.62142286449378;17.460055687400764;19.199328288378283;17.50336425052262;18.82609795916725;16.915696819474864;19.031403674325013;19.098802355026503;17.34404865952655;17.770820281680976;19.181321756767716;18.395091815488072;17.44643421944634;16.387244043964042;16.798668975075593;19.324137087557\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"</use>\n",
+
"<use xlink:href=\"#WErAiHgL2\" x=\"-149.79248046875\" y=\"17.5\">\n",
+
"<animate attributeName=\"x\" dur=\"128s\" values=\"-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"<animate attributeName=\"y\" dur=\"128s\" values=\"18.298286552280974;16.164278741976503;19.13176198650438;16.572550051599265;19.145511343472187;16.73825249979784;19.32944684622463;18.324823225470418;17.51699526793327;17.570991024594207;18.105657595867164;17.85177884717794;16.747377298204;16.331273898151707;17.54756663342113;19.236617436535113;17.99306034690349;15.801501476296181;18.781599978848067;18.40379714990919;19.13061448380528;16.265610933216472;18.479130897109417;15.735035585594623;18.1116397097382;16.592398929348597;16.406466116979054;19.001964685792952;15.9250639305821;17.58945066143572;18.91577202873949;16.47932791187607;16.341915754782587;19.022327037465118;17.191670593558783;18.3678443956199;15.627492280506981;16.949427645212964;16.18752396850293;18.19106176565417;15.831612709618302;19.31824866138299;15.601378859307605;18.417694029767215;15.584579478892604;16.52276021622977;18.753417549609104;16.1284731547471;16.234955237003906;18.26598170405436;17.04226352541024;15.672643983189998;19.460006184811206;16.105680435026205;15.645075976973656;16.87680402214718;17.96095793329943;18.46983849250312;15.952459612072307;16.848855092786582;15.623243430506754;17.294613049969175;18.563879746352313;18.4597866548828;18.298286552280974\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"</use>\n",
+
"<use xlink:href=\"#WErAiHgL3\" x=\"-107.95654296875\" y=\"17.5\">\n",
+
"<animate attributeName=\"x\" dur=\"128s\" values=\"-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"<animate attributeName=\"y\" dur=\"128s\" values=\"19.108080633400146;18.52264861470026;18.949783105301712;18.32138056020306;17.391118048393253;16.40211028139155;18.143313994628055;16.765223706913037;15.908196420198793;17.291287445676605;18.99905216539187;16.010145858475152;17.83982279244243;17.07181020042067;17.55921078705671;16.075317855907777;19.338924745948642;16.53638569300882;17.92431175621401;17.179022182622987;15.572132876499706;17.73180049535632;16.062277515845338;15.727123983292785;15.634224984994589;16.144660060724938;15.883487774532732;18.04030279015467;17.533036736142574;19.43386437619011;19.236521274787226;19.478100933038014;16.42989536267124;17.278789820478785;16.503123047364628;17.86494938219199;17.99665620301913;18.700829822537585;18.337993215317397;16.52643715395214;17.192067691050084;17.60475977512234;15.519299124214513;15.641997646831058;17.134905670888298;15.944699868919905;18.39507869147722;16.46346205776665;15.899092347019964;16.2270403132017;16.426101717508562;16.369414538873436;17.582945456191588;17.35761244457536;16.73890428350965;18.0670350307227;16.34979896797796;19.12625070692673;19.35246661931695;18.415724182310097;17.23493547515866;17.54600536886819;17.82430522416064;15.704938974423321;19.108080633400146\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"</use>\n",
+
"<use xlink:href=\"#WErAiHgL4\" x=\"-58.70361328125\" y=\"17.5\">\n",
+
"<animate attributeName=\"x\" dur=\"128s\" values=\"-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"<animate attributeName=\"y\" dur=\"128s\" values=\"17.172065553929826;17.600258129279048;16.224900243300688;15.875147153833273;18.710620834821544;16.96473586617359;17.576838760395944;19.185801390367477;17.942041348412122;16.65832307012065;19.434084294213275;16.988906839736536;15.576220419238417;18.24124268925948;15.904647502452628;16.72368944282506;18.862446748475364;18.190287018579063;15.562888288857353;17.30569383680793;17.142697502294027;17.443451776008004;16.33298757343119;17.854980248616833;15.795157253901012;16.637437403921545;16.991608420187873;19.241081736194044;15.806192881440081;18.519936456991594;16.269436511867294;17.786210965317323;17.06712388145699;17.35289752899603;18.514322022942874;17.080170240631453;15.986917920534678;15.98708039888616;15.822042871014519;18.90028349561939;18.06396637532426;19.33867425358325;18.270610186618597;15.598675090616403;18.136638655375673;18.608847738599195;18.394073119932518;17.49179808171847;16.930338470923886;17.32814285580714;18.69488833471872;16.57576997531061;17.605214979225202;17.410238168226087;19.318787387845322;18.71739990767735;19.228215440872955;18.844022294289577;16.687054675658697;16.42650944460629;17.455157893793455;16.537621365444693;17.210615327823714;18.216560861444595;17.172065553929826\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"</use>\n",
+
"<use xlink:href=\"#WErAiHgL5\" x=\"0.22216796875\" y=\"17.5\">\n",
+
"<animate attributeName=\"x\" dur=\"128s\" values=\"0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"<animate attributeName=\"y\" dur=\"128s\" values=\"19.17432090891334;17.843602321380402;18.7714130155966;15.883789243434663;16.924228911880682;19.490992085494522;16.086004191824927;17.167072173899555;15.767357581811265;15.844597426395598;19.082001408378922;19.45454811483057;18.092328311132544;16.014060019669373;16.685530078130782;16.426798573386762;18.182929303799767;18.224396029991347;17.255383347646312;17.595979073255904;15.948281054378096;17.663572996458;19.29975488689121;18.523109210244115;15.884617845083582;17.566005445975094;18.361459270541847;16.529042070197043;19.079586599991057;17.343763856511888;18.3129248390927;17.116653557850963;19.480532150225436;18.63126295102649;17.793761602906123;16.079060091574764;17.264733037914887;15.6175396385709;17.88065670082722;19.027270070813692;16.22169796803166;17.54068601291097;17.42983330112547;17.119658716133877;18.34184031877659;19.246679675941035;18.32157014751221;17.38999671817144;19.347909645664032;16.822910002269136;18.482451019047755;18.133940103096172;18.546435499777793;18.908292911225235;16.399995521690087;17.984996115800016;17.110895538815566;18.167887520270217;19.40893625858238;18.039323386996088;15.546423176316438;17.358195231067338;18.346305933794774;19.03288374740802;19.17432090891334\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"</use>\n",
+
"<use xlink:href=\"#WErAiHgL6\" x=\"39.46044921875\" y=\"17.5\">\n",
+
"<animate attributeName=\"x\" dur=\"128s\" values=\"39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"<animate attributeName=\"y\" dur=\"128s\" values=\"18.100340133230947;18.76427960677236;15.56854958542948;19.272918260590053;18.417854534663782;17.92577580767231;19.121292120680188;19.038718908208274;15.901829522549633;18.762484865120747;18.568002963113543;16.298152186703973;18.476982770007467;17.844909774667702;16.265977695280423;18.716757583973582;16.051492512478955;17.949294825753448;17.23759193664957;16.514764443440473;17.764378493236507;17.36834751703482;16.319989978444156;19.36712314963816;15.791301469511914;15.512149152328888;17.441692517196124;18.848765989216805;18.133608253760478;18.518678354965495;17.44000181332036;18.19920941955595;16.839562548974325;16.567781057283934;17.511602832684538;15.610111265989747;15.819234359666222;18.515838961232788;16.194798551786665;18.501022903279;18.637503921917954;17.117963798501876;18.199972737584474;18.649689162774;18.956096402393687;16.039481015624983;16.150280579909165;17.026653676995508;17.358626773228572;16.679275487537627;15.541600951035464;17.729686691385268;19.367655038887854;16.965838731349027;17.651997680963095;17.029316620955058;17.271201914680454;18.981974830520677;16.733721777745423;18.096260968845616;17.435146143723358;17.654276690218072;19.158812502107267;15.806856043874703;18.100340133230947\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"</use>\n",
+
"<use xlink:href=\"#WErAiHgL7\" x=\"83.89404296875\" y=\"17.5\">\n",
+
"<animate attributeName=\"x\" dur=\"128s\" values=\"83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"<animate attributeName=\"y\" dur=\"128s\" values=\"18.797481725009643;16.71667722862086;18.085233090770295;18.68336392379322;18.113637764536453;17.07186625951781;18.86281907321502;15.87179519261299;18.033257609922067;17.064503240471847;17.621863956694423;18.903764341757842;18.691459521790456;18.01536068585924;16.73231712105784;16.431655286240023;17.330152856150356;16.428438291628133;16.60994613353222;19.33101857042053;15.947864254575299;18.774465494972127;17.016856397885;16.958413517883915;16.773566048028542;15.809542248687551;17.32952080257834;16.16598872833203;17.26803588477986;16.667948647173095;19.078292820866682;19.186969808294943;17.267985939703188;18.058480895039562;19.21856883981926;16.804906581609988;15.898217555514494;16.45136751141817;16.25818481303237;18.213882620519925;16.99515331343813;16.92439166129364;18.680390540819793;16.432688333649505;18.734145478922123;18.031626653015802;17.101040476813708;18.794076465052875;16.869012990020046;19.01432525277344;19.20370368942909;17.510425286914632;18.25993322527753;19.29512844444772;18.470239621383687;18.504028192574403;18.977240613237264;19.24228357172442;18.514137267158166;19.41627674449577;16.666423873065096;17.989944804817238;18.18263173319903;16.96973709191637;18.797481725009643\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"</use>\n",
+
"<use xlink:href=\"#WErAiHgL8\" x=\"131.47216796875\" y=\"17.5\">\n",
+
"<animate attributeName=\"x\" dur=\"128s\" values=\"131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"<animate attributeName=\"y\" dur=\"128s\" values=\"17.080711140751834;16.19909689599043;19.330849197546666;16.916014088414155;17.406536045303866;19.074260262355665;16.245800355520224;19.34267799195689;16.00822293662995;15.612118098267;16.903120016070773;16.936699184091;19.170577284050097;19.032777003417344;18.546243746631024;17.245711595069945;17.670745620739233;16.447078997643914;18.83411877139345;17.059644447003237;16.63861329760842;18.05122404994667;16.102314363712715;16.765402084652465;19.204710140778165;15.880181639513852;16.06879981708168;16.31738246474658;16.503920112292988;17.181590210211635;16.50070491698806;16.870759611949783;16.48592557630948;16.460353724003927;17.94241902371801;16.8458323460846;16.991156120537948;18.57126625214372;15.746753360697536;16.076169555707967;18.903294416161224;17.219109518635328;18.615213998315095;16.031173775225227;17.591960357928016;18.881495956075632;16.852166940218158;18.572714469152427;17.941504268568327;17.07829440318809;19.489404918376827;17.06921114821956;17.395173504645857;17.977942820530266;16.767355190057494;18.850555915282087;17.890144231633766;17.852002367688048;17.654345591803718;19.43973441409201;19.455720406809302;18.863165428982644;17.31830481199631;17.147159111082434;17.080711140751834\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"</use>\n",
+
"<use xlink:href=\"#WErAiHgL9\" x=\"161.41357421875\" y=\"17.5\">\n",
+
"<animate attributeName=\"x\" dur=\"128s\" values=\"161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"<animate attributeName=\"y\" dur=\"128s\" values=\"17.599067165036384;15.684629436697106;15.933017326931099;19.48103060100494;16.012842062665598;19.2495377040299;18.21891658008112;19.160361116382305;15.80934600212031;16.723241372635748;18.691710970024175;15.535381801514223;15.923839916845994;16.902564830201552;16.192552924899463;16.087440861085202;18.17905744860602;15.86769170570407;19.3860173767723;18.097445829672445;15.699066515511975;19.094881340111694;16.466106118697553;17.425856057986543;17.735066325377616;16.054534187786356;17.50863562649949;15.741235287438158;16.29842816430794;19.174294144100713;18.788194352812756;17.591539631414015;18.22738288427816;19.00201258774057;16.059864800319716;17.468411654278867;16.027054087523638;15.966081954440476;15.932941808294345;16.3471435248239;15.712626296759733;16.3608679958098;17.016526709101825;17.99075155400056;18.934422862346082;19.116739330737822;18.370339170828384;17.528350763945213;19.16794945822426;16.15198700162683;15.92178177336796;18.771235410910514;18.008527128612236;16.341258113785702;17.009200698193677;16.689575235266634;17.22345328759322;17.21091887743478;17.092623332550826;18.69095955190943;18.746019425967766;17.74985102116934;17.39111954785501;16.63783697865552;17.599067165036384\" keyTimes=\"0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1\" repeatCount=\"indefinite\" fill=\"freeze\" calcMode=\"spline\" keySplines=\"0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1\" />\n",
+
"</use>\n",
+
"</clipPath>\n",
+
"</defs>\n",
+
"<rect x=\"-500\" y=\"-50\" width=\"1000\" height=\"100\" fill=\"url(#WErAiHgL0)\" clip-path=\"url(#WErAiHgL10)\" />\n",
+
"<g id=\"scrub\" visibility=\"hidden\">\n",
+
"<path d=\"M-168.0,40.0 L168.0,40.0\" stroke=\"#ccc\" stroke-width=\"4\" stroke-linecap=\"round\" />\n",
+
"<rect x=\"-168.0\" y=\"40.0\" width=\"0\" height=\"0.001\" stroke=\"#05f\" stroke-width=\"4\" stroke-linejoin=\"round\">\n",
+
"<animate attributeName=\"width\" dur=\"128s\" values=\"0;336\" keyTimes=\"0;1\" repeatCount=\"indefinite\" fill=\"freeze\" />\n",
+
"</rect>\n",
+
"<g id=\"scrub-capture\" data-xmin=\"-168.0\" data-xmax=\"168.0\" data-totaldur=\"128\" data-startdelay=\"0\" data-enddelay=\"0\" data-pauseonload=\"0\">\n",
+
"<rect x=\"-170.0\" y=\"30.0\" width=\"340\" height=\"20\" fill=\"rgba(255,255,255,0)\" />\n",
+
"<circle cx=\"-168.0\" cy=\"40.0\" r=\"6\" fill=\"#05f\" id=\"scrub-knob\" visibility=\"hidden\">\n",
+
"<animate attributeName=\"cx\" dur=\"128s\" values=\"-168.0;168.0\" keyTimes=\"0;1\" repeatCount=\"indefinite\" fill=\"freeze\" />\n",
+
"</circle>\n",
+
"</g>\n",
+
"<g id=\"scrub-play\" visibility=\"hidden\">\n",
+
"<rect x=\"-191.0\" y=\"36.0\" width=\"8\" height=\"8\" fill=\"#05f\" stroke=\"#05f\" stroke-width=\"8\" stroke-linejoin=\"round\" />\n",
+
"<path d=\"M-191.0,36.0 v8.0 l8.0,-4.0 Z\" fill=\"#eee\" />\n",
+
"</g>\n",
+
"<g id=\"scrub-pause\" visibility=\"hidden\">\n",
+
"<rect x=\"-191.0\" y=\"36.0\" width=\"8\" height=\"8\" fill=\"#05f\" stroke=\"#05f\" stroke-width=\"8\" stroke-linejoin=\"round\" />\n",
+
"<rect x=\"-190.0\" y=\"36.0\" width=\"2.0\" height=\"8.0\" fill=\"#eee\" />\n",
+
"<rect x=\"-186.0\" y=\"36.0\" width=\"2.0\" height=\"8.0\" fill=\"#eee\" />\n",
+
"</g>\n",
+
"</g>\n",
+
"<script>/*<![CDATA[*/\n",
+
"/* Animation playback controls generated by drawsvg */\n",
+
"/* https://github.com/cduck/drawsvg/ */\n",
+
"function svgOnLoad(event) {\n",
+
" /* Support standalone SVG or embedded in HTML or iframe */\n",
+
" if (event && event.target && event.target.ownerDocument) {\n",
+
" svgSetup(event.target.ownerDocument);\n",
+
" } else if (document && document.currentScript\n",
+
" && document.currentScript.parentElement) {\n",
+
" svgSetup(document.currentScript.parentElement);\n",
+
" }\n",
+
"}\n",
+
"function svgSetup(doc) {\n",
+
" var svgRoot = doc.documentElement || doc;\n",
+
" var scrubCapture = doc.getElementById(\"scrub-capture\");\n",
+
" /* Block multiple setups */\n",
+
" if (!scrubCapture || scrubCapture.getAttribute(\"svgSetupDone\")) {\n",
+
" return;\n",
+
" }\n",
+
" scrubCapture.setAttribute(\"svgSetupDone\", true);\n",
+
" var scrubContainer = doc.getElementById(\"scrub\");\n",
+
" var scrubPlay = doc.getElementById(\"scrub-play\");\n",
+
" var scrubPause = doc.getElementById(\"scrub-pause\");\n",
+
" var scrubKnob = doc.getElementById(\"scrub-knob\");\n",
+
" var scrubXMin = parseFloat(scrubCapture.dataset.xmin);\n",
+
" var scrubXMax = parseFloat(scrubCapture.dataset.xmax);\n",
+
" var scrubTotalDur = parseFloat(scrubCapture.dataset.totaldur);\n",
+
" var scrubStartDelay = parseFloat(scrubCapture.dataset.startdelay);\n",
+
" var scrubEndDelay = parseFloat(scrubCapture.dataset.enddelay);\n",
+
" var scrubPauseOnLoad = parseFloat(scrubCapture.dataset.pauseonload);\n",
+
" var paused = false;\n",
+
" var dragXOffset = 0;\n",
+
" var point = svgRoot.createSVGPoint();\n",
+
"\n",
+
" function screenToSvgX(p) {\n",
+
" var matrix = scrubKnob.getScreenCTM().inverse();\n",
+
" point.x = p.x;\n",
+
" point.y = p.y;\n",
+
" return point.matrixTransform(matrix).x;\n",
+
" };\n",
+
" function screenToProgress(p) {\n",
+
" var matrix = scrubKnob.getScreenCTM().inverse();\n",
+
" point.x = p.x;\n",
+
" point.y = p.y;\n",
+
" var x = point.matrixTransform(matrix).x;\n",
+
" if (x <= scrubXMin) {\n",
+
" return scrubStartDelay / scrubTotalDur;\n",
+
" }\n",
+
" if (x >= scrubXMax) {\n",
+
" return (scrubTotalDur - scrubEndDelay) / scrubTotalDur;\n",
+
" }\n",
+
" return (scrubStartDelay/scrubTotalDur\n",
+
" + (x - dragXOffset - scrubXMin)\n",
+
" / (scrubXMax - scrubXMin)\n",
+
" * (scrubTotalDur - scrubStartDelay - scrubEndDelay)\n",
+
" / scrubTotalDur);\n",
+
" };\n",
+
" function currentScrubX() {\n",
+
" return scrubKnob.cx.animVal.value;\n",
+
" };\n",
+
" function pause() {\n",
+
" svgRoot.pauseAnimations();\n",
+
" scrubPlay.setAttribute(\"visibility\", \"visible\");\n",
+
" scrubPause.setAttribute(\"visibility\", \"hidden\");\n",
+
" paused = true;\n",
+
" };\n",
+
" function play() {\n",
+
" svgRoot.unpauseAnimations();\n",
+
" scrubPause.setAttribute(\"visibility\", \"visible\");\n",
+
" scrubPlay.setAttribute(\"visibility\", \"hidden\");\n",
+
" paused = false;\n",
+
" };\n",
+
" function scrub(playbackFraction) {\n",
+
" var t = scrubTotalDur * playbackFraction;\n",
+
" /* Stop 10ms before end to avoid loop (>=1ms needed on FF) */\n",
+
" var limit = scrubTotalDur - 10e-3;\n",
+
" if (t < 0) t = 0;\n",
+
" else if (t > limit) t = limit;\n",
+
" svgRoot.setCurrentTime(t);\n",
+
" };\n",
+
" function mousedown(e) {\n",
+
" svgRoot.pauseAnimations();\n",
+
" if (e.target == scrubKnob) {\n",
+
" dragXOffset = screenToSvgX(e) - currentScrubX();\n",
+
" } else {\n",
+
" dragXOffset = 0;\n",
+
" }\n",
+
" scrub(screenToProgress(e));\n",
+
" /* Global document listeners */\n",
+
" document.addEventListener('mousemove', mousemove);\n",
+
" document.addEventListener('mouseup', mouseup);\n",
+
" e.preventDefault();\n",
+
" };\n",
+
" function mouseup(e) {\n",
+
" dragXOffset = 0;\n",
+
" document.removeEventListener('mousemove', mousemove);\n",
+
" document.removeEventListener('mouseup', mouseup);\n",
+
" if (!paused) {\n",
+
" svgRoot.unpauseAnimations();\n",
+
" }\n",
+
" e.preventDefault();\n",
+
" };\n",
+
" function mousemove(e) {\n",
+
" scrub(screenToProgress(e));\n",
+
" };\n",
+
" scrubPause.addEventListener(\"click\", pause);\n",
+
" scrubPlay.addEventListener(\"click\", play);\n",
+
" scrubCapture.addEventListener(\"mousedown\", mousedown);\n",
+
" scrubContainer.setAttribute(\"visibility\", \"visible\");\n",
+
" scrubKnob.setAttribute(\"visibility\", \"visible\");\n",
+
" if (scrubPauseOnLoad) {\n",
+
" pause();\n",
+
" scrub(0);\n",
+
" } else {\n",
+
" play();\n",
+
" }\n",
+
"};\n",
+
"svgOnLoad();\n",
+
"/*]]>*/</script>\n",
+
"</svg>"
+
],
+
"text/plain": [
+
"<drawsvg.drawing.Drawing at 0x1061aa9b0>"
+
]
+
},
+
"execution_count": 2,
+
"metadata": {},
+
"output_type": "execute_result"
+
}
+
],
+
"source": [
+
"d # Display as embedded SVG"
+
]
+
},
+
{
+
"cell_type": "code",
+
"execution_count": 3,
+
"id": "30cb15ba",
+
"metadata": {},
+
"outputs": [
+
{
+
"data": {
+
"image/png": "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\n",
+
"text/plain": [
+
"<drawsvg.raster.Raster at 0x1061a9d20>"
+
]
+
},
+
"execution_count": 3,
+
"metadata": {},
+
"output_type": "execute_result"
+
}
+
],
+
"source": [
+
"d.rasterize() # Display as static PNG"
+
]
+
}
+
],
+
"metadata": {
+
"kernelspec": {
+
"display_name": "Python 3",
+
"language": "python",
+
"name": "python3"
+
},
+
"language_info": {
+
"codemirror_mode": {
+
"name": "ipython",
+
"version": 3
+
},
+
"file_extension": ".py",
+
"mimetype": "text/x-python",
+
"name": "python",
+
"nbconvert_exporter": "python",
+
"pygments_lexer": "ipython3",
+
"version": "3.10.4"
+
}
+
},
+
"nbformat": 4,
+
"nbformat_minor": 5
+
}
+204
examples/logo.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="100" viewBox="-200.0 -50.0 400 100" onload="svgOnLoad(event);">
+
<defs>
+
<linearGradient x1="-1250" y1="0" x2="1250" y2="0" gradientUnits="userSpaceOnUse" id="d0">
+
<stop offset="0" stop-color="#5544ee" stop-opacity="1" />
+
<stop offset="0.2" stop-color="#5544ee" stop-opacity="1" />
+
<stop offset="0.4" stop-color="#ee0055" stop-opacity="1" />
+
<stop offset="0.6" stop-color="#ee0055" stop-opacity="1" />
+
<stop offset="0.8" stop-color="#5544ee" stop-opacity="1" />
+
<stop offset="1" stop-color="#5544ee" stop-opacity="1" />
+
<animate attributeName="x1" dur="128s" values="-2250;-250" keyTimes="0;1" repeatCount="indefinite" fill="freeze" />
+
<animate attributeName="x2" dur="128s" values="250;2250" keyTimes="0;1" repeatCount="indefinite" fill="freeze" />
+
</linearGradient>
+
<path d="M85 438q-1 21 -1 42q0 55 8 113q10 80 45.5 156.5t93.5 131.5q51 49 111.5 81.5t115.5 45.5q56 14 111 18q26 2 50 2q25 0 49 -3q44 -5 79.5 -11t54.5 -12l19 -7q-1 7 -3 19.5t-5.5 50t-4.5 73.5v17q0 31 2 69q4 50 12.5 92.5t28 86t47.5 74t72.5 50t100.5 19.5 q63 0 115.5 -21t86.5 -54t60 -73t39.5 -80.5t22 -73.5t10.5 -54l2 -21q2 -16 4.5 -43.5t5.5 -111.5q2 -42 1 -84q0 -40 -1 -82q-3 -82 -17.5 -194t-41.5 -208q57 -27 89 -77t35 -105v-15q0 -47 -16 -95q-18 -54 -55 -93q-28 -30 -63 -47q-36 -17 -68 -20q-15 -1 -30 -1 q-18 -1 -35 1q-33 4 -60 13t-48.5 18t-33.5 15l-12 7q-159 -83 -371 -91q-19 -1 -38 0q-187 0 -324 69q-7 4 -19 11t-45 32t-60.5 54t-56 76t-41.5 99q-17 67 -21 141zM729 483q5 -75 82 -75q35 0 57.5 20.5t22.5 54.5t-22 60t-58 26q-35 -1 -60 -27q-22 -24 -22 -52v-7z" transform="scale(0.0341796875,-0.0341796875)" id="d1" />
+
<path d="M49 548q0 83 3.5 154.5t9.5 112.5l6 41q16 73 54 125t80.5 74.5t83 34.5t66.5 12h27h13q56 0 102 -16q52 -18 79.5 -45.5t46 -55.5t24.5 -47l5 -20q39 56 90.5 91.5t96.5 46t84.5 12.5t62.5 -2l23 -5q58 -15 99.5 -46t60.5 -70t27 -71t9 -65v-14q0 -41 -10 -79 q-12 -44 -30.5 -71t-37 -47t-32.5 -28l-13 -9q-49 -29 -103 -35q-20 -2 -38 -2q-30 0 -58 7q-42 11 -78.5 26.5t-56.5 28.5l-20 14q13 -99 13 -178q0 -34 -2 -64q-8 -102 -36 -168.5t-65.5 -108t-79 -61.5t-76 -27.5t-57.5 -6.5h-23h-9q-42 0 -80 9q-42 10 -71 27.5t-53 39 t-39 43t-25 39.5t-14 29l-4 11q-27 82 -41 181.5t-14 182.5z" transform="scale(0.0341796875,-0.0341796875)" id="d2" />
+
<path d="M59 496q0 22 5 74q5 58 17.5 111t43.5 119t76 116t121.5 88t173.5 47q31 3 60 3q48 0 88 -8q66 -13 103 -34t64 -50t35 -44.5t12 -26.5q2 8 7.5 22t23.5 42.5t42 51t67 40.5t94 18q62 0 112.5 -29t82 -75t54.5 -101.5t33.5 -111t16.5 -101.5t7 -75l1 -29q2 -42 2 -81 q0 -54 -4 -103q-7 -84 -22.5 -141t-37.5 -102t-47.5 -71.5t-51 -44.5t-49.5 -25t-42.5 -10.5t-29.5 -2.5l-11 1q-1 2 -11.5 0t-29.5 2t-41.5 8.5t-46.5 19.5t-45 34.5t-37.5 53t-22.5 75.5q-2 -9 -8 -23.5t-35 -50.5t-71 -62q-42 -25 -122 -40q-45 -8 -95 -8q-41 0 -86 5 q-8 0 -22.5 1.5t-55 12.5t-78 28.5t-83 54.5t-78 85.5t-55 127t-22.5 174.5q-2 17 -2 35zM686 483q5 -75 82 -75q35 0 57.5 20.5t22.5 54.5t-22 60t-58 26q-35 -1 -60 -27q-22 -24 -22 -52v-7z" transform="scale(0.0341796875,-0.0341796875)" id="d3" />
+
<path d="M35 735q-2 23 -2 44q0 43 8 79q12 56 35 90.5t52.5 60.5t59.5 38.5t55.5 19.5t41.5 9l16 1q69 0 125.5 -19.5t94 -50t66.5 -74t45 -83.5t26 -86.5t13.5 -76t4.5 -58.5q2 59 22 101t47 60t54 28t46 10l19 1q44 0 79.5 -13.5t56.5 -34t36.5 -45.5t21.5 -50t9.5 -45.5 t3.5 -33.5v-13q0 81 11 149.5t30 115.5t44 85t52 60t54 38t52 22.5t44 10t30 3.5l11 -1q63 -1 113.5 -20t80.5 -48.5t51 -65t29.5 -70.5t13 -64.5t3.5 -47.5v-18q-3 -96 -18 -187.5t-34.5 -153.5t-38 -110t-31.5 -70l-12 -23q-36 -66 -84.5 -113.5t-91.5 -68.5t-80.5 -32 t-59.5 -11l-23 -1q-92 0 -161.5 26.5t-105 61.5t-57 79t-26.5 71t-5 45q-3 -22 -10 -48t-31 -71t-59 -79.5t-99.5 -60.5t-146.5 -26q-64 0 -121.5 24.5t-94 59.5t-64.5 70t-40 59l-12 25q-60 115 -97 246.5t-45 205.5z" transform="scale(0.0341796875,-0.0341796875)" id="d4" />
+
<path d="M70 169v15v10q0 50 18 90q20 44 54.5 72t76.5 48.5t84 29.5t77 14.5t57 5.5l22 1q-75 18 -134.5 44t-95.5 53t-61.5 56.5t-36.5 55t-16.5 46t-5.5 32.5v12q3 71 34 128t77 91t101.5 59t110.5 35.5t100 16.5t73 6h28q112 -2 194 -16.5t131.5 -36t78.5 -53.5t39 -66 q8 -28 7 -61v-15q-2 -50 -27.5 -88t-64.5 -58t-85.5 -32.5t-92 -15t-83.5 -2.5t-62 2l-23 3q97 -12 174.5 -37.5t123 -56.5t78 -66t46 -67.5t20 -58.5t5.5 -42v-16q0 -73 -24.5 -133t-64 -97.5t-87.5 -65.5t-95.5 -41.5t-87 -21.5t-63.5 -10l-25 -1q-39 -2 -75 -2 q-80 0 -148 8q-98 11 -156.5 32.5t-100.5 48.5t-59.5 55t-27 51.5t-8.5 38.5z" transform="scale(0.0341796875,-0.0341796875)" id="d5" />
+
<path d="M23 773q0 65 18.5 118.5t70 97.5t132.5 60q30 6 58 6q36 1 70 -9q60 -16 101.5 -53.5t76 -85.5t55 -98t34.5 -92t19 -69l6 -27q2 21 7 55t33.5 118t70.5 144t125 98q52 24 113 24q37 0 77 -9q71 -16 115 -55.5t58.5 -90.5t15.5 -109v-8q0 -54 -12 -106q-13 -56 -27 -100 t-26 -72l-12 -27q-4 -10 -12.5 -28t-37 -69.5t-59.5 -98t-80 -104t-99.5 -98.5t-116.5 -69t-133 -28q-98 0 -189 46t-154.5 110t-119.5 148.5t-85 145t-50 115.5q-19 48 -31 101.5t-12 118.5z" transform="scale(0.0341796875,-0.0341796875)" id="d6" />
+
<path d="M85 390q-11 61 -11 117q0 90 28 167q21 57 51 108.5t58.5 83t54 55.5t40.5 34l16 10q129 77 288 86q26 1 51 1q131 0 246 -40q137 -47 228 -133q63 -59 106.5 -140.5t64 -164t30.5 -168.5q7 -64 7 -123q0 -18 -1 -36q-3 -74 -8 -133t-11 -92l-6 -34 q-21 -119 -64.5 -213.5t-99.5 -153.5t-122.5 -101t-135 -60t-135.5 -27q-46 -6 -88 -6q-18 0 -36 1q-59 4 -103.5 9t-70.5 11l-26 7q-76 27 -120 68t-54 81q-7 32 -7 62v12q2 36 11 58l10 22q17 37 48 61t64 32t64 11t50 1l20 -2q68 3 116.5 20t69 39t31.5 44t11 37v15 q-14 -4 -39 -8.5t-96 -8.5q-24 -1 -47 -1q-46 0 -88 5q-65 8 -143.5 41.5t-133.5 92.5q-89 94 -118 253zM641 444q5 -75 82 -75q35 0 57.5 20.5t22.5 54.5t-22 60t-58 26q-35 0 -60 -26q-22 -24 -22 -53v-7z" transform="scale(0.0341796875,-0.0341796875)" id="d7" />
+
<path d="" transform="scale(0.0341796875,-0.0341796875)" id="d8" />
+
<path d="M59 1126q-2 56 18.5 104.5t56 80t79 57t87.5 39.5t80.5 23.5t59.5 12.5l23 3q78 6 147 -1.5t117 -24.5t87.5 -39t63.5 -45.5t40.5 -43.5t23.5 -32l6 -13q39 -73 52 -152t2 -148t-33.5 -135.5t-52.5 -118t-57 -91.5t-45 -61l-19 -21h19t46.5 -4.5t66 -13.5t69.5 -29.5 t65 -49.5t45 -76.5t16 -106.5q-1 -45 -22 -82.5t-55 -63t-77.5 -44.5t-91.5 -30t-95.5 -18t-90.5 -9t-76.5 -2.5t-53.5 0.5l-19 1q-70 0 -130 8.5t-103 22.5t-78 32t-57.5 38t-39 40t-25 38t-13.5 32t-6 22l-1 8q-8 71 3.5 127t51.5 107t69 78t97 81q14 12 22 18.5t21 16.5 t23 19q30 25 53.5 53.5t39.5 60t15.5 64.5t-20.5 60t-48 40.5t-54 13t-49.5 -4t-37.5 -9.5l-14 -6q-51 -10 -90.5 4t-59.5 41.5t-32.5 56.5t-15.5 50z" transform="scale(0.0341796875,-0.0341796875)" id="d9" />
+
<clipPath id="d10">
+
<use xlink:href="#d1" x="-202.12158203125" y="17.5">
+
<animate attributeName="x" dur="128s" values="-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125;-202.12158203125" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="19.324137087557;19.291309948237398;15.726205470907235;15.839487980635687;18.841995512517798;18.443879956274092;18.178921605760884;16.732545830356578;17.92377666271385;17.92720693456335;17.824816068448012;16.133531481019222;17.222678561165075;17.074127280821486;18.39204832494986;19.47927825179897;19.297581892372975;17.67670818971728;17.279416754903416;16.572962966597313;15.64369731757143;15.609779428363275;17.359575448389247;16.77386051141471;17.020059687602846;19.06715783131315;17.603011076584114;17.742041444105997;16.444493628460247;15.595432316563128;16.80057171504464;16.046789571945865;17.540895383348804;19.49473427277021;18.19791878938348;16.227373987292577;19.074286146331954;18.687039685686557;18.43760676757591;19.126374599590243;18.551541935332285;18.658990549847054;16.915147911366414;19.423906292288507;19.347603751592903;16.144738613216077;18.51601628660749;18.360603592949815;17.34562679096791;17.62142286449378;17.460055687400764;19.199328288378283;17.50336425052262;18.82609795916725;16.915696819474864;19.031403674325013;19.098802355026503;17.34404865952655;17.770820281680976;19.181321756767716;18.395091815488072;17.44643421944634;16.387244043964042;16.798668975075593;19.324137087557" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
<use xlink:href="#d2" x="-149.79248046875" y="17.5">
+
<animate attributeName="x" dur="128s" values="-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875;-149.79248046875" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="18.298286552280974;16.164278741976503;19.13176198650438;16.572550051599265;19.145511343472187;16.73825249979784;19.32944684622463;18.324823225470418;17.51699526793327;17.570991024594207;18.105657595867164;17.85177884717794;16.747377298204;16.331273898151707;17.54756663342113;19.236617436535113;17.99306034690349;15.801501476296181;18.781599978848067;18.40379714990919;19.13061448380528;16.265610933216472;18.479130897109417;15.735035585594623;18.1116397097382;16.592398929348597;16.406466116979054;19.001964685792952;15.9250639305821;17.58945066143572;18.91577202873949;16.47932791187607;16.341915754782587;19.022327037465118;17.191670593558783;18.3678443956199;15.627492280506981;16.949427645212964;16.18752396850293;18.19106176565417;15.831612709618302;19.31824866138299;15.601378859307605;18.417694029767215;15.584579478892604;16.52276021622977;18.753417549609104;16.1284731547471;16.234955237003906;18.26598170405436;17.04226352541024;15.672643983189998;19.460006184811206;16.105680435026205;15.645075976973656;16.87680402214718;17.96095793329943;18.46983849250312;15.952459612072307;16.848855092786582;15.623243430506754;17.294613049969175;18.563879746352313;18.4597866548828;18.298286552280974" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
<use xlink:href="#d3" x="-107.95654296875" y="17.5">
+
<animate attributeName="x" dur="128s" values="-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875;-107.95654296875" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="19.108080633400146;18.52264861470026;18.949783105301712;18.32138056020306;17.391118048393253;16.40211028139155;18.143313994628055;16.765223706913037;15.908196420198793;17.291287445676605;18.99905216539187;16.010145858475152;17.83982279244243;17.07181020042067;17.55921078705671;16.075317855907777;19.338924745948642;16.53638569300882;17.92431175621401;17.179022182622987;15.572132876499706;17.73180049535632;16.062277515845338;15.727123983292785;15.634224984994589;16.144660060724938;15.883487774532732;18.04030279015467;17.533036736142574;19.43386437619011;19.236521274787226;19.478100933038014;16.42989536267124;17.278789820478785;16.503123047364628;17.86494938219199;17.99665620301913;18.700829822537585;18.337993215317397;16.52643715395214;17.192067691050084;17.60475977512234;15.519299124214513;15.641997646831058;17.134905670888298;15.944699868919905;18.39507869147722;16.46346205776665;15.899092347019964;16.2270403132017;16.426101717508562;16.369414538873436;17.582945456191588;17.35761244457536;16.73890428350965;18.0670350307227;16.34979896797796;19.12625070692673;19.35246661931695;18.415724182310097;17.23493547515866;17.54600536886819;17.82430522416064;15.704938974423321;19.108080633400146" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
<use xlink:href="#d4" x="-58.70361328125" y="17.5">
+
<animate attributeName="x" dur="128s" values="-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125;-58.70361328125" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="17.172065553929826;17.600258129279048;16.224900243300688;15.875147153833273;18.710620834821544;16.96473586617359;17.576838760395944;19.185801390367477;17.942041348412122;16.65832307012065;19.434084294213275;16.988906839736536;15.576220419238417;18.24124268925948;15.904647502452628;16.72368944282506;18.862446748475364;18.190287018579063;15.562888288857353;17.30569383680793;17.142697502294027;17.443451776008004;16.33298757343119;17.854980248616833;15.795157253901012;16.637437403921545;16.991608420187873;19.241081736194044;15.806192881440081;18.519936456991594;16.269436511867294;17.786210965317323;17.06712388145699;17.35289752899603;18.514322022942874;17.080170240631453;15.986917920534678;15.98708039888616;15.822042871014519;18.90028349561939;18.06396637532426;19.33867425358325;18.270610186618597;15.598675090616403;18.136638655375673;18.608847738599195;18.394073119932518;17.49179808171847;16.930338470923886;17.32814285580714;18.69488833471872;16.57576997531061;17.605214979225202;17.410238168226087;19.318787387845322;18.71739990767735;19.228215440872955;18.844022294289577;16.687054675658697;16.42650944460629;17.455157893793455;16.537621365444693;17.210615327823714;18.216560861444595;17.172065553929826" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
<use xlink:href="#d5" x="0.22216796875" y="17.5">
+
<animate attributeName="x" dur="128s" values="0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875;0.22216796875" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="19.17432090891334;17.843602321380402;18.7714130155966;15.883789243434663;16.924228911880682;19.490992085494522;16.086004191824927;17.167072173899555;15.767357581811265;15.844597426395598;19.082001408378922;19.45454811483057;18.092328311132544;16.014060019669373;16.685530078130782;16.426798573386762;18.182929303799767;18.224396029991347;17.255383347646312;17.595979073255904;15.948281054378096;17.663572996458;19.29975488689121;18.523109210244115;15.884617845083582;17.566005445975094;18.361459270541847;16.529042070197043;19.079586599991057;17.343763856511888;18.3129248390927;17.116653557850963;19.480532150225436;18.63126295102649;17.793761602906123;16.079060091574764;17.264733037914887;15.6175396385709;17.88065670082722;19.027270070813692;16.22169796803166;17.54068601291097;17.42983330112547;17.119658716133877;18.34184031877659;19.246679675941035;18.32157014751221;17.38999671817144;19.347909645664032;16.822910002269136;18.482451019047755;18.133940103096172;18.546435499777793;18.908292911225235;16.399995521690087;17.984996115800016;17.110895538815566;18.167887520270217;19.40893625858238;18.039323386996088;15.546423176316438;17.358195231067338;18.346305933794774;19.03288374740802;19.17432090891334" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
<use xlink:href="#d6" x="39.46044921875" y="17.5">
+
<animate attributeName="x" dur="128s" values="39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875;39.46044921875" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="18.100340133230947;18.76427960677236;15.56854958542948;19.272918260590053;18.417854534663782;17.92577580767231;19.121292120680188;19.038718908208274;15.901829522549633;18.762484865120747;18.568002963113543;16.298152186703973;18.476982770007467;17.844909774667702;16.265977695280423;18.716757583973582;16.051492512478955;17.949294825753448;17.23759193664957;16.514764443440473;17.764378493236507;17.36834751703482;16.319989978444156;19.36712314963816;15.791301469511914;15.512149152328888;17.441692517196124;18.848765989216805;18.133608253760478;18.518678354965495;17.44000181332036;18.19920941955595;16.839562548974325;16.567781057283934;17.511602832684538;15.610111265989747;15.819234359666222;18.515838961232788;16.194798551786665;18.501022903279;18.637503921917954;17.117963798501876;18.199972737584474;18.649689162774;18.956096402393687;16.039481015624983;16.150280579909165;17.026653676995508;17.358626773228572;16.679275487537627;15.541600951035464;17.729686691385268;19.367655038887854;16.965838731349027;17.651997680963095;17.029316620955058;17.271201914680454;18.981974830520677;16.733721777745423;18.096260968845616;17.435146143723358;17.654276690218072;19.158812502107267;15.806856043874703;18.100340133230947" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
<use xlink:href="#d7" x="83.89404296875" y="17.5">
+
<animate attributeName="x" dur="128s" values="83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875;83.89404296875" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="18.797481725009643;16.71667722862086;18.085233090770295;18.68336392379322;18.113637764536453;17.07186625951781;18.86281907321502;15.87179519261299;18.033257609922067;17.064503240471847;17.621863956694423;18.903764341757842;18.691459521790456;18.01536068585924;16.73231712105784;16.431655286240023;17.330152856150356;16.428438291628133;16.60994613353222;19.33101857042053;15.947864254575299;18.774465494972127;17.016856397885;16.958413517883915;16.773566048028542;15.809542248687551;17.32952080257834;16.16598872833203;17.26803588477986;16.667948647173095;19.078292820866682;19.186969808294943;17.267985939703188;18.058480895039562;19.21856883981926;16.804906581609988;15.898217555514494;16.45136751141817;16.25818481303237;18.213882620519925;16.99515331343813;16.92439166129364;18.680390540819793;16.432688333649505;18.734145478922123;18.031626653015802;17.101040476813708;18.794076465052875;16.869012990020046;19.01432525277344;19.20370368942909;17.510425286914632;18.25993322527753;19.29512844444772;18.470239621383687;18.504028192574403;18.977240613237264;19.24228357172442;18.514137267158166;19.41627674449577;16.666423873065096;17.989944804817238;18.18263173319903;16.96973709191637;18.797481725009643" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
<use xlink:href="#d8" x="131.47216796875" y="17.5">
+
<animate attributeName="x" dur="128s" values="131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875;131.47216796875" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="17.080711140751834;16.19909689599043;19.330849197546666;16.916014088414155;17.406536045303866;19.074260262355665;16.245800355520224;19.34267799195689;16.00822293662995;15.612118098267;16.903120016070773;16.936699184091;19.170577284050097;19.032777003417344;18.546243746631024;17.245711595069945;17.670745620739233;16.447078997643914;18.83411877139345;17.059644447003237;16.63861329760842;18.05122404994667;16.102314363712715;16.765402084652465;19.204710140778165;15.880181639513852;16.06879981708168;16.31738246474658;16.503920112292988;17.181590210211635;16.50070491698806;16.870759611949783;16.48592557630948;16.460353724003927;17.94241902371801;16.8458323460846;16.991156120537948;18.57126625214372;15.746753360697536;16.076169555707967;18.903294416161224;17.219109518635328;18.615213998315095;16.031173775225227;17.591960357928016;18.881495956075632;16.852166940218158;18.572714469152427;17.941504268568327;17.07829440318809;19.489404918376827;17.06921114821956;17.395173504645857;17.977942820530266;16.767355190057494;18.850555915282087;17.890144231633766;17.852002367688048;17.654345591803718;19.43973441409201;19.455720406809302;18.863165428982644;17.31830481199631;17.147159111082434;17.080711140751834" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
<use xlink:href="#d9" x="161.41357421875" y="17.5">
+
<animate attributeName="x" dur="128s" values="161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875;161.41357421875" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
<animate attributeName="y" dur="128s" values="17.599067165036384;15.684629436697106;15.933017326931099;19.48103060100494;16.012842062665598;19.2495377040299;18.21891658008112;19.160361116382305;15.80934600212031;16.723241372635748;18.691710970024175;15.535381801514223;15.923839916845994;16.902564830201552;16.192552924899463;16.087440861085202;18.17905744860602;15.86769170570407;19.3860173767723;18.097445829672445;15.699066515511975;19.094881340111694;16.466106118697553;17.425856057986543;17.735066325377616;16.054534187786356;17.50863562649949;15.741235287438158;16.29842816430794;19.174294144100713;18.788194352812756;17.591539631414015;18.22738288427816;19.00201258774057;16.059864800319716;17.468411654278867;16.027054087523638;15.966081954440476;15.932941808294345;16.3471435248239;15.712626296759733;16.3608679958098;17.016526709101825;17.99075155400056;18.934422862346082;19.116739330737822;18.370339170828384;17.528350763945213;19.16794945822426;16.15198700162683;15.92178177336796;18.771235410910514;18.008527128612236;16.341258113785702;17.009200698193677;16.689575235266634;17.22345328759322;17.21091887743478;17.092623332550826;18.69095955190943;18.746019425967766;17.74985102116934;17.39111954785501;16.63783697865552;17.599067165036384" keyTimes="0;0.016;0.031;0.047;0.062;0.078;0.094;0.109;0.125;0.141;0.156;0.172;0.188;0.203;0.219;0.234;0.25;0.266;0.281;0.297;0.312;0.328;0.344;0.359;0.375;0.391;0.406;0.422;0.438;0.453;0.469;0.484;0.5;0.516;0.531;0.547;0.562;0.578;0.594;0.609;0.625;0.641;0.656;0.672;0.688;0.703;0.719;0.734;0.75;0.766;0.781;0.797;0.812;0.828;0.844;0.859;0.875;0.891;0.906;0.922;0.938;0.953;0.969;0.984;1" repeatCount="indefinite" fill="freeze" calcMode="spline" keySplines="0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1;0.5 0 0.5 1" />
+
</use>
+
</clipPath>
+
</defs>
+
<rect x="-500" y="-50" width="1000" height="100" fill="url(#d0)" clip-path="url(#d10)" />
+
<g id="scrub" visibility="hidden">
+
<path d="M-168.0,40.0 L168.0,40.0" stroke="#ccc" stroke-width="4" stroke-linecap="round" />
+
<rect x="-168.0" y="40.0" width="0" height="0.001" stroke="#05f" stroke-width="4" stroke-linejoin="round">
+
<animate attributeName="width" dur="128s" values="0;336" keyTimes="0;1" repeatCount="indefinite" fill="freeze" />
+
</rect>
+
<g id="scrub-capture" data-xmin="-168.0" data-xmax="168.0" data-totaldur="128" data-startdelay="0" data-enddelay="0" data-pauseonload="0">
+
<rect x="-170.0" y="30.0" width="340" height="20" fill="rgba(255,255,255,0)" />
+
<circle cx="-168.0" cy="40.0" r="6" fill="#05f" id="scrub-knob" visibility="hidden">
+
<animate attributeName="cx" dur="128s" values="-168.0;168.0" keyTimes="0;1" repeatCount="indefinite" fill="freeze" />
+
</circle>
+
</g>
+
<g id="scrub-play" visibility="hidden">
+
<rect x="-191.0" y="36.0" width="8" height="8" fill="#05f" stroke="#05f" stroke-width="8" stroke-linejoin="round" />
+
<path d="M-191.0,36.0 v8.0 l8.0,-4.0 Z" fill="#eee" />
+
</g>
+
<g id="scrub-pause" visibility="hidden">
+
<rect x="-191.0" y="36.0" width="8" height="8" fill="#05f" stroke="#05f" stroke-width="8" stroke-linejoin="round" />
+
<rect x="-190.0" y="36.0" width="2.0" height="8.0" fill="#eee" />
+
<rect x="-186.0" y="36.0" width="2.0" height="8.0" fill="#eee" />
+
</g>
+
</g>
+
<script>/*<![CDATA[*/
+
/* Animation playback controls generated by drawsvg */
+
/* https://github.com/cduck/drawsvg/ */
+
function svgOnLoad(event) {
+
/* Support standalone SVG or embedded in HTML or iframe */
+
if (event && event.target && event.target.ownerDocument) {
+
svgSetup(event.target.ownerDocument);
+
} else if (document && document.currentScript
+
&& document.currentScript.parentElement) {
+
svgSetup(document.currentScript.parentElement);
+
}
+
}
+
function svgSetup(doc) {
+
var svgRoot = doc.documentElement || doc;
+
var scrubCapture = doc.getElementById("scrub-capture");
+
/* Block multiple setups */
+
if (!scrubCapture || scrubCapture.getAttribute("svgSetupDone")) {
+
return;
+
}
+
scrubCapture.setAttribute("svgSetupDone", true);
+
var scrubContainer = doc.getElementById("scrub");
+
var scrubPlay = doc.getElementById("scrub-play");
+
var scrubPause = doc.getElementById("scrub-pause");
+
var scrubKnob = doc.getElementById("scrub-knob");
+
var scrubXMin = parseFloat(scrubCapture.dataset.xmin);
+
var scrubXMax = parseFloat(scrubCapture.dataset.xmax);
+
var scrubTotalDur = parseFloat(scrubCapture.dataset.totaldur);
+
var scrubStartDelay = parseFloat(scrubCapture.dataset.startdelay);
+
var scrubEndDelay = parseFloat(scrubCapture.dataset.enddelay);
+
var scrubPauseOnLoad = parseFloat(scrubCapture.dataset.pauseonload);
+
var paused = false;
+
var dragXOffset = 0;
+
var point = svgRoot.createSVGPoint();
+
+
function screenToSvgX(p) {
+
var matrix = scrubKnob.getScreenCTM().inverse();
+
point.x = p.x;
+
point.y = p.y;
+
return point.matrixTransform(matrix).x;
+
};
+
function screenToProgress(p) {
+
var matrix = scrubKnob.getScreenCTM().inverse();
+
point.x = p.x;
+
point.y = p.y;
+
var x = point.matrixTransform(matrix).x;
+
if (x <= scrubXMin) {
+
return scrubStartDelay / scrubTotalDur;
+
}
+
if (x >= scrubXMax) {
+
return (scrubTotalDur - scrubEndDelay) / scrubTotalDur;
+
}
+
return (scrubStartDelay/scrubTotalDur
+
+ (x - dragXOffset - scrubXMin)
+
/ (scrubXMax - scrubXMin)
+
* (scrubTotalDur - scrubStartDelay - scrubEndDelay)
+
/ scrubTotalDur);
+
};
+
function currentScrubX() {
+
return scrubKnob.cx.animVal.value;
+
};
+
function pause() {
+
svgRoot.pauseAnimations();
+
scrubPlay.setAttribute("visibility", "visible");
+
scrubPause.setAttribute("visibility", "hidden");
+
paused = true;
+
};
+
function play() {
+
svgRoot.unpauseAnimations();
+
scrubPause.setAttribute("visibility", "visible");
+
scrubPlay.setAttribute("visibility", "hidden");
+
paused = false;
+
};
+
function scrub(playbackFraction) {
+
var t = scrubTotalDur * playbackFraction;
+
/* Stop 10ms before end to avoid loop (>=1ms needed on FF) */
+
var limit = scrubTotalDur - 10e-3;
+
if (t < 0) t = 0;
+
else if (t > limit) t = limit;
+
svgRoot.setCurrentTime(t);
+
};
+
function mousedown(e) {
+
svgRoot.pauseAnimations();
+
if (e.target == scrubKnob) {
+
dragXOffset = screenToSvgX(e) - currentScrubX();
+
} else {
+
dragXOffset = 0;
+
}
+
scrub(screenToProgress(e));
+
/* Global document listeners */
+
document.addEventListener('mousemove', mousemove);
+
document.addEventListener('mouseup', mouseup);
+
e.preventDefault();
+
};
+
function mouseup(e) {
+
dragXOffset = 0;
+
document.removeEventListener('mousemove', mousemove);
+
document.removeEventListener('mouseup', mouseup);
+
if (!paused) {
+
svgRoot.unpauseAnimations();
+
}
+
e.preventDefault();
+
};
+
function mousemove(e) {
+
scrub(screenToProgress(e));
+
};
+
scrubPause.addEventListener("click", pause);
+
scrubPlay.addEventListener("click", play);
+
scrubCapture.addEventListener("mousedown", mousedown);
+
scrubContainer.setAttribute("visibility", "visible");
+
scrubKnob.setAttribute("visibility", "visible");
+
if (scrubPauseOnLoad) {
+
pause();
+
scrub(0);
+
} else {
+
play();
+
}
+
};
+
svgOnLoad();
+
/*]]>*/</script>
+
</svg>
examples/orbit-spritesheet.png

This is a binary file and will not be displayed.

+169
examples/playback-controls.html
···
+
<!DOCTYPE html>
+
<head>
+
<meta charset="utf-8">
+
</head>
+
<body>
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+
width="400" height="200" viewBox="-200.0 -100.0 400 200" onload="svgOnLoad(event);">
+
<defs>
+
</defs>
+
<rect x="-200" y="-100" width="400" height="200" fill="#eee" />
+
<circle cx="0" cy="0" r="40" fill="green" />
+
<circle cx="0" cy="0" r="0" fill="gray">
+
<animate attributeName="cx" dur="8s" values="-100;0;100;0;-100" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" />
+
<animate attributeName="cy" dur="8s" values="0;-100;0;100;0" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" />
+
<animate attributeName="r" dur="8s" values="0;40;0;40;0" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" />
+
</circle>
+
<rect x="0" y="0" width="0" height="0" fill="silver">
+
<animate attributeName="x" dur="8s" values="-100;-20;100;-20;-100" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" />
+
<animate attributeName="y" dur="8s" values="0;-120;0;80;0" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" />
+
<animate attributeName="width" dur="8s" values="0;40;0;40;0" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" />
+
<animate attributeName="height" dur="8s" values="0;40;0;40;0" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" />
+
</rect>
+
<text x="0" y="1" font-size="30" fill="yellow" text-anchor="middle" dominant-baseline="central">0<animate attributeName="visibility" dur="8s" values="visible;hidden;hidden" keyTimes="0;0.25;1" repeatCount="indefinite" fill="freeze" /></text>
+
<text x="0" y="1" font-size="30" fill="yellow" text-anchor="middle" dominant-baseline="central">1<animate attributeName="visibility" dur="8s" values="hidden;visible;hidden;hidden" keyTimes="0;0.25;0.5;1" repeatCount="indefinite" fill="freeze" /></text>
+
<text x="0" y="1" font-size="30" fill="yellow" text-anchor="middle" dominant-baseline="central">2<animate attributeName="visibility" dur="8s" values="hidden;hidden;visible;hidden;hidden" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" /></text>
+
<text x="0" y="1" font-size="30" fill="yellow" text-anchor="middle" dominant-baseline="central">3<animate attributeName="visibility" dur="8s" values="hidden;hidden;visible;visible" keyTimes="0;0.5;0.75;1" repeatCount="indefinite" fill="freeze" /></text>
+
<g id="scrub">
+
<path d="M-168.0,90.0 L168.0,90.0" stroke="#ccc" stroke-width="4" stroke-linecap="round" />
+
<rect x="-168.0" y="90.0" width="0" height="0.001" stroke="#05f" stroke-width="4" stroke-linejoin="round">
+
<animate attributeName="width" dur="8s" values="0;336" keyTimes="0;1" repeatCount="indefinite" fill="freeze" />
+
</rect>
+
<g id="scrub-capture" data-xmin="-168.0" data-xmax="168.0" data-totaldur="8" data-startdelay="0" data-enddelay="0" data-pauseonload="0">
+
<rect x="-170.0" y="80.0" width="340" height="20" fill="rgba(255,255,255,0)" />
+
<circle cx="-168.0" cy="90.0" r="6" fill="#05f" id="scrub-knob" visibility="hidden">
+
<animate attributeName="cx" dur="8s" values="-168.0;168.0" keyTimes="0;1" repeatCount="indefinite" fill="freeze" />
+
</circle>
+
</g>
+
<g id="scrub-play" visibility="hidden">
+
<rect x="-191.0" y="86.0" width="8" height="8" fill="#05f" stroke="#05f" stroke-width="8" stroke-linejoin="round" />
+
<path d="M-191.0,86.0 v8.0 l8.0,-4.0 Z" fill="#eee" />
+
</g>
+
<g id="scrub-pause" visibility="hidden">
+
<rect x="-191.0" y="86.0" width="8" height="8" fill="#05f" stroke="#05f" stroke-width="8" stroke-linejoin="round" />
+
<rect x="-190.0" y="86.0" width="2.0" height="8.0" fill="#eee" />
+
<rect x="-186.0" y="86.0" width="2.0" height="8.0" fill="#eee" />
+
</g>
+
</g>
+
<script>/*<![CDATA[*/
+
/* Animation playback controls generated by drawsvg */
+
/* https://github.com/cduck/drawsvg/ */
+
function svgOnLoad(event) {
+
/* Support standalone SVG or embedded in HTML or iframe */
+
if (event && event.target && event.target.ownerDocument) {
+
svgSetup(event.target.ownerDocument);
+
} else if (document && document.currentScript
+
&& document.currentScript.parentElement) {
+
svgSetup(document.currentScript.parentElement);
+
}
+
}
+
function svgSetup(doc) {
+
var svgRoot = doc.documentElement || doc;
+
var scrubCapture = doc.getElementById("scrub-capture");
+
/* Block multiple setups */
+
if (!scrubCapture || scrubCapture.getAttribute("svgSetupDone")) {
+
return;
+
}
+
scrubCapture.setAttribute("svgSetupDone", true);
+
var scrubContainer = doc.getElementById("scrub");
+
var scrubPlay = doc.getElementById("scrub-play");
+
var scrubPause = doc.getElementById("scrub-pause");
+
var scrubKnob = doc.getElementById("scrub-knob");
+
var scrubXMin = parseFloat(scrubCapture.dataset.xmin);
+
var scrubXMax = parseFloat(scrubCapture.dataset.xmax);
+
var scrubTotalDur = parseFloat(scrubCapture.dataset.totaldur);
+
var scrubStartDelay = parseFloat(scrubCapture.dataset.startdelay);
+
var scrubEndDelay = parseFloat(scrubCapture.dataset.enddelay);
+
var scrubPauseOnLoad = parseFloat(scrubCapture.dataset.pauseonload);
+
var paused = false;
+
var dragXOffset = 0;
+
var point = svgRoot.createSVGPoint();
+
+
function screenToSvgX(p) {
+
var matrix = scrubKnob.getScreenCTM().inverse();
+
point.x = p.x;
+
point.y = p.y;
+
return point.matrixTransform(matrix).x;
+
};
+
function screenToProgress(p) {
+
var matrix = scrubKnob.getScreenCTM().inverse();
+
point.x = p.x;
+
point.y = p.y;
+
var x = point.matrixTransform(matrix).x;
+
if (x <= scrubXMin) {
+
return scrubStartDelay / scrubTotalDur;
+
}
+
if (x >= scrubXMax) {
+
return (scrubTotalDur - scrubEndDelay) / scrubTotalDur;
+
}
+
return (scrubStartDelay/scrubTotalDur
+
+ (x - dragXOffset - scrubXMin)
+
/ (scrubXMax - scrubXMin)
+
* (scrubTotalDur - scrubStartDelay - scrubEndDelay)
+
/ scrubTotalDur);
+
};
+
function currentScrubX() {
+
return scrubKnob.cx.animVal.value;
+
};
+
function pause() {
+
svgRoot.pauseAnimations();
+
scrubPlay.setAttribute("visibility", "visible");
+
scrubPause.setAttribute("visibility", "hidden");
+
paused = true;
+
};
+
function play() {
+
svgRoot.unpauseAnimations();
+
scrubPause.setAttribute("visibility", "visible");
+
scrubPlay.setAttribute("visibility", "hidden");
+
paused = false;
+
};
+
function scrub(playbackFraction) {
+
var t = scrubTotalDur * playbackFraction;
+
/* Stop 10ms before end to avoid loop (>=1ms needed on FF) */
+
var limit = scrubTotalDur - 10e-3;
+
if (t < 0) t = 0;
+
else if (t > limit) t = limit;
+
svgRoot.setCurrentTime(t);
+
};
+
function mousedown(e) {
+
svgRoot.pauseAnimations();
+
if (e.target == scrubKnob) {
+
dragXOffset = screenToSvgX(e) - currentScrubX();
+
} else {
+
dragXOffset = 0;
+
}
+
scrub(screenToProgress(e));
+
/* Global document listeners */
+
document.addEventListener('mousemove', mousemove);
+
document.addEventListener('mouseup', mouseup);
+
e.preventDefault();
+
};
+
function mouseup(e) {
+
dragXOffset = 0;
+
document.removeEventListener('mousemove', mousemove);
+
document.removeEventListener('mouseup', mouseup);
+
if (!paused) {
+
svgRoot.unpauseAnimations();
+
}
+
e.preventDefault();
+
};
+
function mousemove(e) {
+
scrub(screenToProgress(e));
+
};
+
scrubPause.addEventListener("click", pause);
+
scrubPlay.addEventListener("click", play);
+
scrubCapture.addEventListener("mousedown", mousedown);
+
scrubContainer.setAttribute("visibility", "visible");
+
scrubKnob.setAttribute("visibility", "visible");
+
if (scrubPauseOnLoad) {
+
pause();
+
scrub(0);
+
} else {
+
play();
+
}
+
};
+
svgOnLoad();
+
/*]]>*/</script>
+
</svg>
+
</body>
+
</html>
+163
examples/playback-controls.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="-200.0 -100.0 400 200" onload="svgOnLoad(event);">
+
<defs>
+
</defs>
+
<rect x="-200" y="-100" width="400" height="200" fill="#eee" />
+
<circle cx="0" cy="0" r="40" fill="green" />
+
<circle cx="0" cy="0" r="0" fill="gray">
+
<animate attributeName="cx" dur="8s" values="-100;0;100;0;-100" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" />
+
<animate attributeName="cy" dur="8s" values="0;-100;0;100;0" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" />
+
<animate attributeName="r" dur="8s" values="0;40;0;40;0" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" />
+
</circle>
+
<rect x="0" y="0" width="0" height="0" fill="silver">
+
<animate attributeName="x" dur="8s" values="-100;-20;100;-20;-100" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" />
+
<animate attributeName="y" dur="8s" values="0;-120;0;80;0" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" />
+
<animate attributeName="width" dur="8s" values="0;40;0;40;0" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" />
+
<animate attributeName="height" dur="8s" values="0;40;0;40;0" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" />
+
</rect>
+
<text x="0" y="1" font-size="30" fill="yellow" text-anchor="middle" dominant-baseline="central">0<animate attributeName="visibility" dur="8s" values="visible;hidden;hidden" keyTimes="0;0.25;1" repeatCount="indefinite" fill="freeze" /></text>
+
<text x="0" y="1" font-size="30" fill="yellow" text-anchor="middle" dominant-baseline="central">1<animate attributeName="visibility" dur="8s" values="hidden;visible;hidden;hidden" keyTimes="0;0.25;0.5;1" repeatCount="indefinite" fill="freeze" /></text>
+
<text x="0" y="1" font-size="30" fill="yellow" text-anchor="middle" dominant-baseline="central">2<animate attributeName="visibility" dur="8s" values="hidden;hidden;visible;hidden;hidden" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" /></text>
+
<text x="0" y="1" font-size="30" fill="yellow" text-anchor="middle" dominant-baseline="central">3<animate attributeName="visibility" dur="8s" values="hidden;hidden;visible;visible" keyTimes="0;0.5;0.75;1" repeatCount="indefinite" fill="freeze" /></text>
+
<g id="scrub">
+
<path d="M-168.0,90.0 L168.0,90.0" stroke="#ccc" stroke-width="4" stroke-linecap="round" />
+
<rect x="-168.0" y="90.0" width="0" height="0.001" stroke="#05f" stroke-width="4" stroke-linejoin="round">
+
<animate attributeName="width" dur="8s" values="0;336" keyTimes="0;1" repeatCount="indefinite" fill="freeze" />
+
</rect>
+
<g id="scrub-capture" data-xmin="-168.0" data-xmax="168.0" data-totaldur="8" data-startdelay="0" data-enddelay="0" data-pauseonload="0">
+
<rect x="-170.0" y="80.0" width="340" height="20" fill="rgba(255,255,255,0)" />
+
<circle cx="-168.0" cy="90.0" r="6" fill="#05f" id="scrub-knob" visibility="hidden">
+
<animate attributeName="cx" dur="8s" values="-168.0;168.0" keyTimes="0;1" repeatCount="indefinite" fill="freeze" />
+
</circle>
+
</g>
+
<g id="scrub-play" visibility="hidden">
+
<rect x="-191.0" y="86.0" width="8" height="8" fill="#05f" stroke="#05f" stroke-width="8" stroke-linejoin="round" />
+
<path d="M-191.0,86.0 v8.0 l8.0,-4.0 Z" fill="#eee" />
+
</g>
+
<g id="scrub-pause" visibility="hidden">
+
<rect x="-191.0" y="86.0" width="8" height="8" fill="#05f" stroke="#05f" stroke-width="8" stroke-linejoin="round" />
+
<rect x="-190.0" y="86.0" width="2.0" height="8.0" fill="#eee" />
+
<rect x="-186.0" y="86.0" width="2.0" height="8.0" fill="#eee" />
+
</g>
+
</g>
+
<script>/*<![CDATA[*/
+
/* Animation playback controls generated by drawsvg */
+
/* https://github.com/cduck/drawsvg/ */
+
function svgOnLoad(event) {
+
/* Support standalone SVG or embedded in HTML or iframe */
+
if (event && event.target && event.target.ownerDocument) {
+
svgSetup(event.target.ownerDocument);
+
} else if (document && document.currentScript
+
&& document.currentScript.parentElement) {
+
svgSetup(document.currentScript.parentElement);
+
}
+
}
+
function svgSetup(doc) {
+
var svgRoot = doc.documentElement || doc;
+
var scrubCapture = doc.getElementById("scrub-capture");
+
/* Block multiple setups */
+
if (!scrubCapture || scrubCapture.getAttribute("svgSetupDone")) {
+
return;
+
}
+
scrubCapture.setAttribute("svgSetupDone", true);
+
var scrubContainer = doc.getElementById("scrub");
+
var scrubPlay = doc.getElementById("scrub-play");
+
var scrubPause = doc.getElementById("scrub-pause");
+
var scrubKnob = doc.getElementById("scrub-knob");
+
var scrubXMin = parseFloat(scrubCapture.dataset.xmin);
+
var scrubXMax = parseFloat(scrubCapture.dataset.xmax);
+
var scrubTotalDur = parseFloat(scrubCapture.dataset.totaldur);
+
var scrubStartDelay = parseFloat(scrubCapture.dataset.startdelay);
+
var scrubEndDelay = parseFloat(scrubCapture.dataset.enddelay);
+
var scrubPauseOnLoad = parseFloat(scrubCapture.dataset.pauseonload);
+
var paused = false;
+
var dragXOffset = 0;
+
var point = svgRoot.createSVGPoint();
+
+
function screenToSvgX(p) {
+
var matrix = scrubKnob.getScreenCTM().inverse();
+
point.x = p.x;
+
point.y = p.y;
+
return point.matrixTransform(matrix).x;
+
};
+
function screenToProgress(p) {
+
var matrix = scrubKnob.getScreenCTM().inverse();
+
point.x = p.x;
+
point.y = p.y;
+
var x = point.matrixTransform(matrix).x;
+
if (x <= scrubXMin) {
+
return scrubStartDelay / scrubTotalDur;
+
}
+
if (x >= scrubXMax) {
+
return (scrubTotalDur - scrubEndDelay) / scrubTotalDur;
+
}
+
return (scrubStartDelay/scrubTotalDur
+
+ (x - dragXOffset - scrubXMin)
+
/ (scrubXMax - scrubXMin)
+
* (scrubTotalDur - scrubStartDelay - scrubEndDelay)
+
/ scrubTotalDur);
+
};
+
function currentScrubX() {
+
return scrubKnob.cx.animVal.value;
+
};
+
function pause() {
+
svgRoot.pauseAnimations();
+
scrubPlay.setAttribute("visibility", "visible");
+
scrubPause.setAttribute("visibility", "hidden");
+
paused = true;
+
};
+
function play() {
+
svgRoot.unpauseAnimations();
+
scrubPause.setAttribute("visibility", "visible");
+
scrubPlay.setAttribute("visibility", "hidden");
+
paused = false;
+
};
+
function scrub(playbackFraction) {
+
var t = scrubTotalDur * playbackFraction;
+
/* Stop 10ms before end to avoid loop (>=1ms needed on FF) */
+
var limit = scrubTotalDur - 10e-3;
+
if (t < 0) t = 0;
+
else if (t > limit) t = limit;
+
svgRoot.setCurrentTime(t);
+
};
+
function mousedown(e) {
+
svgRoot.pauseAnimations();
+
if (e.target == scrubKnob) {
+
dragXOffset = screenToSvgX(e) - currentScrubX();
+
} else {
+
dragXOffset = 0;
+
}
+
scrub(screenToProgress(e));
+
/* Global document listeners */
+
document.addEventListener('mousemove', mousemove);
+
document.addEventListener('mouseup', mouseup);
+
e.preventDefault();
+
};
+
function mouseup(e) {
+
dragXOffset = 0;
+
document.removeEventListener('mousemove', mousemove);
+
document.removeEventListener('mouseup', mouseup);
+
if (!paused) {
+
svgRoot.unpauseAnimations();
+
}
+
e.preventDefault();
+
};
+
function mousemove(e) {
+
scrub(screenToProgress(e));
+
};
+
scrubPause.addEventListener("click", pause);
+
scrubPlay.addEventListener("click", play);
+
scrubCapture.addEventListener("mousedown", mousedown);
+
scrubContainer.setAttribute("visibility", "visible");
+
scrubKnob.setAttribute("visibility", "visible");
+
if (scrubPauseOnLoad) {
+
pause();
+
scrub(0);
+
} else {
+
play();
+
}
+
};
+
svgOnLoad();
+
/*]]>*/</script>
+
</svg>
+4
mkdocs.yml
···
+
site_name: Drawsvg Quick Reference
+
nav:
+
- Home: index.md
+
theme: readthedocs
+25 -9
setup.py
···
import logging
logger = logging.getLogger(__name__)
-
version = '1.2.2'
+
version = '2.4.0'
try:
with open('README.md', 'r') as f:
···
long_desc = None
setup(
-
name = 'drawSvg',
+
name = 'drawsvg',
packages = find_packages(),
version = version,
-
description = 'A Python 3 library for programmatically generating SVG images (vector drawings) and rendering them or displaying them in an iPython notebook.',
+
description = 'A Python 3 library for programmatically generating SVG (vector) images and animations. Drawsvg can also render to PNG, MP4, and display your drawings in Jupyter notebook and Jupyter lab.',
long_description = long_desc,
long_description_content_type = 'text/markdown',
author = 'Casey Duckering',
#author_email = '',
-
url = 'https://github.com/cduck/drawSvg',
-
download_url = 'https://github.com/cduck/drawSvg/archive/{}.tar.gz'.format(version),
-
keywords = ['SVG', 'draw', 'graphics', 'iPython', 'Jupyter', 'widget'],
+
url = 'https://github.com/cduck/drawsvg',
+
download_url = 'https://github.com/cduck/drawsvg/archive/{}.tar.gz'.format(version),
+
keywords = ['SVG', 'draw', 'graphics', 'iPython', 'Jupyter', 'widget', 'animation'],
classifiers = [
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 3',
···
'Framework :: Jupyter',
],
install_requires = [
-
'cairoSVG',
-
'numpy',
-
'imageio',
],
+
extras_require = {
+
'raster': [
+
'cairoSVG~=2.3',
+
'numpy~=1.16',
+
'imageio~=2.5',
+
'imageio_ffmpeg~=0.4',
+
],
+
'color': [
+
'pwkit~=1.0',
+
'numpy~=1.16',
+
],
+
'all': [
+
'cairoSVG~=2.3',
+
'numpy~=1.16',
+
'imageio~=2.5',
+
'imageio_ffmpeg~=0.4',
+
'pwkit~=1.0',
+
],
+
},
)