├── .gitignore ├── Algebra ├── cempty.h ├── cintersection.cpp ├── cintersection.h ├── cmatrix.cpp ├── cmatrix.h ├── cpoint.cpp ├── cpoint.h ├── cpolynomial.cpp ├── cpolynomial.h ├── csegment.cpp ├── csegment.h ├── csize.cpp ├── csize.h ├── cvector2d.cpp ├── cvector2d.h ├── equal.cpp └── equal.h ├── CSS ├── block.cpp ├── block.h ├── cssparser.cpp ├── cssparser.h ├── measureunit.cpp ├── measureunit.h ├── style.cpp └── style.h ├── SVG ├── Assets │ ├── cboundingbox.cpp │ ├── cboundingbox.h │ ├── cdef.cpp │ ├── cdef.h │ ├── cdefs.cpp │ ├── cdefs.h │ ├── cnode.h │ ├── cpoints.cpp │ ├── cpoints.h │ ├── cprimitive.cpp │ ├── cprimitive.h │ ├── fgradient.cpp │ ├── fgradient.h │ ├── inode.h │ └── iprimitive.h ├── Classes │ ├── carc.cpp │ ├── carc.h │ ├── cbezier.cpp │ ├── cbezier.h │ ├── ccircle.cpp │ ├── ccircle.h │ ├── cdoc.cpp │ ├── cdoc.h │ ├── cellipse.cpp │ ├── cellipse.h │ ├── cgroup.cpp │ ├── cgroup.h │ ├── cimage.cpp │ ├── cimage.h │ ├── cline.cpp │ ├── cline.h │ ├── cpath.cpp │ ├── cpath.h │ ├── cpolygon.cpp │ ├── cpolygon.h │ ├── cpolyline.cpp │ ├── cpolyline.h │ ├── crect.cpp │ ├── crect.h │ ├── csvg.cpp │ ├── csvg.h │ ├── fclippath.cpp │ ├── fclippath.h │ ├── flineargradient.cpp │ ├── flineargradient.h │ ├── fprimitive.cpp │ ├── fprimitive.h │ ├── fradialgradient.cpp │ └── fradialgradient.h ├── Generator │ ├── svggenerator.cpp │ └── svggenerator.h └── Parser │ ├── svgparser.cpp │ └── svgparser.h ├── SVG2QML.pro ├── Shaders ├── mask.frag ├── mask.frag.qsb ├── mask.vert └── mask.vert.qsb ├── WDropArea.qml ├── appcore.cpp ├── appcore.h ├── examples ├── buttons1.png ├── buttons1.qml ├── buttons1.svg └── buttons1_original.png ├── main.cpp ├── main.qml ├── qml.qrc ├── qmlgenerator.cpp ├── qmlgenerator.h ├── readme.md └── tests ├── ClipPathBase.svg ├── gradient_60_120.svg ├── gradient_transforms.svg ├── gradient_transforms2.svg ├── test1.svg ├── test_bezier1.svg ├── test_ellipses.svg ├── test_lines.svg ├── test_polyline.svg └── test_rects.svg /.gitignore: -------------------------------------------------------------------------------- 1 | # This file is used to ignore files which are generated 2 | # ---------------------------------------------------------------------------- 3 | 4 | *~ 5 | *.autosave 6 | *.a 7 | *.core 8 | *.moc 9 | *.o 10 | *.obj 11 | *.orig 12 | *.rej 13 | *.so 14 | *.so.* 15 | *_pch.h.cpp 16 | *_resource.rc 17 | *.qm 18 | .#* 19 | *.*# 20 | core 21 | !core/ 22 | tags 23 | .DS_Store 24 | .directory 25 | *.debug 26 | Makefile* 27 | *.prl 28 | *.app 29 | moc_*.cpp 30 | ui_*.h 31 | qrc_*.cpp 32 | Thumbs.db 33 | *.res 34 | *.rc 35 | /.qmake.cache 36 | /.qmake.stash 37 | 38 | # qtcreator generated files 39 | *.pro.user* 40 | 41 | # xemacs temporary files 42 | *.flc 43 | 44 | # Vim temporary files 45 | .*.swp 46 | 47 | # Visual Studio generated files 48 | *.ib_pdb_index 49 | *.idb 50 | *.ilk 51 | *.pdb 52 | *.sln 53 | *.suo 54 | *.vcproj 55 | *vcproj.*.*.user 56 | *.ncb 57 | *.sdf 58 | *.opensdf 59 | *.vcxproj 60 | *vcxproj.* 61 | 62 | # MinGW generated files 63 | *.Debug 64 | *.Release 65 | 66 | # Python byte code 67 | *.pyc 68 | 69 | # Binaries 70 | # -------- 71 | *.dll 72 | *.exe 73 | 74 | -------------------------------------------------------------------------------- /Algebra/cempty.h: -------------------------------------------------------------------------------- 1 | #ifndef CEMPTY_H 2 | #define CEMPTY_H 3 | 4 | /** 5 | * @brief 6 | */ 7 | class CEmpty { 8 | public: 9 | CEmpty() {}; 10 | 11 | class CEmptyPriv { 12 | public: 13 | bool operator==(const float &f) const { return (f!=f); } 14 | }; 15 | 16 | const static CEmptyPriv CEMPTY; 17 | 18 | virtual ~CEmpty() {}; 19 | virtual bool operator==(const CEmptyPriv &) const = 0; 20 | 21 | private: 22 | 23 | }; 24 | 25 | const CEmpty::CEmptyPriv CEMPTY; 26 | 27 | 28 | #endif // CEMPTY_H 29 | -------------------------------------------------------------------------------- /Algebra/cintersection.h: -------------------------------------------------------------------------------- 1 | #ifndef CINTERSECTION_H 2 | #define CINTERSECTION_H 3 | 4 | #include "cpoint.h" 5 | #include "cvector2d.h" 6 | #include "cpolynomial.h" 7 | 8 | /** 9 | * @brief Определяем пересечения прямых/безье и тд 10 | */ 11 | class CIntersection 12 | { 13 | public: 14 | CIntersection(); 15 | 16 | class CIntersectionResult { 17 | public: 18 | enum STATUS { 19 | S_NOINTERSECT, //-- не пересекаются 20 | S_INTERSECT, //-- пересекаются 21 | S_COINCIDENT, //-- совпадают 22 | S_PARALLEL //--параллельны 23 | }; 24 | 25 | STATUS status = S_NOINTERSECT; 26 | QList intersectionPoints; 27 | 28 | /** 29 | * @brief отдаём ту, у которой наименьшее расстояние до указанной 30 | * @param CPoint from 31 | * @return 32 | */ 33 | CPoint minFromPoint; 34 | double minFromLength=std::numeric_limits::max(); 35 | bool updateMinFrom(const CPoint & from) { 36 | if (status!=S_INTERSECT || intersectionPoints.length()==0) return false; 37 | 38 | CPoint p, result; 39 | double cl; 40 | for(int i=0; i 5 | 6 | /** 7 | * @brief CMatrix constructor 8 | * @param cols count 9 | * @param rows count 10 | */ 11 | CMatrix::CMatrix(int cols, int rows): _cols(cols), _rows(rows) 12 | { 13 | _matrix.reserve(_cols*_rows); 14 | } 15 | 16 | CMatrix::CMatrix(int cols, int rows, const TMatrix& m): _cols(cols), _rows(rows), _matrix(m) 17 | { 18 | 19 | } 20 | 21 | CMatrix::CMatrix(const CMatrix& m): _cols(m._cols), _rows(m._rows), _matrix(m._matrix) 22 | { 23 | 24 | } 25 | 26 | /** 27 | * @brief Construct with zeros data 28 | * @param cols 29 | * @param rows 30 | * @return 31 | */ 32 | CMatrix CMatrix::zeros(int cols, int rows) 33 | { 34 | CMatrix mz(cols, rows); 35 | mz.toZeros(); 36 | return mz; 37 | } 38 | 39 | /** 40 | * @brief Construct with identity matrix 41 | * @param cols 42 | * @param rows 43 | * @return 44 | */ 45 | CMatrix CMatrix::identity(int cols, int rows) 46 | { 47 | CMatrix md(cols, rows); 48 | md.toIdentity(); 49 | return md; 50 | } 51 | 52 | /** 53 | * @brief Construct from data 54 | * @param cols 55 | * @param rows 56 | * @param l 57 | * @return 58 | */ 59 | CMatrix CMatrix::initializer(int cols, int rows, const TMatrix l) 60 | { 61 | CMatrix mi(cols, rows); 62 | mi.setData(l); 63 | return mi; 64 | } 65 | 66 | /** 67 | * @brief Size of matrix 68 | * @return 69 | */ 70 | QSize CMatrix::size() const 71 | { 72 | return QSize(_cols, _rows); 73 | } 74 | 75 | /** 76 | * @brief Rows count 77 | * @return 78 | */ 79 | int CMatrix::rows() const 80 | { 81 | return _rows; 82 | } 83 | 84 | /** 85 | * @brief Cols count 86 | * @return 87 | */ 88 | int CMatrix::cols() const 89 | { 90 | return _cols; 91 | } 92 | 93 | /** 94 | * @brief Data values as QMap 95 | * @return 96 | */ 97 | CMatrix::TMatrix CMatrix::data() const 98 | { 99 | return _matrix; 100 | } 101 | 102 | /** 103 | * @brief Data of row 104 | * @param r 105 | * @return 106 | */ 107 | CMatrix::TMatrix CMatrix::row(int r) const 108 | { 109 | TMatrix rl; 110 | for (int i=0; i<_cols; ++i) { 111 | rl<<_matrix[r*_cols+i]; 112 | } 113 | return rl; 114 | } 115 | 116 | /** 117 | * @brief data of column 118 | * @param c 119 | * @return 120 | */ 121 | CMatrix::TMatrix CMatrix::col(int c) const 122 | { 123 | TMatrix cl; 124 | for (int i=0; i<_rows; ++i) { 125 | cl<<_matrix[i*_rows+c]; 126 | } 127 | return cl; 128 | } 129 | 130 | /** 131 | * @brief Set values to matrix 132 | * @param matrix 133 | * @return 134 | */ 135 | CMatrix & CMatrix::setData(const TMatrix & matrix) 136 | { 137 | _matrix = matrix; 138 | return *this; 139 | } 140 | 141 | /** 142 | * @brief Set values to matrix 143 | * @param cols 144 | * @param rows 145 | * @param matrix 146 | * @param dir 147 | * @return 148 | */ 149 | CMatrix &CMatrix::setBy(int cols, int rows, const QList &matrix, CMatrix::SETBY dir) 150 | { 151 | if ( matrix.count()>cols*rows ) { throw 20; } 152 | 153 | for (int i=0; i 7 | #include 8 | 9 | /** 10 | * @brief The Matrix class 11 | */ 12 | class CMatrix: public CEmpty 13 | { 14 | public: 15 | typedef QVector TMatrix; 16 | 17 | enum SETBY { //-- для перевода из другого типа матриц в нашу 18 | SET_BY_ROWS, 19 | SET_BY_COLS 20 | }; 21 | 22 | CMatrix(const CMatrix &m); 23 | 24 | static CMatrix zeros(int cols, int rows); 25 | static CMatrix identity(int cols, int rows); 26 | static CMatrix initializer(int cols, int rows, const TMatrix l); 27 | 28 | QSize size() const; 29 | int rows() const; 30 | int cols() const; 31 | 32 | TMatrix data() const; 33 | CMatrix & setData(const TMatrix & matrix); 34 | CMatrix & setBy(int cols, int rows, const TMatrix &matrix, SETBY dir=SET_BY_ROWS); 35 | 36 | TMatrix row(int r) const; 37 | TMatrix col(int c) const; 38 | 39 | double getAt(int col, int row) const; 40 | CMatrix & setAt(int col, int row, double val); 41 | 42 | CMatrix & translate(double tx, double ty); 43 | CMatrix & scale(double sx, double sy); 44 | CMatrix & rotate(double radians); 45 | CMatrix & rotateD(double degrees); 46 | 47 | CMatrix & addition(const CMatrix & m); 48 | CMatrix & subtraction(const CMatrix & m); 49 | CMatrix & multiplication(const CMatrix & m); 50 | CMatrix & multiplication(double n); 51 | CMatrix & inverse(); 52 | 53 | CMatrix & toIdentity(); 54 | CMatrix & toZeros(); 55 | 56 | CMatrix cofactor(int p, int q) const; 57 | CMatrix adjoint() const; 58 | 59 | double determinant() const; 60 | 61 | CMatrix clon() const; 62 | CMatrix apply(const CMatrix & m) const; 63 | 64 | bool isIdentity(double tolerance = Equal::EPS) const; 65 | bool isZeros(double tolerance = Equal::EPS) const; 66 | 67 | double& operator[] (int idx); 68 | double operator[] (int idx) const; 69 | 70 | friend QDebug operator<<(QDebug dbg, const CMatrix & m); 71 | 72 | bool operator==(const CEmptyPriv &) const; 73 | CMatrix &operator =(const CMatrix &o); 74 | 75 | private: 76 | CMatrix(int cols, int rows); 77 | CMatrix(int cols, int rows, const TMatrix &m); 78 | 79 | int _cols; 80 | int _rows; 81 | TMatrix _matrix; //-- Matrix data (one-dimensional array) 82 | }; 83 | 84 | 85 | 86 | #endif // CMATRIX_H 87 | -------------------------------------------------------------------------------- /Algebra/cpoint.cpp: -------------------------------------------------------------------------------- 1 | #include "cpoint.h" 2 | 3 | CPoint::CPoint(): 4 | marked(false), _x(NAN), _y(NAN), _epsilon(Equal::EPS) 5 | { 6 | } 7 | 8 | CPoint::CPoint(double x, double y, double epsilon): 9 | marked(false), _x(x), _y(y), _epsilon(epsilon) 10 | { 11 | 12 | } 13 | 14 | CPoint::CPoint(const CPoint & p): 15 | marked(p.marked), _x(p._x), _y(p._y), _epsilon(p._epsilon) 16 | { 17 | 18 | } 19 | 20 | CPoint::CPoint(const QPointF & p): 21 | marked(false), _x(p.x()), _y(p.y()), _epsilon(Equal::EPS) 22 | { 23 | 24 | } 25 | 26 | CPoint CPoint::fromZero() 27 | { 28 | return CPoint(0.0, 0.0); 29 | } 30 | 31 | double CPoint::x() const 32 | { 33 | return _x; 34 | } 35 | 36 | double CPoint::y() const 37 | { 38 | return _y; 39 | } 40 | 41 | double CPoint::epsilon() const 42 | { 43 | return _epsilon; 44 | } 45 | 46 | CPoint & CPoint::set(const CPoint & p2) 47 | { 48 | _x = p2._x; 49 | _y = p2._y; 50 | _epsilon = p2._epsilon; 51 | return *this; 52 | } 53 | 54 | CPoint & CPoint::set(double x, double y) 55 | { 56 | _x = x; 57 | _y = y; 58 | return *this; 59 | } 60 | 61 | CPoint &CPoint::setX(double x) 62 | { 63 | _x = x; 64 | return *this; 65 | } 66 | 67 | CPoint &CPoint::setY(double y) 68 | { 69 | _y = y; 70 | return *this; 71 | } 72 | 73 | CPoint &CPoint::incX(double x) 74 | { 75 | _x += x; 76 | return *this; 77 | } 78 | 79 | CPoint &CPoint::incY(double y) 80 | { 81 | _y += y; 82 | return *this; 83 | } 84 | 85 | 86 | CPoint & CPoint::subtract(const CPoint & p2) 87 | { 88 | _x -= p2._x; 89 | _y -= p2._y; 90 | return *this; 91 | } 92 | 93 | CPoint & CPoint::multiply(double scalar) 94 | { 95 | _x *= scalar; 96 | _y *= scalar; 97 | return *this; 98 | } 99 | 100 | CPoint & CPoint::divide(double scalar) 101 | { 102 | _x /= scalar; 103 | _y /= scalar; 104 | return *this; 105 | } 106 | 107 | /** 108 | * @brief Distance to other point 109 | * @param sw - scale axis X 110 | * @param sh - scale axis Y 111 | */ 112 | double CPoint::lengthTo(const CPoint & p2, double sw, double sh) const 113 | { 114 | double dx = (p2._x - _x) / sw; 115 | double dy = (p2._y - _y) / sh; 116 | return sqrt(dx*dx + dy*dy); 117 | } 118 | 119 | CPoint & CPoint::add(const CPoint & p2) 120 | { 121 | _x += p2._x; 122 | _y += p2._y; 123 | return *this; 124 | } 125 | 126 | CPoint & CPoint::add(double x, double y) 127 | { 128 | _x += x; 129 | _y += y; 130 | return *this; 131 | } 132 | 133 | 134 | /** 135 | * @brief Reflect by self 136 | * @return 137 | */ 138 | CPoint & CPoint::reflect() 139 | { 140 | _x = -_x; 141 | _y = -_y; 142 | return *this; 143 | } 144 | 145 | /** 146 | * @brief Reflect relative other point 147 | * @param p2 148 | * @return 149 | */ 150 | CPoint & CPoint::reflectP(const CPoint & p2) 151 | { 152 | double dx = (p2._x - _x); 153 | double dy = (p2._y - _y); 154 | 155 | _x = p2._x + dx; 156 | _y = p2._y + dy; 157 | 158 | return *this; 159 | } 160 | 161 | CPoint CPoint::clon() const 162 | { 163 | CPoint p2(_x, _y); 164 | return p2; 165 | } 166 | 167 | bool CPoint::isZero() const 168 | { 169 | return ( isZeroX() && isZeroY() ); 170 | } 171 | 172 | bool CPoint::isZeroX() const 173 | { 174 | return ( fabs(_x)<_epsilon ); 175 | } 176 | 177 | bool CPoint::isZeroY() const 178 | { 179 | return ( fabs(_y)<_epsilon ); 180 | } 181 | 182 | /** 183 | * @brief Заданы или нет были координаты 184 | * @return 185 | */ 186 | bool CPoint::isEmpty() const 187 | { 188 | return (_x!=_x || _y!=_y); //-- Check if NaN 189 | } 190 | 191 | QString CPoint::toString() const 192 | { 193 | return QString("%1,%2").arg(x()).arg(y()); 194 | } 195 | 196 | CPoint & CPoint::toZero() 197 | { 198 | _x = 0.0; 199 | _y = 0.0; 200 | return *this; 201 | } 202 | 203 | bool CPoint::isEq(const CPoint &p2) const 204 | { 205 | return ( Equal::almostEqual(_x, p2._x) && Equal::almostEqual(_y, p2._y) ); 206 | } 207 | 208 | bool CPoint::isEq(double x, double y) const 209 | { 210 | return ( Equal::almostEqual(_x, x) && Equal::almostEqual(_y, y) ); 211 | } 212 | 213 | bool CPoint::lt(const CPoint & that) const 214 | { 215 | return(_xthat._x&&_y>that._y); 226 | } 227 | 228 | bool CPoint::gte(const CPoint & that) const 229 | { 230 | return(_x>=that._x&&_y>=that._y); 231 | } 232 | 233 | CPoint & CPoint::lerp(const CPoint & that, double t) 234 | { 235 | _x = _x+(that._x-_x)*t; 236 | _y = _y+(that._y-_y)*t; 237 | 238 | return *this; 239 | } 240 | 241 | /** 242 | * @brief Угол между точками p1 и p2 с центром у нас 243 | * @param p1 244 | * @param p2 245 | * @return 246 | */ 247 | double CPoint::angle(const CPoint &p1, const CPoint &p2) const 248 | { 249 | double dx1 = p1.x() - _x; 250 | double dy1 = p1.y() - _y; 251 | double dx2 = p2.x() - _x; 252 | double dy2 = p2.y() - _y; 253 | double cross = dx1*dy2 - dy1*dx2; 254 | double dot = dx1*dx2 + dy1*dy2; 255 | return atan2(cross, dot); 256 | } 257 | 258 | /** 259 | * @brief Apply tranform matrix 260 | */ 261 | CPoint & CPoint::transform(const CMatrix & m1) 262 | { 263 | CMatrix r = m1.apply(CMatrix::initializer(1 ,3, {_x, _y, 1})); 264 | set(r.getAt(0, 0), r.getAt(0, 1)); 265 | return *this; 266 | } 267 | 268 | 269 | CPoint CPoint::min(const CPoint & p2) const 270 | { 271 | return CPoint(std::min(_x, p2._x), std::min(_y, p2._y)); 272 | } 273 | 274 | CPoint CPoint::max(const CPoint & p2) const 275 | { 276 | return CPoint(std::max(_x, p2._x), std::max(_y, p2._y)); 277 | } 278 | 279 | 280 | QDebug operator<<(QDebug dbg, const CPoint & p) 281 | { 282 | dbg.nospace()<<"CPoint("< 5 | #include 6 | #include 7 | #include 8 | 9 | #include "equal.h" 10 | #include "cmatrix.h" 11 | #include "cempty.h" 12 | 13 | /** 14 | * @brief The Point class 15 | */ 16 | class CPoint: public CEmpty 17 | { 18 | public: 19 | CPoint(double x, double y, double epsilon=Equal::EPS); 20 | CPoint(const CPoint & p); 21 | CPoint(const QPointF & p); 22 | CPoint(); 23 | 24 | static CPoint fromZero(); 25 | 26 | double x() const; 27 | double y() const; 28 | double epsilon() const; 29 | 30 | CPoint & set(const CPoint & p2); 31 | CPoint & set(double x, double y); 32 | 33 | CPoint & setX(double x); 34 | CPoint & setY(double y); 35 | 36 | CPoint & incX(double x); 37 | CPoint & incY(double y); 38 | 39 | CPoint & add(const CPoint & p2); 40 | CPoint & add(double x, double y); 41 | CPoint & subtract(const CPoint & p2); 42 | CPoint & multiply(double scalar); 43 | CPoint & divide(double scalar); 44 | 45 | CPoint min(const CPoint & p2) const; 46 | CPoint max(const CPoint & p2) const; 47 | 48 | CPoint & reflect(); 49 | CPoint & reflectP(const CPoint & p2); 50 | CPoint & toZero(); 51 | CPoint & transform(const CMatrix & m1); 52 | CPoint & lerp(const CPoint & that, double t); 53 | 54 | double angle(const CPoint &p1, const CPoint &p2) const; 55 | 56 | //-- Compares 57 | bool lt(const CPoint & that) const; 58 | bool lte(const CPoint & that) const; 59 | bool gt(const CPoint & that) const; 60 | bool gte(const CPoint & that) const; 61 | bool isEq(const CPoint & p2) const; 62 | bool isEq(double x, double y) const; 63 | 64 | CPoint clon() const; 65 | double lengthTo(const CPoint & p2, double sw=1, double sh=1) const; 66 | bool isZero() const; 67 | bool isZeroX() const; 68 | bool isZeroY() const; 69 | bool isEmpty() const; 70 | 71 | QString toString() const; 72 | 73 | CPoint operator *(double v) const; 74 | CPoint operator -(const CPoint &p) const; 75 | CPoint operator +(const CPoint &p) const; 76 | bool operator ==(const CPoint &p) const; 77 | bool operator ==(const CEmptyPriv &) const; 78 | bool operator !=(const CPoint &p) const; 79 | CPoint & operator =(const CPoint &p); 80 | CPoint & operator -=(const CPoint &p); 81 | CPoint & operator +=(const CPoint &p); 82 | 83 | friend QDebug operator<<(QDebug dbg, const CPoint & p); 84 | friend QDataStream& operator <<(QDataStream &dataStream, const CPoint &p); 85 | friend bool operator<(const CPoint &p1, const CPoint& p2); 86 | 87 | operator QPointF() const; 88 | 89 | bool marked; 90 | 91 | private: 92 | double _x; 93 | double _y; 94 | double _epsilon; 95 | }; 96 | 97 | inline uint qHash(const CPoint &key, uint seed) { 98 | return qHash(key.x(), seed) + qHash(key.y(), seed); 99 | } 100 | 101 | Q_DECLARE_METATYPE(CPoint*) 102 | 103 | #endif // CPOINT_H 104 | -------------------------------------------------------------------------------- /Algebra/cpolynomial.cpp: -------------------------------------------------------------------------------- 1 | #include "cpolynomial.h" 2 | 3 | CPolynomial::CPolynomial() 4 | { 5 | 6 | } 7 | 8 | 9 | double CPolynomial::eval(double x) 10 | { 11 | double result=0; 12 | for(int i=coefs.length()-1; i>=0; i--) 13 | result=result*x+coefs.at(i); 14 | return result; 15 | } 16 | 17 | 18 | int CPolynomial::getDegree() const 19 | { 20 | return coefs.length()-1; 21 | } 22 | 23 | CPolynomial CPolynomial::multiply(const CPolynomial & that) 24 | { 25 | CPolynomial result; 26 | int i,j; 27 | for(i=0; i<= getDegree()+that.getDegree(); i++) 28 | result.coefs.append(0); 29 | for(i=0; i<=getDegree(); i++) 30 | for(j=0; j<=that.getDegree(); j++) 31 | result.coefs[i+j]+= coefs.at(i)*that.coefs.at(j); 32 | return result; 33 | } 34 | 35 | 36 | CPolynomial & CPolynomial::divide_scalar(double scalar) 37 | { 38 | for(int i=0; i=0; i--){ 48 | if(abs(coefs.at(i))<= TOLERANCE) 49 | ;//coefs.removeLast(); 50 | else 51 | break; 52 | } 53 | return *this; 54 | } 55 | 56 | 57 | double CPolynomial::bisection(double min, double max) 58 | { 59 | double minValue = eval(min); 60 | double maxValue = eval(max); 61 | double result = 0; 62 | if ( fabs(minValue)<=TOLERANCE ) 63 | result=min; 64 | else if( fabs(maxValue)<=TOLERANCE ) 65 | result=max; 66 | else if ( minValue*maxValue<=0 ) { 67 | double tmp1=log(max-min); 68 | double tmp2=log(10) * ACCURACY; 69 | double iters=ceil((tmp1+tmp2)/log(2)); 70 | for (int i=0; i=0; i--) { 94 | double value= coefs.at(i); 95 | QString sValue; 96 | if( qAbs(value)0) sValue = ( qAbs(value-1)1 ) sValue+=QString("^%1").arg(i); 101 | 102 | sSigns.append(sign); 103 | sCoefs.append(sValue); 104 | } 105 | } 106 | 107 | sSigns[0] = (sSigns[0]==" + ")? "" : "-"; 108 | 109 | QString result = ""; 110 | for (int i=0;i < sCoefs.length(); i++) { 111 | result += sSigns.at(i)+sCoefs.at(i); 112 | } 113 | return result; 114 | } 115 | 116 | 117 | 118 | CPolynomial CPolynomial::getDerivative() 119 | { 120 | CPolynomial derivative; 121 | for(int i=1; i0 ) { 164 | root= bisection(min,droots[0]); 165 | if ( qAbs(root)0){ 200 | double e= sqrt(d); 201 | results.append(0.5*(-b+e)); 202 | results.append(0.5*(-b-e)); 203 | } else if( qAbs(d)0) { 228 | double e= sqrt(discrim); 229 | double tmp; 230 | double root; 231 | tmp=-halfB+e; 232 | if(tmp>=0) 233 | root= pow(tmp, 1/3.0); 234 | else 235 | root= -pow(-tmp,1/3.0); 236 | tmp=-halfB-e; 237 | if(tmp>=0) 238 | root+=pow(tmp,1/3.0); 239 | else 240 | root-=pow(-tmp,1/3.0); 241 | results.append(root-offset); 242 | } else if(discrim<0) { 243 | double distance= sqrt(-a/3.0); 244 | double angle= atan2( sqrt(-discrim),-halfB)/3.0; 245 | double cosa= cos(angle); 246 | double sina= sin(angle); 247 | double sqrt3 = sqrt(3.0); 248 | results.append(2*distance*cosa-offset); 249 | results.append(-distance*(cosa+sqrt3*sina)-offset); 250 | results.append(-distance*(cosa-sqrt3*sina)-offset); 251 | } else { 252 | double tmp; 253 | if(halfB>=0) 254 | tmp=-pow(halfB,1/3); 255 | else 256 | tmp=pow(-halfB,1/3); 257 | results.append(2*tmp-offset); 258 | results.append(-tmp-offset); 259 | } 260 | } 261 | return results; 262 | } 263 | 264 | CPolynomial::TCoefs CPolynomial::getQuarticRoots() 265 | { 266 | TCoefs results; 267 | if(getDegree()==4){ 268 | double c4=coefs.at(4); 269 | double c3=coefs.at(3)/c4; 270 | double c2=coefs.at(2)/c4; 271 | double c1=coefs.at(1)/c4; 272 | double c0=coefs.at(0)/c4; 273 | 274 | CPolynomial resolveRootsPolynominal(1,-c2,c3*c1-4*c0,-c3*c3*c0+4*c2*c0-c1*c1); 275 | TCoefs resolveRoots = resolveRootsPolynominal.getCubicRoots(); 276 | 277 | double y=resolveRoots.at(0); 278 | double discrim=c3*c3/4-c2+y; 279 | if(fabs (discrim)<= TOLERANCE) 280 | discrim=0; 281 | if(discrim>0) { 282 | double e= sqrt(discrim); 283 | double t1=3*c3*c3/4-e*e-2*c2; 284 | double t2=(4*c3*c2-8*c1-c3*c3*c3)/(4*e); 285 | double plus=t1+t2; 286 | double minus=t1-t2; 287 | if(fabs (plus)<= TOLERANCE) 288 | plus=0; 289 | if(fabs (minus)<= TOLERANCE) 290 | minus=0; 291 | if(plus>=0){ 292 | double f= sqrt(plus); 293 | results.append(-c3/4 + (e+f)/2); 294 | results.append(-c3/4 + (e-f)/2); 295 | } 296 | if(minus>=0) { 297 | double f= sqrt(minus); 298 | results.append(-c3/4 + (f-e)/2); 299 | results.append(-c3/4 - (f+e)/2); 300 | } 301 | } else if(discrim<0) { 302 | } else { 303 | double t2=y*y-4*c0; 304 | double t1=3*c3*c3/4-2*c2; 305 | if(t2>=-TOLERANCE) { 306 | if(t2<0) 307 | t2=0; 308 | t2=2*sqrt(t2); 309 | if(t1+t2>= TOLERANCE) { 310 | double d= sqrt(t1+t2); 311 | results.append(-c3/4 + d/2); 312 | results.append(-c3/4 - d/2); 313 | } 314 | if(t1-t2>= TOLERANCE) { 315 | double d= sqrt(t1-t2); 316 | results.append(-c3/4 + d/2); 317 | results.append(-c3/4 - d/2); 318 | } 319 | } 320 | } 321 | } 322 | return results; 323 | } 324 | -------------------------------------------------------------------------------- /Algebra/cpolynomial.h: -------------------------------------------------------------------------------- 1 | #ifndef CPOLYNOMIAL_H 2 | #define CPOLYNOMIAL_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | class CPolynomial 10 | { 11 | public: 12 | 13 | const double TOLERANCE = 1e-6; 14 | const double ACCURACY = 6; 15 | 16 | 17 | void set() { } 18 | template 19 | void set( double coef, Args ... argss) { 20 | coefs.prepend(coef); 21 | return set(argss...); 22 | } 23 | 24 | template 25 | CPolynomial(Args ... args) { 26 | set(args...); 27 | } 28 | CPolynomial(); 29 | 30 | typedef QList TCoefs; 31 | 32 | double eval(double x); 33 | int getDegree() const; 34 | double bisection(double min, double max); 35 | 36 | 37 | CPolynomial multiply(const CPolynomial & that); 38 | CPolynomial & divide_scalar(double scalar); 39 | CPolynomial & simplify(); 40 | CPolynomial getDerivative(); 41 | 42 | TCoefs getRoots(); 43 | TCoefs getRootsInInterval(double min, double max); 44 | TCoefs getLinearRoot(); 45 | TCoefs getQuadraticRoots(); 46 | TCoefs getCubicRoots(); 47 | TCoefs getQuarticRoots(); 48 | 49 | QString toString(); 50 | 51 | TCoefs coefs; 52 | private: 53 | 54 | 55 | }; 56 | 57 | #endif // CPOLYNOMIAL_H 58 | -------------------------------------------------------------------------------- /Algebra/csegment.cpp: -------------------------------------------------------------------------------- 1 | #include "csegment.h" 2 | 3 | CSegment::CSegment(const CPoint &a, const CPoint &b): _a(a), _b(b) 4 | { 5 | 6 | } 7 | 8 | /** 9 | * @brief Лежит ли точка p на отрезке, за исключением концов 10 | * @param p 11 | * @return 12 | */ 13 | bool CSegment::onSegment(const CPoint &p) const 14 | { 15 | 16 | //-- Вертикальная линия 17 | if ( Equal::almostEqual(_a.x(), _b.x()) && Equal::almostEqual(p.x(), _a.x()) ){ 18 | if( !Equal::almostEqual(p.y(), _b.y()) && !Equal::almostEqual(p.y(), _a.y()) && p.y() < qMax(_b.y(), _a.y()) && p.y() > qMin(_b.y(), _a.y()) ) { 19 | return true; 20 | } else{ 21 | return false; 22 | } 23 | } 24 | 25 | //-- Горизонтальная 26 | if ( Equal::almostEqual(_a.y(), _b.y()) && Equal::almostEqual(p.y(), _a.y()) ){ 27 | if( !Equal::almostEqual(p.x(), _b.x()) && !Equal::almostEqual(p.x(), _a.x()) && p.x() < qMax(_b.x(), _a.x()) && p.x() > qMin(_b.x(), _a.x()) ) { 28 | return true; 29 | } else { 30 | return false; 31 | } 32 | } 33 | 34 | //-- Проверим, что хотя бы лежит в пределах наших координат 35 | if ( (p.x() < _a.x() && p.x() < _b.x()) || (p.x() > _a.x() && p.x() > _b.x()) || (p.y() < _a.y() && p.y() < _b.y()) || (p.y() > _a.y() && p.y() > _b.y()) ) { 36 | return false; 37 | } 38 | 39 | //-- Убедимся, что ен на концах 40 | if ( p.isEq(_a) || p.isEq(_b) ){ 41 | return false; 42 | } 43 | 44 | double cr = cross(p); 45 | 46 | if( qAbs(cr)>Equal::EPS ){ 47 | return false; 48 | } 49 | 50 | double dt = dot(p); 51 | 52 | if( (dt<0) || Equal::almostEqual(dt, 0) ) { 53 | return false; 54 | } 55 | 56 | double l2 = len2(); 57 | 58 | if( (dt>l2) || (Equal::almostEqual(dt, l2)) ) { 59 | return false; 60 | } 61 | 62 | return true; 63 | } 64 | 65 | double CSegment::cross(const CPoint &p) const 66 | { 67 | return (p.y()-_a.y()) * (_b.x() - _a.x()) - (p.x() - _a.x()) * (_b.y() - _a.y()); 68 | } 69 | 70 | double CSegment::dot(const CPoint &p) const 71 | { 72 | return (p.x() - _a.x()) * (_b.x() - _a.x()) + (p.y() - _a.y())*(_b.y() - _a.y()); 73 | } 74 | 75 | /** 76 | * @brief Длина отрезка * 2 77 | * @return 78 | */ 79 | double CSegment::len2() const 80 | { 81 | return (_b.x() - _a.x())*(_b.x() - _a.x()) + (_b.y() - _a.y())*(_b.y() - _a.y()); 82 | } 83 | 84 | /** 85 | * @brief Точка пересечения 86 | * @param other 87 | * @param infinite - считаем, что бесконечной длины отрезки 88 | * @return 89 | */ 90 | CPoint CSegment::intersect(const CSegment &other, bool infinite) const 91 | { 92 | CPoint ip; 93 | double a1, a2, b1, b2, c1, c2, x, y; 94 | 95 | a1 = _b.y()-_a.y(); 96 | b1 = _a.x()-_b.x(); 97 | c1 = _b.x()*_a.y() - _a.x()*_b.y(); 98 | a2 = other._b.y()-other._a.y(); 99 | b2 = other._a.x()-other._b.x(); 100 | c2 = other._b.x()*other._a.y() - other._a.x()*other._b.y(); 101 | 102 | double denom = a1*b2 - a2*b1; 103 | 104 | x = (b1*c2 - b2*c1)/denom; 105 | y = (a2*c1 - a1*c2)/denom; 106 | 107 | if ( x!=x || y!=y ) { //-- NAN 108 | return ip; 109 | } 110 | 111 | 112 | if( !infinite ){ 113 | //-- Совпадающие точки не считаются пересекающимися 114 | if (qAbs(_a.x()-_b.x()) > Equal::EPS && (( _a.x() < _b.x() ) ? x < _a.x() || x > _b.x() : x > _a.x() || x < _b.x() )) return ip; 115 | if (qAbs(_a.y()-_b.y()) > Equal::EPS && (( _a.y() < _b.y() ) ? y < _a.y() || y > _b.y() : y > _a.y() || y < _b.y() )) return ip; 116 | if (qAbs(other._a.x()-other._b.x()) > Equal::EPS && (( other._a.x() < other._b.x() ) ? x < other._a.x() || x > other._b.x() : x > other._a.x() || x < other._b.x() )) return ip; 117 | if (qAbs(other._a.y()-other._b.y()) > Equal::EPS && (( other._a.y() < other._b.y() ) ? y < other._a.y() || y > other._b.y() : y > other._a.y() || y < other._b.y() )) return ip; 118 | } 119 | 120 | ip.set(x, y); 121 | return ip; 122 | } 123 | 124 | /** 125 | * @brief Расстояние до точки 126 | * @param p 127 | * @param normal 128 | * @param infinite 129 | * @return 130 | */ 131 | double CSegment::pointDistance(const CPoint &p, const CVector2D &normal, bool infinite) const 132 | { 133 | CVector2D vNormal = normal; 134 | vNormal.normalize(); 135 | CPoint dir(vNormal.y(), -vNormal.x()); 136 | 137 | double pdot = p.x()*dir.x() + p.y()*dir.y(); 138 | double s1dot = _a.x()*dir.x() + _a.y()*dir.y(); 139 | double s2dot = _b.x()*dir.x() + _b.y()*dir.y(); 140 | 141 | double pdotnorm = p.x()*vNormal.x() + p.y()*vNormal.y(); 142 | double s1dotnorm = _a.x()*vNormal.x() + _a.y()*vNormal.y(); 143 | double s2dotnorm = _b.x()*vNormal.x() + _b.y()*vNormal.y(); 144 | 145 | if ( !infinite ) { 146 | if ( ((pdots1dot || Equal::almostEqual(pdot, s1dot)) && (pdot>s2dot || Equal::almostEqual(pdot, s2dot))) ) { 147 | return NAN_DOUBLE; //-- dot doesn't collide with segment, or lies directly on the vertex 148 | } 149 | if ( (Equal::almostEqual(pdot, s1dot) && Equal::almostEqual(pdot, s2dot)) && (pdotnorm>s1dotnorm && pdotnorm>s2dotnorm) ) { 150 | return qMin(pdotnorm - s1dotnorm, pdotnorm - s2dotnorm); 151 | } 152 | if ( (Equal::almostEqual(pdot, s1dot) && Equal::almostEqual(pdot, s2dot)) && (pdotnormEFmax ) { 195 | return NAN_DOUBLE; 196 | } 197 | 198 | double overlap = 0; 199 | 200 | if ( (ABmax>EFmax && ABminABmax && EFmin distances; 243 | 244 | //-- coincident points 245 | if ( Equal::almostEqual(dotA, dotE) ) { 246 | distances.append(crossA-crossE); 247 | } else 248 | if ( Equal::almostEqual(dotA, dotF) ) { 249 | distances.append(crossA-crossF); 250 | } else 251 | if ( dotA>EFmin && dotAEFmin && dotBcrossB A currently touches EF, but AB is moving away from EF 274 | double dA = CSegment(_a, other._a).pointDistance(other._b, reverse, true); 275 | if( dA<0 || Equal::almostEqual(dA*overlap, 0) ) { 276 | d = NAN_DOUBLE; 277 | } 278 | } 279 | if ( d==d ) { 280 | distances.append(d); 281 | } 282 | } 283 | 284 | if ( dotE>ABmin && dotEABmin && dotF1 ) { div = 1; } 66 | if ( div<-1 ) { div = -1; } 67 | 68 | return sign * acos(div); 69 | } 70 | 71 | CVector2D & CVector2D::divide(double scalar) 72 | { 73 | _p.divide(scalar); 74 | return *this; 75 | } 76 | 77 | 78 | CVector2D & CVector2D::unit() 79 | { 80 | return divide(length()); 81 | } 82 | 83 | 84 | CVector2D & CVector2D::add(const CVector2D & that) 85 | { 86 | _p.add(that._p); 87 | return *this; 88 | } 89 | 90 | CVector2D & CVector2D::subtract(const CVector2D & that) 91 | { 92 | _p.set(_p.x() - that._p.x(), _p.y() - that._p.y()); 93 | return *this; 94 | } 95 | 96 | 97 | CVector2D & CVector2D:: multiply(double scalar) 98 | { 99 | _p.set( _p.x() * scalar, _p.y()*scalar ); 100 | return *this; 101 | } 102 | 103 | 104 | CVector2D & CVector2D::perp() 105 | { 106 | _p.set(_p.x(), -_p.y()); 107 | return *this; 108 | } 109 | 110 | CVector2D & CVector2D::fromPoints(const CPoint & p1, const CPoint & p2) 111 | { 112 | _p.set( p2.x()- p1.x(), p2.y()- p1.y() ); 113 | return *this; 114 | } 115 | 116 | /** 117 | * @brief В единичный 118 | */ 119 | void CVector2D::normalize() 120 | { 121 | _p.multiply(1.0/length()); 122 | } 123 | 124 | QDebug operator<<(QDebug dbg, const CVector2D &v) 125 | { 126 | dbg<<"|"< 5 | #include "equal.h" 6 | #include "cpoint.h" 7 | /* 8 | * Кэп, подсказывает, что это вектор 9 | */ 10 | class CVector2D 11 | { 12 | public: 13 | CVector2D(); 14 | CVector2D(const CPoint & p); 15 | CVector2D(double x, double y); 16 | 17 | double x() const; 18 | double y() const; 19 | CPoint point() const; 20 | 21 | double length() const; 22 | double dot(const CVector2D & that) const; 23 | double cross(const CVector2D & that) const; 24 | double angle(const CVector2D & that) const; 25 | 26 | CVector2D clon(); 27 | CVector2D & divide(double scalar); 28 | CVector2D & unit(); 29 | CVector2D & add(const CVector2D & that); 30 | CVector2D & subtract(const CVector2D & that); 31 | CVector2D & multiply(double scalar); 32 | 33 | CVector2D &perp(); 34 | CVector2D & fromPoints(const CPoint & p1, const CPoint & p2); 35 | 36 | void normalize(); 37 | 38 | friend QDebug operator<<(QDebug dbg, const CVector2D & v); 39 | 40 | private: 41 | CPoint _p; 42 | 43 | }; 44 | 45 | #endif // CVECTOR2D_H 46 | -------------------------------------------------------------------------------- /Algebra/equal.cpp: -------------------------------------------------------------------------------- 1 | #include "equal.h" 2 | #include 3 | 4 | Equal::Equal() 5 | { 6 | 7 | } 8 | 9 | bool Equal::almostEqual(double a, double b, double tolerance) 10 | { 11 | return fabs(a-b)<=tolerance; 12 | } 13 | -------------------------------------------------------------------------------- /Algebra/equal.h: -------------------------------------------------------------------------------- 1 | #ifndef EQUAL_H 2 | #define EQUAL_H 3 | 4 | #include 5 | 6 | #define NAN_DOUBLE std::numeric_limits::quiet_NaN() 7 | #define MAX_DOUBLE 99999999 8 | #define MIN_DOUBLE -99999999 9 | 10 | class Equal 11 | { 12 | public: 13 | Equal(); 14 | constexpr static const double EPS = 0.00000001; 15 | static bool almostEqual(double a, double b, double tolerance = EPS); 16 | }; 17 | 18 | #endif // EQUAL_H 19 | -------------------------------------------------------------------------------- /CSS/block.cpp: -------------------------------------------------------------------------------- 1 | #include "block.h" 2 | 3 | #include 4 | #include 5 | 6 | using namespace CSS; 7 | 8 | Block::Block() {} 9 | 10 | 11 | bool Block::parse(QString block) 12 | { 13 | QRegularExpressionMatchIterator rxStyles = QRegularExpression("([a-zA-Z-]+):([^;]+)").globalMatch(block); //-- Парсим стили (ключ-значение разделяем по ";" ) 14 | 15 | while ( rxStyles.hasNext() ) { 16 | QRegularExpressionMatch match = rxStyles.next(); 17 | QString val = match.captured(2); 18 | set(match.captured(1), val); 19 | } 20 | 21 | return true; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /CSS/block.h: -------------------------------------------------------------------------------- 1 | #ifndef BLOCK_H 2 | #define BLOCK_H 3 | 4 | #include "style.h" 5 | #include 6 | 7 | /** 8 | * @brief Парсим всё, что между {...}. 9 | */ 10 | namespace CSS { 11 | class Block: public Style 12 | { 13 | public: 14 | Block(); 15 | bool parse(QString block); 16 | 17 | protected: 18 | 19 | }; 20 | } 21 | #endif // BLOCK_H 22 | -------------------------------------------------------------------------------- /CSS/cssparser.cpp: -------------------------------------------------------------------------------- 1 | #include "cssparser.h" 2 | #include 3 | 4 | using namespace CSS; 5 | CssParser::CssParser(QObject *parent) : QObject(parent) 6 | { 7 | 8 | } 9 | 10 | /** 11 | * @brief Парсим стили 12 | * @param styles 13 | * @return 14 | */ 15 | bool CssParser::parse(QString styles) 16 | { 17 | //-- Удалим всё лишнее, а то регулярка выростает шибко 18 | styles = styles.remove(QRegularExpression("(\\n|\\t)")); 19 | 20 | QRegularExpression rxStyles("(?:\\}|^)?(.+)\\{(.+)\\}"); 21 | rxStyles.setPatternOptions(QRegularExpression::InvertedGreedinessOption); 22 | 23 | QRegularExpressionMatchIterator i = rxStyles.globalMatch(styles); 24 | 25 | while ( i.hasNext() ) { 26 | QRegularExpressionMatch match = i.next(); 27 | 28 | QStringList tokens = match.captured(1).split(','); 29 | QString tokenStyles = match.captured(2); 30 | 31 | foreach (QString token, tokens) { 32 | Block block; 33 | 34 | if ( block.parse(tokenStyles) ) { 35 | if ( _blocks.contains(token) ) { 36 | _blocks[token].unite(block); 37 | } else { 38 | _blocks.insert(token, block); 39 | } 40 | } else { 41 | qWarning()<<"Unable parse styles"<(block); 62 | } 63 | -------------------------------------------------------------------------------- /CSS/cssparser.h: -------------------------------------------------------------------------------- 1 | #ifndef CSSPARSER_H 2 | #define CSSPARSER_H 3 | 4 | #include 5 | #include 6 | 7 | #include "CSS/block.h" 8 | 9 | /** 10 | * Парсим CSS 11 | */ 12 | namespace CSS { 13 | class CssParser : public QObject 14 | { 15 | Q_OBJECT 16 | public: 17 | explicit CssParser(QObject *parent = nullptr); 18 | 19 | bool parse(QString styles); 20 | 21 | Style applyStyles(QString identifier, const Block &priorityBlock = Block()); 22 | 23 | signals: 24 | 25 | public slots: 26 | 27 | private: 28 | QMap _blocks; //-- Все блоки. Ключ - идентификатор, значение - стили блока 29 | 30 | }; 31 | } 32 | 33 | #endif // CSSPARSER_H 34 | -------------------------------------------------------------------------------- /CSS/measureunit.cpp: -------------------------------------------------------------------------------- 1 | #include "measureunit.h" 2 | #include 3 | 4 | using namespace CSS; 5 | 6 | MeasureUnit::MeasureUnit() 7 | { 8 | 9 | } 10 | 11 | MeasureUnit::~MeasureUnit() 12 | { 13 | 14 | } 15 | 16 | MeasureUnit::MeasureUnit(const MeasureUnit &other) 17 | { 18 | _val = other._val; 19 | _type = other._type; 20 | } 21 | 22 | CSS::MeasureUnit::MeasureUnit(double val, CSS::MeasureUnit::TMeasureUnitType type): _val(val), _type(type) 23 | { 24 | 25 | } 26 | 27 | void MeasureUnit::setVal(double val) 28 | { 29 | _val = val; 30 | } 31 | 32 | void MeasureUnit::setType(MeasureUnit::TMeasureUnitType type) 33 | { 34 | _type = type; 35 | } 36 | 37 | double CSS::MeasureUnit::val() const 38 | { 39 | return _val; 40 | } 41 | 42 | MeasureUnit::TMeasureUnitType MeasureUnit::type() const 43 | { 44 | return _type; 45 | } 46 | 47 | /** 48 | * @brief Преобразуем в пиксели 49 | * @return 50 | */ 51 | int MeasureUnit::asPx() const 52 | { 53 | if ( _type==MU_PX ) { return _val; } 54 | if ( _type==MU_PT ) { return _val*1.827; } //TODO[critical]: How to convert units?! 55 | 56 | qWarning()<<"Unsupported conversion MeasureUnit from"<<_type<<"to px."; 57 | return 0; 58 | } 59 | 60 | /** 61 | * @brief Выводим строкой (для дебага) 62 | */ 63 | CSS::MeasureUnit::operator QString() const 64 | { 65 | return QString("MeasureUnit(%1, %2)").arg(val()).arg(type()); 66 | } 67 | 68 | MeasureUnit& MeasureUnit::operator =(const MeasureUnit& other) 69 | { 70 | _val = other.val(); 71 | _type = other.type(); 72 | return *this; 73 | } 74 | -------------------------------------------------------------------------------- /CSS/measureunit.h: -------------------------------------------------------------------------------- 1 | #ifndef MEASUREUNIT_H 2 | #define MEASUREUNIT_H 3 | 4 | #include 5 | 6 | namespace CSS { 7 | 8 | /** 9 | * @brief Значение с единицами измерения 10 | */ 11 | class MeasureUnit 12 | { 13 | public: 14 | 15 | enum TMeasureUnitType { 16 | MU_PT, 17 | MU_PX, 18 | MU_PERCENT 19 | }; 20 | 21 | MeasureUnit(); 22 | MeasureUnit(const MeasureUnit &other); 23 | MeasureUnit(double val, TMeasureUnitType type = MU_PT); 24 | ~MeasureUnit(); 25 | 26 | void setVal(double val); 27 | void setType(TMeasureUnitType type); 28 | 29 | double val() const; 30 | TMeasureUnitType type() const; 31 | 32 | operator QString() const; 33 | MeasureUnit & operator =(const MeasureUnit &other); 34 | 35 | int asPx() const; 36 | 37 | private: 38 | double _val; 39 | TMeasureUnitType _type; 40 | }; 41 | 42 | } 43 | 44 | Q_DECLARE_METATYPE(CSS::MeasureUnit) 45 | 46 | #endif // MEASUREUNIT_H 47 | -------------------------------------------------------------------------------- /CSS/style.cpp: -------------------------------------------------------------------------------- 1 | #include "style.h" 2 | #include 3 | 4 | using namespace CSS; 5 | 6 | Style::Style() 7 | { 8 | 9 | } 10 | 11 | void Style::set(QString styleName, QString value) 12 | { 13 | //-- Известные для Qt типы сразу парсим в нативные значения 14 | QRegularExpressionMatch rxURL = QRegularExpression("url\\((.+)\\)").match(value); //-- Для url() 15 | QRegularExpressionMatchIterator rxMeasureUnit = QRegularExpression("(\\.?[0-9]+(\\.[0-9]+)?)(px|%)?").globalMatch(value); //-- Число с единицами измерения 16 | QVariant vr; 17 | 18 | QColor color = parseColor(value); 19 | 20 | if ( color.isValid() ) { 21 | vr = QVariant::fromValue(color); 22 | } else 23 | if ( rxURL.hasMatch() ) { //-- URL 24 | vr = QVariant::fromValue(QUrl(rxURL.captured(1))); 25 | } else 26 | if ( rxMeasureUnit.hasNext() ) { //-- Число с единицами измерения 27 | 28 | QList muList; 29 | while ( rxMeasureUnit.hasNext() ) { 30 | QRegularExpressionMatch match = rxMeasureUnit.next(); 31 | 32 | MeasureUnit mu; 33 | mu.setVal(match.captured(1).toDouble()); 34 | if ( match.captured(3)=="px" ) { mu.setType(MeasureUnit::MU_PX); } 35 | else if ( match.captured(3)=="%" ) { mu.setType(MeasureUnit::MU_PERCENT); } 36 | else { mu.setType(MeasureUnit::MU_PT); } 37 | muList.append(mu); 38 | } 39 | if ( muList.count()==1 ) { 40 | vr = QVariant::fromValue(muList[0]); 41 | } else { 42 | vr = QVariant::fromValue(muList); 43 | } 44 | 45 | } else { //-- Ничего не подходит, ставим как есть 46 | vr = value; 47 | } 48 | 49 | _styles.insert(styleName, vr); 50 | } 51 | 52 | QVariant Style::get(QString styleName) const 53 | { 54 | return _styles.value(styleName, QVariant()); 55 | } 56 | 57 | bool Style::has(const QString &key) const 58 | { 59 | return _styles.contains(key); 60 | } 61 | 62 | /** 63 | * @brief Объединяем стили 64 | * @param other 65 | * @return 66 | */ 67 | bool Style::unite(const Style &other) 68 | { 69 | for (TStyles::const_iterator i = other._styles.constBegin(); i!=other._styles.constEnd(); ++i) { 70 | _styles[i.key()] = i.value(); 71 | } 72 | return true; 73 | } 74 | 75 | /** 76 | * @brief Выводим одной строкой 77 | * @return 78 | */ 79 | QString Style::toString() const 80 | { 81 | QStringList res; 82 | for(TStyles::const_iterator i=_styles.constBegin(); i!=_styles.constEnd(); ++i) { 83 | res.append(QString("%1:%2;").arg(i.key()).arg(i.value().toString())); 84 | } 85 | return res.join(" "); 86 | } 87 | 88 | /** 89 | * @brief Парсим цвет 90 | * @param color 91 | * @return 92 | */ 93 | QColor Style::parseColor(QString value) 94 | { 95 | QRegularExpressionMatch rxColorPercent = QRegularExpression("rgb\\(([0-9\\.]+)(%?),([0-9\\.]+)(%?),([0-9\\.]+)(%?)\\)").match(value); //-- Регулярка для цвета в формате rgb(a,b,c) или rgb(a%,b%,c%) 96 | QRegularExpressionMatch rxColorHex = QRegularExpression("(#[0-9a-fA-F]{3,6})").match(value); //-- Регулярка для цвета в формате hex 97 | 98 | QColor color; 99 | 100 | if ( rxColorPercent.hasMatch() ) { //-- Проверяем, не цвет ли это в rgb 101 | color = QColor( 102 | (rxColorPercent.captured(2)=="%")? rxColorPercent.captured(1).toDouble()*2.55 : rxColorPercent.captured(1).toInt(), //-- Если есть проценты, то переводим с лимитом в 255 103 | (rxColorPercent.captured(4)=="%")? rxColorPercent.captured(3).toDouble()*2.55 : rxColorPercent.captured(3).toInt(), 104 | (rxColorPercent.captured(6)=="%")? rxColorPercent.captured(5).toDouble()*2.55 : rxColorPercent.captured(5).toInt() 105 | ); 106 | } else 107 | if ( rxColorHex.hasMatch() ) { //-- Или hex 108 | color = QColor(rxColorHex.captured(1)); 109 | } 110 | 111 | return color; 112 | } 113 | 114 | /** 115 | * @brief Для дебага 116 | */ 117 | CSS::Style::operator QString() const 118 | { 119 | QString str; 120 | TStyles::const_iterator i = _styles.begin(); 121 | while (i != _styles.end()) { 122 | str.append( QString("%1: %2; ").arg(i.key()).arg(i.value().toString()) ); 123 | ++i; 124 | } 125 | return str; 126 | } 127 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /CSS/style.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLE_H 2 | #define STYLE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "measureunit.h" 11 | 12 | /** 13 | * @brief Стили, которые есть в боке (Block) 14 | */ 15 | namespace CSS { 16 | 17 | class Style 18 | { 19 | public: 20 | Style(); 21 | 22 | void set(QString styleName, QString value); 23 | QVariant get(QString styleName) const; 24 | bool has(const QString &key) const; 25 | 26 | bool unite(const Style & other); 27 | 28 | operator QString() const; 29 | 30 | QString toString() const; 31 | 32 | static QColor parseColor(QString color); 33 | 34 | protected: 35 | typedef QHash TStyles; 36 | TStyles _styles; 37 | 38 | }; 39 | } 40 | #endif // STYLE_H 41 | -------------------------------------------------------------------------------- /SVG/Assets/cboundingbox.cpp: -------------------------------------------------------------------------------- 1 | #include "cboundingbox.h" 2 | #include "SVG/Classes/cpolygon.h" 3 | 4 | CBoundingBox::CBoundingBox() 5 | { 6 | clear(); 7 | } 8 | 9 | CBoundingBox::CBoundingBox(const QVector &points) 10 | { 11 | clear(); 12 | addPoints(points); 13 | } 14 | 15 | /** 16 | * @brief Учитываем новую точку в размере ограничительнгой рамки 17 | * @param p 18 | * @return 19 | */ 20 | bool CBoundingBox::addPoint(const CPoint &p) 21 | { 22 | if ( p.isEmpty() ) return false; 23 | 24 | _tl = _tl.min(p); 25 | _br = _br.max(p); 26 | 27 | return true; 28 | } 29 | 30 | /** 31 | * @brief Добавляем новые точки, кэп 32 | * @param points 33 | */ 34 | void CBoundingBox::addPoints(const QVector &points) 35 | { 36 | foreach(const CPoint &p, points) { 37 | addPoint(p); 38 | } 39 | } 40 | 41 | /** 42 | * @brief Учитываем другую ограничительную рамку 43 | * @param b 44 | * @return 45 | */ 46 | bool CBoundingBox::addBBox(const CBoundingBox &b) 47 | { 48 | addPoint(b.tl()); 49 | addPoint(b.br()); 50 | return true; 51 | } 52 | 53 | /** 54 | * @brief Обнуляем полностью 55 | */ 56 | void CBoundingBox::clear() 57 | { 58 | _tl.set(MAX_DOUBLE, MAX_DOUBLE); 59 | _br.set(MIN_DOUBLE, MIN_DOUBLE); 60 | } 61 | 62 | /** 63 | * @brief Return size 64 | * @return 65 | */ 66 | CSize CBoundingBox::size() const 67 | { 68 | return CSize(width(), height()); 69 | } 70 | 71 | /** 72 | * @brief Top Left coordinates 73 | * @return 74 | */ 75 | CPoint CBoundingBox::tl() const 76 | { 77 | return _tl; 78 | } 79 | 80 | /** 81 | * @brief Bottom Right coordinates 82 | * @return 83 | */ 84 | CPoint CBoundingBox::br() const 85 | { 86 | return _br; 87 | } 88 | 89 | /** 90 | * @brief Move bounding box 91 | * @param D - расстояние в приращениях 92 | */ 93 | CBoundingBox& CBoundingBox::move(const CPoint &D) 94 | { 95 | if ( isEmpty() ) { return *this; } 96 | _tl.add(D); 97 | _br.add(D); 98 | return *this; 99 | } 100 | 101 | CBoundingBox & CBoundingBox::transform(const CMatrix& transformMatrix) 102 | { 103 | if ( isEmpty() ) { return *this; } 104 | 105 | CPoint tl = _tl; 106 | CPoint tr = _tl+CPoint(width(), 0); 107 | CPoint br = _br; 108 | CPoint bl = _tl+CPoint(0, height()); 109 | 110 | 111 | tl.transform(transformMatrix); 112 | tr.transform(transformMatrix); 113 | br.transform(transformMatrix); 114 | bl.transform(transformMatrix); 115 | 116 | CBoundingBox bbTr({tl, tr, br, bl}); 117 | 118 | _tl = bbTr.tl(); 119 | _br = bbTr.br(); 120 | 121 | return *this; 122 | } 123 | 124 | /** 125 | * @brief isEmpty 126 | * @return 127 | */ 128 | bool CBoundingBox::isEmpty() const 129 | { 130 | return ( _tl.isEq(_br) || (_tl.isEq(MAX_DOUBLE, MAX_DOUBLE) && _br.isEq(MIN_DOUBLE, MIN_DOUBLE)) ); 131 | } 132 | 133 | /** 134 | * @brief Проверяем, не находимся ли мы внутри другой ограничительной рамки 135 | * @param other 136 | * @return 137 | */ 138 | bool CBoundingBox::inside(const CBoundingBox &other) const 139 | { 140 | CPolygon p1(tl(), br()); 141 | CPolygon p2(other.tl(), other.br()); 142 | return p1.inside(p2); 143 | } 144 | 145 | bool CBoundingBox::operator <(const CBoundingBox &other) const 146 | { 147 | return ( (width()(const CBoundingBox &other) const 151 | { 152 | return !(operator <(other)); 153 | } 154 | 155 | void CBoundingBox::operator =(const QRectF &rect) 156 | { 157 | if ( rect.isEmpty() ) { 158 | clear(); 159 | } else { 160 | _tl.set(rect.left(), rect.top()); 161 | _br.set(rect.right(), rect.bottom()); 162 | } 163 | } 164 | 165 | double CBoundingBox::width() const 166 | { 167 | return _br.x()-_tl.x(); 168 | } 169 | 170 | double CBoundingBox::height() const 171 | { 172 | return _br.y()-_tl.y(); 173 | } 174 | 175 | double CBoundingBox::left() const 176 | { 177 | return _tl.x(); 178 | } 179 | 180 | double CBoundingBox::top() const 181 | { 182 | return _tl.y(); 183 | } 184 | 185 | CBoundingBox::operator QRectF() const 186 | { 187 | return QRectF(_tl, _br); 188 | } 189 | -------------------------------------------------------------------------------- /SVG/Assets/cboundingbox.h: -------------------------------------------------------------------------------- 1 | #ifndef CBOUNDINGBOX_H 2 | #define CBOUNDINGBOX_H 3 | 4 | #include "Algebra/cpoint.h" 5 | #include "Algebra/csize.h" 6 | #include "Algebra/cmatrix.h" 7 | 8 | /** 9 | * @brief Вычисляем размеры ограничительной рамки 10 | */ 11 | class CBoundingBox 12 | { 13 | public: 14 | CBoundingBox(); 15 | CBoundingBox(const QVector &points); 16 | 17 | bool addPoint(const CPoint &p); 18 | void addPoints(const QVector &points); 19 | bool addBBox(const CBoundingBox &b); 20 | void clear(); 21 | CSize size() const; 22 | CPoint tl() const; 23 | CPoint br() const; 24 | 25 | CBoundingBox & move(const CPoint &D); 26 | CBoundingBox & transform(const CMatrix &transformMatrix); 27 | bool isEmpty() const; 28 | 29 | bool inside(const CBoundingBox &other) const; 30 | 31 | bool operator <(const CBoundingBox &other) const; 32 | bool operator >(const CBoundingBox &other) const; 33 | void operator =(const QRectF &rect); 34 | 35 | operator QRectF() const; 36 | 37 | double width() const; 38 | double height() const; 39 | double left() const; 40 | double top() const; 41 | 42 | private: 43 | CPoint _tl; //-- Top Left point 44 | CPoint _br; //-- Bottom Right point 45 | 46 | }; 47 | 48 | #endif // CBOUNDINGBOX_H 49 | -------------------------------------------------------------------------------- /SVG/Assets/cdef.cpp: -------------------------------------------------------------------------------- 1 | #include "cdef.h" 2 | 3 | CDef::CDef() : CPrimitive(PT_DEF), _relDef(nullptr) 4 | { 5 | 6 | } 7 | 8 | bool CDef::hasRelDef() const 9 | { 10 | return ( _relDef!=nullptr ); 11 | } 12 | 13 | CDef* CDef::relDef() const 14 | { 15 | return _relDef; 16 | } 17 | 18 | void CDef::setRelDef(CDef* relDef) 19 | { 20 | _relDef = relDef; 21 | } 22 | -------------------------------------------------------------------------------- /SVG/Assets/cdef.h: -------------------------------------------------------------------------------- 1 | #ifndef CDEF_H 2 | #define CDEF_H 3 | 4 | #include "cnode.h" 5 | #include "Algebra/cempty.h" 6 | 7 | #include "cprimitive.h" 8 | 9 | /** 10 | * @brief Базовый клас для шаблонных элементов 11 | */ 12 | class CDef : public CPrimitive 13 | { 14 | public: 15 | explicit CDef(); 16 | 17 | enum TDefType { 18 | DF_NONE, 19 | DF_LINEARGRADIENT, 20 | DF_RADIALGRADIENT, 21 | DF_CLIPPATH, 22 | DF_CPRIMITIVE 23 | }; 24 | 25 | virtual TDefType defType() const { return DF_NONE; } 26 | 27 | bool hasRelDef() const; 28 | CDef * relDef() const; 29 | void setRelDef(CDef * relDef); 30 | 31 | #define RELDEFVAL(var) \ 32 | if ( hasRelDef() && (dynamic_cast(relDef())!=nullptr) && !( (dynamic_cast(relDef()))->var==CEMPTY) ) { \ 33 | return (dynamic_cast(relDef()))->var; \ 34 | } 35 | 36 | #define RELDEFVALN(var) \ 37 | if ( hasRelDef() && (dynamic_cast(relDef())!=nullptr) && !( (dynamic_cast(relDef()))->var!=dynamic_cast(relDef())->var) ) { \ 38 | return (dynamic_cast(relDef()))->var; \ 39 | } 40 | 41 | 42 | private: 43 | CDef * _relDef; 44 | }; 45 | 46 | #endif // CDEF_H 47 | -------------------------------------------------------------------------------- /SVG/Assets/cdefs.cpp: -------------------------------------------------------------------------------- 1 | #include "cdefs.h" 2 | 3 | CDefs::CDefs() 4 | { 5 | 6 | } 7 | 8 | CDef *CDefs::get(const QUrl &link) const 9 | { 10 | QString id = link.toString().mid(1, -1); 11 | return value(id, nullptr); 12 | } 13 | 14 | bool CDefs::isCDefLink(const QUrl &link) 15 | { 16 | return (link.toString().left(1)=="#"); 17 | } 18 | -------------------------------------------------------------------------------- /SVG/Assets/cdefs.h: -------------------------------------------------------------------------------- 1 | #ifndef CDEFS_H 2 | #define CDEFS_H 3 | 4 | #include 5 | #include 6 | 7 | #include "cdef.h" 8 | 9 | /** 10 | * @brief Список CDef 11 | **/ 12 | class CDefs: public QMap 13 | { 14 | public: 15 | CDefs(); 16 | CDef * get(const QUrl &link) const; 17 | 18 | 19 | static bool isCDefLink(const QUrl &link); 20 | }; 21 | 22 | #endif // CDEFS_H 23 | -------------------------------------------------------------------------------- /SVG/Assets/cnode.h: -------------------------------------------------------------------------------- 1 | #ifndef CNODE_H 2 | #define CNODE_H 3 | 4 | #include "inode.h" 5 | 6 | /** 7 | * @brief Базовый класс, что бы можно было по быстрому связать примитивы пути группы и тд 8 | * у каждого элемента доно быть установлены указатели на 9 | * next/prev - следующий/предыдущий элемент на одном уровне 10 | * first/last - первый/последний элемент на одном уровне (для текущего уровня получать через верхний уровень (up) ), 11 | * up - первый элемент из уровня выше 12 | * down - первый элемент из уровня ниже 13 | * если чего-то нету, то nullptr 14 | */ 15 | class CNodeInterface: public virtual INodeInterface 16 | { 17 | public: 18 | 19 | CNodeInterface(): _first(nullptr), _last(nullptr), _up(nullptr), _down(nullptr), _next(nullptr), _prev(nullptr) { } 20 | virtual ~CNodeInterface() {} 21 | 22 | /** 23 | * @brief Сбрасываем состояние ноды 24 | * @param node 25 | */ 26 | void reset() override 27 | { 28 | CNodeInterface * node = dynamic_cast(this); 29 | node->_down = nullptr; 30 | node->_next = nullptr; 31 | node->_prev = nullptr; 32 | node->_up = nullptr; 33 | node->_last = nullptr; 34 | node->_first = nullptr; 35 | } 36 | 37 | /** 38 | * @brief Добавляем на текущий уровень ещё один элемент в конец 39 | * @param node 40 | */ 41 | void addNext(INodeInterface * iNode) override 42 | { 43 | CNodeInterface * level = dynamic_cast(this); 44 | CNodeInterface * node = dynamic_cast(iNode); 45 | 46 | if ( level->_first==nullptr ) { level->_first = node; } 47 | 48 | if ( level->_last!=nullptr ) { 49 | level->_last->_next=node; 50 | node->_prev = level->_last; 51 | } 52 | 53 | level->_last = node; 54 | node->_up = level; 55 | 56 | if ( level->_down==nullptr ) { level->_down = node; } 57 | } 58 | 59 | /** 60 | * @brief Добавляем на текущий уровень ещё один элемент в начало 61 | * @param node 62 | */ 63 | void addPrev(INodeInterface * iNode) override 64 | { 65 | CNodeInterface * level = dynamic_cast(this); 66 | CNodeInterface * node = dynamic_cast(iNode); 67 | 68 | if ( level->_last==nullptr ) { level->_last = node; } 69 | 70 | if ( level->_first!=nullptr ) { 71 | level->_first->_prev = node; 72 | node->_next = level->_first; 73 | } 74 | 75 | level->_first = node; 76 | node->_up = level; 77 | level->_down = node; 78 | } 79 | 80 | /** 81 | * @brief Создаём уровень ниже 82 | * @param node 83 | */ 84 | INodeInterface * levelDown(INodeInterface * node) override 85 | { 86 | addNext(node); 87 | return node; 88 | } 89 | 90 | /** 91 | * @brief Переходим на уровень выше 92 | */ 93 | INodeInterface * levelUp() override 94 | { 95 | return up(); 96 | } 97 | 98 | /** 99 | * @brief Убираем из уровня ноду 100 | */ 101 | void removeFromLevel() override 102 | { 103 | CNodeInterface * node = this; 104 | if ( node->_up!=nullptr ) { 105 | if ( node->_up->_last==node ) { node->_up->_last = node->_prev; } 106 | if ( node->_up->_first==node ) { node->_up->_first = node->_next; } 107 | if ( node->_up->_down==node ) { node->_up->_down = node->_next; } 108 | } 109 | node->_up = nullptr; 110 | 111 | if ( node->_next!=nullptr ) { node->_next->_prev = node->_prev; } 112 | if ( node->_prev!=nullptr ) { node->_prev->_next = node->_next; } 113 | 114 | node->_next = nullptr; 115 | node->_prev = nullptr; 116 | } 117 | 118 | INodeInterface * first() const override { return _first; } 119 | INodeInterface * last() const override { return _last; } 120 | INodeInterface * up() const override { return _up; } 121 | INodeInterface * down() const override { return _down; } 122 | INodeInterface * next() const override { return _next; } 123 | INodeInterface * prev() const override { return _prev; } 124 | 125 | private: 126 | CNodeInterface * _first; 127 | CNodeInterface * _last; 128 | CNodeInterface * _up; 129 | CNodeInterface * _down; 130 | CNodeInterface * _next; 131 | CNodeInterface * _prev; 132 | 133 | }; 134 | 135 | /** 136 | * @brief Пробегаемся по всем итемам с учётом уровней 137 | */ 138 | class CNodeInterfaceIterator 139 | { 140 | public: 141 | CNodeInterfaceIterator(INodeInterface * rootItm): _rootItm(rootItm), _curItm(rootItm), _curLevel(rootItm), _curType(IT_NONE) { 142 | if ( _curItm!=nullptr ) { _curType = IT_STARTELEMENT; } 143 | } 144 | 145 | //-- Устанавливаем при переходе к каждому элементу 146 | enum IterationType: int { 147 | IT_NONE = 2, 148 | IT_STARTELEMENT = 4, 149 | IT_STARTLEVEL = 8, 150 | IT_ENDLEVEL = 16 151 | }; 152 | 153 | /** 154 | * @brief Переходим к следующему элементу 155 | * @return 156 | */ 157 | bool next() 158 | { 159 | if ( _curItm==nullptr ) { return false; } 160 | 161 | if ( (_curType&IT_STARTELEMENT) && (_curItm->down()!=nullptr) ) { 162 | _curLevel = _curItm; 163 | _curItm = _curItm->down(); 164 | _curType = (IT_STARTELEMENT | IT_STARTLEVEL); 165 | return true; 166 | } else 167 | if ( _curItm->next()!=nullptr ) { 168 | _curItm = _curItm->next(); 169 | _curType = IT_STARTELEMENT; 170 | return true; 171 | } else 172 | if ( _curItm->up()!=_rootItm->up() ) { 173 | _curItm = _curItm->up(); 174 | _curLevel = _curItm; 175 | _curType = IT_ENDLEVEL; 176 | return true; 177 | } 178 | 179 | return false; 180 | } 181 | 182 | /** 183 | * @brief Переходим сразу на следующий уровень и не важно в каком месте сейчас 184 | * @return 185 | */ 186 | bool nextLevel() 187 | { 188 | if ( _curItm->last()!=nullptr ) { _curItm = _curItm->last(); } 189 | if ( _curItm->up()==_rootItm->up() ) { _curItm = nullptr; return false; } 190 | _curItm = _curItm->up(); 191 | _curLevel = _curItm; 192 | _curType = IT_ENDLEVEL; 193 | return true; 194 | } 195 | 196 | template 197 | T item() const { return dynamic_cast(_curItm); } 198 | 199 | template 200 | T level() const { return dynamic_cast(_curLevel); } 201 | 202 | template 203 | T rootItem() const { return dynamic_cast(_rootItm); } 204 | 205 | int type() const { return _curType; } 206 | 207 | private: 208 | INodeInterface * _rootItm; 209 | INodeInterface * _curItm; 210 | INodeInterface * _curLevel; 211 | int _curType; 212 | }; 213 | 214 | #endif // CNODE_H 215 | -------------------------------------------------------------------------------- /SVG/Assets/cpoints.cpp: -------------------------------------------------------------------------------- 1 | #include "cpoints.h" 2 | 3 | CPoints::CPoints(): QVector() 4 | { 5 | } 6 | 7 | CPoints::CPoints(const QVector &points): QVector(points) 8 | { 9 | } 10 | 11 | const CPoint &CPoints::p1() const 12 | { 13 | return at(0); 14 | } 15 | 16 | const CPoint &CPoints::p2() const 17 | { 18 | return at(1); 19 | } 20 | 21 | const CPoint &CPoints::p3() const 22 | { 23 | return at(2); 24 | } 25 | 26 | const CPoint &CPoints::p4() const 27 | { 28 | return at(3); 29 | } 30 | 31 | const CPoint &CPoints::p5() const 32 | { 33 | return at(4); 34 | } 35 | 36 | const CPoint &CPoints::p6() const 37 | { 38 | return at(5); 39 | } 40 | 41 | void CPoints::add(const CPoint &p) 42 | { 43 | append(p); 44 | } 45 | 46 | void CPoints::reverse() 47 | { 48 | std::reverse(begin(), end()); 49 | } 50 | 51 | void CPoints::move(const CPoint &d) 52 | { 53 | for(int i=0; i 10 | { 11 | public: 12 | CPoints(); 13 | CPoints(const QVector &points); 14 | 15 | const CPoint &p1() const; 16 | const CPoint &p2() const; 17 | const CPoint &p3() const; 18 | const CPoint &p4() const; 19 | const CPoint &p5() const; 20 | const CPoint &p6() const; 21 | 22 | void add(const CPoint &p); 23 | void reverse(); 24 | void move(const CPoint &d); 25 | void rotate(const CPoint ¢er, double degrees); 26 | 27 | private: 28 | 29 | 30 | }; 31 | 32 | #endif // CPOINTS_H 33 | -------------------------------------------------------------------------------- /SVG/Assets/cprimitive.cpp: -------------------------------------------------------------------------------- 1 | #include "cprimitive.h" 2 | 3 | template 4 | CPrimitive::CPrimitive(): CNodeInterface(), _type(PT_NONE), _id("") 5 | { 6 | needUpdate(); 7 | } 8 | 9 | template 10 | CPrimitive::CPrimitive(PrimitiveType type): CPrimitive() 11 | { 12 | _type = type; 13 | needUpdate(); 14 | } 15 | 16 | template 17 | CPrimitive::CPrimitive(const CPrimitive&other) 18 | : CNodeInterface(other), _type(other._type), _points(other._points), _styles(other._styles), _boundingBox(other._boundingBox), _id(other._id), 19 | _className(other._className), _transformMatrix(other._transformMatrix), _title(other._title), _descr(other._descr) 20 | { 21 | 22 | } 23 | 24 | template 25 | CPrimitive::CPrimitive(IPrimitive* other): CNodeInterface(*dynamic_cast(dynamic_cast(other))), 26 | _type(other->type()), _points(other->points()), _styles(other->styles()), _boundingBox(other->boundingBox()), _id(other->ID()), 27 | _className(other->className()), _transformMatrix(other->transformation()), _title(other->title()), _descr(other->descr()) 28 | { 29 | 30 | } 31 | 32 | template 33 | CPrimitive::CPrimitive(PrimitiveType type, const CPoint &p1): CPrimitive(type) 34 | { 35 | _points.add(p1); 36 | needUpdate(); 37 | } 38 | 39 | template 40 | CPrimitive::CPrimitive(PrimitiveType type, const CPoint &p1, const CPoint &p2): CPrimitive(type) 41 | { 42 | _points.add(p1); 43 | _points.add(p2); 44 | needUpdate(); 45 | } 46 | 47 | template 48 | CPrimitive::CPrimitive(PrimitiveType type, const CPoint &p1, const CPoint &p2, const CPoint &p3, const CPoint &p4): CPrimitive(type) 49 | { 50 | _points.add(p1); 51 | _points.add(p2); 52 | _points.add(p3); 53 | _points.add(p4); 54 | needUpdate(); 55 | } 56 | 57 | template 58 | CPrimitive::CPrimitive(PrimitiveType type, const CPoint &p1, const CPoint &p2, const CPoint &p3, const CPoint &p4, const CPoint &p5, const CPoint &p6): CPrimitive(type) 59 | { 60 | _points.add(p1); 61 | _points.add(p2); 62 | _points.add(p3); 63 | _points.add(p4); 64 | _points.add(p5); 65 | _points.add(p6); 66 | needUpdate(); 67 | } 68 | 69 | template 70 | CPrimitive::CPrimitive(CPrimitive::PrimitiveType type, const CPoints &points): CPrimitive(type) 71 | { 72 | _points = points; 73 | needUpdate(); 74 | } 75 | 76 | template 77 | CPrimitive::~CPrimitive() 78 | { 79 | for (INodeInterface * ni=this->down(); ni!=nullptr; ni=ni->next()) { 80 | delete ni; 81 | } 82 | removeFromLevel(); 83 | } 84 | 85 | template 86 | typename CPrimitive::PrimitiveType CPrimitive::type() const 87 | { 88 | return _type; 89 | } 90 | 91 | template 92 | CPoints CPrimitive::points() const 93 | { 94 | return _points; 95 | } 96 | 97 | template 98 | void CPrimitive::setPoints(const CPoints &points) 99 | { 100 | _points = points; 101 | needUpdate(); 102 | } 103 | 104 | template 105 | int CPrimitive::pointsCount() const 106 | { 107 | return _points.count(); 108 | } 109 | 110 | template 111 | CSS::Style CPrimitive::styles() const 112 | { 113 | return _styles; 114 | } 115 | 116 | /** 117 | * @brief Return bounding gox (with childs) 118 | * @param withTransform - consider transformation matrix 119 | * @return CBoundingBox 120 | */ 121 | template 122 | CBoundingBox CPrimitive::boundingBox(bool withTransform) const 123 | { 124 | if ( !_boundingBox.isEmpty() ) { return _boundingBox; } 125 | _boundingBox.addPoints(_points); 126 | 127 | if ( withTransform ) { _boundingBox.transform(_transformMatrix); } 128 | 129 | boundingBoxChilds(withTransform); 130 | 131 | return _boundingBox; 132 | } 133 | 134 | /** 135 | * @brief Суммируем все ограничительные рамки у вложенных итемов 136 | * @param withTransform 137 | * @return 138 | */ 139 | template 140 | CBoundingBox CPrimitive::boundingBoxChilds(bool withTransform) const 141 | { 142 | for (INodeInterface *ni = down(); ni!=nullptr; ni=ni->next()) { 143 | IPrimitive * pi = dynamic_cast(ni); 144 | _boundingBox.addBBox(pi->boundingBox(withTransform)); 145 | } 146 | return _boundingBox; 147 | } 148 | 149 | 150 | template 151 | void CPrimitive::setStyles(CSS::Style styles) 152 | { 153 | _styles = styles; 154 | } 155 | 156 | /** 157 | * @brief Изменяем направление на противоположное 158 | */ 159 | template 160 | void CPrimitive::reverse() 161 | { 162 | _points.reverse(); 163 | } 164 | 165 | /** 166 | * @brief Превращаем в набор отрезков 167 | * @param tol 168 | * @return 169 | */ 170 | template 171 | CPoints CPrimitive::lianirize(double tol) const 172 | { 173 | CPoints res; 174 | for (INodeInterface * ni = down(); ni!=nullptr; ni=ni->next()) { 175 | IPrimitive * pr = dynamic_cast(ni); 176 | res.append(pr->lianirize(tol)); 177 | } 178 | return res; 179 | } 180 | 181 | /** 182 | * @brief Clone nesteed 183 | */ 184 | template 185 | CPrimitive * CPrimitive::clone() const 186 | { 187 | T * clon = new T(*dynamic_cast(this)); 188 | 189 | clon->reset(); 190 | 191 | for (INodeInterface * ni=this->down(); ni!=nullptr; ni=ni->next()) { 192 | IPrimitive * pr = dynamic_cast(ni); 193 | IPrimitive * prCopy = pr->clone(); 194 | clon->addNext(prCopy); 195 | } 196 | 197 | return clon; 198 | } 199 | 200 | /** 201 | * @brief Применяем заданную матрицу трансформаций 202 | * @param matrix 203 | * @return 204 | */ 205 | template 206 | bool CPrimitive::applyTransform(const CMatrix &matrix) 207 | { 208 | //-- Себя 209 | for (int i=0; i<_points.count(); ++i) { 210 | _points[i].transform(matrix); 211 | } 212 | 213 | needUpdate(); 214 | 215 | //-- Остальным на нижнем уровне, если есть 216 | if ( this->down()!=nullptr ) { 217 | for (INodeInterface * ni=this->down(); ni!=nullptr; ni=ni->next()) { 218 | IPrimitive * pi = dynamic_cast(ni); 219 | pi->applyTransform(matrix); 220 | } 221 | } 222 | 223 | return true; 224 | } 225 | 226 | /** 227 | * @brief Преобразовываем себя в путь и добавляем к себе же уровнем ниже 228 | * @return 229 | */ 230 | template 231 | bool CPrimitive::toPath() 232 | { 233 | return false; 234 | } 235 | 236 | template 237 | QString CPrimitive::ID() const 238 | { 239 | return _id; 240 | } 241 | 242 | template 243 | void CPrimitive::setID(QString id) 244 | { 245 | _id = id; 246 | } 247 | 248 | template 249 | QString CPrimitive::className() const 250 | { 251 | return _className; 252 | } 253 | 254 | template 255 | void CPrimitive::setClassName(QString className) 256 | { 257 | _className = className; 258 | } 259 | 260 | template 261 | void CPrimitive::setTitle(QString title) 262 | { 263 | _title = title; 264 | } 265 | 266 | template 267 | QString CPrimitive::title() const 268 | { 269 | return _title; 270 | } 271 | 272 | template 273 | void CPrimitive::setDescr(QString descr) 274 | { 275 | _descr = descr; 276 | } 277 | 278 | template 279 | QString CPrimitive::descr() const 280 | { 281 | return _descr; 282 | } 283 | 284 | template 285 | CPoint &CPrimitive::operator[](int i) 286 | { 287 | return _points[i]; 288 | } 289 | 290 | template 291 | const CPoint &CPrimitive::operator[](int i) const 292 | { 293 | return _points[i]; 294 | } 295 | 296 | /** 297 | * @brief Сбрасываем все запомненные расчёты 298 | */ 299 | template 300 | void CPrimitive::needUpdate() 301 | { 302 | _boundingBox.clear(); 303 | 304 | //-- Придётся и у предка то же, т.к. может быть основан на наших 305 | IPrimitive * upItm = dynamic_cast(up()); 306 | if ( upItm!=nullptr ) { upItm->needUpdate(); } 307 | } 308 | 309 | template 310 | CMatrix CPrimitive::transformation() const 311 | { 312 | return _transformMatrix; 313 | } 314 | 315 | template 316 | void CPrimitive::setTransformation(const CMatrix &matrix) 317 | { 318 | _transformMatrix = matrix; 319 | } 320 | 321 | -------------------------------------------------------------------------------- /SVG/Assets/cprimitive.h: -------------------------------------------------------------------------------- 1 | #ifndef CPRIMITIVE_H 2 | #define CPRIMITIVE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "iprimitive.h" 9 | 10 | #include "Algebra/cpoint.h" 11 | #include "Algebra/cmatrix.h" 12 | 13 | 14 | /** 15 | * @brief Базовый класс с общими методами для всех отображаемых примитивов SVG (путь, линия, круг, группа и т.д) 16 | */ 17 | template 18 | class CPrimitive: public CNodeInterface, public virtual IPrimitive 19 | { 20 | public: 21 | CPrimitive(); 22 | CPrimitive(PrimitiveType type); 23 | CPrimitive(const CPrimitive&other); 24 | CPrimitive(IPrimitive * other); 25 | 26 | virtual ~CPrimitive(); 27 | 28 | CPrimitive(PrimitiveType type, const CPoint &p1); 29 | CPrimitive(PrimitiveType type, const CPoint &p1, const CPoint &p2); 30 | CPrimitive(PrimitiveType type, const CPoint &p1, const CPoint &p2, const CPoint &p3, const CPoint &p4); 31 | CPrimitive(PrimitiveType type, const CPoint &p1, const CPoint &p2, const CPoint &p3, const CPoint &p4, const CPoint &p5, const CPoint &p6); 32 | CPrimitive(PrimitiveType type, const CPoints &points); 33 | 34 | PrimitiveType type() const override; 35 | CPoints points() const override; 36 | void setPoints(const CPoints &points) override; 37 | int pointsCount() const override; 38 | 39 | void cstream(QDataStream &dataStream, double scale) override { Q_UNUSED(dataStream); Q_UNUSED(scale); } 40 | 41 | CBoundingBox boundingBox(bool withTransform=true) const override; 42 | 43 | void setStyles(CSS::Style styles) override; 44 | CSS::Style styles() const override; 45 | void reverse() override; 46 | CPoints lianirize(double tol) const override; 47 | CPrimitive * clone() const override; 48 | 49 | bool applyTransform(const CMatrix &matrix) override; 50 | 51 | bool toPath() override; 52 | 53 | QString ID() const override; 54 | void setID(QString id) override; 55 | 56 | QString className() const override; 57 | void setClassName(QString className) override; 58 | 59 | void setTitle(QString title) override; 60 | QString title() const override; 61 | 62 | void setDescr(QString descr) override; 63 | QString descr() const override; 64 | 65 | friend QDataStream& operator <<(QDataStream &dataStream, const CPrimitive &p) { Q_UNUSED(p); return dataStream; } 66 | 67 | CPoint &operator[](int i) override; 68 | const CPoint &operator[](int i) const override; 69 | 70 | void needUpdate() override; 71 | 72 | CMatrix transformation() const override; 73 | void setTransformation(const CMatrix &matrix) override; 74 | 75 | protected: 76 | PrimitiveType _type; 77 | CPoints _points; 78 | CSS::Style _styles; //-- Собранные стили этого элемента 79 | mutable CBoundingBox _boundingBox; //-- Bounding Box 80 | QString _id; //-- ID from SVG 81 | QString _className; //-- Class name from SVG 82 | 83 | CMatrix _transformMatrix = CMatrix::identity(3,3); 84 | 85 | QString _title; 86 | QString _descr; 87 | 88 | CBoundingBox boundingBoxChilds(bool withTransform=true) const; 89 | 90 | }; 91 | #include "cprimitive.cpp" 92 | 93 | #endif // CPRIMITIVE_H 94 | -------------------------------------------------------------------------------- /SVG/Assets/fgradient.cpp: -------------------------------------------------------------------------------- 1 | #include "fgradient.h" 2 | 3 | FGradient::FGradient(): CDef() 4 | { 5 | 6 | } 7 | 8 | FGradient::FGradient(const FGradient &other): CDef(other) 9 | { 10 | _stops = other._stops; 11 | } 12 | 13 | void FGradient::addStop(const FGradient::TGradientStop &gs) 14 | { 15 | _stops.append(gs); 16 | } 17 | 18 | QList FGradient::stops() const 19 | { 20 | QList stopsList; 21 | 22 | if ( hasRelDef() ) { 23 | FGradient * rg = dynamic_cast(relDef()); 24 | stopsList.append(rg->stops()); 25 | } 26 | 27 | stopsList.append(_stops); //TODO: Combine by offset? 28 | 29 | return stopsList; 30 | } 31 | 32 | void FGradient::setTransform(const CMatrix &transform) 33 | { 34 | _transform = transform; 35 | } 36 | 37 | CMatrix FGradient::transform() const 38 | { 39 | return _transform; 40 | } 41 | -------------------------------------------------------------------------------- /SVG/Assets/fgradient.h: -------------------------------------------------------------------------------- 1 | #ifndef FGRADIENT_H 2 | #define FGRADIENT_H 3 | 4 | #include "cdef.h" 5 | #include "Algebra/cmatrix.h" 6 | #include 7 | #include 8 | 9 | class FGradient: public CDef 10 | { 11 | public: 12 | FGradient(); 13 | FGradient(const FGradient &other); 14 | 15 | struct TGradientStop { 16 | float position; 17 | QColor color; 18 | }; 19 | 20 | void addStop(const TGradientStop &gs); 21 | 22 | QList stops() const; 23 | 24 | void setTransform(const CMatrix &transform); 25 | CMatrix transform() const; 26 | 27 | protected: 28 | QList _stops; 29 | CMatrix _transform = CMatrix::identity(3,3); 30 | 31 | }; 32 | 33 | #endif // FGRADIENT_H 34 | -------------------------------------------------------------------------------- /SVG/Assets/inode.h: -------------------------------------------------------------------------------- 1 | #ifndef INODE_H 2 | #define INODE_H 3 | 4 | class INodeInterface 5 | { 6 | public: 7 | virtual ~INodeInterface() {}; 8 | virtual void reset() =0; 9 | virtual void addNext(INodeInterface * iNode) =0; 10 | virtual void addPrev(INodeInterface * iNode) =0; 11 | virtual INodeInterface * levelDown(INodeInterface * node) =0; 12 | virtual INodeInterface * levelUp() =0; 13 | virtual void removeFromLevel() =0; 14 | 15 | virtual INodeInterface * first() const =0; 16 | virtual INodeInterface * last() const =0; 17 | virtual INodeInterface * up() const =0; 18 | virtual INodeInterface * down() const =0; 19 | virtual INodeInterface * next() const =0; 20 | virtual INodeInterface * prev() const =0; 21 | }; 22 | 23 | #endif // INODE_H 24 | -------------------------------------------------------------------------------- /SVG/Assets/iprimitive.h: -------------------------------------------------------------------------------- 1 | #ifndef IPRIMITIVE_H 2 | #define IPRIMITIVE_H 3 | 4 | #include "cpoints.h" 5 | #include "cboundingbox.h" 6 | #include "CSS/style.h" 7 | #include "cnode.h" 8 | 9 | class IPrimitive: public virtual INodeInterface 10 | { 11 | public: 12 | enum PrimitiveType { 13 | PT_NONE, 14 | PT_SVG, 15 | PT_BEZIER, 16 | PT_LINE, 17 | PT_PATH, 18 | PT_GROUP, 19 | PT_IMAGE, 20 | PT_POLYGON, 21 | PT_ARC, 22 | PT_RECT, 23 | PT_CIRCLE, 24 | PT_ELLIPSE, 25 | PT_POLYLINE, 26 | PT_DEF 27 | }; 28 | 29 | virtual PrimitiveType type() const =0; 30 | virtual CPoints points() const =0; 31 | virtual void setPoints(const CPoints &points) =0; 32 | virtual int pointsCount() const =0; 33 | 34 | virtual void cstream(QDataStream &dataStream, double scale) =0; 35 | 36 | virtual CBoundingBox boundingBox(bool withTransform=true) const =0; 37 | 38 | virtual void setStyles(CSS::Style styles) =0; 39 | virtual CSS::Style styles() const =0; 40 | virtual void reverse() =0; 41 | virtual CPoints lianirize(double tol) const =0; 42 | virtual IPrimitive * clone() const =0; 43 | 44 | virtual bool applyTransform(const CMatrix &matrix) =0; 45 | 46 | virtual bool toPath() =0; 47 | 48 | virtual QString ID() const =0; 49 | virtual void setID(QString id) =0; 50 | 51 | virtual QString className() const =0; 52 | virtual void setClassName(QString className) =0; 53 | 54 | virtual void setTitle(QString title) =0; 55 | virtual QString title() const =0; 56 | 57 | virtual void setDescr(QString descr) =0; 58 | virtual QString descr() const =0; 59 | 60 | virtual CPoint &operator[](int i) =0; 61 | virtual const CPoint &operator[](int i) const =0; 62 | 63 | virtual void needUpdate() =0; 64 | 65 | virtual CMatrix transformation() const =0; 66 | virtual void setTransformation(const CMatrix &matrix) =0; 67 | }; 68 | 69 | #endif // IPRIMITIVE_H 70 | -------------------------------------------------------------------------------- /SVG/Classes/carc.cpp: -------------------------------------------------------------------------------- 1 | #include "carc.h" 2 | #include "math.h" 3 | #include "Algebra/equal.h" 4 | #include "Algebra/cvector2d.h" 5 | 6 | CArc::CArc() 7 | :CPrimitive(PT_ARC) 8 | { 9 | 10 | } 11 | 12 | /** 13 | * @brief Дуга 14 | * @param startPoint - координаты начальной точки 15 | * @param rx - радиус по X 16 | * @param ry - радиус по Y 17 | * @param rotation - угол поворота по X 18 | * @param largeArc - выводить большую часть дуги 19 | * @param sweep - направление отрисовки (по часовой/против) 20 | * @param endPoint - координаты конечной точки 21 | */ 22 | CArc::CArc(CPoint startPoint, double rx, double ry, double rotation, bool largeArc, bool sweep, CPoint endPoint) 23 | :CPrimitive(CPrimitive::PT_ARC, startPoint, endPoint), _rx(rx), _ry(ry), 24 | _rotation(rotation), _largeArc(largeArc), _sweep(sweep) 25 | { 26 | 27 | } 28 | 29 | double CArc::rx() const 30 | { 31 | return _rx; 32 | } 33 | 34 | double CArc::ry() const 35 | { 36 | return _ry; 37 | } 38 | 39 | double CArc::rotation() const 40 | { 41 | return _rotation; 42 | } 43 | 44 | bool CArc::largeArcFlag() const 45 | { 46 | return _largeArc; 47 | } 48 | 49 | bool CArc::sweepFlag() const 50 | { 51 | return _sweep; 52 | } 53 | 54 | /** 55 | * @brief Преобразуем в путь 56 | * @return 57 | * @ref https://github.com/colinmeinke/svg-arc-to-cubic-bezier 58 | * @ref https://ru.wikipedia.org/wiki/%D0%9A%D1%80%D0%B8%D0%B2%D0%B0%D1%8F_%D0%91%D0%B5%D0%B7%D1%8C%D0%B5 59 | */ 60 | bool CArc::toPath() 61 | { 62 | if ( (_rx==0) || (_ry==0) ) return false; 63 | 64 | CPath * path = new CPath(); 65 | CPoint s = _points.p1(); 66 | CPoint e = _points.p2(); 67 | 68 | double sinphi = sin( _rotation * TAU / 360 ); 69 | double cosphi = cos( _rotation * TAU / 360 ); 70 | 71 | double pxp = cosphi * (s.x() - e.x()) / 2 + sinphi * (s.y() - e.y()) / 2; 72 | double pyp = -sinphi * (s.x() - e.x()) / 2 + cosphi * (s.y() - e.y()) / 2; 73 | 74 | if ( Equal::almostEqual(pxp, 0) && Equal::almostEqual(pyp, 0) ) { return false; } 75 | 76 | double rx = abs(_rx); 77 | double ry = abs(_ry); 78 | 79 | double lambda = pow(pxp, 2) / pow(rx, 2) + pow(pyp, 2) / pow(ry, 2); 80 | 81 | if ( lambda>1 ) { 82 | rx *= sqrt(lambda); 83 | ry *= sqrt(lambda); 84 | } 85 | 86 | TArcCenter arcCenter = getArcCenter(s, e, rx, ry, _largeArc, _sweep, sinphi, cosphi, pxp, pyp); 87 | 88 | int segments = qMax(ceil(abs(arcCenter.ang2) / (TAU / 4)), 1.0); 89 | arcCenter.ang2 /= (double)segments; 90 | 91 | QList> curves; 92 | for (int i = 0; i curve, curves) { 99 | CPoint p2 = mapToEllipse(curve[0], rx, ry, cosphi, sinphi, arcCenter.p); 100 | CPoint p3 = mapToEllipse(curve[1], rx, ry, cosphi, sinphi, arcCenter.p); 101 | CPoint p4 = mapToEllipse(curve[2], rx, ry, cosphi, sinphi, arcCenter.p); 102 | CBezier * b = new CBezier(p1, p2, p3, p4); 103 | path->addNext(b); 104 | p1 = p4; 105 | } 106 | 107 | addNext(path); 108 | 109 | return true; 110 | } 111 | 112 | CArc::TArcCenter CArc::getArcCenter(CPoint s, CPoint e, double rx, double ry, bool largeArc, bool sweep, double sinphi, double cosphi, double pxp, double pyp) const 113 | { 114 | double rxsq = pow(rx, 2); 115 | double rysq = pow(ry, 2); 116 | double pxpsq = pow(pxp, 2); 117 | double pypsq = pow(pyp, 2); 118 | double radicant = ( rxsq * rysq ) - ( rxsq * pypsq ) - ( rysq * pxpsq ); 119 | if ( radicant<0 ) { radicant = 0; } 120 | radicant /= ( rxsq * pypsq ) + ( rysq * pxpsq ); 121 | radicant = sqrt(radicant) * ((largeArc==sweep)? -1 : 1); 122 | double centerxp = radicant * rx/ry * pyp; 123 | double centeryp = radicant * -ry/rx * pxp; 124 | 125 | double centerX = cosphi * centerxp - sinphi * centeryp + ( s.x() + e.x() ) / 2; 126 | double centerY = sinphi * centerxp + cosphi * centeryp + ( s.y() + e.y() ) / 2; 127 | 128 | double vx1 = ( pxp - centerxp ) / rx; 129 | double vy1 = ( pyp - centeryp ) / ry; 130 | double vx2 = ( -pxp - centerxp ) / rx; 131 | double vy2 = ( -pyp - centeryp ) / ry; 132 | 133 | double ang1 = CVector2D(1, 0).angle(CVector2D(vx1, vy1)); 134 | double ang2 = CVector2D(vx1, vy1).angle(CVector2D(vx2, vy2)); 135 | 136 | if ( !sweep && ang2>0 ) { ang2 -= TAU; } 137 | if ( sweep && ang2<0 ) { ang2 += TAU; } 138 | 139 | return {CPoint(centerX, centerY), ang1, ang2}; 140 | } 141 | 142 | QList CArc::approxUnitArc(double ang1, double ang2) const 143 | { 144 | double a = 4 / 3 * tan(ang2 / 4); 145 | double x1 = cos(ang1); 146 | double y1 = sin(ang1); 147 | double x2 = cos(ang1 + ang2); 148 | double y2 = sin(ang1 + ang2); 149 | 150 | 151 | QList l; 152 | l.append(CPoint(x1 - y1 * a, y1 + x1 * a)); 153 | l.append(CPoint(x2 + y2 * a, y2 - x2 * a)); 154 | l.append(CPoint(x2, y2)); 155 | 156 | return l; 157 | } 158 | 159 | CPoint CArc::mapToEllipse(CPoint p, double rx, double ry, double cosphi, double sinphi, CPoint cp) const 160 | { 161 | double x = p.x()*rx; 162 | double y = p.y()*ry; 163 | double xp = cosphi * x - sinphi * y; 164 | double yp = sinphi * x + cosphi * y; 165 | return CPoint(xp + cp.x(), yp + cp.y()); 166 | } 167 | -------------------------------------------------------------------------------- /SVG/Classes/carc.h: -------------------------------------------------------------------------------- 1 | #ifndef CARC_H 2 | #define CARC_H 3 | 4 | #include "../Assets/cprimitive.h" 5 | 6 | #include "cbezier.h" 7 | #include "cpath.h" 8 | 9 | class CArc: public CPrimitive 10 | { 11 | public: 12 | CArc(); 13 | CArc(CPoint startPoint, double rx, double ry, double rotation, bool largeArc, bool sweep, CPoint endPoint); 14 | 15 | double rx() const; 16 | double ry() const; 17 | double rotation() const; 18 | bool largeArcFlag() const; 19 | bool sweepFlag() const; 20 | 21 | bool toPath() override; 22 | 23 | const double TAU = M_PI*2; 24 | 25 | private: 26 | double _rx; 27 | double _ry; 28 | double _rotation; 29 | bool _largeArc; 30 | bool _sweep; 31 | 32 | struct TArcCenter { 33 | CPoint p; 34 | double ang1; 35 | double ang2; 36 | }; 37 | 38 | TArcCenter getArcCenter(CPoint s, CPoint e, double rx, double ry, bool largeArc, bool sweep, double sinphi, double cosphi, double pxp, double pyp) const; 39 | QList approxUnitArc(double ang1, double ang2) const; 40 | CPoint mapToEllipse(CPoint p, double rx, double ry, double cosphi, double sinphi, CPoint cp) const; 41 | }; 42 | 43 | #endif // CARC_H 44 | -------------------------------------------------------------------------------- /SVG/Classes/cbezier.h: -------------------------------------------------------------------------------- 1 | #ifndef CBEZIER_H 2 | #define CBEZIER_H 3 | 4 | #include "SVG/Assets/cprimitive.h" 5 | 6 | class CBezier : public CPrimitive 7 | { 8 | public: 9 | CBezier(); 10 | CBezier(const CPoint &s, const CPoint &a1, const CPoint &a2, const CPoint &e); 11 | CBezier(const CPoint &s, const CPoint &a, const CPoint &e); 12 | CBezier(const CBezier&other); 13 | CBezier(const CPoints &points); 14 | 15 | void cstream(QDataStream &dataStream, double scale) override; 16 | 17 | CPoints lianirize(double tol) const override; 18 | 19 | CBoundingBox boundingBox(bool withTransform=true) const override; 20 | 21 | QPair subDivide(double t) const; 22 | QList split(double t1, double t2 = -1) const; 23 | 24 | QList makeOffset(double d) const; 25 | 26 | bool direction(); 27 | 28 | CPoint get(double t) const; 29 | QList reduce() const; 30 | 31 | bool isFlat(double tol) const; 32 | bool simple() const; 33 | 34 | CBezier & operator =(const CBezier &b); 35 | 36 | protected: 37 | struct TExtrema { 38 | QMap> result; 39 | QList roots; 40 | }; 41 | struct TOffset { 42 | CPoint c; 43 | CPoint n; 44 | CPoint e; 45 | }; 46 | CBezier * bscale(double d); 47 | CPoint normal(double t) const; 48 | CPoint derivative(double t) const; 49 | double evalBez(const QVector poly, double t) const; 50 | QList droots(const QList &p) const; 51 | QList dPoints() const; 52 | QList derive(const CPoints &points) const; 53 | double map(double v, double ds, double de, double ts, double te) const; 54 | CPoint lerp(double r, const CPoint &v1, const CPoint &v2) const; 55 | CPoints hull(double t) const; 56 | TExtrema extrema() const; 57 | CPoint lli4(const CPoint &p1, const CPoint &p2, const CPoint &p3, CPoint const &p4) const; 58 | TOffset boffset(double t, double d); 59 | QList boffset(double t) const; 60 | 61 | private: 62 | double _t1 = 0; 63 | double _t2 = 1; 64 | mutable QList _dpoints; 65 | 66 | }; 67 | 68 | #endif // CBEZIER_H 69 | -------------------------------------------------------------------------------- /SVG/Classes/ccircle.cpp: -------------------------------------------------------------------------------- 1 | #include "ccircle.h" 2 | #include "cpath.h" 3 | #include "cbezier.h" 4 | #include "cellipse.h" 5 | 6 | CCircle::CCircle(): CPrimitive(PT_CIRCLE, CPoint()) 7 | { 8 | 9 | } 10 | 11 | CCircle::CCircle(const CCircle &other): CPrimitive(other) 12 | { 13 | _radius = other._radius; 14 | } 15 | 16 | 17 | CCircle::CCircle(CPoint center, double radius): CPrimitive(PT_CIRCLE, center), _radius(radius) 18 | { 19 | 20 | } 21 | 22 | void CCircle::setRadius(double radius) 23 | { 24 | _radius = radius; 25 | } 26 | 27 | double CCircle::radius() const 28 | { 29 | return _radius; 30 | } 31 | 32 | /** 33 | * @brief Преобразуем в путь и добавляем к себе вниз 34 | * @return 35 | */ 36 | bool CCircle::toPath() 37 | { 38 | CPath * path = new CPath(); 39 | CPoint center = _points.p1(); 40 | 41 | CBezier * b1 = CEllipse::drawBezierEllipseQuarter(center, QSize(-_radius, -_radius)); 42 | CBezier * b2 = CEllipse::drawBezierEllipseQuarter(center, QSize(_radius, -_radius)); 43 | CBezier * b3 = CEllipse::drawBezierEllipseQuarter(center, QSize(_radius, _radius)); 44 | CBezier * b4 = CEllipse::drawBezierEllipseQuarter(center, QSize(-_radius, _radius)); 45 | 46 | b2->reverse(); 47 | b4->reverse(); 48 | 49 | path->addNext(b1); 50 | path->addNext(b2); 51 | path->addNext(b3); 52 | path->addNext(b4); 53 | addNext(path); 54 | 55 | path->setIsClosed(true); 56 | 57 | return true; 58 | } 59 | 60 | CBoundingBox CCircle::boundingBox(bool withTransform) const 61 | { 62 | if ( !_boundingBox.isEmpty() ) { return _boundingBox; } 63 | 64 | CPoint R(_radius, _radius); 65 | _boundingBox.addPoint(_points.p1()-R); 66 | _boundingBox.addPoint(_points.p1()+R); 67 | 68 | if ( withTransform ) { _boundingBox.transform(_transformMatrix); } 69 | 70 | boundingBoxChilds(withTransform); 71 | 72 | return _boundingBox; 73 | } 74 | -------------------------------------------------------------------------------- /SVG/Classes/ccircle.h: -------------------------------------------------------------------------------- 1 | #ifndef CCIRCLE_H 2 | #define CCIRCLE_H 3 | 4 | #include "../Assets/cprimitive.h" 5 | 6 | class CCircle: public CPrimitive 7 | { 8 | public: 9 | CCircle(); 10 | CCircle(CPoint center, double radius); 11 | CCircle(const CCircle &other); 12 | 13 | void setRadius(double radius); 14 | double radius() const; 15 | 16 | bool toPath() override; 17 | 18 | CBoundingBox boundingBox(bool withTransform=true) const override; 19 | 20 | private: 21 | double _radius; 22 | 23 | }; 24 | 25 | #endif // CCIRCLE_H 26 | -------------------------------------------------------------------------------- /SVG/Classes/cdoc.cpp: -------------------------------------------------------------------------------- 1 | #include "cdoc.h" 2 | 3 | CDoc::CDoc(): CPrimitive(PT_GROUP) 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /SVG/Classes/cdoc.h: -------------------------------------------------------------------------------- 1 | #ifndef CDOC_H 2 | #define CDOC_H 3 | 4 | #include "../Assets/cprimitive.h" 5 | 6 | /** 7 | * @brief Корневой элемент после парсинга 8 | */ 9 | class CDoc: public CPrimitive 10 | { 11 | public: 12 | CDoc(); 13 | 14 | private: 15 | 16 | }; 17 | 18 | #endif // CDOC_H 19 | -------------------------------------------------------------------------------- /SVG/Classes/cellipse.cpp: -------------------------------------------------------------------------------- 1 | #include "cellipse.h" 2 | #include "cpath.h" 3 | #include "cbezier.h" 4 | 5 | CEllipse::CEllipse(const CPoint& centerPoint): CPrimitive(PT_ELLIPSE, centerPoint) 6 | { 7 | 8 | } 9 | 10 | CPoint CEllipse::center() const 11 | { 12 | return _points[0]; 13 | } 14 | 15 | CEllipse::TRadius CEllipse::radius() const 16 | { 17 | return _radius; 18 | } 19 | 20 | void CEllipse::setCX(double x) 21 | { 22 | _points[0].setX(x); 23 | } 24 | 25 | void CEllipse::setCY(double y) 26 | { 27 | _points[0].setY(y); 28 | } 29 | 30 | void CEllipse::setRX(double rx) 31 | { 32 | _radius.first = rx; 33 | } 34 | 35 | void CEllipse::setRY(double ry) 36 | { 37 | _radius.second = ry; 38 | } 39 | 40 | /** 41 | * @brief Выводим часть элипса 42 | * @param center 43 | * @param size 44 | * @return 45 | */ 46 | CBezier *CEllipse::drawBezierEllipseQuarter(CPoint center, QSizeF size) 47 | { 48 | double c = 0.55191502449; 49 | CPoint p1(center.x()-size.width(), center.y()); 50 | CPoint p2(center.x()-size.width(), center.y()-(c*size.height())); 51 | CPoint p3(center.x()-(c*size.width()), center.y()-size.height()); 52 | CPoint p4(center.x(), center.y()-size.height()); 53 | 54 | CBezier * b = new CBezier(p1, p2, p3, p4); 55 | 56 | return b; 57 | } 58 | 59 | bool CEllipse::toPath() 60 | { 61 | CPath * path = new CPath(); 62 | const CPoint & cp = _points[0]; 63 | 64 | CBezier * b1 = drawBezierEllipseQuarter(cp, QSizeF(_radius.first, _radius.second)); 65 | CBezier * b2 = drawBezierEllipseQuarter(cp, QSizeF(-_radius.first, _radius.second)); 66 | CBezier * b3 = drawBezierEllipseQuarter(cp, QSizeF(-_radius.first, -_radius.second)); 67 | CBezier * b4 = drawBezierEllipseQuarter(cp, QSizeF(_radius.first, -_radius.second)); 68 | 69 | b2->reverse(); 70 | b4->reverse(); 71 | 72 | path->addNext(b1); 73 | path->addNext(b2); 74 | path->addNext(b3); 75 | path->addNext(b4); 76 | addNext(path); 77 | 78 | return true; 79 | } 80 | 81 | CBoundingBox CEllipse::boundingBox(bool withTransform) const 82 | { 83 | if ( !_boundingBox.isEmpty() ) { return _boundingBox; } 84 | 85 | CPoint R(_radius.first, _radius.second); 86 | _boundingBox.addPoint(_points.p1()-R); 87 | _boundingBox.addPoint(_points.p1()+R); 88 | 89 | if ( withTransform ) { _boundingBox.transform(_transformMatrix); } 90 | 91 | boundingBoxChilds(withTransform); 92 | 93 | return _boundingBox; 94 | } 95 | -------------------------------------------------------------------------------- /SVG/Classes/cellipse.h: -------------------------------------------------------------------------------- 1 | #ifndef CELLIPSE_H 2 | #define CELLIPSE_H 3 | 4 | #include "../Assets/cprimitive.h" 5 | #include "cbezier.h" 6 | 7 | class CEllipse: public CPrimitive 8 | { 9 | public: 10 | CEllipse(const CPoint ¢erPoint = CPoint()); 11 | 12 | typedef QPair TRadius; 13 | 14 | CPoint center() const; 15 | TRadius radius() const; 16 | 17 | void setCX(double x); 18 | void setCY(double y); 19 | void setRX(double rx); 20 | void setRY(double ry); 21 | 22 | static CBezier* drawBezierEllipseQuarter(CPoint center, QSizeF size); 23 | 24 | bool toPath() override; 25 | 26 | CBoundingBox boundingBox(bool withTransform=true) const override; 27 | 28 | private: 29 | TRadius _radius; 30 | 31 | }; 32 | 33 | #endif // CELLIPSE_H 34 | -------------------------------------------------------------------------------- /SVG/Classes/cgroup.cpp: -------------------------------------------------------------------------------- 1 | #include "cgroup.h" 2 | 3 | CGroup::CGroup(): 4 | CPrimitive(CPrimitive::PT_GROUP) 5 | { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /SVG/Classes/cgroup.h: -------------------------------------------------------------------------------- 1 | #ifndef CGROUP_H 2 | #define CGROUP_H 3 | 4 | #include "SVG/Assets/cprimitive.h" 5 | 6 | class CGroup:public CPrimitive 7 | { 8 | public: 9 | CGroup(); 10 | 11 | }; 12 | 13 | #endif // CGROUP_H 14 | -------------------------------------------------------------------------------- /SVG/Classes/cimage.cpp: -------------------------------------------------------------------------------- 1 | #include "cimage.h" 2 | #include 3 | 4 | CImage::CImage(CPoint topLeft, QString imgType, QString encoding, QByteArray data): 5 | CPrimitive(PT_IMAGE), _imgType(imgType), _encoding(encoding), _data(data) 6 | { 7 | qDebug()<<"IMAGE"< 11 | { 12 | public: 13 | CImage(CPoint topLeft, QString type, QString encoding, QByteArray data); 14 | void cstream(QDataStream &dataStream, double scale) override; 15 | CPoints lianirize(double tol) const override; 16 | QString imageType() const; 17 | 18 | private: 19 | QString _imgType; 20 | QString _encoding; 21 | QByteArray _data; 22 | }; 23 | 24 | #endif // CIMAGE_H 25 | -------------------------------------------------------------------------------- /SVG/Classes/cline.cpp: -------------------------------------------------------------------------------- 1 | #include "cline.h" 2 | #include "cpath.h" 3 | 4 | /** 5 | * @brief CLine 6 | * @param s - точка начала 7 | * @param e - точка конца 8 | */ 9 | CLine::CLine(CPoint s, CPoint e): 10 | CPrimitive(PT_LINE, s, e) 11 | { 12 | 13 | } 14 | 15 | CLine::CLine(const CLine &other): CPrimitive(other) 16 | { 17 | 18 | } 19 | 20 | void CLine::cstream(QDataStream &dataStream, double scale) 21 | { 22 | dataStream<addNext(line); 39 | addNext(path); 40 | return true; 41 | } 42 | -------------------------------------------------------------------------------- /SVG/Classes/cline.h: -------------------------------------------------------------------------------- 1 | #ifndef CLINE_H 2 | #define CLINE_H 3 | 4 | #include "SVG/Assets/cprimitive.h" 5 | 6 | class CLine: public CPrimitive 7 | { 8 | public: 9 | CLine(CPoint s, CPoint e); 10 | CLine(const CLine &other); 11 | void cstream(QDataStream &dataStream, double scale) override; 12 | CPoints lianirize(double tol) const override; 13 | bool toPath() override; 14 | }; 15 | 16 | #endif // CLINE_H 17 | -------------------------------------------------------------------------------- /SVG/Classes/cpath.cpp: -------------------------------------------------------------------------------- 1 | #include "cpath.h" 2 | #include "cbezier.h" 3 | #include 4 | 5 | CPath::CPath(): 6 | CPrimitive(CPrimitive::PT_PATH), _isClosed(false) 7 | { 8 | 9 | } 10 | 11 | bool CPath::isClosed() const 12 | { 13 | return _isClosed; 14 | } 15 | 16 | void CPath::setIsClosed(bool closed) 17 | { 18 | _isClosed = closed; 19 | } 20 | 21 | /** 22 | * @brief Изменяем направление пути на противоположное 23 | */ 24 | void CPath::reverse() 25 | { 26 | for (INodeInterface * ni = down(); ni!=nullptr; ni=ni->next()) { 27 | IPrimitive * pr = dynamic_cast(ni); 28 | pr->reverse(); 29 | pr->removeFromLevel(); 30 | addPrev(pr); 31 | } 32 | } 33 | 34 | /** 35 | * @brief Создаём параллельный путь 36 | * @param d - смещение 37 | * @return 38 | */ 39 | CPath *CPath::makeOffset(double d) 40 | { 41 | CPath * offsetPath = new CPath(); 42 | offsetPath->setIsClosed(isClosed()); 43 | 44 | //TODO: Доделать смещение остальных примитивов 45 | for (INodeInterface * ni = down(); ni!=nullptr; ni=ni->next()) { 46 | IPrimitive * pr = dynamic_cast(ni); 47 | 48 | if ( pr->type()==PT_BEZIER ) { 49 | CBezier * b = dynamic_cast(pr); 50 | QList obl = b->makeOffset(d); 51 | foreach (CBezier * ob, obl) { 52 | offsetPath->addNext(ob); 53 | } 54 | } else { 55 | qWarning()<<"Not supported primitive for offset"<type(); 56 | } 57 | 58 | } 59 | 60 | return offsetPath; 61 | } 62 | -------------------------------------------------------------------------------- /SVG/Classes/cpath.h: -------------------------------------------------------------------------------- 1 | #ifndef CPATH_H 2 | #define CPATH_H 3 | 4 | #include "SVG/Assets/cprimitive.h" 5 | 6 | class CPath: public CPrimitive 7 | { 8 | public: 9 | CPath(); 10 | bool isClosed() const; 11 | void setIsClosed(bool closed); 12 | 13 | void reverse(); 14 | 15 | bool toPath() { return true; } 16 | 17 | CPath * makeOffset(double d); 18 | 19 | private: 20 | bool _isClosed; 21 | }; 22 | 23 | #endif // CPATH_H 24 | -------------------------------------------------------------------------------- /SVG/Classes/cpolygon.h: -------------------------------------------------------------------------------- 1 | #ifndef CPOLYGON_H 2 | #define CPOLYGON_H 3 | 4 | #include "SVG/Assets/cprimitive.h" 5 | #include "Algebra/cvector2d.h" 6 | 7 | class CPolygon : public CPrimitive 8 | { 9 | public: 10 | CPolygon(); 11 | CPolygon(const CPoints &points); 12 | CPolygon(const CPoint &tl, const CPoint &br); 13 | 14 | void addPoint(const CPoint &p); 15 | 16 | CPoints lianirize(double tol) const override; 17 | double area() const; 18 | bool isClosed() const; 19 | void close(); 20 | 21 | bool toPath() override; 22 | 23 | int pointInPolygon(const CPoint &point) const; 24 | bool isRectangle(double tolerance = Equal::EPS) const; 25 | bool intersect(const CPolygon &other) const; 26 | bool inside(const CPolygon &other) const; 27 | double projectionDistance(const CPolygon &other, const CVector2D &direction) const; 28 | double slideDistance(const CPolygon &other, const CVector2D &direction, bool ignoreNegative) const; 29 | bool isEq(const CPolygon &other) const; 30 | bool operator==(const CPolygon &other) const; 31 | CPoint massCenter() const; 32 | }; 33 | 34 | #endif // CPOLYGON_H 35 | -------------------------------------------------------------------------------- /SVG/Classes/cpolyline.cpp: -------------------------------------------------------------------------------- 1 | #include "cpolyline.h" 2 | #include "cpath.h" 3 | #include "cline.h" 4 | 5 | CPolyline::CPolyline(): CPrimitive(PT_POLYLINE) 6 | { 7 | 8 | } 9 | 10 | void CPolyline::addPoint(const CPoint &p) 11 | { 12 | _points.add(p); 13 | } 14 | 15 | bool CPolyline::toPath() 16 | { 17 | if ( _points.count()==0 ) return false; 18 | 19 | CPath * path = new CPath(); 20 | 21 | CPoint prevPoint = _points[0]; 22 | for(int i=1; i<_points.count(); ++i) { 23 | CLine * line = new CLine(prevPoint, _points[i]); 24 | path->addNext(line); 25 | prevPoint = _points[i]; 26 | } 27 | 28 | addNext(path); 29 | return true; 30 | } 31 | -------------------------------------------------------------------------------- /SVG/Classes/cpolyline.h: -------------------------------------------------------------------------------- 1 | #ifndef CPOLYLINE_H 2 | #define CPOLYLINE_H 3 | 4 | #include "../Assets/cprimitive.h" 5 | 6 | class CPolyline: public CPrimitive 7 | { 8 | public: 9 | CPolyline(); 10 | 11 | void addPoint(const CPoint &p); 12 | 13 | bool toPath() override; 14 | }; 15 | 16 | #endif // CPOLYLINE_H 17 | -------------------------------------------------------------------------------- /SVG/Classes/crect.cpp: -------------------------------------------------------------------------------- 1 | #include "crect.h" 2 | #include "cpath.h" 3 | #include "cellipse.h" 4 | #include "cline.h" 5 | 6 | CRect::CRect(const CPoint& tl, const CPoint& br): CPrimitive(PT_RECT, tl, br) 7 | { 8 | 9 | } 10 | 11 | void CRect::setX(double x) 12 | { 13 | _points[0].setX(x); 14 | } 15 | 16 | void CRect::setY(double y) 17 | { 18 | _points[0].setY(y); 19 | } 20 | 21 | void CRect::setWidth(double width) 22 | { 23 | _points[1].setX(_points[0].x()+width); 24 | } 25 | 26 | void CRect::setHeight(double height) 27 | { 28 | _points[1].setY(_points[0].y()+height); 29 | } 30 | 31 | void CRect::setRX(double x) 32 | { 33 | _radius.first = x; 34 | } 35 | 36 | void CRect::setRY(double y) 37 | { 38 | _radius.second = y; 39 | } 40 | 41 | CPoint CRect::topLeft() const 42 | { 43 | return _points[0]; 44 | } 45 | 46 | CPoint CRect::bottomRight() const 47 | { 48 | return _points[1]; 49 | } 50 | 51 | CSize CRect::size() const 52 | { 53 | return CSize(_points[0], _points[1]); 54 | } 55 | 56 | CRect::TRadius CRect::radius() const 57 | { 58 | return _radius; 59 | } 60 | 61 | /** 62 | * @brief Преобразуем в path 63 | * @return 64 | */ 65 | bool CRect::toPath() 66 | { 67 | CPoint tl = _points[0]; 68 | CSize s = size(); 69 | 70 | double x = tl.x(); 71 | double y = tl.y(); 72 | double w = s.width(); 73 | double h = s.height(); 74 | double rx = _radius.first; 75 | double ry = _radius.second; 76 | 77 | CPath * p = new CPath(); 78 | if ( Equal::almostEqual(rx, 0) && Equal::almostEqual(ry, 0) ) { //-- Если без скруглений, то тупо 4 линии 79 | CLine * lt = new CLine(CPoint(x, y), CPoint(x+w, y)); 80 | CLine * lr = new CLine(CPoint(x+w, y), CPoint(x+w, y+h)); 81 | CLine * lb = new CLine(CPoint(x+w, y+h), CPoint(x, y+h)); 82 | CLine * ll = new CLine(CPoint(x, y+h), CPoint(x, y)); 83 | 84 | p->addNext(lt); 85 | p->addNext(lr); 86 | p->addNext(lb); 87 | p->addNext(ll); 88 | } else { //-- Придётся делать скругления через Безье. Скругление возьмём от Элипса 89 | CLine * lt = new CLine(CPoint(x+rx, y), CPoint(x+w-rx, y)); 90 | CBezier * btr = CEllipse::drawBezierEllipseQuarter(CPoint(x+w-rx, y+ry), QSizeF(-rx, ry)); 91 | CLine * lr = new CLine(CPoint(x+w, y+ry), CPoint(x+w, y+h-ry)); 92 | CBezier * bbr = CEllipse::drawBezierEllipseQuarter(CPoint(x+w-rx, y+h-ry), QSizeF(-rx, -ry)); 93 | CLine * lb = new CLine(CPoint(x+w-rx, y+h), CPoint(x+rx, y+h)); 94 | CBezier * bbl = CEllipse::drawBezierEllipseQuarter(CPoint(x+rx, y+h-ry), QSizeF(rx, -ry)); 95 | CLine * ll = new CLine(CPoint(x, y+h-ry), CPoint(x, y+ry)); 96 | CBezier * btl = CEllipse::drawBezierEllipseQuarter(CPoint(x+rx, y+ry), QSizeF(rx, ry)); 97 | 98 | btr->reverse(); 99 | bbl->reverse(); 100 | 101 | p->addNext(lt); 102 | p->addNext(btr); 103 | p->addNext(lr); 104 | p->addNext(bbr); 105 | p->addNext(lb); 106 | p->addNext(bbl); 107 | p->addNext(ll); 108 | p->addNext(btl); 109 | } 110 | 111 | addNext(p); 112 | return true; 113 | } 114 | -------------------------------------------------------------------------------- /SVG/Classes/crect.h: -------------------------------------------------------------------------------- 1 | #ifndef CRECT_H 2 | #define CRECT_H 3 | 4 | #include "../Assets/cprimitive.h" 5 | #include "Algebra/csize.h" 6 | 7 | class CRect: public CPrimitive 8 | { 9 | public: 10 | CRect(const CPoint &tl = CPoint(), const CPoint &br = CPoint()); 11 | typedef QPair TRadius; 12 | 13 | void setX(double x); 14 | void setY(double y); 15 | void setWidth(double width); 16 | void setHeight(double height); 17 | void setRX(double x); 18 | void setRY(double y); 19 | 20 | CPoint topLeft() const; 21 | CPoint bottomRight() const; 22 | CSize size() const; 23 | 24 | TRadius radius() const; 25 | 26 | bool toPath() override; 27 | 28 | private: 29 | TRadius _radius; 30 | 31 | }; 32 | 33 | #endif // CRECT_H 34 | -------------------------------------------------------------------------------- /SVG/Classes/csvg.cpp: -------------------------------------------------------------------------------- 1 | #include "csvg.h" 2 | 3 | CSVG::CSVG(): 4 | CPrimitive(PT_SVG) 5 | { 6 | 7 | } 8 | 9 | QRectF CSVG::viewBox() const 10 | { 11 | return _viewBox; 12 | } 13 | 14 | void CSVG::setViewBox(const QRectF& viewBox) 15 | { 16 | _viewBox = viewBox; 17 | } 18 | 19 | CSize CSVG::size() const 20 | { 21 | return _size; 22 | } 23 | 24 | void CSVG::setSize(const CSize& s) 25 | { 26 | _size = s; 27 | } 28 | -------------------------------------------------------------------------------- /SVG/Classes/csvg.h: -------------------------------------------------------------------------------- 1 | #ifndef CSVG_H 2 | #define CSVG_H 3 | 4 | #include "../Assets/cprimitive.h" 5 | #include "Algebra/csize.h" 6 | 7 | #include 8 | 9 | class CSVG: public CPrimitive 10 | { 11 | public: 12 | CSVG(); 13 | 14 | QRectF viewBox() const; 15 | void setViewBox(const QRectF &viewBox); 16 | 17 | CSize size() const; 18 | void setSize(const CSize &s); 19 | 20 | private: 21 | QRectF _viewBox; 22 | CSize _size; 23 | 24 | }; 25 | 26 | #endif // CSVG_H 27 | -------------------------------------------------------------------------------- /SVG/Classes/fclippath.cpp: -------------------------------------------------------------------------------- 1 | #include "fclippath.h" 2 | #include "cgroup.h" 3 | 4 | FClipPath::FClipPath() : CDef() 5 | { 6 | clipPath = new CGroup(); 7 | } 8 | -------------------------------------------------------------------------------- /SVG/Classes/fclippath.h: -------------------------------------------------------------------------------- 1 | #ifndef FCLIPPATH_H 2 | #define FCLIPPATH_H 3 | 4 | #include "../Assets/cdef.h" 5 | #include "../Assets/cprimitive.h" 6 | 7 | /** 8 | * @brief Контур обрезки 9 | */ 10 | class FClipPath : public CDef 11 | { 12 | public: 13 | explicit FClipPath(); 14 | 15 | IPrimitive * clipPath; 16 | 17 | virtual TDefType defType() const { return DF_CLIPPATH; } 18 | 19 | }; 20 | 21 | #endif // FCLIPPATH_H 22 | -------------------------------------------------------------------------------- /SVG/Classes/flineargradient.cpp: -------------------------------------------------------------------------------- 1 | #include "flineargradient.h" 2 | 3 | FLinearGradient::FLinearGradient() : FGradient() 4 | { 5 | 6 | } 7 | 8 | FLinearGradient::FLinearGradient(const FLinearGradient &other): FGradient(other) 9 | { 10 | _startPoint = other._startPoint; 11 | _endPoint = other._endPoint; 12 | } 13 | 14 | FLinearGradient::FLinearGradient(const FGradient &other): FGradient(other) 15 | { 16 | if ( other.defType()==DF_LINEARGRADIENT ) { 17 | const FLinearGradient otherLinear = dynamic_cast(other); 18 | _startPoint = otherLinear._startPoint; 19 | _endPoint = otherLinear._endPoint; 20 | } 21 | } 22 | 23 | void FLinearGradient::setX1(float x) 24 | { 25 | _startPoint.setX(x); 26 | } 27 | 28 | void FLinearGradient::setY1(float y) 29 | { 30 | _startPoint.setY(y); 31 | } 32 | 33 | void FLinearGradient::setX2(float x) 34 | { 35 | _endPoint.setX(x); 36 | } 37 | 38 | void FLinearGradient::setY2(float y) 39 | { 40 | _endPoint.setY(y); 41 | } 42 | 43 | void FLinearGradient::setStartPoint(const CPoint &s) 44 | { 45 | _startPoint = s; 46 | } 47 | 48 | void FLinearGradient::setEndPoint(const CPoint &e) 49 | { 50 | _endPoint = e; 51 | } 52 | 53 | CPoint FLinearGradient::startPoint() const 54 | { 55 | RELDEFVAL(_startPoint); 56 | return _startPoint; 57 | } 58 | 59 | CPoint FLinearGradient::endPoint() const 60 | { 61 | RELDEFVAL(_endPoint); 62 | return _endPoint; 63 | } 64 | -------------------------------------------------------------------------------- /SVG/Classes/flineargradient.h: -------------------------------------------------------------------------------- 1 | #ifndef FLINEARGRADIENT_H 2 | #define FLINEARGRADIENT_H 3 | 4 | #include "SVG/Assets/fgradient.h" 5 | #include "Algebra/cpoint.h" 6 | 7 | class FLinearGradient : public FGradient 8 | { 9 | public: 10 | FLinearGradient(); 11 | FLinearGradient(const FLinearGradient &other); 12 | FLinearGradient(const FGradient &other); 13 | 14 | void setX1(float x); 15 | void setY1(float y); 16 | void setX2(float x); 17 | void setY2(float y); 18 | 19 | void setStartPoint(const CPoint &s); 20 | void setEndPoint(const CPoint &e); 21 | 22 | CPoint startPoint() const; 23 | CPoint endPoint() const; 24 | 25 | TDefType defType() const override { return DF_LINEARGRADIENT; } 26 | 27 | private: 28 | CPoint _startPoint; 29 | CPoint _endPoint; 30 | 31 | }; 32 | 33 | #endif // FLINEARGRADIENT_H 34 | -------------------------------------------------------------------------------- /SVG/Classes/fprimitive.cpp: -------------------------------------------------------------------------------- 1 | #include "fprimitive.h" 2 | 3 | FPrimitive::FPrimitive(): CDef() 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /SVG/Classes/fprimitive.h: -------------------------------------------------------------------------------- 1 | #ifndef FPRIMITIVE_H 2 | #define FPRIMITIVE_H 3 | 4 | 5 | #include "../Assets/cdef.h" 6 | #include "../Assets/cprimitive.h" 7 | 8 | /** 9 | * @brief Примитив внутри секции defs 10 | */ 11 | class FPrimitive: public CDef 12 | { 13 | public: 14 | FPrimitive(); 15 | 16 | CPrimitive * primitive; 17 | 18 | }; 19 | 20 | #endif // FPRIMITIVE_H 21 | -------------------------------------------------------------------------------- /SVG/Classes/fradialgradient.cpp: -------------------------------------------------------------------------------- 1 | #include "fradialgradient.h" 2 | 3 | FRadialGradient::FRadialGradient(): FGradient(), _radius(0), _focalRadius(0) 4 | { 5 | 6 | } 7 | 8 | FRadialGradient::FRadialGradient(const FRadialGradient &other): FGradient(other) 9 | { 10 | _centerPoint = other._centerPoint; 11 | _focalPoint = other._focalPoint; 12 | _radius = other._radius; 13 | _focalRadius = other._focalRadius; 14 | } 15 | 16 | FRadialGradient::FRadialGradient(const FGradient &other): FGradient(other), _radius(0), _focalRadius(0) 17 | { 18 | if ( other.defType()==DF_RADIALGRADIENT ) { 19 | const FRadialGradient otherRadial = dynamic_cast(other); 20 | _centerPoint = otherRadial._centerPoint; 21 | _focalPoint = otherRadial._focalPoint; 22 | _radius = otherRadial._radius; 23 | _focalRadius = otherRadial._focalRadius; 24 | } 25 | } 26 | 27 | void FRadialGradient::setCX(float x) 28 | { 29 | _centerPoint.setX(x); 30 | } 31 | 32 | void FRadialGradient::setCY(float y) 33 | { 34 | _centerPoint.setY(y); 35 | } 36 | 37 | void FRadialGradient::setFX(float x) 38 | { 39 | _focalPoint.setX(x); 40 | } 41 | 42 | void FRadialGradient::setFY(float y) 43 | { 44 | _focalPoint.setY(y); 45 | } 46 | 47 | void FRadialGradient::setCenterPoint(const CPoint& cp) 48 | { 49 | _centerPoint = cp; 50 | } 51 | 52 | CPoint FRadialGradient::centerPoint() const 53 | { 54 | RELDEFVAL(_centerPoint); 55 | return _centerPoint; 56 | } 57 | 58 | void FRadialGradient::setFocalPoint(const CPoint& fp) 59 | { 60 | _focalPoint = fp; 61 | } 62 | 63 | CPoint FRadialGradient::focalPoint() const 64 | { 65 | RELDEFVAL(_focalPoint); 66 | return _focalPoint; 67 | } 68 | 69 | void FRadialGradient::setRadius(float r) 70 | { 71 | _radius = r; 72 | } 73 | 74 | float FRadialGradient::radius() const 75 | { 76 | RELDEFVALN(_radius); 77 | return _radius; 78 | } 79 | 80 | void FRadialGradient::setFocalRadius(float r) 81 | { 82 | _focalRadius = r; 83 | } 84 | 85 | float FRadialGradient::focalRadius() const 86 | { 87 | RELDEFVALN(_focalRadius); 88 | return _focalRadius; 89 | } 90 | -------------------------------------------------------------------------------- /SVG/Classes/fradialgradient.h: -------------------------------------------------------------------------------- 1 | #ifndef FRADIALGRADIENT_H 2 | #define FRADIALGRADIENT_H 3 | 4 | #include "SVG/Assets/fgradient.h" 5 | #include "Algebra/cpoint.h" 6 | 7 | class FRadialGradient: public FGradient 8 | { 9 | public: 10 | FRadialGradient(); 11 | FRadialGradient(const FRadialGradient &other); 12 | FRadialGradient(const FGradient &other); 13 | 14 | void setCX(float x); 15 | void setCY(float y); 16 | void setFX(float x); 17 | void setFY(float y); 18 | 19 | void setCenterPoint(const CPoint &cp); 20 | CPoint centerPoint() const; 21 | 22 | void setFocalPoint(const CPoint &fp); 23 | CPoint focalPoint() const; 24 | 25 | void setRadius(float r); 26 | float radius() const; 27 | 28 | void setFocalRadius(float r); 29 | float focalRadius() const; 30 | 31 | TDefType defType() const override { return DF_RADIALGRADIENT; } 32 | 33 | private: 34 | CPoint _centerPoint; 35 | CPoint _focalPoint; 36 | float _radius; 37 | float _focalRadius; 38 | 39 | }; 40 | 41 | #endif // FRADIALGRADIENT_H 42 | -------------------------------------------------------------------------------- /SVG/Generator/svggenerator.cpp: -------------------------------------------------------------------------------- 1 | #include "svggenerator.h" 2 | 3 | #include 4 | 5 | SVGGenerator::SVGGenerator(QObject *parent) : QObject(parent) 6 | { 7 | 8 | } 9 | 10 | SVGGenerator::GenerateStatus SVGGenerator::generate(QIODevice *device, IPrimitive * rootItm) 11 | { 12 | if ( !device->isOpen() && !device->open(QIODevice::WriteOnly|QIODevice::Text) ) { 13 | qWarning()<<"Device not opened!"; 14 | return GS_NOFILE; 15 | } 16 | 17 | _xml = new QXmlStreamWriter(device); 18 | 19 | _xml->writeStartDocument(); 20 | _xml->writeStartElement("svg"); 21 | 22 | CNodeInterfaceIterator i(rootItm); 23 | 24 | while ( i.next() ) { 25 | 26 | if ( i.type()&CNodeInterfaceIterator::IT_STARTLEVEL ) { 27 | IPrimitive * lvl = i.level(); 28 | if ( lvl->type()==IPrimitive::PT_GROUP ) { 29 | _xml->writeStartElement("g"); 30 | } 31 | } 32 | 33 | if ( i.type()&CNodeInterfaceIterator::IT_STARTELEMENT ) { 34 | IPrimitive * p = i.item(); 35 | 36 | if ( p->type()==IPrimitive::PT_PATH ) { 37 | CPath * path = dynamic_cast(p); 38 | _xml->writeStartElement("path"); 39 | _xml->writeAttribute("d", generatePath(path)); 40 | _xml->writeAttribute("style", path->styles().toString()); 41 | _xml->writeEndElement(); 42 | i.nextLevel(); 43 | continue; 44 | } 45 | } 46 | 47 | 48 | if ( i.type()&CNodeInterfaceIterator::IT_ENDLEVEL ) { 49 | IPrimitive * lvl = i.level(); 50 | if ( lvl->type()==IPrimitive::PT_GROUP ) { 51 | _xml->writeEndElement(); 52 | } 53 | } 54 | 55 | }; 56 | 57 | _xml->writeEndElement(); 58 | _xml->writeEndDocument(); 59 | 60 | return GS_OK; 61 | } 62 | 63 | QString SVGGenerator::generatePath(IPrimitive *item) 64 | { 65 | QStringList res; 66 | 67 | CPath * path = dynamic_cast(item); 68 | 69 | if ( path==nullptr ) { throw -1; } 70 | if ( path->down() == nullptr ) { return QString(); } 71 | 72 | bool isClosed = path->isClosed(); 73 | bool isFirst = true; 74 | 75 | IPrimitive * p = dynamic_cast(path->down()); 76 | 77 | while (true) { 78 | if ( p==nullptr ) break; 79 | 80 | if ( isFirst ) { 81 | res.append("M"); 82 | res.append(p->points().p1().toString()); 83 | isFirst = false; 84 | } 85 | 86 | if ( p->type()==IPrimitive::PT_BEZIER ) { 87 | CBezier * b = dynamic_cast(p); 88 | res.append("C"); 89 | res.append(b->points().p2().toString()); 90 | res.append(b->points().p3().toString()); 91 | res.append(b->points().p4().toString()); 92 | } else 93 | if ( p->type()==IPrimitive::PT_LINE ) { 94 | CLine * l = dynamic_cast(p); 95 | res.append("L"); 96 | res.append(l->points().p2().toString()); 97 | } else 98 | if ( p->type()==IPrimitive::PT_ARC ) { 99 | CArc * a = dynamic_cast(p); 100 | res.append("A"); 101 | res.append(QString::number(a->rx())); 102 | res.append(QString::number(a->ry())); 103 | res.append(QString::number(a->rotation())); 104 | res.append((a->largeArcFlag())? "1" : "0"); 105 | res.append((a->sweepFlag())? "1" : "0"); 106 | res.append(a->points().p2().toString()); 107 | } else 108 | if ( p->type()==IPrimitive::PT_PATH ) { //-- Составной путь. Завершаем и начинаем новый. 109 | if ( isClosed ) { res.append("Z"); } 110 | CPath * subPath = dynamic_cast(p); 111 | isClosed = subPath->isClosed(); 112 | isFirst = true; 113 | p = dynamic_cast(p->down()); 114 | continue; 115 | } 116 | else { 117 | qWarning()<<"Unknow path type"<type(); 118 | } 119 | 120 | if ( p->next()==nullptr ) { break; } 121 | p = dynamic_cast(p->next()); 122 | } 123 | 124 | if ( isClosed ) res.append("Z"); 125 | 126 | return res.join(" "); 127 | } 128 | 129 | -------------------------------------------------------------------------------- /SVG/Generator/svggenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef SVGGENERATOR_H 2 | #define SVGGENERATOR_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "CSS/cssparser.h" 10 | 11 | #include "Algebra/cmatrix.h" 12 | #include "Algebra/cpoint.h" 13 | 14 | #include "SVG/Classes/cbezier.h" 15 | #include "SVG/Classes/cline.h" 16 | #include "SVG/Classes/cpath.h" 17 | #include "SVG/Classes/cgroup.h" 18 | #include "SVG/Classes/cimage.h" 19 | #include "SVG/Classes/carc.h" 20 | #include "SVG/Classes/ccircle.h" 21 | #include "SVG/Classes/crect.h" 22 | #include "SVG/Classes/cellipse.h" 23 | #include "SVG/Classes/cpolyline.h" 24 | #include "SVG/Classes/cpolygon.h" 25 | 26 | #include "SVG/Classes/flineargradient.h" 27 | #include "SVG/Classes/fradialgradient.h" 28 | #include "SVG/Classes/fclippath.h" 29 | 30 | #include "SVG/Assets/cdefs.h" 31 | 32 | class SVGGenerator : public QObject 33 | { 34 | Q_OBJECT 35 | public: 36 | explicit SVGGenerator(QObject *parent = nullptr); 37 | 38 | enum GenerateStatus { 39 | GS_OK, 40 | GS_NOFILE, 41 | GS_SYNTAXERROR 42 | }; 43 | 44 | GenerateStatus generate(QIODevice *device, IPrimitive * rootItm); 45 | 46 | signals: 47 | 48 | public slots: 49 | 50 | protected: 51 | QString generatePath(IPrimitive* item); 52 | 53 | private: 54 | QXmlStreamWriter * _xml; 55 | }; 56 | 57 | #endif // SVGGENERATOR_H 58 | -------------------------------------------------------------------------------- /SVG/Parser/svgparser.h: -------------------------------------------------------------------------------- 1 | #ifndef SVGPARSER_H 2 | #define SVGPARSER_H 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "CSS/cssparser.h" 10 | 11 | #include "Algebra/cmatrix.h" 12 | #include "Algebra/cpoint.h" 13 | 14 | #include "SVG/Classes/cdoc.h" 15 | #include "SVG/Classes/csvg.h" 16 | #include "SVG/Classes/cbezier.h" 17 | #include "SVG/Classes/cline.h" 18 | #include "SVG/Classes/cpath.h" 19 | #include "SVG/Classes/cgroup.h" 20 | #include "SVG/Classes/cimage.h" 21 | #include "SVG/Classes/carc.h" 22 | #include "SVG/Classes/ccircle.h" 23 | #include "SVG/Classes/crect.h" 24 | #include "SVG/Classes/cellipse.h" 25 | #include "SVG/Classes/cpolyline.h" 26 | #include "SVG/Classes/cpolygon.h" 27 | 28 | #include "SVG/Classes/fclippath.h" 29 | #include "SVG/Classes/flineargradient.h" 30 | #include "SVG/Classes/fradialgradient.h" 31 | #include "SVG/Classes/fprimitive.h" 32 | 33 | #include "SVG/Assets/cdefs.h" 34 | 35 | 36 | class SVGParser : public QObject 37 | { 38 | Q_OBJECT 39 | 40 | public: 41 | enum ParseStatus { 42 | PS_OK, 43 | PS_NOFILE, 44 | PS_SYNTAXERROR, 45 | PS_WRONGVERSION 46 | }; 47 | 48 | explicit SVGParser(QObject *parent = nullptr); 49 | ParseStatus parse(QIODevice *device); 50 | 51 | IPrimitive * rootItem() const; 52 | 53 | CDefs defs() const; 54 | 55 | #define RX_PARAMS "([-+]?(\\d{1,20}|\\.)(\\.)?(\\d{1,20})?(e[+-]\\d{1,20})?)" 56 | 57 | signals: 58 | 59 | public slots: 60 | 61 | private: 62 | CDoc * _rootItem; 63 | QXmlStreamReader * _xml; 64 | 65 | CPoint _globalCoords; //-- Глобальные координаты 66 | 67 | CSS::CssParser * _cssParser; 68 | 69 | IPrimitive * parseSVG(INodeInterface **level, QXmlStreamReader * xml); 70 | IPrimitive * parseGroup(INodeInterface** level, QXmlStreamReader * xml); 71 | IPrimitive * parsePath(INodeInterface * level, QXmlStreamReader * xml); 72 | IPrimitive * parseRect(INodeInterface * level, QXmlStreamReader * xml); 73 | IPrimitive * parseLine(INodeInterface * level, QXmlStreamReader * xml); 74 | IPrimitive * parseImage(INodeInterface * level, QXmlStreamReader * xml); 75 | IPrimitive * parseCircle(INodeInterface* level, QXmlStreamReader * xml); 76 | IPrimitive * parseEllipse(INodeInterface* level, QXmlStreamReader * xml); 77 | IPrimitive * parsePolyline(INodeInterface * level, QXmlStreamReader * xml); 78 | IPrimitive * parsePolygon(INodeInterface * level, QXmlStreamReader * xml); 79 | IPrimitive * parseClipPath(INodeInterface ** level, QXmlStreamReader * xml); 80 | IPrimitive * parseLinearGradient(INodeInterface ** level, QXmlStreamReader * xml); 81 | IPrimitive * parseRadialGradient(INodeInterface ** level, QXmlStreamReader * xml); 82 | 83 | bool parseGradientStops(FGradient * gradient, QXmlStreamReader * xml); 84 | bool parseCss(INodeInterface* level, QXmlStreamReader * xml); 85 | QList parseParams(QString attr, QXmlStreamReader * xml) const; 86 | QList parseParams(QString params) const; 87 | 88 | CMatrix parseTransform(QXmlStreamReader * xml, QString attrName="transform"); 89 | CSS::Style parseStyle(IPrimitive* itm, QXmlStreamReader * xml); 90 | 91 | CDefs _defs; 92 | 93 | void parseBaseAttributes(IPrimitive * itm, QXmlStreamReader * xml); 94 | void parseHREF(CDef * el, QXmlStreamReader * xml); 95 | 96 | QMultiHash _dependsCDef; //-- Waiting href element depend will be parsed 97 | 98 | void updateDependsHREFS(CDef * el); 99 | }; 100 | 101 | #endif // SVGPARSER_H 102 | -------------------------------------------------------------------------------- /SVG2QML.pro: -------------------------------------------------------------------------------- 1 | QT += quick 2 | 3 | DEFINES += QT_DEPRECATED_WARNINGS 4 | 5 | SOURCES += \ 6 | Algebra/cintersection.cpp \ 7 | Algebra/cmatrix.cpp \ 8 | Algebra/cpoint.cpp \ 9 | Algebra/cpolynomial.cpp \ 10 | Algebra/csegment.cpp \ 11 | Algebra/csize.cpp \ 12 | Algebra/cvector2d.cpp \ 13 | Algebra/equal.cpp \ 14 | CSS/block.cpp \ 15 | CSS/cssparser.cpp \ 16 | CSS/measureunit.cpp \ 17 | CSS/style.cpp \ 18 | SVG/Assets/cboundingbox.cpp \ 19 | SVG/Assets/cdef.cpp \ 20 | SVG/Assets/cdefs.cpp \ 21 | SVG/Assets/cpoints.cpp \ 22 | SVG/Assets/cprimitive.cpp \ 23 | SVG/Assets/fgradient.cpp \ 24 | SVG/Classes/carc.cpp \ 25 | SVG/Classes/cbezier.cpp \ 26 | SVG/Classes/ccircle.cpp \ 27 | SVG/Classes/cdoc.cpp \ 28 | SVG/Classes/cellipse.cpp \ 29 | SVG/Classes/cgroup.cpp \ 30 | SVG/Classes/cimage.cpp \ 31 | SVG/Classes/cline.cpp \ 32 | SVG/Classes/cpath.cpp \ 33 | SVG/Classes/cpolygon.cpp \ 34 | SVG/Classes/cpolyline.cpp \ 35 | SVG/Classes/crect.cpp \ 36 | SVG/Classes/csvg.cpp \ 37 | SVG/Classes/fclippath.cpp \ 38 | SVG/Classes/flineargradient.cpp \ 39 | SVG/Classes/fprimitive.cpp \ 40 | SVG/Classes/fradialgradient.cpp \ 41 | SVG/Generator/svggenerator.cpp \ 42 | SVG/Parser/svgparser.cpp \ 43 | appcore.cpp \ 44 | main.cpp \ 45 | qmlgenerator.cpp 46 | 47 | RESOURCES += qml.qrc 48 | 49 | HEADERS += \ 50 | Algebra/cempty.h \ 51 | Algebra/cintersection.h \ 52 | Algebra/cmatrix.h \ 53 | Algebra/cpoint.h \ 54 | Algebra/cpolynomial.h \ 55 | Algebra/csegment.h \ 56 | Algebra/csize.h \ 57 | Algebra/cvector2d.h \ 58 | Algebra/equal.h \ 59 | CSS/block.h \ 60 | CSS/cssparser.h \ 61 | CSS/measureunit.h \ 62 | CSS/style.h \ 63 | SVG/Assets/cboundingbox.h \ 64 | SVG/Assets/cdef.h \ 65 | SVG/Assets/cdefs.h \ 66 | SVG/Assets/cnode.h \ 67 | SVG/Assets/cpoints.h \ 68 | SVG/Assets/cprimitive.h \ 69 | SVG/Assets/fgradient.h \ 70 | SVG/Assets/inode.h \ 71 | SVG/Assets/iprimitive.h \ 72 | SVG/Classes/carc.h \ 73 | SVG/Classes/cbezier.h \ 74 | SVG/Classes/ccircle.h \ 75 | SVG/Classes/cdoc.h \ 76 | SVG/Classes/cellipse.h \ 77 | SVG/Classes/cgroup.h \ 78 | SVG/Classes/cimage.h \ 79 | SVG/Classes/cline.h \ 80 | SVG/Classes/cpath.h \ 81 | SVG/Classes/cpolygon.h \ 82 | SVG/Classes/cpolyline.h \ 83 | SVG/Classes/crect.h \ 84 | SVG/Classes/csvg.h \ 85 | SVG/Classes/fclippath.h \ 86 | SVG/Classes/flineargradient.h \ 87 | SVG/Classes/fprimitive.h \ 88 | SVG/Classes/fradialgradient.h \ 89 | SVG/Generator/svggenerator.h \ 90 | SVG/Parser/svgparser.h \ 91 | appcore.h \ 92 | qmlgenerator.h 93 | 94 | DISTFILES += \ 95 | mask.frag.qsb \ 96 | readme.md \ 97 | 98 | -------------------------------------------------------------------------------- /Shaders/mask.frag: -------------------------------------------------------------------------------- 1 | #version 440 2 | layout(location = 0) in vec2 qt_TexCoord0; 3 | layout(location = 0) out vec4 fragColor; 4 | layout(std140, binding = 0) uniform buf { 5 | mat4 qt_Matrix; 6 | float qt_Opacity; 7 | }; 8 | layout(binding = 1) uniform sampler2D source; 9 | layout(binding = 2) uniform sampler2D maskSource; 10 | void main() { 11 | fragColor = texture(source, qt_TexCoord0) * texture(maskSource, qt_TexCoord0).a * qt_Opacity; 12 | } -------------------------------------------------------------------------------- /Shaders/mask.frag.qsb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Riflio/SVG2QML/109bd490ef215bc403f436839a9761ff0c4730ac/Shaders/mask.frag.qsb -------------------------------------------------------------------------------- /Shaders/mask.vert: -------------------------------------------------------------------------------- 1 | #version 440 2 | layout(location = 0) in vec4 qt_Vertex; 3 | layout(location = 1) in vec2 qt_MultiTexCoord0; 4 | layout(location = 0) out vec2 coord; 5 | layout(std140, binding = 0) uniform buf { 6 | mat4 qt_Matrix; 7 | float qt_Opacity; 8 | }; 9 | void main() { 10 | coord = qt_MultiTexCoord0; 11 | gl_Position = qt_Matrix * qt_Vertex; 12 | } -------------------------------------------------------------------------------- /Shaders/mask.vert.qsb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Riflio/SVG2QML/109bd490ef215bc403f436839a9761ff0c4730ac/Shaders/mask.vert.qsb -------------------------------------------------------------------------------- /WDropArea.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Dialogs 3 | 4 | Item { 5 | id: dropArea 6 | property var fileExtensionFilters: ([]) //-- "Vector (*.svg)", "Text (*.txt)", ... 7 | property bool multiple: false 8 | property bool enableSelectDialog: true 9 | property bool enableSelectDialogDblClick: true 10 | property string helpText: "Drop files here" 11 | 12 | signal filesAccepted(var files); 13 | 14 | QtObject { 15 | id: _private 16 | property string fileExtensionFiltersStr: fileExtensionFilters.join(",") 17 | } 18 | 19 | DropArea { 20 | anchors.fill: parent 21 | onEntered: (drag) => { 22 | let acceptedCount = 0; 23 | for(let i=0; i0) || ( !dropArea.multiple && drag.urls.length===1)) && acceptedCount===drag.urls.length ); 27 | } 28 | 29 | onDropped: (drag) => { 30 | dropArea.filesAccepted(drag.urls); 31 | } 32 | 33 | function validateFileExtension(filePath) 34 | { 35 | if ( !filePath ) { return false; } 36 | if ( dropArea.fileExtensionFilters.length===0 ) { return true; } 37 | return ( _private.fileExtensionFiltersStr.indexOf("(*."+((""+filePath).split('.').pop())+")")>-1 ); 38 | } 39 | 40 | FileDialog { 41 | id: fileSelectDialog 42 | nameFilters: fileExtensionFilters 43 | fileMode: (dropArea.multiple)? FileDialog.OpenFiles : FileDialog.OpenFile 44 | title: qsTr("Select file") 45 | onAccepted: { 46 | dropArea.filesAccepted(fileSelectDialog.selectedFiles); 47 | } 48 | } 49 | 50 | Canvas{ 51 | id: borderDashed 52 | anchors.fill: parent 53 | 54 | property color borderColor: (dropArea.containsDrag)? "#FF9200" : "#909090" 55 | onBorderColorChanged: { requestPaint(); } 56 | 57 | onPaint: { 58 | let ctx = getContext("2d"); 59 | ctx.setLineDash([1, 3]); 60 | ctx.strokeStyle = borderColor; 61 | ctx.lineWidth = 4; 62 | ctx.lineCap = "round"; 63 | 64 | ctx.beginPath(); 65 | let lw2 = ctx.lineWidth/2.0; 66 | ctx.moveTo(0+lw2, 0+lw2); 67 | ctx.lineTo(borderDashed.width-lw2, 0+lw2); 68 | ctx.lineTo(borderDashed.width-lw2, borderDashed.height-lw2); 69 | ctx.lineTo(0+lw2, borderDashed.height-lw2); 70 | ctx.lineTo(0+lw2, 0+lw2); 71 | ctx.stroke(); 72 | } 73 | } 74 | 75 | MouseArea { 76 | anchors.fill: parent 77 | onDoubleClicked: { 78 | if ( dropArea.enableSelectDialog && dropArea.enableSelectDialogDblClick ) { 79 | fileSelectDialog.visible = true; 80 | } 81 | } 82 | } 83 | 84 | Column { 85 | id: helpWrapper 86 | visible: (dropArea.helpText!=="") 87 | anchors.centerIn: parent 88 | height: childrenRect.height 89 | 90 | Text { 91 | text: dropArea.helpText 92 | font.pixelSize: 20 93 | color: "#909090" 94 | anchors.horizontalCenter: parent.horizontalCenter 95 | } 96 | 97 | Text { 98 | text: qsTr("or") 99 | anchors.horizontalCenter: parent.horizontalCenter 100 | font.pixelSize: 14 101 | color: "#dfdfdf" 102 | } 103 | 104 | Rectangle { 105 | id: btn 106 | width: btnTxt.contentWidth+20 107 | height: btnTxt.contentHeight+10 108 | visible: dropArea.enableSelectDialog 109 | anchors.horizontalCenter: parent.horizontalCenter 110 | 111 | property string text: "btn" 112 | signal clicked(); 113 | 114 | radius: 20 115 | color: (btnMa.containsMouse)? "#dfdfdf" : "transparent" 116 | border.width: 2 117 | border.color: "#909090" 118 | MouseArea { 119 | id: btnMa 120 | anchors.fill: parent 121 | hoverEnabled: true 122 | Text { 123 | id: btnTxt 124 | anchors.centerIn: parent 125 | text: qsTr("browse") 126 | font.pixelSize: 14 127 | color: "#909090" 128 | } 129 | onClicked: { 130 | fileSelectDialog.visible = true; 131 | } 132 | } 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /appcore.cpp: -------------------------------------------------------------------------------- 1 | #include "appcore.h" 2 | #include 3 | 4 | AppCore::AppCore(QObject *parent) : QObject(parent) 5 | { 6 | _parser = new SVGParser(this); 7 | _qmlGenerator = new QMLGenerator(this); 8 | } 9 | 10 | QUrl AppCore::generatedQMLPath() const 11 | { 12 | return _generatedQMLPath; 13 | } 14 | 15 | /** 16 | * @brief Парсим SVG 17 | * @param source 18 | * @return 19 | */ 20 | bool AppCore::parse(QUrl source) 21 | { 22 | if ( source.isEmpty() ) { return false; } 23 | 24 | QFile file(source.toLocalFile()); 25 | if ( !file.open(QIODevice::ReadOnly) ) { qWarning()<<"Unable open file"; return false; } 26 | 27 | qInfo()<<"Begin parse SVG"<parse(&file); 30 | 31 | if ( st!=SVGParser::PS_OK ) { qWarning()<<"Unable parse SVG file"; return false; } 32 | 33 | qInfo()<<"SVG Parsed!"; 34 | 35 | return true; 36 | } 37 | 38 | /** 39 | * @brief Создаём QML 40 | * @param dest 41 | * @return 42 | */ 43 | bool AppCore::generate(QUrl dest) 44 | { 45 | if ( dest.isEmpty() ) { return false; } 46 | qInfo()<<"Begin create QML"<generateQML(&fileQml, _parser->rootItem(), _parser->defs()); 52 | 53 | if ( st!=SVGGenerator::GS_OK ) { qWarning()<<"Unable generate QML"; return false; } 54 | 55 | qInfo()<<"QML Created!"; 56 | 57 | _generatedQMLPath = dest; 58 | emit generateQMLPathChanged(); 59 | 60 | return true; 61 | } 62 | 63 | /** 64 | * @brief Парсим SVG и создём из него QML 65 | * @param source 66 | * @param dest 67 | */ 68 | bool AppCore::svg2qml(QUrl source, QUrl dest) 69 | { 70 | if ( !parse(source) ) { return false; } 71 | if ( !generate(dest) ) { return false; } 72 | 73 | return true; 74 | } 75 | 76 | -------------------------------------------------------------------------------- /appcore.h: -------------------------------------------------------------------------------- 1 | #ifndef APPCORE_H 2 | #define APPCORE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "SVG/Parser/svgparser.h" 9 | #include "qmlgenerator.h" 10 | 11 | class AppCore : public QObject 12 | { 13 | Q_OBJECT 14 | Q_PROPERTY(QUrl generateQMLPath READ generatedQMLPath NOTIFY generateQMLPathChanged) 15 | Q_PROPERTY(QString version READ version NOTIFY versionChanged) 16 | 17 | public: 18 | explicit AppCore(QObject *parent = nullptr); 19 | 20 | QUrl generatedQMLPath() const; 21 | 22 | QString version() const { return "1.3"; } 23 | 24 | signals: 25 | void generateQMLPathChanged(); 26 | void versionChanged(); 27 | 28 | public slots: 29 | bool parse(QUrl source); 30 | bool generate(QUrl dest); 31 | bool svg2qml(QUrl source, QUrl dest); 32 | 33 | private: 34 | SVGParser * _parser; 35 | QMLGenerator * _qmlGenerator; 36 | 37 | QUrl _generatedQMLPath; 38 | 39 | }; 40 | 41 | #endif // APPCORE_H 42 | -------------------------------------------------------------------------------- /examples/buttons1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Riflio/SVG2QML/109bd490ef215bc403f436839a9761ff0c4730ac/examples/buttons1.png -------------------------------------------------------------------------------- /examples/buttons1.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Shapes 1.12 3 | import QtGraphicalEffects 1.12 as GE 4 | 5 | Item { 6 | id: svg2qml 7 | property var scaleShape: Qt.size(1, 1) 8 | property bool thinkLines: false 9 | Item { 10 | width: svg2qml.width 11 | height: svg2qml.height 12 | Item { 13 | id: svg2731 14 | width: svg2qml.width 15 | height: svg2qml.height 16 | Item { 17 | id: layer1 18 | width: svg2qml.width 19 | height: svg2qml.height 20 | Shape { 21 | id: path5300 22 | width: svg2qml.width 23 | height: svg2qml.height 24 | ShapePath { 25 | PathSvg { 26 | path: "M 75.7131,2.35608 C 36.0195,2.35608 3.84153,34.1589 3.84153,73.3898 C 3.84153,112.621 36.0195,144.423 75.7131,144.423 C 115.407,144.423 147.585,112.621 147.585,73.3898 C 147.585,34.1589 115.407,2.35608 75.7131,2.35608 Z" 27 | } 28 | strokeColor: (svg2qml.thinkLines)? "black" : "transparent" 29 | strokeWidth: (svg2qml.thinkLines)? 1 : 7 30 | fillColor: (svg2qml.thinkLines)? "transparent" : "#ff000000" 31 | } 32 | opacity: 0.2 33 | } 34 | Shape { 35 | id: path5302 36 | width: svg2qml.width 37 | height: svg2qml.height 38 | ShapePath { 39 | PathSvg { 40 | path: "M 75.7131,131.888 C 108.38,131.888 134.901,105.676 134.901,73.3897 C 134.901,41.1036 108.38,14.8914 75.7131,14.8914 C 43.0462,14.8914 16.5247,41.1036 16.5247,73.3897 C 16.5247,105.676 43.0462,131.888 75.7131,131.888" 41 | } 42 | strokeColor: (svg2qml.thinkLines)? "black" : "transparent" 43 | strokeWidth: (svg2qml.thinkLines)? 1 : 7 44 | fillColor: (svg2qml.thinkLines)? "transparent" : "#ff000000" 45 | } 46 | opacity: 0.443 47 | } 48 | Shape { 49 | id: path5304 50 | width: svg2qml.width 51 | height: svg2qml.height 52 | ShapePath { 53 | PathSvg { 54 | path: "M 75.7131,19.0699 C 45.3592,19.0699 20.7525,43.3897 20.7525,73.3898 C 20.7525,103.39 45.3592,127.71 75.7131,127.71 C 106.067,127.71 130.674,103.39 130.674,73.3898 C 130.674,43.3897 106.067,19.0699 75.7131,19.0699 Z" 55 | } 56 | strokeColor: (svg2qml.thinkLines)? "black" : "transparent" 57 | strokeWidth: (svg2qml.thinkLines)? 1 : 7 58 | fillColor: (svg2qml.thinkLines)? "transparent" : "#ff494949" 59 | } 60 | } 61 | Shape { 62 | id: path5306 63 | width: svg2qml.width 64 | height: svg2qml.height 65 | ShapePath { 66 | PathSvg { 67 | path: "M 75.7131,19.0699 C 45.3592,19.0699 20.7525,43.3897 20.7525,73.3898 C 20.7525,103.39 45.3592,127.71 75.7131,127.71 C 106.067,127.71 130.674,103.39 130.674,73.3898 C 130.674,43.3897 106.067,19.0699 75.7131,19.0699 Z" 68 | } 69 | strokeColor: (svg2qml.thinkLines)? "black" : "transparent" 70 | strokeWidth: (svg2qml.thinkLines)? 1 : 7 71 | fillColor: "#00333333" 72 | } 73 | Item { 74 | width: svg2qml.width 75 | height: svg2qml.height 76 | Shape { 77 | width: svg2qml.width 78 | height: svg2qml.height 79 | ShapePath { 80 | PathSvg { 81 | path: "M 75.7131,19.0699 C 45.3592,19.0699 20.7525,43.3897 20.7525,73.3898 C 20.7525,103.39 45.3592,127.71 75.7131,127.71 C 106.067,127.71 130.674,103.39 130.674,73.3898 C 130.674,43.3897 106.067,19.0699 75.7131,19.0699 Z" 82 | } 83 | strokeWidth: 0 84 | strokeColor: "transparent" 85 | fillGradient: LinearGradient { 86 | x1:52.5; y1:309.115 87 | x2:52.5; y2:298.351 88 | GradientStop { position: 0; color: (svg2qml.thinkLines)? "transparent" : "#ff333333"; } 89 | GradientStop { position: 1; color: (svg2qml.thinkLines)? "transparent" : "#00333333"; } 90 | } 91 | } 92 | transform: Matrix4x4 {matrix: Qt.matrix4x4(0, -4.22774, 0, 1392.07, -4.17845, 0, 0, 240.528, 0, 0, 1, 0, 0, 0, 0, 1); } 93 | } 94 | layer.enabled: true 95 | layer.effect: GE.OpacityMask { 96 | maskSource: Shape { 97 | width: svg2qml.width 98 | height: svg2qml.height 99 | ShapePath { 100 | PathSvg { 101 | path: "M 75.7131,19.0699 C 45.3592,19.0699 20.7525,43.3897 20.7525,73.3898 C 20.7525,103.39 45.3592,127.71 75.7131,127.71 C 106.067,127.71 130.674,103.39 130.674,73.3898 C 130.674,43.3897 106.067,19.0699 75.7131,19.0699 Z" 102 | } 103 | fillColor: "#000000" 104 | strokeWidth: 0 105 | strokeColor: "transparent" 106 | } 107 | } 108 | } 109 | } 110 | } 111 | Shape { 112 | id: path5308 113 | width: svg2qml.width 114 | height: svg2qml.height 115 | ShapePath { 116 | PathSvg { 117 | path: "M 75.7131,23.2483 C 47.6941,23.2483 24.9802,45.6974 24.9802,73.3898 C 24.9802,101.082 47.6941,123.531 75.7131,123.531 C 103.732,123.531 126.446,101.082 126.446,73.3898 C 126.446,45.6974 103.732,23.2483 75.7131,23.2483 Z" 118 | } 119 | strokeColor: (svg2qml.thinkLines)? "black" : "transparent" 120 | strokeWidth: (svg2qml.thinkLines)? 1 : 7 121 | fillColor: (svg2qml.thinkLines)? "transparent" : "#ff2e2e2e" 122 | } 123 | } 124 | Shape { 125 | id: path5310 126 | width: svg2qml.width 127 | height: svg2qml.height 128 | ShapePath { 129 | PathSvg { 130 | path: "M 75.7131,23.2483 C 47.6941,23.2483 24.9802,45.6974 24.9802,73.3898 C 24.9802,101.082 47.6941,123.531 75.7131,123.531 C 103.732,123.531 126.446,101.082 126.446,73.3898 C 126.446,45.6974 103.732,23.2483 75.7131,23.2483 Z" 131 | } 132 | strokeColor: (svg2qml.thinkLines)? "black" : "transparent" 133 | strokeWidth: (svg2qml.thinkLines)? 1 : 7 134 | fillColor: "#00595959" 135 | } 136 | Item { 137 | width: svg2qml.width 138 | height: svg2qml.height 139 | Shape { 140 | width: svg2qml.width 141 | height: svg2qml.height 142 | ShapePath { 143 | PathSvg { 144 | path: "M 75.7131,23.2483 C 47.6941,23.2483 24.9802,45.6974 24.9802,73.3898 C 24.9802,101.082 47.6941,123.531 75.7131,123.531 C 103.732,123.531 126.446,101.082 126.446,73.3898 C 126.446,45.6974 103.732,23.2483 75.7131,23.2483 Z" 145 | } 146 | strokeWidth: 0 147 | strokeColor: "transparent" 148 | fillGradient: RadialGradient { 149 | centerX: 40.3062; centerY: 342.4; 150 | focalX: 40.3062; focalY: 342.4; 151 | centerRadius: 12 152 | focalRadius: 0 153 | GradientStop { position: 0; color: (svg2qml.thinkLines)? "transparent" : "#ff595959"; } 154 | GradientStop { position: 1; color: (svg2qml.thinkLines)? "transparent" : "#00595959"; } 155 | } 156 | } 157 | transform: Matrix4x4 {matrix: Qt.matrix4x4(-0.177749, -9.16879, 0, 3300.02, -6.21518, 0.256142, 0, 233.801, 0, 0, 1, 0, 0, 0, 0, 1); } 158 | } 159 | layer.enabled: true 160 | layer.effect: GE.OpacityMask { 161 | maskSource: Shape { 162 | width: svg2qml.width 163 | height: svg2qml.height 164 | ShapePath { 165 | PathSvg { 166 | path: "M 75.7131,23.2483 C 47.6941,23.2483 24.9802,45.6974 24.9802,73.3898 C 24.9802,101.082 47.6941,123.531 75.7131,123.531 C 103.732,123.531 126.446,101.082 126.446,73.3898 C 126.446,45.6974 103.732,23.2483 75.7131,23.2483 Z" 167 | } 168 | fillColor: "#000000" 169 | strokeWidth: 0 170 | strokeColor: "transparent" 171 | } 172 | } 173 | } 174 | } 175 | opacity: 0.6625 176 | } 177 | Shape { 178 | id: path5312 179 | width: svg2qml.width 180 | height: svg2qml.height 181 | ShapePath { 182 | PathSvg { 183 | path: "M 79.9409,48.3191 L 46.1189,81.7467 L 58.8022,94.282 L 79.9409,73.3898 L 101.08,94.282 L 113.763,81.7467 L 79.9409,48.3191 Z" 184 | } 185 | strokeColor: (svg2qml.thinkLines)? "black" : "transparent" 186 | strokeWidth: (svg2qml.thinkLines)? 1 : 7 187 | fillColor: (svg2qml.thinkLines)? "transparent" : "#ff000000" 188 | } 189 | opacity: 0.38 190 | } 191 | Shape { 192 | id: path5314 193 | width: svg2qml.width 194 | height: svg2qml.height 195 | ShapePath { 196 | PathSvg { 197 | path: "M 75.7131,48.3191 L 41.8912,81.7467 L 54.5744,94.282 L 75.7131,73.3898 L 96.8518,94.282 L 109.535,81.7467 L 75.7131,48.3191 Z" 198 | } 199 | strokeColor: (svg2qml.thinkLines)? "black" : "transparent" 200 | strokeWidth: (svg2qml.thinkLines)? 1 : 7 201 | fillColor: (svg2qml.thinkLines)? "transparent" : "#ff79ff00" 202 | } 203 | } 204 | Shape { 205 | id: path5316 206 | width: svg2qml.width 207 | height: svg2qml.height 208 | ShapePath { 209 | PathSvg { 210 | path: "M 75.7131,48.3191 L 41.8912,81.7467 L 54.5744,94.282 L 75.7131,73.3898 L 75.7131,48.3191 Z" 211 | } 212 | strokeColor: (svg2qml.thinkLines)? "black" : "transparent" 213 | strokeWidth: (svg2qml.thinkLines)? 1 : 7 214 | fillColor: (svg2qml.thinkLines)? "transparent" : "#ff57b700" 215 | } 216 | } 217 | } 218 | } 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /examples/buttons1.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 18 | 22 | 26 | 27 | 29 | 33 | 37 | 38 | 47 | 57 | 59 | 63 | 67 | 68 | 70 | 74 | 78 | 79 | 80 | 82 | 83 | 85 | image/svg+xml 86 | 88 | 89 | 90 | 91 | 92 | 94 | 98 | 106 | 110 | 114 | 118 | 122 | 126 | 130 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /examples/buttons1_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Riflio/SVG2QML/109bd490ef215bc403f436839a9761ff0c4730ac/examples/buttons1_original.png -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "appcore.h" 8 | 9 | int main(int argc, char *argv[]) 10 | { 11 | QGuiApplication app(argc, argv); 12 | 13 | QSurfaceFormat format; 14 | format.setSamples(8); 15 | QSurfaceFormat::setDefaultFormat(format); 16 | 17 | 18 | QCommandLineParser parser; 19 | parser.setApplicationDescription("SVG to QML converter."); 20 | parser.addHelpOption(); 21 | parser.addVersionOption(); 22 | parser.addPositionalArgument("source", QCoreApplication::translate("main", "Source svg file.")); 23 | parser.addPositionalArgument("destination", QCoreApplication::translate("main", "Destination qml file.")); 24 | 25 | parser.process(app); 26 | const QStringList args = parser.positionalArguments(); 27 | 28 | QString source = (args.count()>0)? args[0] : ""; 29 | QString dest = (args.count()>1)? args[1] : "file:./tmp.qml"; 30 | 31 | AppCore * appCore = new AppCore(nullptr); 32 | 33 | QQmlApplicationEngine engine; 34 | engine.rootContext()->setContextProperty("appCore", QVariant::fromValue(appCore)); 35 | 36 | const QUrl url(QStringLiteral("qrc:/main.qml")); 37 | QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { 38 | if (!obj && url == objUrl) { QCoreApplication::exit(-1); } 39 | }, Qt::QueuedConnection); 40 | 41 | engine.rootContext()->setContextProperty("appCore", appCore); 42 | 43 | engine.load(url); 44 | 45 | //-- Create 46 | if ( !source.isEmpty() ) { 47 | appCore->svg2qml(source, dest); 48 | } 49 | 50 | return app.exec(); 51 | } 52 | -------------------------------------------------------------------------------- /main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Window 3 | import QtQuick.Layouts 4 | import QtQuick.Dialogs 5 | 6 | Window { 7 | visible: true 8 | width: 640 9 | height: 480 10 | title: "SVG to QML converter. By pavelk.ru." + " V" + appCore.version; 11 | //color: "#505050" 12 | 13 | ColumnLayout { 14 | anchors.fill: parent 15 | anchors.margins: 10 16 | 17 | Rectangle { 18 | Layout.fillHeight: true 19 | Layout.fillWidth: true 20 | color: "transparent" 21 | 22 | WDropArea { 23 | anchors.fill: parent 24 | fileExtensionFilters: ["Vector (*.svg)"] 25 | helpText: ( generatedQmlLoader.status===Loader.Null )? qsTr("Drop SVG file to here") : ""; 26 | 27 | onFilesAccepted: (files) => { 28 | appCore.svg2qml(files[0], "file:./tmp.qml"); 29 | } 30 | 31 | Item { 32 | anchors.fill: parent 33 | anchors.margins: 10 34 | clip: true 35 | 36 | Connections { 37 | target: appCore 38 | function onGenerateQMLPathChanged() { 39 | generatedQmlLoader.source = appCore.generateQMLPath + "?t=" + Date.now(); //-- Force update when file name not changed 40 | } 41 | } 42 | 43 | Loader { 44 | id: generatedQmlLoader 45 | anchors.fill: parent 46 | 47 | onLoaded: { 48 | console.log("Generated qml loaded") 49 | } 50 | } 51 | 52 | FileDialog { 53 | id: saveFileDialog 54 | fileMode: FileDialog.SaveFile 55 | onAccepted: { 56 | appCore.saveAs(saveFileDialog.selectedFile); 57 | } 58 | 59 | } 60 | 61 | Text { 62 | text: qsTr("Saved as tmp.qml") 63 | visible: ( generatedQmlLoader.status===Loader.Ready ) 64 | anchors.right: parent.right 65 | anchors.bottom: parent.bottom 66 | color: "#dfdfdf" 67 | } 68 | } 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /qml.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main.qml 4 | WDropArea.qml 5 | Shaders/mask.frag 6 | Shaders/mask.vert 7 | Shaders/mask.frag.qsb 8 | Shaders/mask.vert.qsb 9 | 10 | 11 | -------------------------------------------------------------------------------- /qmlgenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef QMLGENERATOR_H 2 | #define QMLGENERATOR_H 3 | 4 | #include "SVG/Generator/svggenerator.h" 5 | 6 | class QMLGenerator : public SVGGenerator 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit QMLGenerator(QObject *parent = nullptr); 11 | 12 | struct TSettings { 13 | QString rootName = "svg2qml"; 14 | }; 15 | 16 | void setSettings(TSettings settings); 17 | TSettings settings() const; 18 | 19 | GenerateStatus generateQML(QIODevice *device, IPrimitive * rootItm, const CDefs &defs); 20 | 21 | class QShape 22 | { 23 | public: 24 | QShape(IPrimitive * itm): itm(itm), x(0.0), y(0.0), r(0.0) {} 25 | IPrimitive * itm; 26 | QString pathCommands; 27 | CBoundingBox boundingBox; 28 | double ml; 29 | double mt; 30 | double x; 31 | double y; 32 | double r; 33 | }; 34 | 35 | signals: 36 | 37 | private: 38 | QTextStream _qml; 39 | int _lvl; 40 | 41 | TSettings _settings; 42 | IPrimitive * _rootItm; 43 | CDefs _defs; 44 | 45 | QString tab(int c); 46 | QString sanitizeID(QString id); 47 | 48 | QString primitiveToPathCommands(IPrimitive * p, double offset=0, CMatrix transforms = CMatrix::identity(3, 3)); 49 | 50 | bool makeFill(QShape * shape, bool isSimple); 51 | 52 | bool makeOpacity(QShape* shape); 53 | 54 | void makeFillGradient(QShape* shape, FGradient * gr, CDef::TDefType type); 55 | void makeFillGradientTransform(QShape* shape, FGradient * gr, CDef::TDefType type); 56 | void makeRadialGradient(FRadialGradient * gr); 57 | void makeLinearGradient(FLinearGradient * gr); 58 | void makeGradientStops(FGradient * gr); 59 | 60 | void makeStroke(QShape* shape); 61 | 62 | void makeElement(IPrimitive * el); 63 | 64 | void makeTransform(const CMatrix &m); 65 | 66 | void makeID(IPrimitive* itm); 67 | 68 | void writeStartLvlProp(QString propName, QString elementName); 69 | void writeStartLvl(QString elementName, bool isInline=false); 70 | void writeEndLvl(bool isInline=false); 71 | void writePropVal(QString name, QVariant val, bool quoted=false, bool isInline=false); 72 | void writePropThinkLinesVal(QString name, QVariant valThink, QVariant val, bool quoted=false, bool isInline=false); 73 | void writePropElVal(QString name, QString elementName, QVariant val); 74 | }; 75 | 76 | #endif // QMLGENERATOR_H 77 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # SVG2QML 2 | 3 | Convert SVG file to native QML file. 4 | Преобразует SVG файл в готовый нативный QML файл. 5 | 6 | ![Original SVG file](/examples/buttons1_original.png) 7 | ![Converted QML file](/examples/buttons1.png) 8 | 9 | See [Original SVG file](/examples/buttons1.svg) and [Converted QML file](/examples/buttons1.qml) 10 | 11 | ## Changelog 12 | Version 1.3 13 | - Shape x,y,width,height as content 14 | - Fix parent styles 15 | - Fix gradients in negative values 16 | 17 | Version 1.2 18 | - Reworked the creation of gradients and gradientTransform 19 | - Separately output transform matrix 20 | - Defs order 21 | 22 | Version 1.1 23 | - Migrated to Qt 6 (6.2.0) 24 | - Support quadratic Bezier (path qQ) 25 | - Added Drag&Drop area for files to convert 26 | - Fix bug with transform 27 | 28 | ## Поддерживаемые элементы: 29 | 30 | В скобках указаны поддерживаемые индивидуальные параметры. 31 | Список общих дальше. 32 | - svg 33 | - defs 34 | - title 35 | - desc 36 | - g 37 | - line (x1, y1, x2, y2) 38 | - rect (x, y, width, height, rx, ry) 39 | - circle (cx, cy, r) 40 | - polyline (points) 41 | - polygon (points) 42 | - ellipse (cx, cy, rx, ry) 43 | - path ("d" commands: "mcsqlvhvazMCSQLVHAZ") 44 | - clipPath (g, rect, circle, ellipse, path) 45 | - linearGradient (x1, y1, x2, y2, gradientTransform) 46 | - stop (offset, stop-color, stop-opacity) 47 | - radialGradient (cx, cy, r, fx, fy, gradientTransform) 48 | - stop (offset, stop-color, stop-opacity) 49 | - style (base selector "#id", ".class") 50 | 51 | ## Поддерживаемые общие параметры 52 | 53 | - opacity 54 | - fill (color, opacity, gradient) 55 | - stroke (color, opacity, width, linejoin: bevel/miter/roud, linecap: butt/round/square, miterlimit, dasharray) 56 | - style (only upper parameters) 57 | - transform (translate, scale, rotate, matrix3x2) 58 | - id 59 | - class 60 | 61 | ## В планах 62 | 63 | - text 64 | - image 65 | 66 | ## Problems 67 | 68 | 69 | 70 | ## Tips 71 | - In Inkscape save as "simple svg". 72 | - In Adobe Illustrator save as "SVG Tiny 1.1". 73 | 74 | ### Как работает 75 | 76 | Все SVG.g преобразуются в QML.Item, соблюдая вложенность.
77 | Все SVG.circle, SVG.line, SVG.ellipse, SVG.rect преобразуются сначала в SVG.path,
78 | за тем, если указан SVG.transform, то он сразу применяется к SVG.path.
79 | Все SVG.path преобразуются в QML.Shape { QML.ShapePath { QML.PathSvg { path: "path command" }}}.
80 | Все SVG.clipPath преобразуются сначала в SVG.path, а за тем
81 | преобразуются в QML.OpacityMask { maskSource: QML.Shape { ... } }
82 | Все SVG.linearGradient, SVG.radialGradient преобразуются в QML.LinearGradient и QML.RadialGradient для каждой QML.ShapePath соответственно.
83 | Если у градиентов есть gradientTransform, то будет две QML.Shape у одной заливка обычным цветом, у другой градиент и трансформация.
84 | Так же будет сделана QML.OpacityMask, что бы градиент не вылезал за пределы основной фигуры.
85 | 86 | ### Что не поддерживается 87 | 88 | - Всё остальное, что не указано, увы =(. 89 | 90 | Почему? Для наших дизайнов этого хватает. 91 | Вы можете в pull request прислать примеры svg, чего бы хотелось. 92 | 93 | ### Зависимости 94 | 95 | - Qt >= 6.2.0, thats all =) 96 | 97 | License 98 | ---- 99 | GPL V3 100 | -------------------------------------------------------------------------------- /tests/ClipPathBase.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/gradient_60_120.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | gradient_60_120 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /tests/gradient_transforms.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 32 | 34 | 42 | 46 | 50 | 51 | 52 | 56 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /tests/gradient_transforms2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 36 | 38 | 41 | 45 | 49 | 50 | 61 | 62 | 66 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /tests/test1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 37 | 38 | 41 | 45 | 49 | 50 | 53 | 60 | 61 | 64 | 69 | 70 | 79 | 88 | 89 | 108 | 110 | 111 | 113 | image/svg+xml 114 | 116 | 117 | 118 | 119 | 120 | 121 | TTTTTIITLE 122 | 123 | 127 | 132 | 137 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /tests/test_bezier1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/test_ellipses.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 55 | 62 | 69 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /tests/test_lines.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /tests/test_polyline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /tests/test_rects.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 17 | 18 | 20 | image/svg+xml 21 | 23 | 24 | 25 | 26 | 27 | 29 | 36 | 37 | 46 | 47 | 48 | 49 | --------------------------------------------------------------------------------