[Computational Geometry] Judging whether a line segment intersects with an arc & C++ code implementation


I. Introduction

I recently worked on a project and needed to judge whether a line segment intersects with a circular arc, and I couldn’t find a good answer on the Internet (the most important thing is that there is no code that can be directly used, or the idea is too deep, I can’t understand it), So I decided to think of a way, write a blog, and share the implementation ideas and complete code


2. Code representation of line segment and arc

2.1 Line segment code representation

A line segment can be represented by two points. The point object is as follows, which contains x and y coordinate information:

class Point {
    
    
public:
    Point(double px=0.0, double py=0.0) {
    
    
        x = px;
        y = py;
    }
    double x;
    double y;
};

2.2 Arc code representation

An arc consists of center coordinates, radius, start and end angles:

class Arc
{
    
    
public:
	Point centerpoint; // 圆心
	double radius; // 圆弧半径
	double bangle; // 起点角度
	double eangle; // 终点角度
};

3. Implementation ideas and mathematical derivation

3.1 The first step (rough judgment)

The first step (rough judgment): regard the line segment as a straight line and the arc as a circle. If the line segment does not intersect the circle, then the line segment and the arc must not intersect. Otherwise, proceed to the next step of judgment

First, expand the line segment into a straight line whose equation is: y = kx + cy=kx+cy=kx+c

