没什么特别的,利用了圆的标准公式来表示圆
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); /* 求直线与矩形的交点 */
};
前面几个方法没什么好说的,很容易理解,重点是求交点的方法,这里重载了求交点的函数,直线与直线要么有一个交点,要么没有交点,因此用一个指针来返回交点的信息
其中用到二分法来求交点,因为直接用
// 以指针的形式返回直线与直线的交点
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+,自认为还是不错的,发出来给之后来受苦的同学参考,如果觉得不错,还请您点个赞