[Geometría computacional] Juzgar si un segmento de línea se cruza con un arco y la implementación de código C++


I. Introducción

Recientemente trabajé en un proyecto y necesitaba juzgar si un segmento de línea se cruza con un arco circular, y no pude encontrar una buena respuesta en Internet (lo más importante es que no hay un código que se pueda usar directamente, o la idea es demasiado profunda, no puedo entenderla), así que decidí pensar en una manera, escribir un blog y compartir las ideas de implementación y el código completo


2. Representación de código de segmento de línea y arco

2.1 Representación de código de segmento de línea

Un segmento de línea se puede representar mediante dos puntos. El objeto de punto es el siguiente, que contiene información de las coordenadas x e y:

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

2.2 Representación del código de arco

Un arco consta de coordenadas centrales, radio, ángulos inicial y final:

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

3. Ideas de implementación y derivación matemática

3.1 El primer paso (juicio aproximado)

El primer paso (juicio aproximado): considere el segmento de línea como una línea recta y el arco como un círculo. Si el segmento de línea no interseca al círculo, entonces el segmento de línea y el arco no deben intersecar. De lo contrario, continúe con el siguiente paso de juicio

Primero, expanda el segmento de línea en una línea recta cuya ecuación es: y = kx + cy=kx+cy=k x+C

Según los dos puntos del segmento de recta (digamos p 1 p_1pag1y p 2 p_2pag2,且p 1 . X ≤ pags 2 . x p_1.x\le p_2.xpag1. Xpag2.x ) información, podemos encontrar fácilmente kkk yccvalor de c

p 1 p_1pag1y p 2 p_2pag2Sustituir en la ecuación de la recta:

