Lab1实验过程记录
2.Turtle Graph
第二题实现TurleSoup类中的各种方法,最终使用Turtle Graph实现简单的图形绘制 ,考察了部分Java类方法,使用JUnit进行函数检测,一些简单的数学平面几何知识以及凸包算法GiftWraping。
从Problem1到Problem5的内容较为简单,仅涉及基本的类方法调用以及角度计算,在此略过。
Problem6,实现方法calculateBearings()。对给定顺序的点集计算各次相应的旋转角度,以变长数组形式返回。
先实现方法calculateBearingToPoint()。根据作业提示查阅了Math.atan2()文档,发现该方法以及相近的Math.atan()都可以用于反正切值计算。调用该方法计算给定的两点计算他们的夹角值,得到的弧度制,需要先将其转换为角度制。另外,得出的角度是按数学习惯的相对x轴正半轴的逆时针角度,还需要转换为相对于y轴正半轴的顺时针角度。
完成后,实现calculateBearings()只需多次调用calculateBearingToPoint(),将所得的角度值依次存入List中,全部计算完毕后返回List即可。
Problem7,实现方法convexHull。对给定的点集,计算其对应的凸包(位于凸包边上的顶点除外),以集合形式返回。
此实现过程需要用到凸包算法GiftWraping。简单的描述其思路,先从点集中找出位于最“边界”的某个顶点(例如在我的函数实现中,取点集中x,y值最小的最左下角的点),显然此点必在凸包之中。从该点开始,过点作射线,顺时针或逆时针旋转,碰到的第一个顶点也应该在凸包中。对于位于凸包边上的顶点,相应大的旋转将会一次碰到多个顶点,此时比较对应边的长度即可判断。多次循环,直至找到的下一个顶点为起始顶点,就完成了凸包的查找。整个过程类似于用胶带缠包裹,与算法名字十分贴切。
理解了GiftWraping算法,将其转换为相应的代码语言即可。值得一提的是,“旋转"需要使用叉积的计算来实现。每次寻找下一个顶点时,遍历每个其余顶点,将此时所在顶点与其形成的向量与当前标记的最外侧顶点向量做叉积,根据叉积正负即可判断向量的方向关系,多次更新,最终找到最外侧顶点即可。
最终实现的代码如下:
public static Set<Point> convexHull(Set<Point> points) { //实现:计算凸包顶点集合
if(points.size() <= 3) return points; //集合元素数量小于等于3,直接返回原集合
List<Point> list = new ArrayList<>(points);
Set<Point> set = new HashSet<>(); //返回集合
int first = 0; //起始顶点下标
for(int i = 1; i < list.size(); i++) { //首先找出最左下的点 必在凸包中
if(list.get(i).x() < list.get(first).x() ||
(list.get(i).x() == list.get(first).x() && list.get(i).y() < list.get(first).y()))
first = i;
}
Point temp = list.get(first);
Point next; //当前最外侧的点
double vProduct;
int i;
do {
if(temp == list.get(first)) i = (first+1)%list.size();
else i = first;
next = list.get(i);
for(int y = 0; y < list.size() ; y++) { //顺时针寻找下一个顶点
i = (i+1)%list.size();
if(set.contains(list.get(i))) continue; //当前点已在集中,跳过
vProduct = (next.x() - temp.x()) * (list.get(i).y() - temp.y()) //计算叉积x1*y2-x2*y1
- (list.get(i).x() - temp.x()) * (next.y() - temp.y());
if(vProduct > 0)
next = list.get(i); //叉积为正,逆时针旋转,在外侧
else if(vProduct == 0 && temp != list.get(i) &&
Math.sqrt(next.x() - temp.x()) + Math.sqrt(next.y() - temp.y())
< Math.sqrt(list.get(i).x() - temp.x()) + Math.sqrt(list.get(i).y() - temp.y()))
next = list.get(i); //叉积为0,不为自身,且距离更远
}
if(!set.contains(next)) set.add(next); //循环结束,向set中加入新顶点
temp = next;
}while(temp != list.get(first)); //当循环回到起点,返回
return set;
}
Problem8,用以上所写的代码自己画一幅画。随便画一画就好了。(圆形按正n边形处理即可)