summaryrefslogtreecommitdiff
path: root/Build/source/texk/dvisvgm/dvisvgm-src/src/GraphicsPath.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/dvisvgm/dvisvgm-src/src/GraphicsPath.hpp')
-rw-r--r--Build/source/texk/dvisvgm/dvisvgm-src/src/GraphicsPath.hpp738
1 files changed, 0 insertions, 738 deletions
diff --git a/Build/source/texk/dvisvgm/dvisvgm-src/src/GraphicsPath.hpp b/Build/source/texk/dvisvgm/dvisvgm-src/src/GraphicsPath.hpp
deleted file mode 100644
index 27d45472524..00000000000
--- a/Build/source/texk/dvisvgm/dvisvgm-src/src/GraphicsPath.hpp
+++ /dev/null
@@ -1,738 +0,0 @@
-/*************************************************************************
-** GraphicsPath.hpp **
-** **
-** This file is part of dvisvgm -- a fast DVI to SVG converter **
-** Copyright (C) 2005-2021 Martin Gieseking <martin.gieseking@uos.de> **
-** **
-** This program is free software; you can redistribute it and/or **
-** modify it under the terms of the GNU General Public License as **
-** published by the Free Software Foundation; either version 3 of **
-** the License, or (at your option) any later version. **
-** **
-** This program is distributed in the hope that it will be useful, but **
-** WITHOUT ANY WARRANTY; without even the implied warranty of **
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the **
-** GNU General Public License for more details. **
-** **
-** You should have received a copy of the GNU General Public License **
-** along with this program; if not, see <http://www.gnu.org/licenses/>. **
-*************************************************************************/
-
-#pragma once
-
-#include <array>
-#include <cctype>
-#include <cmath>
-#include <deque>
-#include <ostream>
-#include <type_traits>
-#include <mpark/variant.hpp>
-#include "BoundingBox.hpp"
-#include "EllipticalArc.hpp"
-#include "Matrix.hpp"
-#include "Pair.hpp"
-#include "utility.hpp"
-#include "XMLString.hpp"
-
-template <typename T>
-class GraphicsPath;
-
-namespace gp {
-
-/// Base class for all path data commands, like moveto, lineto, curveto, etc.
-struct CommandBase {};
-
-/** Base class for all path data commands with NUM_POINTS point parameters
- * @tparam NUM_POINTS number of parameter pairs representing points, e.g. 1 for moveto and lineto */
-template <typename T, int NUM_POINTS>
-class Command : public CommandBase {
- friend class GraphicsPath<T>;
- public:
- int numPoints () const {return NUM_POINTS;}
- Pair<T>& point (int n) {return points[n];}
- const Pair<T>& point (int n) const {return points[n];}
-
- /** Transforms the command by a given transformation matrix.
- * @params[in] matrix describes the affine transformation to apply
- * @params[in] currentPoint the untransformed end point of the preceding command */
- void transform (const Matrix &matrix, const Pair<T> &currentPoint) {
- for (Pair<T> &p : points)
- p = matrix * p;
- }
-
- /** Returns true if all points are identical to those of another command. */
- bool pointsEqual (const Command &cmd) const {
- for (int i=0; i < NUM_POINTS; i++)
- if (points[i] != cmd.points[i])
- return false;
- return true;
- }
-
- protected:
- explicit Command () =default;
- explicit Command (std::array<Pair<T>, NUM_POINTS> &&pts) : points(std::move(pts)) {}
-
- protected:
- std::array<Pair<T>, NUM_POINTS> points;
-};
-
-template <typename T>
-struct MoveTo : public Command<T, 1> {
- explicit MoveTo (const Pair<T> &p) : Command<T, 1>({p}) {}
-};
-
-template <typename T>
-struct LineTo : public Command<T, 1> {
- explicit LineTo (const Pair<T> &p) : Command<T, 1>({p}) {}
-};
-
-template <typename T>
-struct CubicTo : public Command<T, 3> {
- explicit CubicTo (const Pair<T> &p1, const Pair<T> &p2, const Pair<T> &p3) : Command<T, 3>({p1, p2, p3}) {}
-};
-
-template <typename T>
-struct QuadTo : public Command<T, 2> {
- explicit QuadTo (const Pair<T> &p1, const Pair<T> &p2) : Command<T, 2>({p1, p2}) {}
-};
-
-template <typename T>
-struct ClosePath : public Command<T, 0> {
- ClosePath () : Command<T, 0>() {}
-};
-
-template <typename T>
-struct ArcTo : Command<T, 1> {
- ArcTo (T rxx, T ryy, double xrot, bool laf, bool sf, const Pair<T> &pp)
- : Command<T, 1>({pp}), rx(rxx < 0 ? -rxx : rxx), ry(ryy < 0 ? -ryy : ryy),
- xrotation(xrot), largeArcFlag(laf), sweepFlag(sf) {}
-
- bool operator == (const ArcTo &arc) const {
- return rx == arc.rx
- && ry == arc.ry
- && xrotation == arc.xrotation
- && largeArcFlag == arc.largeArcFlag
- && sweepFlag == arc.sweepFlag
- && this->points[0] == arc.points[0];
- }
-
- void transform (const Matrix &matrix, const Pair<T> &currentPoint);
-
- bool operator != (const ArcTo &arc) const {return !(*this == arc);}
-
- T rx, ry; ///< length of the semi-major and semi-minor axes
- double xrotation; ///< rotation of the semi-major axis in degrees
- bool largeArcFlag; ///< if true, the longer arc from start to end point is chosen, else the shorter one
- bool sweepFlag; ///< if true, arc is drawn in direction of positive angles, else the opposite direction
-};
-
-/** Applies an affine transformation described by a given matrix to the arc segment.
- * @params[in] matrix describes the affine transformation to apply
- * @params[in] currentPoint the untransformed end point of the preceding command */
-template <typename T>
-void ArcTo<T>::transform (const Matrix &matrix, const Pair<T> &currentPoint) {
- EllipticalArc arc(currentPoint, rx, ry, math::deg2rad(xrotation), largeArcFlag, sweepFlag, this->points[0]);
- arc.transform(matrix);
- rx = arc.rx();
- ry = arc.ry();
- xrotation = math::rad2deg(arc.rotationAngle());
- largeArcFlag = arc.largeArc();
- sweepFlag = arc.sweepPositive();
- this->points[0] = Pair<T>(arc.endPoint());
-}
-
-/** Returns true if two path command objects are identical (same command and same parameters). */
-template <typename Cmd1, typename Cmd2>
-inline typename std::enable_if<std::is_base_of<CommandBase, Cmd1>::value, bool>::type
-operator == (const Cmd1 &cmd1, const Cmd2 &cmd2) {
- if (std::is_convertible<Cmd1, Cmd2>::value && std::is_convertible<Cmd2, Cmd1>::value)
- return cmd1.pointsEqual(cmd2);
- return false;
-}
-
-/** Returns true if two path command objects differ (different commands or different parameters). */
-template <typename Cmd1, typename Cmd2>
-inline typename std::enable_if<std::is_base_of<CommandBase, Cmd1>::value, bool>::type
-operator != (const Cmd1 &cmd1, const Cmd2 &cmd2) {
- if (std::is_convertible<Cmd1, Cmd2>::value && std::is_convertible<Cmd2, Cmd1>::value)
- return !cmd1.pointsEqual(cmd2);
- return true;
-}
-
-} // namespace gp
-
-
-template <typename T>
-class GraphicsPath {
- friend class PathClipper;
- public:
- enum class WindingRule {EVEN_ODD, NON_ZERO};
- using Point = Pair<T>;
-
- protected:
-
- static XMLString to_param_str (double v, double s, double d, bool leadingSpace) {
- XMLString str(v*s + d);
- if (leadingSpace && (str[0] != '-'))
- str.insert(0, " ");
- return str;
- }
-
- static XMLString to_param_str (double val, double prev, double s, double d, bool leadingSpace) {
- XMLString str((val-prev)*s + d);
- if (leadingSpace && (str[0] != '-'))
- str.insert(0, " ");
- return str;
- }
-
- static std::string to_param_str (const Point &p, double sx, double sy, double dx, double dy, bool leadingSpace) {
- return to_param_str(p.x(), sx, dx, leadingSpace) + to_param_str(p.y(), sy, dy, true);
- }
-
- static std::string to_param_str (const Point &p, const Point &prev, double sx, double sy, double dx, double dy, bool leadingSpace) {
- return to_param_str(p.x()-prev.x(), sx, dx, leadingSpace) + to_param_str(p.y()-prev.y(), sy, dy, true);
- }
-
- using MoveTo = gp::MoveTo<T>;
- using LineTo = gp::LineTo<T>;
- using CubicTo = gp::CubicTo<T>;
- using QuadTo = gp::QuadTo<T>;
- using ArcTo = gp::ArcTo<T>;
- using ClosePath = gp::ClosePath<T>;
-
- /// Variant representing a single path command
- using CommandVariant = mpark::variant<MoveTo, LineTo, CubicTo, QuadTo, ArcTo, ClosePath>;
-
- class IterationVisitor;
-
- public:
- /** Base class providing several template methods being called when executing
- * GraphicsPath::iterate(). */
- class IterationActions {
- friend class IterationVisitor;
- public:
- virtual ~IterationActions () =default;
- virtual void moveto (const Point &p) {}
- virtual void lineto (const Point &p) {}
- virtual void hlineto (const T &x) {}
- virtual void vlineto (const T &y) {}
- virtual void quadto (const Point &p) {}
- virtual void quadto (const Point &p1, const Point &p2) {}
- virtual void cubicto (const Point &p1, const Point &p2) {}
- virtual void cubicto (const Point &p1, const Point &p2, const Point &p3) {}
- virtual void arcto (T rx, T ry, double angle, bool largeArcFlag, bool sweepFlag, const Point &p) {}
- virtual void closepath () {}
- virtual bool quit () {return false;}
- virtual void finished () {}
- const Point& startPoint () const {return _startPoint;}
- const Point& currentPoint () const {return _currentPoint;}
-
- private:
- Point _startPoint; ///< first point of the current sub-path
- Point _currentPoint; ///< point reached by preceding path command, or (0,0) otherwise
- };
-
- protected:
- class ModificationActions : public IterationActions {
- friend class GraphicsPath;
- public:
- explicit ModificationActions (GraphicsPath &path) : _path(path) {}
-
- protected:
- GraphicsPath& path () {return _path;}
- int commandPos () const {return _commandPos;}
-
- private:
- GraphicsPath &_path;
- int _commandPos=0; ///< number of command in path being processed
- };
-
- class WriteActions : public IterationActions {
- public:
- WriteActions (std::ostream &os, bool rel, double sx, double sy, double dx, double dy)
- : _os(os), _relative(rel), _sx(sx), _sy(sy), _dx(dx), _dy(dy) {}
-
- void moveto (const Point &p) override {write('M', {p});}
- void lineto (const Point &p) override {write('L', {p});}
- void hlineto (const T &x) override {write('H', x, this->currentPoint().x(), _sx, _dx);}
- void vlineto (const T &y) override {write('V', y, this->currentPoint().y(), _sy, _dy);}
- void quadto (const Point &p) override {write('T', {p});}
- void quadto (const Point &p1, const Point &p2) override {write('Q', {p1, p2});}
- void cubicto (const Point &p1, const Point &p2) override {write('S', {p1, p2});}
- void cubicto (const Point &p1, const Point &p2, const Point &p3) override {write('C', {p1, p2, p3});}
- void closepath () override {_os << (_relative ? 'z' : 'Z');}
-
- void arcto (T rx, T ry, double angle, bool largeArcFlag, bool sweepFlag, const Point &p) override {
- Point diff = p-this->currentPoint();
- if (std::abs(diff.x()) < 1e-7 && std::abs(diff.y()) < 1e-7)
- return;
- if (std::abs(rx) < 1e-7 && std::abs(ry) < 1e-7)
- lineto(p);
- else {
- if (std::abs(std::abs(_sx) - std::abs(_sy)) < 1e-7) { // symmetric scaling?
- angle *= math::sgn(_sx) * math::sgn(_sy);
- rx *= std::abs(_sx);
- ry *= std::abs(_sx);
- }
- else { // asymmetric scaling => compute new shape parameters
- EllipticalArc arc(this->currentPoint(), double(rx), double(ry), math::deg2rad(angle), largeArcFlag, sweepFlag, p);
- arc.transform(ScalingMatrix(_sx, _sy));
- angle = math::rad2deg(arc.rotationAngle());
- rx = arc.rx();
- ry = arc.ry();
- }
- _os << (_relative ? 'a' : 'A')
- << to_param_str(rx, 1.0, 0, false)
- << to_param_str(ry, 1.0, 0, true)
- << to_param_str(angle, 1.0, 0, true)
- << ' ' << (largeArcFlag ? 1 : 0)
- << ' ' << (sweepFlag ? 1 : 0);
- if (_relative)
- _os << to_param_str(p, this->currentPoint(), _sx, _sy, _dx, _dy, true);
- else
- _os << to_param_str(p, _sx, _sy, _dx, _dy, true);
- }
- }
-
- protected:
- void write (char cmdchar, std::initializer_list<Point> points) const {
- int count=0;
- if (_relative) {
- _os << char(tolower(cmdchar));
- for (const Point &p : points)
- _os << to_param_str(p, this->currentPoint(), _sx, _sy, _dx, _dy, count++ > 0);
- }
- else {
- _os << cmdchar;
- for (const Point &p : points)
- _os << to_param_str(p, _sx, _sy, _dx, _dy, count++ > 0);
- }
- }
-
- void write (char cmdchar, T val, T relval, double s, double d) const {
- if (_relative)
- _os << char(tolower(cmdchar)) << to_param_str(val, relval, s, d, false);
- else
- _os << cmdchar << to_param_str(val, s, d, false);
- }
-
- private:
- std::ostream &_os; ///< write output to this stream
- bool _relative; ///< if true, use relative coordinates in path commands
- double _sx, _sy; ///< horizontal and vertical scaling factors
- double _dx, _dy; ///< horizontal and vertical translation values
- };
-
- ///////////////////////////////////////////////////////////////////////////////
-
- /** Calls the corresponding template method of an Action object for the current path command.
- * If parameter 'useShortCmds' is true, the visitor operators check whether a command
- * can be shortened due to special cases, e.g. horizontal or vertical lines, smooth
- * curve connections etc. Otherwise, the full command templates are triggered. */
- class IterationVisitor {
- public:
- IterationVisitor (IterationActions &actions, bool useShortCmds, double eps=1e-7)
- : _actions(actions), _shortCommandsActive(useShortCmds), _eps(eps) {}
-
- void setPrevCommand (const CommandVariant &prevCommand) {
- _prevCommand = &prevCommand;
- }
-
- void operator () (const MoveTo &cmd) {
- _actions.moveto(cmd.points[0]);
- _actions._startPoint = _actions._currentPoint = cmd.points[0];
- }
-
- void operator () (const LineTo &cmd) {
- Point diff = abs(_actions._currentPoint-cmd.points[0]);
- if (diff.x() >= _eps || diff.y() >= _eps) {
- if (!_shortCommandsActive)
- _actions.lineto(cmd.points[0]);
- else {
- if (diff.x() < _eps)
- _actions.vlineto(cmd.points[0].y());
- else if (diff.y() < _eps)
- _actions.hlineto(cmd.points[0].x());
- else
- _actions.lineto(cmd.points[0]);
- }
- }
- _actions._currentPoint = cmd.points[0];
- }
-
- void operator () (const CubicTo &cmd) {
- bool smooth=false;
- if (_shortCommandsActive) {
- if (auto *prevCubic = mpark::get_if<CubicTo>(_prevCommand)) {
- Point diff = abs(cmd.points[0] - prevCubic->points[2]*T(2) + prevCubic->points[1]);
- if ((smooth = (diff.x() < _eps && diff.y() < _eps)))
- _actions.cubicto(cmd.points[1], cmd.points[2]);
- }
- }
- if (!smooth)
- _actions.cubicto(cmd.points[0], cmd.points[1], cmd.points[2]);
- _actions._currentPoint = cmd.points[2];
- }
-
- void operator () (const QuadTo &cmd) {
- bool smooth=false;
- if (_shortCommandsActive) {
- if (auto *prevQuad = mpark::get_if<QuadTo>(_prevCommand)) {
- Point diff = abs(cmd.points[0] - prevQuad->points[1] * T(2) + prevQuad->points[0]);
- if ((smooth = (diff.x() < _eps && diff.y() < _eps))) // is reflection?
- _actions.quadto(cmd.points[1]);
- }
- }
- if (!smooth)
- _actions.quadto(cmd.points[0], cmd.points[1]);
- _actions._currentPoint = cmd.points[1];
- }
-
- void operator () (const ClosePath &cmd) {
- _actions.closepath();
- _actions._currentPoint = _actions._startPoint;
- }
-
- void operator () (const ArcTo &cmd) {
- _actions.arcto(cmd.rx, cmd.ry, cmd.xrotation, cmd.largeArcFlag, cmd.sweepFlag, cmd.points[0]);
- _actions._currentPoint = cmd.points[0];
- }
-
- private:
- IterationActions &_actions;
- bool _shortCommandsActive=false;
- double _eps=1e-7;
- const CommandVariant *_prevCommand=nullptr;
- };
-
- ///////////////////////////////////////////////////////////////////////////////
-
- /** Transforms all Point parameters of a path command. */
- class TransformVisior {
- public:
- explicit TransformVisior (const Matrix &m) : matrix(m) {}
-
- template <typename Cmd> void operator () (Cmd &cmd) {
- Point cp = cmd.point(cmd.numPoints()-1);
- cmd.transform(matrix, _currentPoint);
- _currentPoint = cp;
- }
-
- void operator () (MoveTo &cmd) {
- Point cp = cmd.point(0);
- cmd.transform(matrix, _currentPoint);
- _startPoint = _currentPoint = cp;
- }
-
- void operator () (ClosePath &cmd) {
- _currentPoint = _startPoint;
- }
-
- private:
- const Matrix &matrix;
- Point _startPoint, _currentPoint; ///< untransformed start end current point
- };
-
- public:
- explicit GraphicsPath (WindingRule wr=WindingRule::NON_ZERO) : _windingRule(wr) {}
-
- void setWindingRule (WindingRule wr) {_windingRule = wr;}
- WindingRule windingRule () const {return _windingRule;}
-
- void clear () {
- _commands.clear();
- }
-
- /// Returns true if the path is empty, i.e. there is nothing to draw
- bool empty () const {
- return _commands.empty();
- }
-
- /// Returns the number of path commands used to describe the path.
- size_t size () const {
- return _commands.size();
- }
-
- const Point& startPoint () const {return _startPoint;}
- const Point& finalPoint () const {return _finalPoint;}
-
- /// Insert another path at the beginning of this one.
- void prepend (const GraphicsPath &path) {
- _commands.insert(_commands.begin(), path._commands.begin(), path._commands.end());
- }
-
- void moveto (const T &x, const T &y) {
- moveto(Point(x, y));
- }
-
- void moveto (const Point &p) {
- // avoid sequences of several MOVETOs; always use latest
- if (_commands.empty() || !mpark::get_if<MoveTo>(&_commands.back()))
- _commands.emplace_back(MoveTo{p});
- else
- mpark::get<MoveTo>(_commands.back()).points[0] = p;
- _startPoint = _finalPoint = p;
- }
-
- void lineto (const T &x, const T &y) {
- lineto(Point(x, y));
- }
-
- void lineto (const Point &p) {
- _commands.emplace_back(LineTo{p});
- _finalPoint = p;
- }
-
- void quadto (const T &x1, const T &y1, const T &x2, const T &y2) {
- quadto(Point(x1, y1), Point(x2, y2));
- }
-
- /** Creates a quadratic Bézier segment. */
- void quadto (const Point &p1, const Point &p2) {
- _commands.emplace_back(QuadTo{p1, p2});
- _finalPoint = p2;
- }
-
- /** Creates a quadratic Bézier segment smoothly extending a preceding one, i.e. the gradients
- * of the two curves are identical at the connection point. The control point of the second
- * curve is computed as the reflection of the preceding curve's control point at the connection
- * point. */
- void quadto (const Point &p2) {
- Point p1;
- if (!_commands.empty()) {
- if (auto qto = mpark::get_if<QuadTo>(&_commands.back()))
- p1 = _finalPoint*T(2) - qto->point(0); // reflect previous control point at current point
- else // previous command isn't a quadto?
- p1 = _finalPoint; // => use current point as control point
- }
- quadto(p1, p2);
- }
-
- void cubicto (const T &x1, const T &y1, const T &x2, const T &y2, const T &x3, const T &y3) {
- cubicto(Point(x1, y1), Point(x2, y2), Point(x3, y3));
- }
-
- /** Creates a cubic Bézier segment. */
- void cubicto (const Point &p1, const Point &p2, const Point &p3) {
- _commands.emplace_back(CubicTo{p1, p2, p3});
- _finalPoint = p3;
- }
-
- /** Creates a cubic Bézier segment smoothly extending a preceding one, i.e. the gradients
- * of the two curves are identical at the connection point. The first control point of
- * the second curve is computed as the reflection of the preceding curve's second control
- * point at the connection point. */
- void cubicto (const Point &p2, const Point &p3) {
- Point p1;
- if (!_commands.empty()) {
- if (auto cto = mpark::get_if<CubicTo>(&_commands.back()))
- p1 = _finalPoint*T(2) - cto->point(1); // reflect previous control point at current point
- else // previous command isn't a cubicto?
- p1 = _finalPoint; // => use current point as control point
- }
- cubicto(p1, p2, p3);
- }
-
- void closepath () {
- if (!_commands.empty() && !mpark::get_if<ClosePath>(&_commands.back())) {
- _commands.emplace_back(ClosePath{});
- _finalPoint = _startPoint;
- }
- }
-
- void arcto (T rx, T ry, double angle, bool laf, bool sweep, const Point &p) {
- _commands.emplace_back(ArcTo{rx, ry, angle, laf, sweep, p});
- _finalPoint = p;
- }
-
- /** Detects all open subpaths and closes them by adding a closePath command.
- * Most font formats only support closed outline paths so there are no explicit closePath statements
- * in the glyph's outline description. All open paths are automatically closed by the renderer.
- * This method detects all open paths and adds the missing closePath statement. */
- void closeOpenSubPaths () {
- CommandVariant *prevCmd = nullptr;
- for (auto it=_commands.begin(); it != _commands.end(); ++it) {
- if (mpark::get_if<MoveTo>(&*it) && prevCmd && !mpark::get_if<ClosePath>(prevCmd)) {
- prevCmd = &*it;
- it = _commands.insert(it, ClosePath{})+1;
- }
- else
- prevCmd = &*it;
- }
- if (!_commands.empty() && !mpark::get_if<ClosePath>(&_commands.back()))
- closepath();
- }
-
- /** Removes redundant path commands commands. Currently, only removes movetos. */
- void removeRedundantCommands () {
- // remove trailing moveto commands
- while (!_commands.empty() && mpark::get_if<MoveTo>(&_commands.back()))
- _commands.pop_back();
- // resolve intermediate sequences of moveto commands
- auto it=_commands.begin();
- if (it == _commands.end())
- return;
- auto prev = it++;
- while (it != _commands.end()) {
- if (!mpark::get_if<MoveTo>(&*prev) || !mpark::get_if<MoveTo>(&*it))
- prev = it++;
- else {
- prev = _commands.erase(prev); // remove leading MOVETO and advance 'prev' to 'it'
- ++it;
- }
- }
- }
-
- /** Writes the path data as SVG path drawing command to a given output stream.
- * @param[in] os output stream used to write the SVG commands to
- * @param[in] relative if true, create relative rather than absolute coordinate values
- * @param[in] sx horizontal scale factor
- * @param[in] sy vertical scale factor
- * @param[in] dx horizontal translation in PS point units
- * @param[in] dy vertical translation in PS point units */
- void writeSVG (std::ostream &os, bool relative, double sx=1.0, double sy=1.0, double dx=0.0, double dy=0.0) const {
- WriteActions actions(os, relative, sx, sy, dx, dy);
- iterate(actions, true);
- }
-
- /** Computes the bounding box of the current path.
- * @param[out] bbox the computed bounding box */
- BoundingBox computeBBox () const {
- BoundingBox bbox;
- struct BBoxActions : IterationActions {
- explicit BBoxActions (BoundingBox &bb) : bbox(bb) {}
- void moveto (const Point &p) override {bbox.embed(p);}
- void lineto (const Point &p) override {bbox.embed(p);}
- void quadto (const Point &p1, const Point &p2) override {bbox.embed(p1); bbox.embed(p2);}
- void cubicto (const Point &p1, const Point &p2, const Point &p3) override {bbox.embed(p1); bbox.embed(p2); bbox.embed(p3);}
- void arcto (T rx, T ry, double angle, bool laf, bool sweep, const Point &p) override {
- bbox.embed(EllipticalArc(this->currentPoint(), double(rx), double(ry), angle, laf, sweep, p).getBBox());
- }
- BoundingBox &bbox;
- } actions(bbox);
- iterate(actions, false);
- return bbox;
- }
-
- /** Checks whether the current path describes a dot/point only (with no extent).
- * @param[out] p coordinates of the point if path describes a dot
- * @return true if path is a dot/point */
- bool isDot (Point &p) const {
- struct DotActions : IterationActions {
- DotActions () : differs(false) {}
- void moveto (const Point &p) override {point = p;}
- void lineto (const Point &p) override {differs = (p != point);}
- void quadto (const Point &p1, const Point &p2) override { differs = (point != p1 || point != p2);}
- void cubicto (const Point &p1, const Point &p2, const Point &p3) override {differs = (point != p1 || point != p2 || point != p3);}
- void arcto (T rx, T ry, double angle, bool largeArcFlag, bool sweepFlag, const Point &p) override { differs = (point != p);}
- bool quit () override {return differs;}
- Point point;
- bool differs;
- } actions;
- iterate(actions, false);
- p = actions.point;
- return !actions.differs;
- }
-
- /** Replaces all elliptic arcs with cubic Bézier curves. */
- void approximateArcs () {
- struct ArcActions : ModificationActions {
- explicit ArcActions (GraphicsPath &path) : ModificationActions(path) {}
- void arcto (T rx, T ry, double angle, bool largeArcFlag, bool sweepFlag, const Point &p) override {
- EllipticalArc arc(this->currentPoint(), rx, ry, angle, largeArcFlag, sweepFlag, p);
- std::vector<CommandVariant> cmds;
- for (const Bezier &bezier : arc.approximate())
- cmds.emplace_back(CubicTo{bezier.point(1), bezier.point(2), bezier.point(3)});
- this->path().replace(this->commandPos(), cmds);
- }
- } actions(*this);
- iterate(actions);
- }
-
- /** Transforms the path according to a given Matrix.
- * @param[in] matrix Matrix describing the affine transformation */
- void transform (const Matrix &matrix) {
- TransformVisior visior(matrix);
- for (CommandVariant &command : _commands)
- mpark::visit(visior, command);
- }
-
- /** Returns true if this path equals another one, i.e. it consists the same sequence
- * of commands and coordinates. */
- bool operator == (const GraphicsPath &path) const {
- if (size() != path.size())
- return false;
- auto it = _commands.begin();
- for (const auto &cmd : path._commands) {
- if (*it++ != cmd)
- return false;
- }
- return true;
- }
-
- /** Returns true if this path differs from another one (command-wise). */
- bool operator != (const GraphicsPath &path) const {
- if (size() != path.size())
- return true;
- auto it = _commands.begin();
- for (const auto &cmd : path._commands) {
- if (*it++ != cmd)
- return true;
- }
- return false;
- }
-
- /** Iterates over all commands defining this path and calls the corresponding template methods.
- * In the case of successive bezier curve sequences, control points or tangent slopes are often
- * identical so that the path description contains redundant information. SVG provides shorthand
- * curve commands that require less parameters. If 'optimize' is true, this method detects such
- * command sequences.
- * @param[in] actions template methods called by each iteration step
- * @param[in] optimize if true, shorthand drawing commands (hlineto, vlineto,...) are considered */
- void iterate (IterationActions &actions, bool optimize) const {
- double eps = XMLString::DECIMAL_PLACES > 0 ? std::pow(10, -XMLString::DECIMAL_PLACES) : 1e-7;
- IterationVisitor visitor(actions, optimize, eps);
- for (const CommandVariant &cmd : _commands) {
- if (actions.quit())
- break;
- mpark::visit(visitor, cmd);
- visitor.setPrevCommand(cmd);
- }
- actions.finished();
- }
-
- protected:
- /** Replaces a command by a sequence of other ones.
- * @param[in] pos position of command to replace (0-based)
- * @param[in] cmds commands to insert */
- void replace (int pos, const std::vector<CommandVariant> &cmds) {
- auto it = _commands.end();
- if (!_commands.empty()) {
- it = _commands.begin()+pos;
- it = _commands.erase(it);
- }
- _commands.insert(it, cmds.begin(), cmds.end());
- }
-
- /** Iterates over all commands of the path and calls the corresponding template methods.
- * In contrast to the public iterate() method, this one allows to modify the command sequence.
- * @param[in] actions template methods called by each iteration step */
- void iterate (ModificationActions &actions) {
- IterationVisitor visitor(actions, false);
- // no iterators here since they may be invalidated during path modifications
- for (size_t i=0; i < _commands.size(); i++) {
- if (actions.quit())
- break;
- actions._commandPos = i;
- mpark::visit(visitor, _commands[i]);
- visitor.setPrevCommand(_commands[i]);
- }
- actions.finished();
- }
-
- private:
- std::deque<CommandVariant> _commands; ///< sequence of path commands
- WindingRule _windingRule = WindingRule::NON_ZERO;
- Point _startPoint; ///< start point of final sub-path
- Point _finalPoint; ///< final point reached by last command in path
-};