已知三点计算角度
已知三点,和设置半径,得到满足的半径
已知两点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;
}