合肥工业大学机器人技术作业:求直线与直线、圆、矩形交点坐标(C++)

使用C++求直线与直线、圆、矩形交点坐标

  • 几何类的编写
    • 矩形
    • 直线
  • 总结

几何类的编写

没什么特别的,利用了圆的标准公式来表示圆

class Circle {
private:
	double x_center_param; /* 圆心的 x 坐标值 */
	double y_center_param; /* 圆心的 y 坐标值 */
	double r_param; /* 半径 */
public:
	explicit Circle(double x0 = 0.0, double y0 = 0.0, double r = 1.0);
	double* getCenterPos(); /* 取参数 x0 y0 */
	double getRadius(); /* 取参数 r */
	vector<double> getValue(double x); /* 根据输入的 x 值返回 y 值 */
};

前面返回参数的方法过于简单就不介绍了,返回y值的方法要讨论几种情况,略复杂一些,实际也很简单

// 返回y的取值
vector<double> Circle::getValue(double x) {
	vector<double> vec;
	if (x < this->x_center_param - this->r_param || x > this->x_center_param + this->r_param) {
		return vec;
	}
	else {
		double y1 = sqrt(pow(this->r_param, 2) - pow(x - this->x_center_param, 2)) + this->y_center_param;
		double y2 = -sqrt(pow(this->r_param, 2) - pow(x - this->x_center_param, 2)) + this->y_center_param;
		if (y1 == y2) {
			vec.push_back(y1);
			return vec;
		}
		else {
			vec.push_back(y1);
			vec.push_back(y2);
			return vec;
		}
	}
}

矩形

矩形实际是4条直线围成的一个图形,这里简化表示,用矩形左上角和右下角的坐标来表示一个矩形

class Rectangle {
private:
	double leftUpPoint[2]; /* 矩形左上角的坐标 */
	double rightDownPoint[2]; /* 矩形右下角的坐标 */
public:
	explicit Rectangle(double x0, double y0, double x1, double y1);
	double* getLeftUpPointPos(); /* 返回矩形左上角的坐标 */
	double* getRightDownPointPos(); /* 返回矩形右下角的坐标 */
	vector<double> getXValue(double y); /* 通过输入的 y 值来取 x 的值 */
	vector<double> getYValue(double x); /* 通过输入的 x 值来取 y 的值 */
};

为了方便求交点,定义了两个访问矩形上的点的函数,其实思路是一样的

// 返回x的取值
vector<double> Rectangle::getXValue(double y) {
	vector<double> vec;
	if (y <= this->leftUpPoint[1] || y >= this->rightDownPoint[1]) {
		return vec;
	}
	else {
		vec.push_back(this->leftUpPoint[0]);
		vec.push_back(this->rightDownPoint[0]);
		return vec;
	}
}

// 返回y的取值
vector<double> Rectangle::getYValue(double x) {
	vector<double> vec;
	if (x <= this->leftUpPoint[0] || x >= this->rightDownPoint[0]) {
		return vec;
	}
	else {
		vec.push_back(this->leftUpPoint[1]);
		vec.push_back(this->rightDownPoint[1]);
		return vec;
	}
}

直线

为了能够表示所有直线,我们选择使用直线的一般式 ax+by+c=0 来表示直线

class Line {
private:
	/* 直线的 3 个参数 a b c */
	double a_param;
	double b_param;
	double c_param;
public:
	explicit Line(double a, double b, double c = 0); /* 构造函数 */
	double* getParameters(); /* 取参数 a b c */
	double getValue(double x); /* 根据输入的 x 值返回 y 值 */
	double* getIntersection(Line* B); /* 求直线与直线的交点 */
	vector<double*> getIntersection(Circle* C); /* 求直线与圆的交点 */
	vector<double*> getIntersection(Rectangle* R); /* 求直线与矩形的交点 */
};

前面几个方法没什么好说的,很容易理解,重点是求交点的方法,这里重载了求交点的函数,直线与直线要么有一个交点,要么没有交点,因此用一个指针来返回交点的信息
其中用到二分法来求交点,因为直接用中定义的DBL_MAX和DBL_MIN来计算会非常非常久,毕竟二分法是通过不断取中点对应的值来验证是否满足需求的,所以我选择自定义两个宏FIND_MAX和FIND_MIN

