软工个人项目(图形交点)

个人项目作业——求图形交点

项目 内容
这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健)
这个作业的要求在哪里 个人项目作业
教学班级 006
项目链接 https://github.com/notasadsong/intersection

一、PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 30 30
· Estimate · 估计这个任务需要多少时间 10 10
Development 开发
· Analysis · 需求分析 (包括学习新技术) 90 150
· Design Spec · 生成设计文档 15 30
· Design Review · 设计复审 (和同事审核设计文档) 10 20
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 15 30
· Design · 具体设计 30 30
· Coding · 具体编码 150 150
· Code Review · 代码复审 20 40
· Test · 测试(自我测试,修改代码,提交修改) 60 120
Reporting 报告 60 90
· Test Report · 测试报告 20 20
· Size Measurement · 计算工作量 20 20
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 10 20
合计 520 740

第一次编写c++程序,对语言熟悉不足,熟悉语言的时间比想象中的要多很多,还有专门为了测试写了几个函数,测试上花的时间也大大超出预期。

二、解题思路描述

采用暴力枚举法,对每一条直线或圆,都去和其他直线或圆求交点,得到的结果存入set,利用set去重后set中的元素的个数就是交点的个数。具体交点的求法如下:

1、直线与直线交点

两直线交点坐标可以直接通过数学公式的推导求得:

两个直线的一般式:

\[ax+by+c=0 ,dx+ey+f=0 \]

联立求解可得:

\[y = (cd-af)/(ae-bd),x = (bf - ce)/(ae-bd) \]

当两直线平行时没有交点,即$$ ae=bd $$,此时不用求解直接返回。

2、直线与圆交点

先计算出圆心到直线距离 d ,找到圆心在该直线上的投影点。然后分三种情况讨论:

  • 若 d 大于半径 r ,无交点。
  • d 等于半径 r ,交点只有一个,为投影点。
  • d 小于半径 r ,交点有两个,采用圆与直线的交点的方法算出。
3、圆与圆交点

先计算出圆心距 d ,两圆中较大的半径为 R ,较小的为 r ,然后分三种情况讨论:

  • d 大于 R + r ,外离圆,无交点。
  • d 小于 R - r ,一个圆包含于另一个圆,无交点。
  • 除上述之外的情况皆有交点。将两圆的标准方程相减得到一条直线方程,问题转化为求该直线与圆的交点,可以有前述方法求得。

三、设计实现过程

1、本次作业中我创立了三个类,分别是:Point, Line, Circle,用于存储交点,直线和圆。求交点的方法都内置于类中,通过调用可以实现全部交点的求解。

  • 直线采用一般式的形式存储a,b, c,内置有方法求线线交点,具体如下:

  • 圆的存储结构是圆心坐标和半径,内置有求圆圆交点和圆线交点方法,代码如下:

  • 点的结构存储了它的x,y坐标,同时在点中重载了 "<" 操作符用于在set中进行比较,代码如下:

2、单元测试的设计

  • 对于直线求交点,设计了平行线、相交线等测试
  • 对于直线与圆求交点,设计了相离、相切、相交三种情况,其中还包括直线与y轴平行的特殊情况
  • 对于圆与圆求交点,实际了外离,外切,相交,内切,内含五种情况

结果均符合预期。

四、性能分析

由于使用的是暴力法,因此肯定因为遍历产生很大的cpu开销,代码分析的结果也证实了这一点。百分之九十以上的cpu资源是耗费在循环求交点的过程中,也就是最多使用的intesect函数,如图所示:


但这已经是经过剪枝处理的n^2复杂度方法了。同时我也考虑过可能是set内部排序也造成了资源的占用,可以尝试使用无序索引如哈希值,有可能会降低成本。至于更优化的个人没有去尝试,希望能在作业截至之后老师评选出优秀作业供大家借鉴学习。

五、代码说明

1、直线直线交点求解函数

直接带入数学公式求解,再次不过多赘述。

Point getLLintersection(Line l) {
		double x = (l.c * b - c * l.b) / (a * l.b - l.a * b);
		double y = (l.a * c - a * l.c) / (a * l.b - l.a * b);
		return Point(x, y);
	}

2、直线圆交点求解函数

先求出圆心到直线的距离,根据距离与半径的关系可以分为三类:相离,相切,相交。

相交时要特殊考虑与y轴平行的直线。

vector<Point> get_c_l(Line l) {
		
	vector<Point> re;
	double dis = line2circle(l);
	if (dis > r) return re;
	//先找到圆心在直线上的投影点,用与直线垂直的线做交点得到
	Line l_(l.b, -l.a, y * l.a - x * l.b);
	Point p = l_.getLLintersection(l);
	if (dis < r) {
		if (l.b == 0) {
			Point p1(p.x, p.y + sqrt(r * r - dis * dis));
			Point p2(p.x, p.y - sqrt(r * r - dis * dis));
			re.push_back(p1);
			re.push_back(p2);
			return re;
		}
		else {
			double delta_x = sqrt((r * r - dis * dis) * l.b * l.b / (l.a * l.a + l.b * l.b));
			double k = -(l.a / l.b);
			Point p1(p.x + delta_x, p.y + k * delta_x);
			Point p2(p.x - delta_x, p.y - k * delta_x);
			re.push_back(p1);
			re.push_back(p2);
			return re;
		}
	}
	else {
	re.push_back(p);
		return re;
	}
}

3、圆圆交点求解函数

分出两种情况,有交点和无交点。

无交点的情况不需要操作直接返回,有焦点的情况可以转化为直线和圆的交点求解。

vector<Point> get_c_c(Circle c) {

    vector<Point> re;
    double dis_center = sqrt((x - c.x) * (x - c.x) + (y - c.y) * (y - c.y));
    //外离
    if (dis_center > r + c.r) return re;
    //包含
    if (dis_center < max(r, c.r) - min(r, c.r)) return re;
    //有交点
    double a_ = -2 * (x - c.x);
    double b_ = -2 * (y - c.y);
    double c_ = x * x + y * y - r * r - c.x * c.x - c.y * c.y + c.r * c.r;
    Line l_(a_, b_, c_);
    re = get_c_l(l_);
    return re;
}

六、截图


posted @ 2020-03-10 17:35  不是小胖  阅读(287)  评论(2编辑  收藏  举报