According to the two points of the line segment (let's say p 1 p_1p1and p 2 p_2p2,且p 1 . x ≤ p 2 . x p_1.x\le p_2.xp1.xp2. x ) information, we can easily findkkk andccvalue of c

p 1 p_1p1and p 2 p_2p2Substitute into the equation of the line:

{ p 1 . y = k p 1 . x + c                  ( 1 ) p 2 . y = k p 2 . x + c                  ( 2 ) \begin{cases} p_1.y=kp_1.x+c\,\, \ \ \ \ \ \ \ \ \ \ \ \ \ \left( 1 \right)\\ p_2.y=kp_2.x+c\,\, \ \ \ \ \ \ \ \ \ \ \ \ \ \left( 2 \right) \end{cases} { p1.y=kp1.x+c             (1)p2.y=kp2.x+c             (2)

( 1 ) − ( 2 ) (1)-(2) (1)( 2 ) available:

p 1 . y − p 2 . y = ( p 1 . x − p 2 . x ) k ⇒ k = p 1 . y − p 2 . y p 1 . x − p 2 . x               ( 3 ) p_1.y-p_2.y=\left( p_1.x-p_2.x \right) k \\ \Rightarrow k=\frac{p_1.y-p_2.y}{p_1.x-p_2.x} \ \ \ \ \ \ \ \ \ \ \ \ \ \left( 3 \right) p1.yp2.y=(p1.xp2.x)kk=p1.xp2.xp1.yp2.y             (3)

Will ( 3 ) (3)( 3 ) into( 1 ) (1)( 1 ) available:

p 1 . y = p 1 . y − p 2 . y p 1 . x − p 2 . x p 1 . x + c ⇒ c = p 1 . y − p 1 . y − p 2 . y p 1 . x − p 2 . x p 1 . x               ( 4 ) p_1.y=\frac{p_1.y-p_2.y}{p_1.x-p_2.x}p_1.x+c \\ \Rightarrow c=p_1.y-\frac{p_1.y-p_2.y}{p_1.x-p_2.x}p_1.x \ \ \ \ \ \ \ \ \ \ \ \ \ \left( 4 \right) p1.y=p1.xp2.xp1.yp2.yp1.x+cc=p1.yp1.xp2.xp1.yp2.yp1.x             (4)

Suppose the coordinates of the center of the circle are ( a , b ) (a,b)(a,b ) with radiusrrr , it is easy to write the equation of the circle formed by the extension of the arc as follows:

( x − a ) 2 + ( y − b ) 2 = r 2               ( 5 ) (x-a)^2+(y-b)^2=r^2 \ \ \ \ \ \ \ \ \ \ \ \ \ \left( 5 \right) (xa)2+(yb)2=r2             (5)

To determine whether a line and a circle intersect, you need to combine the equation of the line and the equation of the circle to get:

{ y = k x + c ( x − a ) 2 + ( y − b ) 2 = r 2 ⇓ ( x − a ) 2 + ( k x + c − b ) 2 = r 2 , 令 d = c − b ⇓ x 2 + a 2 − 2 a x + k 2 x 2 + d 2 + 2 k d x = r 2 ⇓ ( 1 + k 2 ) x 2 + ( 2 k d − 2 a ) x + a 2 + d 2 − r 2 = 0               ( 6 ) \begin{cases} y=kx+c \\ (x-a)^2+(y-b)^2=r^2 \\ \end{cases} \\ \Downarrow \\ (x-a)^2+(kx+c-b)^2=r^2,令d=c-b \\ \Downarrow \\ x^2+a^2-2ax+k^2x^2+d^2+2kdx=r^2 \\ \Downarrow \\ (1+k^2)x^2+(2kd-2a)x+a^2+d^2-r^2=0 \ \ \ \ \ \ \ \ \ \ \ \ \ \left( 6 \right) { y=kx+c(xa)2+(yb)2=r2(xa)2+(kx+cb)2=r2,order d=cbx2+a22ax+k2x _2+d2+2kdx=r2(1+k2)x2+( 2 k d2a)x+a2+d2r2=0             (6)

Judgment of Quadratic Equation in One Variable According to Veda's Theorem ( 6 ) (6)( 6 ) Whether there are real roots:

Δ = ( 2 kd − 2 a ) 2 − 4 ( 1 + k 2 ) ( a 2 + d 2 − r 2 ) ⇒ { Δ < 0 : Formula ( 6 ) There is no real root Δ ≥ 0 : Formula ( 6 ) There are real roots \varDelta=(2kd-2a)^2-4(1+k^2)(a^2+d^2-r^2) \Rightarrow \begin{cases} \varDelta<0: Formula (6 ) does not have a real number root\\ \varDelta\ge0: Formula (6) has a real number root\end{cases}D=( 2 k d2 a )24(1+k2)(a2+d2r2){ D<0:Equation ( 6 ) has no real rootsD0:Equation ( 6 ) has real roots

If formula ( 6 ) (6)( 6 ) There is no real number root, which means that there is no intersection point between the straight line and the circle. At this time, there must be no intersection point between the line segment and the arc, and the program ends.

If formula ( 6 ) (6)( 6 ) If there are real roots, thenthe XXX direction coordinatesx 1 x_1x1and x 2 x_2x2

x 1 = − ( 2 kd − 2 a ) + Δ 2 ( 1 + k 2 ) 和 x 2 = − ( 2 kd − 2 a ) − Δ 2 ( 1 + k 2 ) x_1=\frac{-(2kd- 2a)+\sqrt{\varDelta}}{2(1+k^2)}\and \x_2=\frac{-(2kd-2a)-\sqrt{\varDelta}}{2(1+k^2 )}x1=2(1+k2)( 2 k d2 a )+D  and x 2=2(1+k2)( 2 k d2 a )D

put x 1 x_1x1and x 2 x_2x2Substitute into the straight line equation y = kx + cy=kx+cy=kx+c can get YYof two intersection pointsY direction coordinatey 1 y_1y1y 2 y_2y2

y 1 = kx 1 + c 和 y 2 = kx 2 + c y_1=kx_1+c \ 和\ y_2=kx_2+cy1=kx1+c and y  2=kx2+c

Then proceed to the next step of judgment

3.2 The second step

Step 2: Determine whether the two intersection points of the line and the circle are on the line segment. If not, it means that the line segment and the arc must not intersect. Otherwise, proceed to the next step of judgment

The line segment is continuous, so you can judge whether two intersection points are on the line segment through the interval

In detail, we already know that the interval of the X-axis direction of the line segment is [ p 1 . x , p 2 . x ] [p_1.x\ ,\ p_2.x][p1.x , p2.x]

if x 1 x_1x1not in [ p 1 . x , p 2 . x ] [p_1.x\ ,\ p_2.x][p1.x , p2. x ] interval, then point( x 1 , y 1 ) (x_1,y_1)(x1,y1) is not on the line segment, it is impossible to be the intersection point of the line segment and the arc

Similarly, if x 2 x_2x2not in [ p 1 . x , p 2 . x ] [p_1.x\ ,\ p_2.x][p1.x,  p2. x ] interval, then point( x 2 , y 2 ) (x_2,y_2)(x2,y2) cannot be the intersection point of a line segment and an arc

If point ( x 1 , y 1 ) (x_1,y_1)(x1,y1) sum point( x 2 , y 2 ) (x_2,y_2)(x2,y2) is not the intersection point of the line segment and the arc, it means that the line segment and the arc must not intersect

Otherwise, proceed to the next step

3.3 The third step

Step 3: According to the previous derivation, assume that the intersection point of a line segment of a known line and circle is ( x 1 , y 1 ) (x_1,y_1)(x1,y1) , in this step, it is necessary to judge whether the point is on the arc, if it is, it means that the line segment and the arc intersect, and the intersection point is( x 1 , y 1 ) (x_1,y_1)(x1,y1)

In this step, the parametric equation of the circle needs to be used. For the sake of convenience, it is assumed that the starting angle and ending angle of the arc are θ 1 \theta_1i1Sum θ 2 \theta_2i2, then the parametric equation of the arc is:

{ x = a + rcos θ ( 7 ) y = b + rsin θ ( 8 ) , θ 1 ≤ θ ≤ θ 2 \begin{cases} x=a+rcos\theta \ \ \ \ \left( 7 \right) \\ y=b+rsin\theta \ \ \ \ \left( 8 \right) \\ \end{cases} \ \ \ \ ,\theta_1\and\theta\and\theta_2{ x=a+rcosθ    (7)y=b+rsinθ    (8)    ,i1ii2

( x 1 , y 1 ) (x_1,y_1)(x1,y1) into formula( 7 ) (7)( 7 ) and formula( 8 ) (8)In ( 8 ) :

{ x 1 = a + rcos θ and 1 = b + rsin θ , θ 1 ≤ θ ≤ θ 2 ⇓ { x 1 − a = rcos θ ( 9 ) and 1 − b = rsin θ ( 10 ) , θ 1 ≤ θ ≤ θ 2 \begin{cases} x_1=a+rcos\theta \\ y_1=b+rsin\theta \\ \end{cases} \ \ \ \ ,\theta_1\theta\theta_2 \\ \Downarrow \\ \begin{cases} x_1-a=rcos\theta \ \ \ \ \\left( 9 \right) \\ y_1-b=rsin\theta \ \ \ \ \\left( 10 \right) \\ \end{ cases} \ \ \ \ ,\theta_1\and\theta\and\theta_2{ x1=a+rcosθy1=b+rsinθ    ,i1ii2{ x1a=rcosθ    (9)y1b=rsinθ    (10)    ,i1ii2

( 10 ) (10) (10) ( 9 ) (9) ( 9 ) can get:

y 1 − b x 1 − a = t a n θ ⇓ θ = a r c t a n ( y 1 − b x 1 − a ) \frac{y_1-b}{x_1-a}=tan\theta \\ \Downarrow \\ \theta = arctan(\frac{y_1-b}{x_1-a}) x1ay1b=tanθi=I 'm safe ( _x1ay1b)

If the solved θ \thetaθ is not in the interval[ θ 1 , θ 2 ] [\theta_1,\theta_2][ i1,i2] , then point( x 1 , y 1 ) (x_1,y_1)(x1,y1) is not on the arc

Otherwise, the point ( x 1 , y 1 ) (x_1,y_1)(x1,y1) on the arc and is an intersection of the line segment and the arc

At this point, the proof is over!

Note: use atan atanWhen the a t an function calculates the arctangent value, the value range of the returned value is[ − π 2 , π 2 ] [-\frac{\pi}{2},\frac{\pi}{2}][2p,2p] , andθ \thetaThe value of θ is[ 0 , 2 π ] [0,2\pi][0,2 π ] , so the angle needs to be converted in the actual code


4. Complete code

// 圆周率
#define PI acos(-1)
// 浮点型数据精度
#define ERROR 0.0000001

// lineIsIntersectArc 的辅助函数:接着第二步判断
bool lineIsIntersectArcAuxiliaryFunction(Point p1, Point p2, double a, double b, double theta1, double theta2, double x1, double y1){
    
    
	// 2.3 判断交点是否在线段上
	if (p1.x <= x1 && x1 <= p2.x){
    
    
		// 第三步:交点(x1,y1)在线段上,再判断该点是否在圆弧上,如果在,则说明线段和圆弧相交,且交点为(x1,y1)
		// 3,1 计算 theta ,并转化为角度值
		double theta = atan((y1 - b) / (x1 - a)) / PI * 180.0;
		// 3.2 修正角度值,确保 theta1 <= theta2
		if (theta1 > theta2){
    
    
			if (theta2 >= 0){
    
    
				theta1 -= 360;
			}
			else{
    
    
				theta2 += 360;
			}			
		}
		if (theta1 < 0 && theta2 < 0){
    
    
			theta1 += 360;
			theta2 += 360;
		}
		// 3.3 修正 tan 的角度
		if (x1 < a){
    
    
			theta += 180;
		}
		// 3.4 在端点时,由于精度误差,有时可能会出现判断出错 (即出现 10.000000001 > 10 的情况),故以极小的误差接受(x1,y1)为圆弧的某个端点
		if (abs(theta1 - theta) < ERROR || abs(theta2 - theta) < ERROR){
    
    
			return true;
		}
		// 3.5 判断 theta 是否在圆弧范围内,如果在则(x1,y1)是线段和圆弧的交点,否则不是
		return theta1 <= theta && theta <= theta2;
	}
	return false;
}

// 判断一个线段是否和圆弧相交
bool lineIsIntersectArc(Point p1, Point p2, Arc arc){
    
    
	// 确保 p1.x <= p2.x
	if (p1.x > p2.x){
    
    
		DL_VertexData temp = p1;
		p1 = p2;
		p2 = temp;
	}
	// 简化圆弧的相关变量表示
	double a = arc.centerpoint.x;
	double b = arc.centerpoint.y;
	double r = arc.radius;
	double theta1 = arc.bangle;
	double theta2 = arc.eangle;
	// 第一步(粗略判断):将线段当成直线,将圆弧当成圆,如果直线和圆不相交,则线段和圆弧必然不相交,否则进行下一步判断
	// 1.1 根据公式,计算直线方程 y=kx+c 中的 k 和 c
	double k = (p1.y - p2.y) / (p1.x - p2.x);
	double c = p1.y - (p1.y - p2.y) / (p1.x - p2.x)*p1.x;
	// 1.2 根据韦达定理判断式(6)是否存在实数根
	double d = c - b;
	double varDelta = pow(2 * k*d-2*a, 2) - 4 * (1 + k*k)*(a*a + d*d - r*r);
	if (varDelta >= 0){
    
    
		// 第二步:判断直线和圆的两个交点是否在线段上,如果不在,说明线段和圆弧必然不相交,否则进行下一步判断
		// 2.1 计算两个交点的坐标(x1,y1)和(x2,y2)
		double x1 = (2 * a - 2 * k*d + sqrt(varDelta)) / (2 * (1 + k*k));
		double x2 = (2 * a - 2 * k*d - sqrt(varDelta)) / (2 * (1 + k*k));
		double y1 = k*x1 + c;
		double y2 = k*x2 + c;
		// 2.2 判断两个交点是否相等
		if (varDelta == 0){
    
    
			// 两个交点相等,只需要判断一个就好
			return lineIsIntersectArcAuxiliaryFunction(p1, p2, a, b, theta1, theta2, x1, y1);
		}
		else{
    
    
			// 两个交点不相等,分别进行判断,只要其中一个是线段和圆弧的交点就返回 true
			return lineIsIntersectArcAuxiliaryFunction(p1, p2, a, b, theta1, theta2, x1, y1) || lineIsIntersectArcAuxiliaryFunction(p1, p2, a, b, theta1, theta2, x2, y2);
		}
	}
	return false;
}

5. Effect display

After having a function for judging whether a line segment intersects with an arc, it can be used to judge whether an arc intersects with a polygon. The simplest idea is to use the arc and each side of the polygon to make judgments in turn. If any side of the polygon does not intersect the arc, the arc and the polygon must not intersect.

Before the cross judgment, as can be seen from the figure below, the yellow part is the intersection and overlap of the arc and the polygon

insert image description here

After the intersection judgment, the intersection of the arc and the polygon completely disappears~

insert image description here

Guess you like

Origin blog.csdn.net/weixin_51545953/article/details/130419439