Qt 圆角绘制( 修改QPainterPath )

已知三点计算角度

已知三点,和设置半径,得到满足的半径

已知两点AB和离点A长度,得到两点间第三点C的坐标

已知三点,半径得到内切圆圆心

已知三点,得到角顺时针还是逆时针(与输入关系有关)

思路: 由路径遍历两条线是否直线 (lineto 或者CurveTo的c1,c2两个控制点是一个点),然后得到三个点,接着由三个点算出夹角,以及相切点F4,F5, 然后连接焦点,然后通过圆心定位矩形绘制弧线,连接F5得到一个弧度角,以此类推,得到弧线图形路径

以下不是最优解,内容供参考讨论

代码实现:

    //选中图像设置圆角(未过滤 使用前需要调用 checkSelectShapesEnableCornerRadius()限定了输入框)
    QPainterPath getShapePathWithRadius(QPainterPath path,int _radius);

    //计算角度
    qreal calculateAngle(const QPointF& p1, const QPointF& p2, const QPointF& p3);

    //得到角度radius
    qreal calculateRadius(const QPointF& p1, const QPointF& p2, const QPointF& p3 ,int value);

    //已知两点, 截取长度 得到 截取点(p1为被截点)
    QPointF calculateInterceptPoint (QPointF p1, QPointF p2, qreal len , bool &flag);

    //得到圆心 p2是顶点
    QPointF calculateCenter (QPointF p1,QPointF p2,QPointF p3,int radius, qreal angle , bool &flag);

    //检查曲线辅助点是否是同一个点,如果是,则判断为可以画角
    bool checkSamePoint(QPainterPath::Element elem1 ,QPainterPath::Element elem2);

    //三点判断角是否是顺时针
    int checkAngle(QPointF p1,QPointF p2,QPointF p3);

