Wode "Nimo" Ni
Xuanyuan Zhang
A vector graphics animation library written in modern C++.
Why vector graphics?
Small, Scale-invariant
How to animate?
SVG + JS, SVG + CSS, SVG + SMIL (self-contained!)
<?xml version="1.0"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
<rect x="20" y="20" rx="3" ry="3"
width="100" height="100"
fill="red" stroke="black" stroke-width="5"
id="rect_8">
<animateTransform
attributeName="transform"
type="rotate"
dur="10s"
from="0 100 100"
repeatCount="indefinite"
to="360 100 100" />
</rect>
</svg>
How people create SVG
JavaScript is the first choice
- major front-end language
- easy to pick up (?)
But why we stick with C++ ?
Advantages of C++
Too much...
For this specific task:
- Restrictions on rampant SVG styles.
- Utilizing OOP features for better design.
- Write SVGs in the language that you love!
Existing projects in C++?
There has not been any library in C++ focusing on general purpose SVG manipulation and animation
SVG++: SVG parser mainly for import and export.
Qt: static SVGs, animation supported in QML.
Goal
Provide intuitive abstractions for reading, modifying, composing, and
SVG files!
Library structure
-
<animate.hpp>
-
<anipp/parser.hpp>
-
<anipp/shapes.hpp>
-
<anipp/utils.hpp>
-
Depends on pugixml 1.9 to parse raw XML files
Basic Shapes - Rectangle
Rect r(20, 20, 100, 100, 3, 3);
r.attr({
{"fill", "red"},
{"stroke", "black"},
{"stroke-width", "5"}
});
- Standard SVG API:
- (x, y, width, length, rx, xy)
- rx, ry for rounded corners
- Free attribute specification
- nested list initialization
-
std::map
Other Basic Shapes
- API similar to Rectangle
- Other shapes supported:
- Polygon: (x, y)+
- Polyline: (x, y)+
- Text: <any-string>
- Line: (x1, y1), (x2, y2)
Other Basic Shapes
- API similar to Rectangle
- Other shapes supported:
- Polygon: (x, y)+
- Polyline: (x, y)+
- Text: <any-string>
- Line: (x1, y1), (x2, y2)
What's this?
Basic Shapes - Paths
"Give me a good library and with Bézier curves I will draw the whole world"
-- vector graphics designers
- Full support to parse and compose:
- Lines
- Quadratic and Cubic Bézier curves
- Elliptical curves
- closePath
Path Commands
Path& moveTo(double x, double y, bool relative=false);
Path& lineTo(double x, double y, bool relative=false);
Path& quadraticCurveTo(double cpx, double cpy, double x,
double y, bool relative=false);
Path& cubicCurveTo(double cp1x, double cp1y, double cp2x,
double cp2y, double x, double y, bool relative=false);
Path& arcTo(double rx, double ry, double x_axis_rotation,
double large_arc_flag, double sweep_flag,
double x, double y, bool relative=false);
Path& closePath();
How to draw a heart
Path p;
p.moveTo(121, 251)
.cubicCurveTo(-25, -80, -50,-80, -100, -130, true)
.arcTo(70, 70, -45, 0, 1, 100, -100, true)
.arcTo(70, 70, 45, 0, 1, 100, 100, true)
.cubicCurveTo(-50, 50, -75, 50, -100, 130, true);
p.animate.blink(2);
p.attr("fill", "DeepPink");
File I/O - Output
Ellipse e(100, 50, 100, 50);
e.attr({
{"fill", "blue"},
{"stroke", "black"},
{"stroke-width", "5"},
{"stroke-dasharray", "5, 10"}
});
e.save("sample.svg");
File I/O - Input
One liner!
ShapePtr tiger = load("tiger.svg");
Grouping
Circle c1(40, 40, 25);
Circle c2(60, 60, 25);
Group g(c1, c2);
g.attr({
{"fill", "none"},
{"stroke", "DeepPink"},
{"stroke-width", "5"},
});
- Large SVGs contain multiple shapes and nested group structure
- Intuitive syntax to build groups
- memory management: ShapePtr
The Animator
The animator object: obj.animate
Similar to D3.js, method chaining of basic functions
Rect r(20, 20, 100, 100, 3, 3);
r.attr({
{"fill", "red"},
{"stroke", "black"},
{"stroke-width", "5"}
});
Point center(70, 70);
r.animate.rotate(center, 0, center, 360)
.duration("10s")
.loop(true);
Animation
- Animation class
-
Basic Animation
- translate, rotate, scale...
- User defined composition of Animation possible
Animation - continued
- User defined/composition of Animation possible
- Animating arbitrary attributes
- Animating arbitrary shapes along arbitrary paths
- Simple syntax:
Path p;
p.moveTo(10, 80)
.cubicCurveTo(40, 10, 65, 10, 95, 80)
.cubicCurveTo(150, 150, 180, 80);
tiger->animate.move_along(p)
.duration("6s")
.loop(true);
Library design: OOP
- Inheritance: shape classes
- export_SVG: example of OOP
xml_node Group::export_SVG(xml_document& doc) {
auto group = doc.append_child("g");
for(auto& shp : this->shapes) {
auto node = shp->export_SVG(doc);
group.append_move(node);
}
this->export_attributes(group);
return this->add_animations(doc, group);
}
Testing
Visual test cases: I/O
Visual test cases: composition of SVGs
Modern C++ features
Fold expression!
template<typename... Args>
Group(Args&&... args)
{
(this->shapes.push_back(args.clone()), ...);
}
-
At some point in the project...
- <optional>
- std::any_of, std::none_of
The Wishlist
- Concept
- Prodirect manipulation
- literal for units: em, in, %, mm
-
More SVG features
- <LinearGradient>
- <pattern>
- <use> and <set>
...
Demo
(you have already seen it)
Acknowledgements
Animate++
By Nimo Wode Ni
Animate++
- 1,464