【问题标题】:convert bezier curve to polygonal chain?将贝塞尔曲线转换为多边形链?
【发布时间】:2023-03-20 07:59:01
【问题描述】:

我想将一条贝塞尔曲线分割成一条带有 n 条直线的多边形链。线的数量取决于 2 条连接线之间的最大允许角度。 我正在寻找一种算法来找到最佳解决方案(即尽可能减少直线的数量)。

我知道如何使用 Casteljau 或 Bernstein 多项式分割贝塞尔曲线。我尝试将贝塞尔曲线分成两半计算直线之间的角度,如果连接线之间的角度在某个阈值范围内,则再次拆分,但我可能会遇到捷径。

是否有已知的算法或伪代码可用于进行此转换?

【问题讨论】:

  • 我假设您有可用的 Bezier 控制多边形?这不是一个很好的起点吗?为什么角度在这里很重要?我很好奇你想要达到的目标。
  • 2 个控制点可用。从曲线的起点开始确实是另一种选择,但我很好奇是否有可用的最佳解决方案。我想用它为cnc路由设备生成输入。这台机器只理解直线,所以需要将贝塞尔曲线分割成一组直线。
  • 在阅读您的帖子之前,我并不知道贝塞尔曲线,但考虑将曲线划分为 n st。线条让我想起了康托尔的无穷大理论。 ;)

标签: algorithm bezier polyline


【解决方案1】:

递归使用 de Casteljau 算法,直到控制点近似共线。参见例如http://www.antigrain.com/research/adaptive_bezier/index.html