//选中图像设置圆角(未过滤 使用前需要调用 checkSelectShapesEnableCornerRadius()限定了输入框)
QPainterPath getShapePathWithRadius(QPainterPath path,int _radius);

 if(_radius == 0){
        return path;
    }
    QPainterPath roundedPath;

    // QPointF startPoint;


    QPainterPath pathOrigin =  path ;
    // qDebug()<<"pathOrigin::"<<pathOrigin<<"_radius"<<_radius;
    // const char *types[] = {"MoveTo", "LineTo", "CurveTo", "CurveToData"};
    for (int i = 0; i < pathOrigin.elementCount(); ++i) {
        QPainterPath::Element elem = pathOrigin.elementAt(i);
        // qDebug()<<endl<<"elem::"<<types[elem.type] <<
            // "(x=" << elem.x << ", y=" << elem.y << ')' << Qt::endl;

        if(elem.type == 3){//"CurveToData"
            continue;
        }



        //第一个角 moveto =>lineto
        if( i== 0 && elem.type == 0)
        {
            // qDebug()<<""<<pathOrigin.elementAt(i+1).type;
            if(i+1<path.elementCount()){
                if(pathOrigin.elementAt(i+1).type != 2){

                }else{
                    continue;
                }
            }else{
                return path;
            }

            QPainterPath::Element elem0 = pathOrigin.elementAt(pathOrigin.elementCount()-2);
            QPainterPath::Element elem1 = pathOrigin.elementAt(i);
            QPainterPath::Element elem2 = pathOrigin.elementAt(i+1);

            // qDebug()<<elem0;
            // qDebug()<<elem1;
            // qDebug()<<elem2;

            qreal angle = calculateAngle(QPointF(elem0),QPointF(elem1),QPointF(elem2))/2;
            qreal radius = calculateRadius(QPointF(elem0),QPointF(elem1),QPointF(elem2),_radius);
            bool flag =true;
            QPointF center = calculateCenter(QPointF(elem0),QPointF(elem1),QPointF(elem2),radius,angle,flag);
            if(!flag){
                break ;
            }

            double len;
            QPointF p4;//圆切点1
            QPointF p5;//圆切点2
            len = radius/tan(angle * M_PI / 180.0);
            //求切割点
            p4 = calculateInterceptPoint(QPointF(elem1),QPointF(elem0),len,flag);
            p5 = calculateInterceptPoint(QPointF(elem1),QPointF(elem2),len,flag);
            if(!flag){
                break ;
            }
            qreal startangle = getLineInclinationAngle(QLine(center.x(),center.y(),p4.x(),p4.y()));
            int abs_angle = checkAngle(QPointF(elem0),QPointF(elem1),QPointF(elem2));

            // startangle = fmod(startangle +360,360);
            // if(startangle>270){
            //     startangle -= 180;
            // }
            // roundedPath.moveTo(0, 0);
            // roundedPath.lineTo(0, -100);
            // roundedPath.arcMoveTo(0 , -100 - 25, 50, 50, 180);
            // roundedPath.arcTo(0     , -100 - 25, 50, 50, 180, -90);
            // roundedPath.lineTo(100, -100 - 25);

            if( i == 0 ){
            roundedPath.moveTo(p4.x(),p4.y());
            }

            roundedPath.lineTo(p4.x(),p4.y());
            // roundedPath.arcMoveTo(center.x()-radius ,center.y()-radius ,  2*radius, 2*radius, -startangle);
            roundedPath.arcTo(center.x()-radius ,center.y()-radius ,  2*radius, 2*radius, -startangle, -(180- angle*2) * abs_angle);
            continue;
        }
        //第2~n-2个角
        if( i > 0 &&  i<path.elementCount()-2 && elem.type == 1 )
        {
            QPainterPath::Element elem0 = pathOrigin.elementAt(i-1);
            QPainterPath::Element elem1 = pathOrigin.elementAt(i);
            QPainterPath::Element elem2 = pathOrigin.elementAt(i+1);

            // qDebug()<<elem0;
            // qDebug()<<elem1;
            // qDebug()<<elem2;

            qreal angle = calculateAngle(QPointF(elem0),QPointF(elem1),QPointF(elem2))/2;
            qreal radius = calculateRadius(QPointF(elem0),QPointF(elem1),QPointF(elem2),_radius);
            bool flag =true;
            QPointF center = calculateCenter(QPointF(elem0),QPointF(elem1),QPointF(elem2),radius,angle,flag);
            if(!flag){
                break ;
            }

            double len;
            QPointF p4;//圆切点1
            QPointF p5;//圆切点2
            len = radius/tan(angle * M_PI / 180.0);
            //求切割点
            p4 = calculateInterceptPoint(QPointF(elem1),QPointF(elem0),len,flag);
            p5 = calculateInterceptPoint(QPointF(elem1),QPointF(elem2),len,flag);
            int abs_angle = checkAngle(QPointF(elem0),QPointF(elem1),QPointF(elem2));
            if(!flag){
                break ;
            }

            qreal startangle = getLineInclinationAngle(QLineF(center.x(),center.y(),p4.x(),p4.y()));

            roundedPath.lineTo(p4.x(),p4.y() );
            // roundedPath.arcMoveTo(center.x()-radius ,center.y()-radius ,  2*radius, 2*radius, -startangle);
            roundedPath.arcTo(center.x()-radius ,center.y()-radius ,  2*radius, 2*radius, -startangle, -(180- angle*2) * abs_angle);

            continue;

        }

        //最后一个角
        if( i == path.elementCount()-2 && elem.type == 1 ){
            QPainterPath::Element elem0 = pathOrigin.elementAt(i-1);
            QPainterPath::Element elem1 = pathOrigin.elementAt(i);
            QPainterPath::Element elem2 = pathOrigin.elementAt(0);

            // qDebug()<<elem0;
            // qDebug()<<elem1;
            // qDebug()<<elem2;

            qreal angle = calculateAngle(QPointF(elem0),QPointF(elem1),QPointF(elem2))/2;
            qreal radius = calculateRadius(QPointF(elem0),QPointF(elem1),QPointF(elem2),_radius);
            bool flag =true;
            QPointF center = calculateCenter(QPointF(elem0),QPointF(elem1),QPointF(elem2),radius,angle,flag);
            if(!flag){
                break ;
            }

            double len;
            QPointF p4;//圆切点1
            QPointF p5;//圆切点2
            len = radius/tan(angle * M_PI / 180.0);
            //求切割点
            p4 = calculateInterceptPoint(QPointF(elem1),QPointF(elem0),len,flag);
            p5 = calculateInterceptPoint(QPointF(elem1),QPointF(elem2),len,flag);
            int abs_angle = checkAngle(QPointF(elem0),QPointF(elem1),QPointF(elem2));
            if(!flag){
                break ;
            }

            qreal startangle = getLineInclinationAngle(QLine(center.x(),center.y(),p4.x(),p4.y()));

            roundedPath.lineTo(p4.x(),p4.y() );
            // roundedPath.arcMoveTo(center.x()-radius ,center.y()-radius ,  2*radius, 2*radius, -startangle);
            roundedPath.arcTo(center.x()-radius ,center.y()-radius ,  2*radius, 2*radius, -startangle, -(180- angle*2) * abs_angle);
            roundedPath.lineTo(roundedPath.elementAt(0));

            continue;
        }

        if(elem.type == 2){
            if(i+5<path.elementCount())
            {
                if( checkSamePoint(path.elementAt(i+1) , path.elementAt(i+2)) &&
                    checkSamePoint(path.elementAt(i+4) , path.elementAt(i+5))){
                    QPainterPath::Element elem0 = pathOrigin.elementAt(i);
                    QPainterPath::Element elem1 = pathOrigin.elementAt(i+3);
                    QPainterPath::Element elem2 = pathOrigin.elementAt(i+5);

                    // qDebug()<<"CurveTo::"<<elem0;
                    // qDebug()<<"CurveTo::"<<elem1;
                    // qDebug()<<"CurveTo::"<<elem2;

                    qreal angle = calculateAngle(QPointF(elem0),QPointF(elem1),QPointF(elem2))/2;
                    qreal radius = calculateRadius(QPointF(elem0),QPointF(elem1),QPointF(elem2),_radius);
                    // qDebug()<<"radius::"<<radius;
                    bool flag =true;
                    QPointF center = calculateCenter(QPointF(elem0),QPointF(elem1),QPointF(elem2),radius,angle,flag);
                    if(!flag){
                        break ;
                    }

                    double len;
                    QPointF p4;//圆切点1
                    QPointF p5;//圆切点2
                    len = radius/tan(angle * M_PI / 180.0);
                    //求切割点
                    p4 = calculateInterceptPoint(QPointF(elem1),QPointF(elem0),len,flag);
                    p5 = calculateInterceptPoint(QPointF(elem1),QPointF(elem2),len,flag);
                    if(!flag){
                        break ;
                    }

                    qreal startangle = getLineInclinationAngle(QLine(center.x(),center.y(),p4.x(),p4.y()));

                    int abs_angle = checkAngle(QPointF(elem0),QPointF(elem1),QPointF(elem2));
                    if(i == 1){//不然始终由00点开始
                        roundedPath.moveTo(p4.x(),p4.y());

                    }

                    roundedPath.lineTo(p4.x(),p4.y()/*roundedPath.elementAt(roundedPath.elementCount())*/);
                    // roundedPath.arcMoveTo(center.x()-radius ,center.y()-radius ,  2*radius, 2*radius, -startangle);
                    roundedPath.arcTo(center.x()-radius ,center.y()-radius ,  2*radius, 2*radius, -startangle, -(180- angle*2) * abs_angle);

                    continue;
                }else{
                    if( checkSamePoint(path.elementAt(i+1) , path.elementAt(i+2))&&
                        !checkSamePoint(path.elementAt(i+4) , path.elementAt(i+5))  ){
                        if(i == 1){//不然始终由00点开始
                            roundedPath.moveTo(QPointF(path.elementAt(i+1)));
                        }
                    }else{
                        if(roundedPath.elementCount() > 1){
                            // qDebug()<<"roundedPath.elementCount()xx::"<<roundedPath.elementCount();
                            // roundedPath.moveTo(QPointF(/*path.elementAt(i)*/roundedPath.elementAt(roundedPath.elementCount()-1)));
                            roundedPath.cubicTo(QPointF(path.elementAt(i)),QPointF(path.elementAt(i+1)),QPointF(path.elementAt(i+2)));
                        }else{
                            roundedPath.moveTo(QPointF(path.elementAt(i-1)));
                            roundedPath.cubicTo(QPointF(path.elementAt(i)),QPointF(path.elementAt(i+1)),QPointF(path.elementAt(i+2)));
                            // return path;
                        }
                    }





                    continue;
                }
            }else if(i == path.elementCount()-3)
            {
                if( checkSamePoint(path.elementAt(i+1) , path.elementAt(i+2)) &&
                    checkSamePoint(path.elementAt(2) , path.elementAt(2 + 1))){
                    QPainterPath::Element elem0 = pathOrigin.elementAt(i);
                    QPainterPath::Element elem1 = pathOrigin.elementAt(i+2);
                    QPainterPath::Element elem2 = pathOrigin.elementAt(2);

                    // qDebug()<<"CurveTo::"<<elem0;
                    // qDebug()<<"CurveTo::"<<elem1;
                    // qDebug()<<"CurveTo::"<<elem2;

                    qreal angle = calculateAngle(QPointF(elem0),QPointF(elem1),QPointF(elem2))/2;
                    qreal radius = calculateRadius(QPointF(elem0),QPointF(elem1),QPointF(elem2),_radius);
                    bool flag =true;
                    QPointF center = calculateCenter(QPointF(elem0),QPointF(elem1),QPointF(elem2),radius,angle,flag);
                    if(!flag){
                        break ;
                    }

                    double len;
                    QPointF p4;//圆切点1
                    QPointF p5;//圆切点2
                    len = radius/tan(angle * M_PI / 180.0);
                    //求切割点
                    p4 = calculateInterceptPoint(QPointF(elem1),QPointF(elem0),len,flag);
                    p5 = calculateInterceptPoint(QPointF(elem1),QPointF(elem2),len,flag);
                    if(!flag){
                        break ;
                    }

                    qreal startangle = getLineInclinationAngle(QLine(center.x(),center.y(),p4.x(),p4.y()));

                    int abs_angle = checkAngle(QPointF(elem0),QPointF(elem1),QPointF(elem2));

                    roundedPath.lineTo(p4.x(),p4.y() );
                    // roundedPath.arcMoveTo(center.x()-radius ,center.y()-radius ,  2*radius, 2*radius, -startangle);
                    roundedPath.arcTo(center.x()-radius ,center.y()-radius ,  2*radius, 2*radius, -startangle, -(180- angle*2) * abs_angle);
                    roundedPath.lineTo(roundedPath.elementAt(0));
                    // roundedPath.closeSubpath();
                    // qDebug()<<"roundedPath::"<<roundedPath;


                    continue;
                }else{
                    if(roundedPath.elementCount() > 1){
                        roundedPath.lineTo(QPointF(/*path.elementAt(i)*/roundedPath.elementAt(roundedPath.elementCount()-1)));
                        roundedPath.cubicTo(QPointF(/*path.elementAt(i)*/roundedPath.elementAt(roundedPath.elementCount()-1)),QPointF(path.elementAt(i+1)),QPointF(path.elementAt(i+2)));
                        roundedPath.lineTo(roundedPath.elementAt(0));
                        continue;
                    }else{
                        return path;
                    }
                }
            }

            else{
                return path;
            }
        }

    }




    // qDebug()<<"roundedPath::"<<roundedPath;
    return roundedPath;

    //计算角度
    qreal calculateAngle(const QPointF& p1, const QPointF& p2, const QPointF& p3);