// 以指针的形式返回直线与直线的交点
double* Line::getIntersection(Line* B) {
	// 此时A直线没有斜率
	if (this->b_param == 0) {
		// 此时B直线没有斜率,两直线平行,则没有交点,返回空指针
		if (B->b_param == 0) {
			return nullptr;
		}
		// 此时B直线有斜率,两直线必有交点
		else {
			double x = -(this->c_param / this->a_param);
			double y = B->getValue(x);
			double* point = new double[2];
			point[0] = x;
			point[1] = y;
			return point;
		}
	} 
	// 此时A直线有斜率
	else {
		// 此时B直线没有斜率,两直线必有交点
		if (B->b_param == 0) {
			double x = -(B->c_param / B->a_param);
			double y = this->getValue(x);
			double* point = new double[2];
			point[0] = x;
			point[1] = y;
			return point;
		}
		// 此时B直线有斜率
		else {
			double thisK = -(this->a_param / this->b_param);
			double BK = -(B->a_param / B->b_param);
			// 如果两直线的斜率相同,表明两直线平行,
			// 则不会有交点,返回空指针
			if (thisK == BK) {
				return nullptr;
			}
			// 如果两直线斜率不同则必有交点
			else {
				// 使用二分法求交点的坐标
				auto f = [this, B](double x) {
					return this->getValue(x) - B->getValue(x); 
				};
				double max = FIND_MAX;
				double min = FIND_MIN;
				double lastResult = FIND_MAX + 1.0;
				if (thisK >= 0) {
					while (true) {
						double mid = (min + max) / 2;
						if (f(mid) == 0 || mid == lastResult) {
							double* point = new double[2];
							point[0] = mid;
							point[1] = this->getValue(mid);
							return point;
						}
						else if (f(mid) < 0) {
							min = mid;
							lastResult = mid;
						}
						else if (f(mid) > 0) {
							max = mid;
							lastResult = mid;
						}
					}
				}
				else {
					while (true) {
						double mid = (min + max) / 2;
						if (f(mid) == 0 || mid == lastResult) {
							double* point = new double[2];
							point[0] = mid;
							point[1] = this->getValue(mid);
							return point;
						}
						else if (f(mid) > 0) {
							min = mid;
							lastResult = mid;
						}
						else if (f(mid) < 0) {
							max = mid;
							lastResult = mid;
						}
					}
				}
			}
		}
	}
}

而求直线与圆的交点,则要需要我们手动计算一个公式,这运算量大的,仿佛回高中做了一道圆锥曲线大题。。。
好在互联网发达,我在网上找到了直线与圆的交点公式
作业帮

在这里插入图片描述
在这里插入图片描述
由于我没学过计算几何,只能用解析几何的这种方法来求解,计算量大,而且容易产生浮点误差,日后学习了更优的方法再来优化吧。。

// 以向量的形式返回直线与圆的交点
vector<double*> Line::getIntersection(Circle* C) {
	vector<double*> vec;
	// 直线没有斜率时
	if (this->b_param == 0) {
		double x = -(this->c_param / this->a_param);
		vector<double> y_vec = C->getValue(x);
		int len = y_vec.size();
		if (len == 1) {
			double* point = new double[2];
			point[0] = x;
			point[1] = y_vec[0];
			vec.push_back(point);
			return vec;
		}
		else if (len == 2) {
			double* point1 = new double[2];
			double* point2 = new double[2];
			point1[0] = x;
			point1[1] = y_vec[0];
			point2[0] = x;
			point2[1] = y_vec[1];
			vec.push_back(point1);
			vec.push_back(point2);
			return vec;
		} 
		else {
			return vec;
		}
	}
	// 直线有斜率时
	else {
		double splot = -(this->a_param / this->b_param);
		double intercept = -(this->c_param / this->b_param);
		double x0 = C->getCenterPos()[0];
		double y0 = C->getCenterPos()[1];
		double r = C->getRadius();
		double delta = (pow(splot, 2) + 1) * pow(r, 2) - pow(x0, 2) * pow(splot, 2)
			+ 2 * splot * x0 * (intercept + y0) - pow(y0, 2) - 2 * y0 * intercept 
			- pow(intercept, 2);
		if (delta >= 0) {
			double x1 = (-((y0 + intercept) * splot + x0) - sqrt(delta)) / (1 + pow(splot, 2));
			double y1 = this->getValue(x1);
			double* point1 = new double[2];
			point1[0] = x1;
			point1[1] = y1;
			vec.push_back(point1);
			if (delta == 0) {
				return vec;
			}
			double x2 = (-((y0 + intercept) * splot + x0) + sqrt(delta)) / (1 + pow(splot, 2));
			double y2 = this->getValue(x2);
			double* point2 = new double[2];
			point2[0] = x2;
			point2[1] = y2;
			vec.push_back(point2);
			return vec;
		}
		else {
			return vec;
		}
	}
}