{ pág. 1 . y = kp 1 . X + c ( 1 ) pags 2 . y = kp 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{casos}{ pag1. y=k pag1. X+C             ( 1 )pag2. y=k pag2. X+C             ( 2 )

( 1 ) − ( 2 ) (1)-(2)( 1 )( 2 ) disponible:

pág. 1 y - pags 2 . y = ( pags 1 . X - pags 2 . X ) k ⇒ k = pags 1 . y - pags 2 . sí 1 . X - pags 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} \ \ \ \ \ \ \ \ \ \ \ \ \ \izquierda( 3 \derecha)pag1. ypag2. y=( pag1. Xpag2. x )kk=pag1. Xpag2. Xpag1. ypag2. tu             ( 3 )

Voluntad ( 3 ) (3)( 3 ) en( 1 ) (1)( 1 ) disponible:

pág. 1 y = pag 1 . y - pags 2 . sí 1 . X - pags 2 . xp 1 . X + C ⇒ C = pags 1 . y - pags 1 . y - pags 2 . sí 1 . X - pags 2 . xp 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 \ \ \ \ \ \ \ \ \ \ \ \ \ \izquierda( 4 \derecha)pag1. y=pag1. Xpag2. Xpag1. ypag2. tupag1. X+CC=pag1. ypag1. Xpag2. Xpag1. ypag2. tupag1. X             ( 4 )

Supongamos que las coordenadas del centro del círculo son ( a , b ) (a,b)( un ,b ) con radiorrr , es fácil escribir la ecuación del círculo formado por la extensión del arco de la siguiente manera:

( x − a ) 2 + ( y − b ) 2 = r 2 ( 5 ) (xa)^2+(yb)^2=r^2 \ \ \ \ \ \ \ \ \ \ \ \ \ \left( 5 \derecha)( Xun )2+( ysegundo )2=r2             ( 5 )

Para determinar si una línea y un círculo se intersecan, debes combinar la ecuación de la línea y la ecuación del círculo para obtener:

{ y = kx + c ( x - una ) 2 + ( y - segundo ) 2 = r 2 ⇓ ( x - una ) 2 + ( kx + c - segundo ) 2 = r 2 , 令 re = c - segundo ⇓ x 2 + un 2 - 2 hacha + k 2 x 2 + re 2 + 2 kdx = r 2 ⇓ ( 1 + k 2 ) x 2 + ( 2 kd - 2 un ) x + un 2 + re 2 - r 2 = 0 ( 6 ) \begin{casos} y=kx+c \\ (xa)^2+(yb)^2=r^2 \\ \end{casos} \\ \Downarrow \\ (xa)^2+( kx+cb)^2=r^2,令d=cb \\ \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 \bien){ y=k x+C( Xun )2+( ysegundo )2=r2( Xun )2+( k x+Csegundo )2=r2 ,orden d=CbX2+a22 x _+k2x _2+d2+2 k d x=r2( 1+k2 )x2+( 2 k d2 a ) x+a2+d2r2=0             ( 6 )

Juicio de ecuación cuadrática en una variable según el teorema de Veda ( 6 ) (6)( 6 ) Si hay raíces reales:

Δ = ( 2 kd − 2 a ) 2 − 4 ( 1 + k 2 ) ( a 2 + d 2 − r 2 ) ⇒ { Δ < 0 : Fórmula ( 6 ) No hay raíz real Δ ≥ 0 : Fórmula ( 6 ) Hay raíces reales \varDelta=(2kd-2a)^2-4(1+k^2)(a^2+d^2-r^2) \Rightarrow \begin{cases} \varDelta<0: Formula (6 ) no tiene raíz real\\ \varDelta\ge0: Fórmula (6) tiene raíz real\end{casos}D=( 2 k d2a ) _24 ( 1+k2 )(un2+d2r2 ){ D<0:La ecuación ( 6 ) no tiene raíces realesD0:La ecuación ( 6 ) tiene raíces

Si la fórmula ( 6 ) (6)( 6 ) No hay raíz de número real, lo que significa que no hay punto de intersección entre la línea recta y el círculo. En este momento, no debe haber ningún punto de intersección entre el segmento de línea y el arco, y el programa finaliza.

Si la fórmula ( 6 ) (6)( 6 ) Si hay raíces reales, entoncesel XXCoordenadas de dirección X x 1 x_1X1y 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 re2a ) _+D  yx _ 2=2 ( 1+k2 )( 2 k re2a ) _D

poner x 1 x_1X1y x 2 x_2X2Sustituye en la ecuación de la línea recta y = kx + cy=kx+cy=k x+c puede obtener YYde dos puntos de intersecciónCoordenada de dirección Y y 1 y_1y1 y 2 y_2 y2

y 1 = kx 1 + c 和 y 2 = kx 2 + c y_1=kx_1+c \ 和\ y_2=kx_2+cy1=k x1+c e y  2=k x2+C

Luego proceda al siguiente paso del juicio.

3.2 El segundo paso

Paso 2: Determine si los dos puntos de intersección de la línea y el círculo están en el segmento de línea. Si no, significa que el segmento de línea y el arco no deben intersecarse. De lo contrario, continúe con el siguiente paso de juicio.

El segmento de línea es continuo, por lo que puede juzgar si dos puntos de intersección están en el segmento de línea a través del intervalo

En detalle, ya sabemos que el intervalo de la dirección del eje X del segmento de línea es [ p 1 . x , p 2 . x ] [p_1.x\ ,\ p_2.x][ pag1. x ,  pag2. x ]

si x 1 x_1X1no en [ p 1 . x , p 2 . x ] [p_1.x\ ,\ p_2.x][ pag1. x ,  pag2.x ] intervalo, luego punto ( x 1 , y 1 ) ( x_1,y_1)( X1,y1) no está en el segmento de línea, es imposible que sea el punto de intersección del segmento de línea y el arco

De manera similar, si x 2 x_2X2no en [ p 1 . x , p 2 . x ] [p_1.x\ ,\ p_2.x][ pag1. x ,  pag2.x ] intervalo, luego punto ( x 2 , y 2 ) ( x_2,y_2)( X2,y2) no puede ser el punto de intersección de un segmento de línea y un arco

Si el punto ( x 1 , y 1 ) (x_1,y_1)( X1,y1) punto de suma( x 2 , y 2 ) (x_2,y_2)( X2,y2) no es el punto de intersección del segmento de línea y el arco, significa que el segmento de línea y el arco no deben intersecarse

De lo contrario, continúe con el siguiente paso.

3.3 El tercer paso

Paso 3: De acuerdo con la derivación anterior, suponga que el punto de intersección de un segmento de recta de una recta conocida y un círculo es ( x 1 , y 1 ) (x_1,y_1)( X1,y1) , en este paso, es necesario juzgar si el punto está en el arco, si lo está, significa que el segmento de línea y el arco se intersecan, y el punto de intersección es ( x 1 , y 1 ) (x_1,y_1 )( X1,y1)

En este paso, se necesita usar la ecuación paramétrica del círculo. Por conveniencia, se asume que el ángulo inicial y el ángulo final del arco son θ 1 \ theta_1i1Suma θ 2 \theta_2i2, entonces la ecuación paramétrica del arco es:

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

( x 1 , y 1 ) (x_1,y_1) ( X1,y1) en la fórmula( 7 ) (7)( 7 ) y fórmula( 8 ) (8)En ( 8 ) :

{ X 1 = a + rcos θ y 1 = segundo + rsen θ , θ 1 ≤ θ ≤ θ 2 ⇓ { X 1 − a = rcos θ ( 9 ) y 1 − segundo = rsen θ ( 10 ) , θ 1 ≤ θ ≤ θ 2 \begin{casos} x_1=a+rcos\theta \\ y_1=b+rsen\theta \\ \end{casos} \ \ \ \ ,\theta_1\theta\theta_2 \\ \Downarrow \\ \begin {casos} x_1-a=rcos\theta \ \ \ \ \\left( 9 \right) \\ y_1-b=rsen\theta \ \ \ \ \\left( 10 \right) \\ \end{casos} \ \ \ \ ,\theta_1\y\theta\y\theta_2{ X1=a+rcos θy1=b+rs en θ    ,i1ii2{ X1a=rcos θ    ( 9 )y1b=rs en θ    ( 10 )    ,i1ii2

( 10 ) (10)( 10 )( 9 ) (9)( 9 ) puede obtener:

y 1 − bx 1 − a = tan θ ⇓ θ = arctan ( y 1 − bx 1 − a ) \frac{y_1-b}{x_1-a}=tan\theta \\ \Downarrow \\ \theta = arctan( \frac{y_1-b}{x_1-a})X1ay1segundo=t un θi=Estoy a salvo ( _X1ay1segundo)

Si el θ \theta resueltoθ no está en el intervalo[ θ 1 , θ 2 ] [\theta_1,\theta_2][ yo1,i2] , luego punto( x 1 , y 1 ) (x_1,y_1)( X1,y1) no está en el arco

De lo contrario, el punto ( x 1 , y 1 ) (x_1,y_1)( X1,y1) en el arco y es una intersección del segmento de línea y el arco

En este punto, ¡la prueba ha terminado!

Nota: use atan atanCuando la función a t an calcula el valor del arcotangente, el rango de valores del valor devuelto es[ − π 2 , π 2 ] [-\frac{\pi}{2},\frac{\pi}{2}][ -2pag,2pag] , yθ \thetaEl valor de θ es[ 0 , 2 π ] [0,2\pi][ 0 ,2 π ] , por lo que el ángulo debe convertirse en el código real


4. Código completo

// 圆周率
#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. Visualización de efectos

Después de tener una función para juzgar si un segmento de línea se cruza con un arco, se puede usar para juzgar si un arco se cruza con un polígono. La idea más simple es usar el arco y cada lado del polígono para hacer juicios a su vez. Si algún lado del polígono no interseca el arco, el arco y el polígono no deben intersecar.

Antes del juicio cruzado, como se puede ver en la figura a continuación, la parte amarilla es la intersección y la superposición del arco y el polígono.

inserte la descripción de la imagen aquí

Después del juicio de intersección, la intersección del arco y el polígono desaparece por completo ~

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_51545953/article/details/130419439
Recomendado
Clasificación