Turtle Graphics即为海龟绘图
任务描述
此实验是需要我们使用Java中提供的turtle类来实现简单正多边形的绘制。在此过程中,我们需要解决一系列问题,如多边形内角和与转角之间的转换、求一个凸包等。在编写和测试代码的过程中我们将更为深入了解文件配置与复用思想的知识技能。
实验过程
难点:刚开始看到老师给的github中有这么多java文件时还是有点懵的——不知道哪些是需要我去写的,哪些是不需要动的。后来慢慢看了MIT的说明,才知道只需要编辑TurtleSoup.java这个文件。
第一个任务是绘制一个正方形,这很容易。
第二个任务是绘制一个自定义的正多边形,只需要懂得多边形外角和是360°,就可以知道每个内角是360°/n。
第三个任务是凸包。这里我用的是边界漫游法,即找到一个最左下角的点(最左优先,其次才找最下),当回到起点时即程序结束。
第三个任务是绘制个人作品。这里用了各种不同的颜色,将边长、边数、角度由小到大变化。
任务一——绘制正方形
即4次forward和turn(90):
public static void drawSquare(Turtle turtle, int sideLength) {
for (int i = 0; i < 4; i++) {
turtle.forward(sideLength);
turtle.turn(90);
} //绘制一个四条边相等的正方形
}
任务二——绘制正多边形
与画正方形思路类似,唯一的区别是边数变成了参数,故需要依据边数来求得转角。
先实现一个求正多边形每一个外角的补角度数的函数:
public static double calculateRegularPolygonAngle(int sides) {
return 180-360.0 / sides;
} //正多边形外角和是360度,则平均下来是360/sides
再循环调用此函数获得外角,结合边长画出正多边形:
public static void drawRegularPolygon(Turtle turtle, int sides, int sideLength) {
for (int i = 0; i < sides; i++) {
turtle.forward(sideLength);
turtle.turn(180-calculateRegularPolygonAngle(sides));
} //绘制一个sides边形
}
任务三——计算转角
知道两点的坐标与当前朝向来求转角,即calculateBearingToPoint函数:
public static double calculateBearingToPoint(double currentBearing, int currentX, int currentY, int targetX, int targetY) {
double degree = 90 - Math.toDegrees(Math.atan2(targetY - currentY, targetX - currentX)); //用arctan求得起点到终点的角度
if (degree < 0)
degree += 360; //转换参照系
degree = degree - currentBearing; //求追击度数
if (degree < 0) //若角度小于0,则两者度数均为正数,但方向角度较小
degree += 360; //要追加360度
return degree;
}
接下来即为calculateBearings函数:
public static List<Double> calculateBearings(List<Integer> xCoords, List<Integer> yCoords) {
double bearing = 0;
List<Double> res = new ArrayList<>();
for (int i = 1; i < xCoords.size(); i++) {
bearing = calculateBearingToPoint(bearing, xCoords.get(i - 1), yCoords.get(i - 1), xCoords.get(i), yCoords.get(i));
res.add(bearing);
}
return res;
}
任务四——求凸包
要求一个凸包,即为在一些点中找到一个最小数量的点集合,使得这个点集合形成的多边形能将所有点都包含在内。这里是边界漫游法的算法:
public static Set<Point> convexHull(Set<Point> points) {
if (points.size() <= 3)
return points; //若points小于3则直接返回
Point min_p = null;
for (Point p : points)
if (min_p == null || p.x() < min_p.x() || (p.x() == min_p.x() && p.y() < min_p.y()))
min_p = p;
Set<Point> res = new HashSet<>();
res.add(min_p);//找点:在最左边的前提下,找最下面
//边界漫游法:
Point now_p = min_p;
Point next_p = null;
double bearing = 0;
double now_bearing;
double now_length;
do {
double nextLength = 2147483647;
double nextBearing = 360; //更新最小旋转度数,且在度数相等时找最远的点
for (Point p : points) {
if (p.equals(now_p))
continue;
now_bearing = calculateBearingToPoint(bearing, (int) now_p.x(), (int) now_p.y(), (int) p.x(), (int) p.y());
now_length = Math.pow(p.x() - now_p.x(), 2) + Math.pow(p.y() - now_p.y(), 2);
if (nextBearing > now_bearing) {
nextLength = now_length;
nextBearing = now_bearing;
next_p = p; //若比当前的旋转度数更小则更新
} else if (nextBearing == now_bearing && nextLength < now_length) {
nextLength = now_length;
next_p = p; //否则若相等,则更新为距离更近的点
}
}
now_p = next_p;
bearing += nextBearing;
res.add(next_p);
} while (!min_p.equals(next_p));//最终回到起点则终止程序
return res;
}
任务五——个人创作
我画的是一个大量彩色正多边形旋转形成的花纹图案,调用drawRegularPolygon函数。
public static void drawPersonalArt(Turtle turtle) {
for (int i = 0; i < 20; i++)
for (PenColor pt : PenColor.values()) {
drawRegularPolygon(turtle, i, i * 10);
turtle.turn(i * 5);
turtle.color(pt);
}
}