//132 123 3->2 2->3
qreal calculateAngle(const QPointF &p1, const QPointF &p2, const QPointF &p3)
{
    double theta = atan2(p1.x() - p2.x(), p1.y() - p2.y()) - atan2(p3.x() - p2.x(), p3.y() - p2.y());
    if (theta > M_PI)
        theta -= 2 * M_PI;
    if (theta < -M_PI)
        theta += 2 * M_PI;
    
    theta = abs(theta * 180.0 / M_PI);
    return theta;
}

    //得到角度radius
    qreal calculateRadius(const QPointF& p1, const QPointF& p2, const QPointF& p3 ,int value);

qreal calculateRadius(const QPointF &p1, const QPointF &p2, const QPointF &p3, int value)
{
    QPointF v1 = p1 - p2;
    QPointF v2 = p3 - p2;
    double mov1 = sqrt(v1.x()*v1.x() + v1.y()*v1.y());
    double mov2 = sqrt(v2.x()*v2.x() + v2.y()*v2.y());
    double cosAngle = (v1.x()*v2.x() + v1.y()*v2.y()) / (mov1*mov2);
    auto angle = acos(cosAngle) / M_PI * 180 /2;
    int radius = value;
    
    if(mov1 < 1 || mov2 < 1 || value < 1){
        return 0;
    }
    
    double tanValue = tan(angle * M_PI / 180.0);
    // if(angle > 45){
    //     tanValue = 1/tanValue;
    
    
    
    double shortsideOfMaxRadius = ((mov1 < mov2 ? mov1 : mov2) * tanValue);
    radius = value < shortsideOfMaxRadius/2 ? value : int(shortsideOfMaxRadius/2);
    
    // qDebug()<<"mov1::"<<mov1<<"mov2"<<mov2<<shortsideOfMaxRadius<<"angle::"<<angle<<"tanValue::"<<tanValue;
    // qDebug()<<"calculateRadius radius::"<<radius<<"input radius"<<value;
    return radius;
}

    //已知两点, 截取长度 得到 截取点(p1为被截点)
    QPointF calculateInterceptPoint (QPointF p1, QPointF p2, qreal len , bool &flag);

