Guest User

Untitled

a guest
Oct 17th, 2017
64
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 16.26 KB | None | 0 0
  1.  
  2. #include "p_MWTk_SvgToPathConverter.h"
  3.  
  4.  
  5.  
  6. p_MWTk_SvgToPathConverter::p_MWTk_SvgToPathConverter() { }
  7.  
  8. QPainterPath p_MWTk_SvgToPathConverter::importSvgPathData(QString data)
  9. {
  10.     QPainterPath ret_path;
  11.  
  12.     data = data.replace("m", " m ", Qt::CaseSensitive);
  13.     data = data.replace("M", " M ", Qt::CaseSensitive);
  14.     data = data.replace("c", " c ", Qt::CaseSensitive);
  15.     data = data.replace("C", " C ", Qt::CaseSensitive);
  16.     data = data.replace("l", " l ", Qt::CaseSensitive);
  17.     data = data.replace("L", " L ", Qt::CaseSensitive);
  18.     data = data.replace("v", " v ", Qt::CaseSensitive);
  19.     data = data.replace("V", " V ", Qt::CaseSensitive);
  20.     data = data.replace("h", " h ", Qt::CaseSensitive);
  21.     data = data.replace("H", " H ", Qt::CaseSensitive);
  22.     data = data.replace("a", " a ", Qt::CaseSensitive);
  23.     data = data.replace("A", " A ", Qt::CaseSensitive);
  24.     data = data.replace("t", " t ", Qt::CaseSensitive);
  25.     data = data.replace("T", " T ", Qt::CaseSensitive);
  26.     data = data.replace("q", " q ", Qt::CaseSensitive);
  27.     data = data.replace("Q", " Q ", Qt::CaseSensitive);
  28.     data = data.replace("s", " s ", Qt::CaseSensitive);
  29.     data = data.replace("S", " S ", Qt::CaseSensitive);
  30.     data = data.replace("z", " z ", Qt::CaseInsensitive);
  31.  
  32.     while (data.indexOf("  ") > -1) { data = data.replace("  ", " "); }
  33.  
  34.     QStringList codesUpper;
  35.     codesUpper << "M" << "L" << "C" << "Z" << "Q" << "S" << "T" << "H" << "V";
  36.     QStringList codesLower;
  37.     codesLower << "m" << "l" << "c" << "z" << "q" << "s" << "t" << "h" << "v";
  38.     QStringList codes_list;
  39.     codes_list << codesUpper << codesLower;
  40.  
  41.     QChar      prev_letter     = 'M';
  42.     QChar      last_letter     = 'M';
  43.     SvgAction  last_operation  = MoveTo;
  44.     QPointF    last_position   = QPointF(0.0, 0.0);
  45.     QPointF    last_cp         = QPointF(0.0, 0.0);
  46.  
  47.     bool bypass_test = false;
  48.  
  49.     QStringList         list_chunks = data.split(" ", QString::SkipEmptyParts);
  50.     QStringListIterator it(list_chunks);
  51.  
  52.     while (it.hasNext()) {
  53.  
  54.         if (!bypass_test) {
  55.             last_letter = QChar(it.next().at(0));
  56.             //qDebug() << "INFO : bypass is off, last_letter found :" << last_letter;
  57.         }
  58.         else {
  59.             //qDebug() << "INFO : bypass is on, last_letter used :" << last_letter;
  60.         }
  61.  
  62.         bool is_relative   = last_letter.isLower();
  63.         //qDebug() << "INFO : operation is_relative:" << is_relative;
  64.  
  65.         int  needed_points = -1;
  66.  
  67.         // find the needed code and points
  68.         switch (last_letter.toLower().toAscii()) {
  69.             case 'm' : {
  70.                 last_operation = MoveTo;
  71.                 needed_points = 1;
  72.                 break;
  73.             }
  74.             case 'l' : {
  75.                 last_operation = LineTo;
  76.                 needed_points = 1;
  77.                 break;
  78.             }
  79.             case 'v' : {
  80.                 last_operation = LineTo;
  81.                 needed_points = 1;
  82.                 break;
  83.             }
  84.             case 'h' : {
  85.                 last_operation = LineTo;
  86.                 needed_points = 1;
  87.                 break;
  88.             }
  89.             case 'c': {
  90.                 last_operation = CubicTo;
  91.                 needed_points = 3;
  92.                 break;
  93.             }
  94.             case 's': {
  95.                 last_operation = CubicTo;
  96.                 needed_points = 2;
  97.                 break;
  98.             }
  99.             case 'q' : {
  100.                 last_operation = QuadTo;
  101.                 needed_points = 2;
  102.                 break;
  103.             }
  104.             case 't': {
  105.                 last_operation = QuadTo;
  106.                 needed_points = 1;
  107.                 break;
  108.             }
  109.             case 'a' : {
  110.                 //  a 25,50  -30  0,1  50,-25
  111.                 last_operation = ArcTo;
  112.                 needed_points = 4;
  113.                 break;
  114.             }
  115.             case 'z' : {
  116.                 last_operation = ClosePath;
  117.                 needed_points = 0;
  118.                 break;
  119.             }
  120.             default : {
  121.                 qDebug() << "ERROR : unknown SVG operation code :" << last_letter;
  122.                 break;
  123.             }
  124.         }
  125.  
  126.         // construct a list of points
  127.         QVector<QPointF> vector_points;
  128.         while (vector_points.count() < needed_points) {
  129.             qreal x = 0;
  130.             qreal y = 0;
  131.             QString str_number;
  132.             // iterate the list
  133.             if (it.hasNext()) {
  134.                 str_number = it.next();
  135.             }
  136.             else {
  137.                 qDebug() << "ERROR : at end of the list before having found all needed points !";
  138.                 break;
  139.             }
  140.             if (str_number.contains(",")) {
  141.                 // split the pair
  142.                 x = str_number.split(",").first().toDouble();
  143.                 y = str_number.split(",").last().toDouble();
  144.             }
  145.             else {
  146.                 if (last_letter.toLower() == 'a'
  147.                         && vector_points.count() == 1) {
  148.                     // storing angle in X property
  149.                     x = str_number.toDouble();
  150.                 }
  151.                 if (last_letter.toLower() == 'h') {
  152.                     x = str_number.toDouble();
  153.                     // y is not provided for H
  154.                 }
  155.                 else if (last_letter.toLower() == 'v') {
  156.                     // x is not provided for V
  157.                     y = str_number.toDouble();
  158.                 }
  159.                 else {
  160.                     x = str_number.toDouble();
  161.                     // iterate the list
  162.                     if (it.hasNext()) {
  163.                         str_number = it.next();
  164.                         y = str_number.toDouble();
  165.                     }
  166.                     else {
  167.                         qDebug() << "ERROR : at end of the list before having found all needed points !";
  168.                         break;
  169.                     }
  170.                 }
  171.             }
  172.             vector_points.append(QPointF(x,y));
  173.         }
  174.  
  175.         if (vector_points.count() < needed_points) {
  176.             qDebug() << "ERROR : not enough points found";
  177.             break;
  178.         }
  179.  
  180.         qreal offsetX = is_relative ? last_position.x() : 0;
  181.         qreal offsetY = is_relative ? last_position.y() : 0;
  182.  
  183.         // add operation to the path
  184.         switch (last_operation) {
  185.             case MoveTo: {
  186.                 QPointF p2 = vector_points.last() + QPointF(offsetX,offsetY);
  187.                 ret_path.moveTo(p2); // qDebug() << "INFO : added moveTo to the path.";
  188.                 last_position = p2;
  189.                 // back to default after a move operation
  190.                 last_operation = LineTo;
  191.                 last_letter = last_letter.isLower() ? 'l' : 'L';
  192.                 break;
  193.             }
  194.             case LineTo: {
  195.                 QPointF p2 = vector_points.last() + QPointF(offsetX,offsetY);
  196.                 ret_path.lineTo(p2); // qDebug() << "INFO : added LineTo to the path.";
  197.                 last_position = p2;
  198.                 break;
  199.             }
  200.             case CubicTo: {
  201.                 QPointF p1 = last_position;
  202.                 QPointF c1;
  203.                 QPointF c2;
  204.                 QPointF p2;
  205.                 if (last_letter.toLower() == 'c')
  206.                 {
  207.                     //qDebug() << "INFO : parse cubicto";
  208.                     c1 = vector_points.at(0) + QPointF(offsetX,offsetY);
  209.                     c2 = vector_points.at(1) + QPointF(offsetX,offsetY);
  210.                     p2 = vector_points.at(2) + QPointF(offsetX,offsetY);
  211.                 }
  212.                 else if (last_letter.toLower() == 's')
  213.                 {
  214.                     //qDebug() << "INFO : convert shorthand/smooth curveto";
  215.                     if (prev_letter == 'c' || prev_letter == 'C' ||
  216.                         prev_letter == 's' || prev_letter == 'S') {
  217.                         c1 = QPointF(2 * p1.x() - last_cp.x(),
  218.                                      2 * p1.y() - last_cp.y());
  219.                     }
  220.                     else {
  221.                         c1 = p1;
  222.                     }
  223.                     c2 = vector_points.at(0) + QPointF(offsetX,offsetY);
  224.                     p2 = vector_points.at(1) + QPointF(offsetX,offsetY);
  225.                 }
  226.                 ret_path.cubicTo(c1, c2, p2); // qDebug() << "INFO : added cubicTo to the path.";
  227.                 last_cp = c2;
  228.                 last_position = p2;
  229.                 break;
  230.             }
  231.             case QuadTo: {
  232.                 QPointF p1 = last_position;
  233.                 QPointF c;
  234.                 QPointF p2;
  235.                 if (last_letter.toLower() == 'q')
  236.                 {
  237.                     //qDebug() << "INFO : parse quadto";
  238.                     c = vector_points.at(0) + QPointF(offsetX,offsetY);
  239.                     p2 = vector_points.at(1) + QPointF(offsetX,offsetY);
  240.                 }
  241.                 else if (last_letter.toLower() == 't')
  242.                 {
  243.                     //qDebug() << "INFO : convert Shorthand/smooth quadratic Bézier curveto";
  244.                     p2 = vector_points.at(0) + QPointF(offsetX,offsetY);
  245.                     QPointF c;
  246.                     if (prev_letter == 'q' || prev_letter == 'Q' ||
  247.                         prev_letter == 't' || prev_letter == 'T') {
  248.                         c = QPointF(2 * p1.x() - last_cp.x(),
  249.                                     2 * p1.y() - last_cp.y());
  250.                     }
  251.                     else {
  252.                         c = p1;
  253.                     }
  254.  
  255.                 }
  256.                 ret_path.quadTo(c, p2); // qDebug() << "INFO : added quadTo to the path.";
  257.                 last_cp = c;
  258.                 last_position = p2;
  259.                 break;
  260.             }
  261.             case ArcTo : {
  262.                 QPointF p1 = last_position;
  263.                 QPointF radius = vector_points.at(0);
  264.                 QPointF angle  = vector_points.at(1);
  265.                 QPointF flags  = vector_points.at(2);
  266.                 QPointF p2     = vector_points.at(3);
  267.                 pathArc(ret_path,
  268.                         radius.x(), radius.y(),
  269.                         angle.x(),
  270.                         (int)flags.x(), (int)flags.y(),
  271.                         p2.x(), p2.y(),
  272.                         p1.x(), p1.y());
  273.                 last_position = p2;
  274.                 break;
  275.             }
  276.             case ClosePath: {
  277.                 ret_path.closeSubpath(); // qDebug() << "INFO : added closeSubPath to the path.";
  278.                 // back to default after a close operation
  279.                 last_operation = MoveTo;
  280.                 last_letter = 'm';
  281.                 break;
  282.             }
  283.         }
  284.  
  285.         prev_letter = last_letter;
  286.  
  287.         if (it.hasNext()) {
  288.             QString test_str = it.peekNext();
  289.             if (codes_list.contains(test_str)) {
  290.                 // next chunk is an op code
  291.                 bypass_test = false;
  292.             }
  293.             else {
  294.                 // next chunk is yet a list of points
  295.                 bypass_test = true;
  296.             }
  297.         }
  298.  
  299.     }
  300.  
  301.     return ret_path;
  302. }
  303.  
  304.  
  305.  
  306.  
  307. void p_MWTk_SvgToPathConverter::pathArcSegment(QPainterPath &path,
  308.                                                qreal xc, qreal yc,
  309.                                                qreal th0, qreal th1,
  310.                                                qreal rx, qreal ry, qreal xAxisRotation)
  311. {
  312.     qreal sinTh, cosTh;
  313.     qreal a00, a01, a10, a11;
  314.     qreal x1, y1, x2, y2, x3, y3;
  315.     qreal t;
  316.     qreal thHalf;
  317.  
  318.     sinTh = qSin(xAxisRotation * (MWTk::TRIGO_PI / 180.0));
  319.     cosTh = qCos(xAxisRotation * (MWTk::TRIGO_PI / 180.0));
  320.  
  321.     a00 =  cosTh * rx;
  322.     a01 = -sinTh * ry;
  323.     a10 =  sinTh * rx;
  324.     a11 =  cosTh * ry;
  325.  
  326.     thHalf = 0.5 * (th1 - th0);
  327.     t = (8.0 / 3.0) * qSin(thHalf * 0.5) * qSin(thHalf * 0.5) / qSin(thHalf);
  328.     x1 = xc + qCos(th0) - t * qSin(th0);
  329.     y1 = yc + qSin(th0) + t * qCos(th0);
  330.     x3 = xc + qCos(th1);
  331.     y3 = yc + qSin(th1);
  332.     x2 = x3 + t * qSin(th1);
  333.     y2 = y3 - t * qCos(th1);
  334.  
  335.     path.cubicTo(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
  336.                  a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
  337.                  a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
  338. }
  339.  
  340. // the arc handling code underneath is from XSVG (BSD license)
  341. /*
  342.  * Copyright  2002 USC/Information Sciences Institute
  343.  *
  344.  * Permission to use, copy, modify, distribute, and sell this software
  345.  * and its documentation for any purpose is hereby granted without
  346.  * fee, provided that the above copyright notice appear in all copies
  347.  * and that both that copyright notice and this permission notice
  348.  * appear in supporting documentation, and that the name of
  349.  * Information Sciences Institute not be used in advertising or
  350.  * publicity pertaining to distribution of the software without
  351.  * specific, written prior permission.  Information Sciences Institute
  352.  * makes no representations about the suitability of this software for
  353.  * any purpose.  It is provided "as is" without express or implied
  354.  * warranty.
  355.  *
  356.  * INFORMATION SCIENCES INSTITUTE DISCLAIMS ALL WARRANTIES WITH REGARD
  357.  * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
  358.  * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INFORMATION SCIENCES
  359.  * INSTITUTE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
  360.  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
  361.  * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
  362.  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  363.  * PERFORMANCE OF THIS SOFTWARE.
  364.  *
  365.  */
  366.  
  367. void p_MWTk_SvgToPathConverter::pathArc(QPainterPath &path,
  368.                                         qreal        rx,
  369.                                         qreal        ry,
  370.                                         qreal        x_axis_rotation,
  371.                                         int          large_arc_flag,
  372.                                         int          sweep_flag,
  373.                                         qreal        x,
  374.                                         qreal        y,
  375.                                         qreal        curx,
  376.                                         qreal        cury)
  377. {
  378.     qreal sin_th, cos_th;
  379.     qreal a00, a01, a10, a11;
  380.     qreal x0, y0, x1, y1, xc, yc;
  381.     qreal d, sfactor, sfactor_sq;
  382.     qreal th0, th1, th_arc;
  383.     int i, n_segs;
  384.     qreal dx, dy, dx1, dy1, Pr1, Pr2, Px, Py, check;
  385.  
  386.     rx = qAbs(rx);
  387.     ry = qAbs(ry);
  388.  
  389.     sin_th = qSin(x_axis_rotation * (MWTk::TRIGO_PI / 180.0));
  390.     cos_th = qCos(x_axis_rotation * (MWTk::TRIGO_PI / 180.0));
  391.  
  392.     dx = (curx - x) / 2.0;
  393.     dy = (cury - y) / 2.0;
  394.     dx1 =  cos_th * dx + sin_th * dy;
  395.     dy1 = -sin_th * dx + cos_th * dy;
  396.     Pr1 = rx * rx;
  397.     Pr2 = ry * ry;
  398.     Px = dx1 * dx1;
  399.     Py = dy1 * dy1;
  400.     /* Spec : check if radii are large enough */
  401.     check = Px / Pr1 + Py / Pr2;
  402.     if (check > 1) {
  403.         rx = rx * qSqrt(check);
  404.         ry = ry * qSqrt(check);
  405.     }
  406.  
  407.     a00 =  cos_th / rx;
  408.     a01 =  sin_th / rx;
  409.     a10 = -sin_th / ry;
  410.     a11 =  cos_th / ry;
  411.     x0 = a00 * curx + a01 * cury;
  412.     y0 = a10 * curx + a11 * cury;
  413.     x1 = a00 * x + a01 * y;
  414.     y1 = a10 * x + a11 * y;
  415.     /* (x0, y0) is current point in transformed coordinate space.
  416.        (x1, y1) is new point in transformed coordinate space.
  417.  
  418.        The arc fits a unit-radius circle in this space.
  419.     */
  420.     d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
  421.     sfactor_sq = 1.0 / d - 0.25;
  422.     if (sfactor_sq < 0) {
  423.         sfactor_sq = 0;
  424.     }
  425.     sfactor = qSqrt(sfactor_sq);
  426.     if (sweep_flag == large_arc_flag) {
  427.         sfactor = -sfactor;
  428.     }
  429.     xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
  430.     yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
  431.     /* (xc, yc) is center of the circle. */
  432.  
  433.     th0 = qAtan2(y0 - yc, x0 - xc);
  434.     th1 = qAtan2(y1 - yc, x1 - xc);
  435.  
  436.     th_arc = th1 - th0;
  437.     if (th_arc < 0 && sweep_flag) {
  438.         th_arc += 2 * MWTk::TRIGO_PI;
  439.     }
  440.     else if (th_arc > 0 && !sweep_flag) {
  441.         th_arc -= 2 * MWTk::TRIGO_PI;
  442.     }
  443.  
  444.     n_segs = qCeil(qAbs(th_arc / (MWTk::TRIGO_PI * 0.5 + 0.001)));
  445.  
  446.     for (i = 0; i < n_segs; i++) {
  447.         pathArcSegment(path, xc, yc,
  448.                        th0 + i * th_arc / n_segs,
  449.                        th0 + (i + 1) * th_arc / n_segs,
  450.                        rx, ry, x_axis_rotation);
  451.     }
  452. }
Add Comment
Please, Sign In to add comment