最后是直线与矩形的交点求解,其实相比前两个简单很多了,这里定义的矩形只是4条直线的一个集合罢了

// 以向量的形式返回直线与矩形的交点
vector<double*> Line::getIntersection(Rectangle* R) {
	vector<double*> vec;
	// 直线没有斜率时
	if (this->b_param == 0) {
		double x = -(this->c_param / this->a_param);
		vector<double> y_vec = R->getYValue(x);
		int len = y_vec.size();
		if (len == 2) {
			double* point1 = new double[2];
			double* point2 = new double[2];
			point1[0] = x;
			point1[1] = y_vec[0];
			point2[0] = x;
			point2[1] = y_vec[1];
			vec.push_back(point1);
			vec.push_back(point2);
			return vec;
		}
		else {
			return vec;
		}
	}
	// 直线有斜率时
	else {
		// 斜率为0时
		if (this->a_param == 0) {
			double y = -(this->c_param / this->b_param);
			vector<double> x_vec = R->getYValue(y);
			int len = x_vec.size();
			if (len == 2) {
				double* point1 = new double[2];
				double* point2 = new double[2];
				point1[0] = x_vec[0];
				point1[1] = y;
				point2[0] = x_vec[1];
				point2[1] = y;
				vec.push_back(point1);
				vec.push_back(point2);
				return vec;
			}
			else {
				return vec;
			}
		}
		else {
			/*
			 * 左上角----------L1------------+
			 *   |                           |
			 *   |                           |
			 *   L4        矩形示意图        L2
			 *   |                           |
			 *   |                           |
			 *   +------------L3----------右下角
			 */
			double param1 = R->getLeftUpPointPos()[1];
			double param2 = R->getRightDownPointPos()[0];
			double param3 = R->getRightDownPointPos()[1];
			double param4 = R->getLeftUpPointPos()[0];
			Line L1(0, -1, param1);
			Line L2(-1, 0, param2);
			Line L3(0, -1, param3);
			Line L4(-1, 0, param4);
			double* point1 = this->getIntersection(&L1);
			if (point1) {
				if (point1[0] <= param2 && point1[0] >= param4) {
					vec.push_back(point1);
				}
			}
			double* point2 = this->getIntersection(&L2);
			if (point2) {
				if (point2[1] <= param1 && point2[1] >= param3) {
					vec.push_back(point2);
				}
			}
			double* point3 = this->getIntersection(&L3);
			if (point3) {
				if (point3[0] <= param2 && point3[0] >= param4) {
					vec.push_back(point3);
				}
			}
			double* point4 = this->getIntersection(&L4);
			if (point4) {
				if (point4[1] <= param1 && point4[1] >= param3) {
					vec.push_back(point4);
				}
			}
			// 去除重复的点
			if (vec.size() == 2) {
				double* elem1 = vec[0];
				double* elem2 = vec[1];
				if (elem1[0] == elem2[0] && elem1[1] == elem2[1]) {
					vec.pop_back();
				}
			}
			else if (vec.size() == 4) {
				double* elem1 = vec[0];
				double* elem2 = vec[1];
				double* elem3 = vec[2];
				double* elem4 = vec[3];
				vec.clear();
				if (elem1[0] == elem2[0] && elem1[1] == elem2[1]) {
					vec.push_back(elem1);
					vec.push_back(elem3);
				}
				else if (elem1[0] == elem4[0] && elem1[1] == elem4[1]) {
					vec.push_back(elem1);
					vec.push_back(elem2);
				}
			}
			return vec;
		}
	}
}

总结

通过编写的实例可以发现,运用解析几何的方法求交点难免有浮点误差产生,特别是本来应该是0的值,有可能会变成一个不是0但非常接近0的值,而且由于C++中的浮点表示方法,得到0的时候可能会产生+0和-0这种情况,其实都是小问题,不过这只是一个小作业而已,不打算深入写这么多东西,最终老师也给打了A+,自认为还是不错的,发出来给之后来受苦的同学参考,如果觉得不错,还请您点个赞
合肥工业大学机器人技术作业:求直线与直线、圆、矩形交点坐标(C++)_第1张图片

你可能感兴趣的:(笔记)