【讨论】:

    【解决方案2】:

    这是一个有趣的话题。我唯一要添加的是经过测试的 C# 代码,也许可以为某人省去麻烦。我试图写清楚而不是速度,所以它主要遵循 AGG 网站的关于 Casteljau 算法的 PDF 文档(见上文)。符号遵循该 PDF 中的图表。

    public class Bezier
    {
        public PointF P1;   // Begin Point
        public PointF P2;   // Control Point
        public PointF P3;   // Control Point
        public PointF P4;   // End Point
    
        // Made these global so I could diagram the top solution
        public Line L12;
        public Line L23;
        public Line L34;
    
        public PointF P12;
        public PointF P23;
        public PointF P34;
    
        public Line L1223;
        public Line L2334;
    
        public PointF P123;
        public PointF P234;
    
        public Line L123234;
        public PointF P1234;
    
        public Bezier(PointF p1, PointF p2, PointF p3, PointF p4)
        {
            P1 = p1; P2 = p2; P3 = p3; P4 = p4;
        }
    
        /// <summary>
        /// Consider the classic Casteljau diagram
        /// with the bezier points p1, p2, p3, p4 and lines l12, l23, l34
        /// and their midpoint of line l12 being p12 ...
        /// and the line between p12 p23 being L1223
        /// and the midpoint of line L1223 being P1223 ...
        /// </summary>
        /// <param name="lines"></param>
        public void SplitBezier( List<Line> lines)
        {
            L12 = new Line(this.P1, this.P2);
            L23 = new Line(this.P2, this.P3);
            L34 = new Line(this.P3, this.P4);
    
            P12 = L12.MidPoint();
            P23 = L23.MidPoint();
            P34 = L34.MidPoint();
    
            L1223 = new Line(P12, P23);
            L2334 = new Line(P23, P34);
    
            P123 = L1223.MidPoint();
            P234 = L2334.MidPoint();
    
            L123234 = new Line(P123, P234);
    
            P1234 = L123234.MidPoint();
    
            if (CurveIsFlat())
            {
                lines.Add(new Line(this.P1, this.P4));
                return;
            }
            else
            {
                Bezier bz1 = new Bezier(this.P1, P12, P123, P1234);
                bz1.SplitBezier(lines);
    
                Bezier bz2 = new Bezier(P1234, P234, P34, this.P4);
                bz2.SplitBezier(lines);
            }
    
            return;
        }
    
        /// <summary>
        /// Check if points P1, P1234 and P2 are colinear (enough).
        /// This is very simple-minded algo... there are better...
        /// </summary>
        /// <returns></returns>
        public bool CurveIsFlat()
        {
            float t1 = (P2.Y - P1.Y) * (P3.X - P2.X);
            float t2 = (P3.Y - P2.Y) * (P2.X - P1.X);
    
            float delta = Math.Abs(t1 - t2);
    
            return delta < 0.1; // Hard-coded constant
        }
    

    PointF 来自 System.Drawing,Line 类如下:

    public class Line
    {
        PointF P1; PointF P2;
    
        public Line(PointF pt1, PointF pt2)
        {
            P1 = pt1; P2 = pt2;
        }
    
        public PointF MidPoint()
        {
            return new PointF((P1.X + P2.X) / 2f, (P1.Y + P2.Y) / 2f);
        }
    }
    

    一个示例调用创建具有 4 个点(开始、2 个控制和结束)的 Bezier 对象,并返回一个近似于 Bezier 的线列表:

            TopBezier = new Bezier(Point1, Point2, Point3, Point4 );
    
            List<Line> lines = new List<Line>();
            TopBezier.SplitBezier(lines);
    

    感谢 Jerry 博士、AGG 和所有其他贡献者。

    【讨论】:

      【解决方案3】:

      有一些 RSA 扁平化的替代方法据报道速度更快:

      RSA 与 PAA: http://www.cis.usouthal.edu/~hain/general/Theses/Ahmad_thesis.pdf

      RSA vs CAA vs PAA: http://www.cis.usouthal.edu/~hain/general/Theses/Racherla_thesis.pdf

      RSA = 递归细分算法 PAA = 抛物线逼近算法 CAA = 圆形逼近算法

      根据 Rachela 的说法,CAA 比 PAA 慢 1.5–2 倍。 CAA 与 RSA 一样慢,但在偏移曲线中可以更好地实现所需的平坦度。

      似乎 PAA 是实际曲线的最佳选择,而 CAA 是曲线偏移的最佳选择(在抚摸曲线时)。

      我已经测试了这两篇论文的 PAA,但在某些情况下它们都失败了。 Ahmad 的 PAA 在共线情况下(所有点都在同一条线上)失败,而 Rachela 的 PAA 在共线情况下以及两个控制点相等的情况下失败。通过一些修复,它们可能会按预期工作。

      【讨论】:

        【解决方案4】:

        my website -> DXF -> polybezier 上的视觉示例。 它基本上是与 casteljau 的递归拆分。

        Bezier2Poly.prototype.convert = function(array,init) {
            if (init) {
           this.vertices = [];
            }
            if (!init && (Math.abs(this.controlPointsDiff(array[0], array[2])) < this.threshold 
              || Math.abs(this.controlPointsDiff({x:array[2].x-array[1].x, y:array[2]-array[1].y}, array[2])) < this.threshold)) {
                this.vertices.push(array[2]);
            } else {
                var split = this.splitBezier(array);
                this.convert(split.b1);
                this.convert(split.b2);
            }
            return this.vertices;
        }
        

        并判断为:计算控制点与通过端点的线的夹角。

        Bezier2Poly.prototype.controlPointsDiff = function (vector1, vector2) {
            var angleCp1 = Math.atan2(vector1.y, vector1.x);
            var angleCp2 = Math.atan2(vector2.y, vector2.x);
            return angleCp1 - angleCp2;
        }
        

        【讨论】:

        【解决方案5】:

        我用 qt 为任何 svg 路径(包括贝塞尔曲线)解决它,我在 svg 模块中发现 qsvghandler.cpp 中的一个静态函数,它从你的 svg 路径到 QPainterPath 的 parsePathDataFast 和蛋糕上的樱桃! QPainterPath 具有三个本机函数,可以将路径转换为多边形(大的一个 toFillPolygon 和其他在多边形列表中拆分 toSubpathPolygons 或 toFillPolygons)以及边界框、相交、平移等好东西......准备与 Boost 一起使用: :几何现在,还不错!

        头文件 parsepathdatafast.h

        #ifndef PARSEPATHDATAFAST_H
        #define PARSEPATHDATAFAST_H
        
        #include <QPainterPath>
        #include <QString>
        
        bool parsePathDataFast(const QStringRef &dataStr, QPainterPath &path);
        
        #endif // PARSEPATHDATAFAST_H
        

        代码parsepathdatafast.cpp

        #include <QtCore/qmath.h>
        #include <QtMath>
        #include <QChar>
        #include <QByteArray>
        #include <QMatrix>
        
        
        #include <parsepathdatafast.h>
        
        Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
        
        // '0' is 0x30 and '9' is 0x39
        static inline bool isDigit(ushort ch)
        {
            static quint16 magic = 0x3ff;
            return ((ch >> 4) == 3) && (magic >> (ch & 15));
        }
        
        static qreal toDouble(const QChar *&str)
        {
            const int maxLen = 255;//technically doubles can go til 308+ but whatever
            char temp[maxLen+1];
            int pos = 0;
        
            if (*str == QLatin1Char('-')) {
                temp[pos++] = '-';
                ++str;
            } else if (*str == QLatin1Char('+')) {
                ++str;
            }
            while (isDigit(str->unicode()) && pos < maxLen) {
                temp[pos++] = str->toLatin1();
                ++str;
            }
            if (*str == QLatin1Char('.') && pos < maxLen) {
                temp[pos++] = '.';
                ++str;
            }
            while (isDigit(str->unicode()) && pos < maxLen) {
                temp[pos++] = str->toLatin1();
                ++str;
            }
            bool exponent = false;
            if ((*str == QLatin1Char('e') || *str == QLatin1Char('E')) && pos < maxLen) {
                exponent = true;
                temp[pos++] = 'e';
                ++str;
                if ((*str == QLatin1Char('-') || *str == QLatin1Char('+')) && pos < maxLen) {
                    temp[pos++] = str->toLatin1();
                    ++str;
                }
                while (isDigit(str->unicode()) && pos < maxLen) {
                    temp[pos++] = str->toLatin1();
                    ++str;
                }
            }
        
            temp[pos] = '\0';
        
            qreal val;
            if (!exponent && pos < 10) {
                int ival = 0;
                const char *t = temp;
                bool neg = false;
                if(*t == '-') {
                    neg = true;
                    ++t;
                }
                while(*t && *t != '.') {
                    ival *= 10;
                    ival += (*t) - '0';
                    ++t;
                }
                if(*t == '.') {
                    ++t;
                    int div = 1;
                    while(*t) {
                        ival *= 10;
                        ival += (*t) - '0';
                        div *= 10;
                        ++t;
                    }
                    val = ((qreal)ival)/((qreal)div);
                } else {
                    val = ival;
                }
                if (neg)
                    val = -val;
            } else {
                bool ok = false;
                val = qstrtod(temp, 0, &ok);
            }
            return val;
        
        }
        
        static inline void parseNumbersArray(const QChar *&str, QVarLengthArray<qreal, 8> &points)
        {
            while (str->isSpace())
                ++str;
            while (isDigit(str->unicode()) ||
                   *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
                   *str == QLatin1Char('.')) {
        
                points.append(toDouble(str));
        
                while (str->isSpace())
                    ++str;
                if (*str == QLatin1Char(','))
                    ++str;
        
                //eat the rest of space
                while (str->isSpace())
                    ++str;
            }
        }
        
        /**
        static QVector<qreal> parsePercentageList(const QChar *&str)
        {
            QVector<qreal> points;
            if (!str)
                return points;
        
            while (str->isSpace())
                ++str;
            while ((*str >= QLatin1Char('0') && *str <= QLatin1Char('9')) ||
                   *str == QLatin1Char('-') || *str == QLatin1Char('+') ||
                   *str == QLatin1Char('.')) {
        
                points.append(toDouble(str));
        
                while (str->isSpace())
                    ++str;
                if (*str == QLatin1Char('%'))
                    ++str;
                while (str->isSpace())
                    ++str;
                if (*str == QLatin1Char(','))
                    ++str;
        
                //eat the rest of space
                while (str->isSpace())
                    ++str;
            }
        
            return points;
        }
        **/
        
        static void pathArcSegment(QPainterPath &path,
                                   qreal xc, qreal yc,
                                   qreal th0, qreal th1,
                                   qreal rx, qreal ry, qreal xAxisRotation)
        {
            qreal sinTh, cosTh;
            qreal a00, a01, a10, a11;
            qreal x1, y1, x2, y2, x3, y3;
            qreal t;
            qreal thHalf;
        
            sinTh = qSin(xAxisRotation * (M_PI / 180.0));
            cosTh = qCos(xAxisRotation * (M_PI / 180.0));
        
            a00 =  cosTh * rx;
            a01 = -sinTh * ry;
            a10 =  sinTh * rx;
            a11 =  cosTh * ry;
        
            thHalf = 0.5 * (th1 - th0);
            t = (8.0 / 3.0) * qSin(thHalf * 0.5) * qSin(thHalf * 0.5) / qSin(thHalf);
            x1 = xc + qCos(th0) - t * qSin(th0);
            y1 = yc + qSin(th0) + t * qCos(th0);
            x3 = xc + qCos(th1);
            y3 = yc + qSin(th1);
            x2 = x3 + t * qSin(th1);
            y2 = y3 - t * qCos(th1);
        
            path.cubicTo(a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
                         a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
                         a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
        }
        
        
        // the arc handling code underneath is from XSVG (BSD license)
        /*
         * Copyright  2002 USC/Information Sciences Institute
         *
         * Permission to use, copy, modify, distribute, and sell this software
         * and its documentation for any purpose is hereby granted without
         * fee, provided that the above copyright notice appear in all copies
         * and that both that copyright notice and this permission notice
         * appear in supporting documentation, and that the name of
         * Information Sciences Institute not be used in advertising or
         * publicity pertaining to distribution of the software without
         * specific, written prior permission.  Information Sciences Institute
         * makes no representations about the suitability of this software for
         * any purpose.  It is provided "as is" without express or implied
         * warranty.
         *
         * INFORMATION SCIENCES INSTITUTE DISCLAIMS ALL WARRANTIES WITH REGARD
         * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
         * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL INFORMATION SCIENCES
         * INSTITUTE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
         * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
         * OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
         * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
         * PERFORMANCE OF THIS SOFTWARE.
         *
         */
        static void pathArc(QPainterPath &path,
                            qreal               rx,
                            qreal               ry,
                            qreal               x_axis_rotation,
                            int         large_arc_flag,
                            int         sweep_flag,
                            qreal               x,
                            qreal               y,
                            qreal curx, qreal cury)
        {
            qreal sin_th, cos_th;
            qreal a00, a01, a10, a11;
            qreal x0, y0, x1, y1, xc, yc;
            qreal d, sfactor, sfactor_sq;
            qreal th0, th1, th_arc;
            int i, n_segs;
            qreal dx, dy, dx1, dy1, Pr1, Pr2, Px, Py, check;
        
            rx = qAbs(rx);
            ry = qAbs(ry);
        
            sin_th = qSin(x_axis_rotation * (M_PI / 180.0));
            cos_th = qCos(x_axis_rotation * (M_PI / 180.0));
        
            dx = (curx - x) / 2.0;
            dy = (cury - y) / 2.0;
            dx1 =  cos_th * dx + sin_th * dy;
            dy1 = -sin_th * dx + cos_th * dy;
            Pr1 = rx * rx;
            Pr2 = ry * ry;
            Px = dx1 * dx1;
            Py = dy1 * dy1;
            /* Spec : check if radii are large enough */
            check = Px / Pr1 + Py / Pr2;
            if (check > 1) {
                rx = rx * qSqrt(check);
                ry = ry * qSqrt(check);
            }
        
            a00 =  cos_th / rx;
            a01 =  sin_th / rx;
            a10 = -sin_th / ry;
            a11 =  cos_th / ry;
            x0 = a00 * curx + a01 * cury;
            y0 = a10 * curx + a11 * cury;
            x1 = a00 * x + a01 * y;
            y1 = a10 * x + a11 * y;
            /* (x0, y0) is current point in transformed coordinate space.
               (x1, y1) is new point in transformed coordinate space.
        
               The arc fits a unit-radius circle in this space.
            */
            d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
            sfactor_sq = 1.0 / d - 0.25;
            if (sfactor_sq < 0) sfactor_sq = 0;
            sfactor = qSqrt(sfactor_sq);
            if (sweep_flag == large_arc_flag) sfactor = -sfactor;
            xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
            yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
            /* (xc, yc) is center of the circle. */
        
            th0 = qAtan2(y0 - yc, x0 - xc);
            th1 = qAtan2(y1 - yc, x1 - xc);
        
            th_arc = th1 - th0;
            if (th_arc < 0 && sweep_flag)
                th_arc += 2 * M_PI;
            else if (th_arc > 0 && !sweep_flag)
                th_arc -= 2 * M_PI;
        
            n_segs = qCeil(qAbs(th_arc / (M_PI * 0.5 + 0.001)));
        
            for (i = 0; i < n_segs; i++) {
                pathArcSegment(path, xc, yc,
                               th0 + i * th_arc / n_segs,
                               th0 + (i + 1) * th_arc / n_segs,
                               rx, ry, x_axis_rotation);
            }
        }
        
        bool parsePathDataFast(const QStringRef &dataStr, QPainterPath &path)
        {
            qreal x0 = 0, y0 = 0;              // starting point
            qreal x = 0, y = 0;                // current point
            char lastMode = 0;
            QPointF ctrlPt;
            const QChar *str = dataStr.constData();
            const QChar *end = str + dataStr.size();
        
            while (str != end) {
                while (str->isSpace())
                    ++str;
                QChar pathElem = *str;
                ++str;
                QChar endc = *end;
                *const_cast<QChar *>(end) = 0; // parseNumbersArray requires 0-termination that QStringRef cannot guarantee
                QVarLengthArray<qreal, 8> arg;
                parseNumbersArray(str, arg);
                *const_cast<QChar *>(end) = endc;
                if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z'))
                    arg.append(0);//dummy
                const qreal *num = arg.constData();
                int count = arg.count();
                while (count > 0) {
                    qreal offsetX = x;        // correction offsets
                    qreal offsetY = y;        // for relative commands
                    switch (pathElem.unicode()) {
                    case 'm': {
                        if (count < 2) {
                            num++;
                            count--;
                            break;
                        }
                        x = x0 = num[0] + offsetX;
                        y = y0 = num[1] + offsetY;
                        num += 2;
                        count -= 2;
                        path.moveTo(x0, y0);
        
                         // As per 1.2  spec 8.3.2 The "moveto" commands
                         // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
                         // the subsequent pairs shall be treated as implicit 'lineto' commands.
                         pathElem = QLatin1Char('l');
                    }
                        break;
                    case 'M': {
                        if (count < 2) {
                            num++;
                            count--;
                            break;
                        }
                        x = x0 = num[0];
                        y = y0 = num[1];
                        num += 2;
                        count -= 2;
                        path.moveTo(x0, y0);
        
                        // As per 1.2  spec 8.3.2 The "moveto" commands
                        // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
                        // the subsequent pairs shall be treated as implicit 'lineto' commands.
                        pathElem = QLatin1Char('L');
                    }
                        break;
                    case 'z':
                    case 'Z': {
                        x = x0;
                        y = y0;
                        count--; // skip dummy
                        num++;
                        path.closeSubpath();
                    }
                        break;
                    case 'l': {
                        if (count < 2) {
                            num++;
                            count--;
                            break;
                        }
                        x = num[0] + offsetX;
                        y = num[1] + offsetY;
                        num += 2;
                        count -= 2;
                        path.lineTo(x, y);
        
                    }
                        break;
                    case 'L': {
                        if (count < 2) {
                            num++;
                            count--;
                            break;
                        }
                        x = num[0];
                        y = num[1];
                        num += 2;
                        count -= 2;
                        path.lineTo(x, y);
                    }
                        break;
                    case 'h': {
                        x = num[0] + offsetX;
                        num++;
                        count--;
                        path.lineTo(x, y);
                    }
                        break;
                    case 'H': {
                        x = num[0];
                        num++;
                        count--;
                        path.lineTo(x, y);
                    }
                        break;
                    case 'v': {
                        y = num[0] + offsetY;
                        num++;
                        count--;
                        path.lineTo(x, y);
                    }
                        break;
                    case 'V': {
                        y = num[0];
                        num++;
                        count--;
                        path.lineTo(x, y);
                    }
                        break;
                    case 'c': {
                        if (count < 6) {
                            num += count;
                            count = 0;
                            break;
                        }
                        QPointF c1(num[0] + offsetX, num[1] + offsetY);
                        QPointF c2(num[2] + offsetX, num[3] + offsetY);
                        QPointF e(num[4] + offsetX, num[5] + offsetY);
                        num += 6;
                        count -= 6;
                        path.cubicTo(c1, c2, e);
                        ctrlPt = c2;
                        x = e.x();
                        y = e.y();
                        break;
                    }
                    case 'C': {
                        if (count < 6) {
                            num += count;
                            count = 0;
                            break;
                        }
                        QPointF c1(num[0], num[1]);
                        QPointF c2(num[2], num[3]);
                        QPointF e(num[4], num[5]);
                        num += 6;
                        count -= 6;
                        path.cubicTo(c1, c2, e);
                        ctrlPt = c2;
                        x = e.x();
                        y = e.y();
                        break;
                    }
                    case 's': {
                        if (count < 4) {
                            num += count;
                            count = 0;
                            break;
                        }
                        QPointF c1;
                        if (lastMode == 'c' || lastMode == 'C' ||
                            lastMode == 's' || lastMode == 'S')
                            c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
                        else
                            c1 = QPointF(x, y);
                        QPointF c2(num[0] + offsetX, num[1] + offsetY);
                        QPointF e(num[2] + offsetX, num[3] + offsetY);
                        num += 4;
                        count -= 4;
                        path.cubicTo(c1, c2, e);
                        ctrlPt = c2;
                        x = e.x();
                        y = e.y();
                        break;
                    }
                    case 'S': {
                        if (count < 4) {
                            num += count;
                            count = 0;
                            break;
                        }
                        QPointF c1;
                        if (lastMode == 'c' || lastMode == 'C' ||
                            lastMode == 's' || lastMode == 'S')
                            c1 = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
                        else
                            c1 = QPointF(x, y);
                        QPointF c2(num[0], num[1]);
                        QPointF e(num[2], num[3]);
                        num += 4;
                        count -= 4;
                        path.cubicTo(c1, c2, e);
                        ctrlPt = c2;
                        x = e.x();
                        y = e.y();
                        break;
                    }
                    case 'q': {
                        if (count < 4) {
                            num += count;
                            count = 0;
                            break;
                        }
                        QPointF c(num[0] + offsetX, num[1] + offsetY);
                        QPointF e(num[2] + offsetX, num[3] + offsetY);
                        num += 4;
                        count -= 4;
                        path.quadTo(c, e);
                        ctrlPt = c;
                        x = e.x();
                        y = e.y();
                        break;
                    }
                    case 'Q': {
                        if (count < 4) {
                            num += count;
                            count = 0;
                            break;
                        }
                        QPointF c(num[0], num[1]);
                        QPointF e(num[2], num[3]);
                        num += 4;
                        count -= 4;
                        path.quadTo(c, e);
                        ctrlPt = c;
                        x = e.x();
                        y = e.y();
                        break;
                    }
                    case 't': {
                        if (count < 2) {
                            num += count;
                            count = 0;
                            break;
                        }
                        QPointF e(num[0] + offsetX, num[1] + offsetY);
                        num += 2;
                        count -= 2;
                        QPointF c;
                        if (lastMode == 'q' || lastMode == 'Q' ||
                            lastMode == 't' || lastMode == 'T')
                            c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
                        else
                            c = QPointF(x, y);
                        path.quadTo(c, e);
                        ctrlPt = c;
                        x = e.x();
                        y = e.y();
                        break;
                    }
                    case 'T': {
                        if (count < 2) {
                            num += count;
                            count = 0;
                            break;
                        }
                        QPointF e(num[0], num[1]);
                        num += 2;
                        count -= 2;
                        QPointF c;
                        if (lastMode == 'q' || lastMode == 'Q' ||
                            lastMode == 't' || lastMode == 'T')
                            c = QPointF(2*x-ctrlPt.x(), 2*y-ctrlPt.y());
                        else
                            c = QPointF(x, y);
                        path.quadTo(c, e);
                        ctrlPt = c;
                        x = e.x();
                        y = e.y();
                        break;
                    }
                    case 'a': {
                        if (count < 7) {
                            num += count;
                            count = 0;
                            break;
                        }
                        qreal rx = (*num++);
                        qreal ry = (*num++);
                        qreal xAxisRotation = (*num++);
                        qreal largeArcFlag  = (*num++);
                        qreal sweepFlag = (*num++);
                        qreal ex = (*num++) + offsetX;
                        qreal ey = (*num++) + offsetY;
                        count -= 7;
                        qreal curx = x;
                        qreal cury = y;
                        pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
                                int(sweepFlag), ex, ey, curx, cury);
        
                        x = ex;
                        y = ey;
                    }
                        break;
                    case 'A': {
                        if (count < 7) {
                            num += count;
                            count = 0;
                            break;
                        }
                        qreal rx = (*num++);
                        qreal ry = (*num++);
                        qreal xAxisRotation = (*num++);
                        qreal largeArcFlag  = (*num++);
                        qreal sweepFlag = (*num++);
                        qreal ex = (*num++);
                        qreal ey = (*num++);
                        count -= 7;
                        qreal curx = x;
                        qreal cury = y;
                        pathArc(path, rx, ry, xAxisRotation, int(largeArcFlag),
                                int(sweepFlag), ex, ey, curx, cury);
        
                        x = ex;
                        y = ey;
                    }
                        break;
                    default:
                        return false;
                    }
                    lastMode = pathElem.toLatin1();
                }
            }
            return true;
        }
        

        一个问题,我在标准 qt 标头中找不到 Q_PI 常量,我将其替换为 M_PI 希望没问题!!

        【讨论】:

          猜你喜欢
          • 2011-03-10
          • 1970-01-01
          • 2013-06-07
          • 2016-10-05
          • 2016-02-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-06-15
          相关资源
          最近更新 更多