QPointF calculateInterceptPoint(QPointF p1, QPointF p2, qreal len, bool &flag)
{
    QPointF p3;
    double dx = p2.x() - p1.x();
    double dy = p2.y() - p1.y();
    double length = std::sqrt(dx * dx + dy * dy);
    double unit_dx = dx / length;
    double unit_dy = dy / length;
    
    // 计算点C的坐标
    p3.setX(p1.x() + len * unit_dx);
    p3.setY(p1.y() + len * unit_dy) ;
    
    // qDebug() << "Point C coordinates: (" << p3.x() << ", " << p3.y() << ")\n" <<len * unit_dx <<len * unit_dy;
    
    
    return p3;
}

    //得到圆心 p2是顶点
    QPointF calculateCenter (QPointF p1,QPointF p2,QPointF p3,int radius, qreal angle , bool &flag);

QPointF calculateCenter(QPointF p1, QPointF p2, QPointF p3, int radius, qreal angle, bool &flag)
{
    qreal angle1;
    qreal angle2;
    qreal centerAngle;
    qreal len;
    qreal dx;
    qreal dy;
    // y 轴方向是反的
    angle1 = getLineInclinationAngle(QLine(p2.x(),-p2.y(),p1.x(),-p1.y()));
    angle2 = getLineInclinationAngle(QLine(p2.x(),-p2.y(),p3.x(),-p3.y()));
    angle1 = fmod(angle1 + 360.0, 360.0);
    angle2 = fmod(angle2 + 360.0, 360.0);
    
    
    if(abs(angle1 - angle2)>180){
        centerAngle = (angle1 + angle2)/2 + 180;
    }else{
        centerAngle = (angle1 + angle2)/2;
    }
    
    len = radius/std::sin(angle *  M_PI / 180.0);
    dx = len * std::cos(centerAngle *  M_PI / 180.0);
    dy = len * std::sin(centerAngle *  M_PI / 180.0);
    // y 轴方向是反的
    QPointF p_Center(p2.x() + dx , p2.y() - dy);
    // qDebug()<<endl<<"calculateCenter success"<<p_Center<<"centerAngle::"<<centerAngle<<"angle1::"<<angle1<<"angle2::"<<angle2<<endl;
    
    return p_Center;
    
}

    //检查曲线辅助点是否是同一个点,如果是,则判断为可以画角
    bool checkSamePoint(QPainterPath::Element elem1 ,QPainterPath::Element elem2);

bool checkSamePoint(QPainterPath::Element elem1, QPainterPath::Element elem2)
{
    int value = 1;
    if(abs(elem1.x - elem2.x) > value){
        return false;
    }
    if(abs(elem1.y - elem2.y) > value){
        return false;
    }
    return true;
}

    //三点判断角是否是顺时针
    int checkAngle(QPointF p1,QPointF p2,QPointF p3);

int checkAngle(QPointF p1, QPointF p2, QPointF p3)
{
    double ans=(p1.x()-p2.x())*(p3.y()-p2.y())-(p1.y()-p2.y())*(p3.x()-p2.x());//表示向量AB与AC的叉积的结果
    if(ans>0)
        return -1;
    if(ans<0)
        return 1;
    if(ans==0)
        return 0;
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值