Logo是麻省理工学院发明的一种编程语言,最初用于在太空中移动机器人。
海龟图形,添加到Logo语言中,允许程序员向屏幕上的“海龟”发出一系列指令,
并在其移动时画出一条线。海龟图形也被添加到许多不同的编程语言中,包括Python,
它是标准库的一部分。
这次实验就是学着用turtle画一些图案,以及对图案进行一些角度计算。很简单,提供了两个函数,forward(units)和turn(degrees) ,forward(units)是指前进多少长度,turn(degrees)是指顺时针转多少度。
/**
* Draw a square.
*
* @param turtle the turtle context
* @param sideLength length of each side
*/
public static void drawSquare(Turtle turtle, int sideLength) {
for (int i = 0; i < 4; i++) {
turtle.forward(sideLength);
turtle.turn(90);
}
}
/**
* Determine inside angles of a regular polygon.
*
* There is a simple formula for calculating the inside angles of a polygon; you
* should derive it and use it here.
*
* @param sides number of sides, where sides must be > 2
* @return angle in degrees, where 0 <= angle < 360
*/
public static double calculateRegularPolygonAngle(int sides) {
return (sides - 2) * 180.0 / sides;
}
/**
* Given the number of sides, draw a regular polygon.
*
* (0,0) is the lower-left corner of the polygon; use only right-hand turns to
* draw.
*
* @param turtle the turtle context
* @param sides number of sides of the polygon to draw
* @param sideLength length of each side
*/
public static void drawRegularPolygon(Turtle turtle, int sides, int sideLength) {
double angle = 180 - calculateRegularPolygonAngle(sides);
for (int i = 0; i < sides; i++) {
turtle.forward(sideLength);
turtle.turn(angle);
}
}
/**
* Given the current direction, current location, and a target location,
* calculate the Bearing towards the target point.
*
* The return value is the angle input to turn() that would point the turtle in
* the direction of the target point (targetX,targetY), given that the turtle is
* already at the point (currentX,currentY) and is facing at angle
* currentBearing. The angle must be expressed in degrees, where 0 <= angle <
* 360.
*
* HINT: look at http://en.wikipedia.org/wiki/Atan2 and Java's math libraries
*
* @param currentBearing current direction as clockwise from north
* @param currentX current location x-coordinate
* @param currentY current location y-coordinate
* @param targetX target point x-coordinate
* @param targetY target point y-coordinate
* @return adjustment to Bearing (right turn amount) to get to target point,
* must be 0 <= angle < 360
*/
public static double calculateBearingToPoint(double currentBearing, int currentX, int currentY, int targetX,
int targetY) {
double angle = Math.atan2(targetX - currentX, targetY - currentY) * 180 / Math.PI - currentBearing;
if (angle < 0)
angle += 360;
return angle;
}
/**
* Given a sequence of points, calculate the Bearing adjustments needed to get
* from each point to the next.
*
* Assumes that the turtle starts at the first point given, facing up (i.e. 0
* degrees). For each subsequent point, assumes that the turtle is still facing
* in the direction it was facing when it moved to the previous point. You
* should use calculateBearingToPoint() to implement this function.
*
* @param xCoords list of x-coordinates (must be same length as yCoords)
* @param yCoords list of y-coordinates (must be same length as xCoords)
* @return list of Bearing adjustments between points, of size 0 if (# of
* points) == 0, otherwise of size (# of points) - 1
*/
public static List calculateBearings(List xCoords, List yCoords) {
List list = new ArrayList<>();
int N = xCoords.size();
double face = 0;
int i = 0;
while (i < N - 1) {
double k = calculateBearingToPoint(face, xCoords.get(i), yCoords.get(i), xCoords.get(i + 1),
yCoords.get(i + 1));
list.add(k);
face = (face + k) % 360;
i++;
}
System.out.println(list);
return list;
}
/**
* Given a set of points, compute the convex hull, the smallest convex set that
* contains all the points in a set of input points. The gift-wrapping algorithm
* is one simple approach to this problem, and there are other algorithms too.
*
* @param points a set of points with xCoords and yCoords. It might be empty,
* contain only 1 point, two points or more.
* @return minimal subset of the input points that form the vertices of the
* perimeter of the convex hull
*/
public static Set convexHull(Set points) {
Point p0, p1;
p0 = new Point(0, 0);
p1 = new Point(0, 0);
double dis, an1, an2, an3;
ArrayList list1 = new ArrayList();
Set list2 = new HashSet<>();
if (points.size() <= 3) {
return points;
} else {
for (Point p : points) {
dis = p.x() * p.x() + p.y() * p.y();
if (dis > p0.x() * p0.x() + p0.y() * p0.y()) {
p0 = p;
}
}
list1.add(p0);
an1 = 361;
for (Point p : points) {
an2 = calculateBearingToPoint(0, (int) p0.x(), (int) p0.y(), (int) p.x(), (int) p.y());
if (an2 < an1 && (p != p0)) {
an1 = an2;
p1 = p;
}
}
list1.add(p1);
an1 = 361;
for (int i = 0; i < points.size(); i++) {
an3 = calculateBearingToPoint(0, (int) list1.get(list1.size() - 2).x(),
(int) list1.get(list1.size() - 2).y(), (int) list1.get(list1.size() - 1).x(),
(int) list1.get(list1.size() - 1).y());
an1 = 361;
for (Point p : points) {
an2 = calculateBearingToPoint(an3, (int) list1.get(list1.size() - 1).x(),
(int) list1.get(list1.size() - 1).y(), (int) p.x(), (int) p.y());
if (an2 < an1 && (p != list1.get(list1.size() - 1))) {
an1 = an2;
p1 = p;
}
}
if (p1 == list1.get(0)) {
break;
} else {
if (an1 == 0) {
list1.remove(list1.size() - 1);
}
list1.add(p1);
}
}
for (Point p : list1) {
list2.add(p);
}
return list2;
}
}
public static void drawPersonalArt(Turtle turtle) {
for (int i = 0; i < 1000; i++) {
turtle.forward(i / 2);
switch ((i / 10) % 10) {
case 0:
turtle.color(PenColor.BLACK);
break;
case 1:
turtle.color(PenColor.GRAY);
break;
case 2:
turtle.color(PenColor.RED);
break;
case 3:
turtle.color(PenColor.PINK);
break;
case 4:
turtle.color(PenColor.ORANGE);
break;
case 5:
turtle.color(PenColor.YELLOW);
break;
case 6:
turtle.color(PenColor.GREEN);
break;
case 7:
turtle.color(PenColor.CYAN);
break;
case 8:
turtle.color(PenColor.BLUE);
break;
case 9:
turtle.color(PenColor.MAGENTA);
break;
}
turtle.turn(135);
}
}
这个任务中需要注意的内容就是如何用库函数求解偏转角度以及计算结果的精度舍入问题。在Java的Math库中,提供了这样的一个方法:atan2方法。该方法通过传入一个向量,即可计算出其对应的角的大小。这个方法的返回值,实际上是直角坐标系y轴正方向和向量所成角的大小,而且返回值为对应角的弧度值。因而要对这个角有一个正确的认识,并将其单位进行一个简单的转换。