diff --git a/src/main/java/seng302/controllers/CanvasController.java b/src/main/java/seng302/controllers/CanvasController.java index 398cc712..4850b4f4 100644 --- a/src/main/java/seng302/controllers/CanvasController.java +++ b/src/main/java/seng302/controllers/CanvasController.java @@ -50,6 +50,7 @@ public class CanvasController { private final int RHS_BUFFER = BUFFER_SIZE + MARK_SIZE / 2; private final int TOP_BUFFER = BUFFER_SIZE; private final int BOT_BUFFER = TOP_BUFFER + MARK_SIZE / 2; + private boolean horizontalInversion = false; private double distanceScaleFactor; private ScaleDirection scaleDirection; @@ -188,9 +189,9 @@ public class CanvasController { boatGroup.move(); } for (MarkGroup markGroup : markGroups) { - for (long id : markGroup.getRaceIds()) { - if (StreamParser.markPositions.containsKey(id)) { - UpdateMarkGroup(id, markGroup); + for (int id : markGroup.getRaceIds()) { + if (StreamParser.boatPositions.containsKey(id)) { + updateMarkGroup(id, markGroup); } } } @@ -223,8 +224,8 @@ public class CanvasController { } } - private void UpdateMarkGroup (long raceId, MarkGroup markGroup) { - PriorityBlockingQueue movementQueue = StreamParser.markPositions.get(raceId); + void updateMarkGroup (int raceId, MarkGroup markGroup) { + PriorityBlockingQueue movementQueue = StreamParser.boatPositions.get(raceId); if (movementQueue.size() > 0){ try { BoatPositionPacket positionPacket = movementQueue.take(); @@ -353,16 +354,11 @@ public class CanvasController { //If the course is on a point on the earth where longitudes wrap around. Limit minLonMark = sortedPoints.get(0); Limit maxLonMark = sortedPoints.get(sortedPoints.size()-1); - SingleMark thisMinLon = new SingleMark(minLonMark.toString(), minLonMark.getLat(), minLonMark.getLng(), minLonMark.getSeqID()); - SingleMark thisMaxLon = new SingleMark(maxLonMark.toString(), maxLonMark.getLat(), maxLonMark.getLng(), maxLonMark.getSeqID()); - // TODO: 30/03/17 cir27 - Correctly account for longitude wrapping around. - if (thisMaxLon.getLongitude() - thisMinLon.getLongitude() > 180) { - SingleMark temp = thisMinLon; - thisMinLon = thisMaxLon; - thisMaxLon = temp; + minLonPoint = new SingleMark(minLonMark.toString(), minLonMark.getLat(), minLonMark.getLng(), minLonMark.getSeqID()); + maxLonPoint = new SingleMark(maxLonMark.toString(), maxLonMark.getLat(), maxLonMark.getLng(), maxLonMark.getSeqID()); + if (maxLonPoint.getLongitude() - minLonPoint.getLongitude() > 180) { + horizontalInversion = true; } - minLonPoint = thisMinLon; - maxLonPoint = thisMaxLon; } /** @@ -393,6 +389,9 @@ public class CanvasController { referencePointX += distanceScaleFactor * Math.sin(referenceAngle) * Mark.calculateDistance(referencePoint, minLonPoint); referencePointX += ((CANVAS_WIDTH - (LHS_BUFFER + RHS_BUFFER)) - (minLonToMaxLon * distanceScaleFactor)) / 2; } + if(horizontalInversion) { + referencePointX = CANVAS_WIDTH - RHS_BUFFER - (referencePointX - LHS_BUFFER); + } } @@ -452,6 +451,9 @@ public class CanvasController { xAxisLocation -= (int) Math.round(distanceScaleFactor * Math.cos(angleFromReference) * distanceFromReference); yAxisLocation += (int) Math.round(distanceScaleFactor * Math.sin(angleFromReference) * distanceFromReference); } + if(horizontalInversion) { + xAxisLocation = CANVAS_WIDTH - RHS_BUFFER - (xAxisLocation - LHS_BUFFER); + } return new Point2D(xAxisLocation, yAxisLocation); } diff --git a/src/main/java/seng302/controllers/RaceViewController.java b/src/main/java/seng302/controllers/RaceViewController.java index 8492015b..43c04a9b 100644 --- a/src/main/java/seng302/controllers/RaceViewController.java +++ b/src/main/java/seng302/controllers/RaceViewController.java @@ -105,7 +105,7 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel // Load FXML and set CSS fxmlLoader .setLocation(getClass().getResource("/views/importantAnnotationSelectView.fxml")); - Scene scene = new Scene(fxmlLoader.load(), 469, 248); + Scene scene = new Scene(fxmlLoader.load(), 469, 298); scene.getStylesheets().add(getClass().getResource("/css/master.css").toString()); stage.initStyle(StageStyle.UNDECORATED); diff --git a/src/main/java/seng302/controllers/StartScreenController.java b/src/main/java/seng302/controllers/StartScreenController.java index 94851d05..debeb371 100644 --- a/src/main/java/seng302/controllers/StartScreenController.java +++ b/src/main/java/seng302/controllers/StartScreenController.java @@ -58,10 +58,10 @@ public class StartScreenController implements Initializable { contentPane.getChildren().addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl))); } catch(javafx.fxml.LoadException e){ - System.err.println(e.getCause()); + e.printStackTrace(); } catch(IOException e){ - System.err.println(e); + e.printStackTrace(); } } @@ -132,6 +132,7 @@ public class StartScreenController implements Initializable { } public void switchToRaceView() { + StreamParser.boatPositions.clear(); switchedToRaceView = true; setContentPane("/views/RaceView.fxml"); } diff --git a/src/main/java/seng302/models/BoatGroup.java b/src/main/java/seng302/models/BoatGroup.java index 28e9561f..9ce9db91 100644 --- a/src/main/java/seng302/models/BoatGroup.java +++ b/src/main/java/seng302/models/BoatGroup.java @@ -1,11 +1,14 @@ package seng302.models; import javafx.event.EventHandler; +import javafx.geometry.Point2D; import javafx.scene.CacheHint; import javafx.scene.Group; import javafx.scene.input.MouseDragEvent; import javafx.scene.input.MouseEvent; import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Line; import javafx.scene.shape.Polygon; import javafx.scene.text.Text; import javafx.scene.transform.Rotate; @@ -39,6 +42,7 @@ public class BoatGroup extends Group{ private double xIncrement; private double yIncrement; private long lastTimeValid = 0; + private Double lastRotation = 0.0; private long framesToMove; //Graphical objects private Yacht boat; @@ -49,6 +53,10 @@ public class BoatGroup extends Group{ private Text estTimeToNextMarkObject; private Text legTimeObject; private Wake wake; + private Double distanceTravelled = 0.0; + private Point2D lastPoint; + private boolean destinationSet; + private Color textColor = Color.RED; private Boolean isSelected = true; //All boats are initalised as selected @@ -61,6 +69,7 @@ public class BoatGroup extends Group{ public BoatGroup (Yacht boat, Color color){ this.boat = boat; initChildren(color); + this.textColor = color; } /** @@ -76,12 +85,31 @@ public class BoatGroup extends Group{ initChildren(color, points); } + /** + * Return a text object with caching and a color applied + * @param defaultText The default text to display + * @param fill The text fill color + * @return The text object + */ + private Text getTextObject(String defaultText, Color fill){ + Text text = new Text(defaultText); + + text.setFill(fill); + text.setCacheHint(CacheHint.SPEED); + text.setCache(true); + + return text; + } + /** * Creates the javafx objects that will be the in the group by default. * @param color The colour of the boat polygon and the trailing line. * @param points An array of co-ordinates x1,y1,x2,y2,x3,y3... that will make up the boat polygon. */ private void initChildren (Color color, double... points) { + textColor = color; + destinationSet = false; + boatPoly = new Polygon(points); boatPoly.setFill(color); boatPoly.setOnMouseEntered(event -> boatPoly.setFill(Color.FLORALWHITE)); @@ -90,24 +118,8 @@ public class BoatGroup extends Group{ boatPoly.setCache(true); boatPoly.setCacheHint(CacheHint.SPEED); - - teamNameObject = new Text(boat.getShortName()); - teamNameObject.setCache(true); - teamNameObject.setCacheHint(CacheHint.SPEED); - velocityObject = new Text(String.valueOf(boat.getVelocity())); - DateFormat format = new SimpleDateFormat("mm:ss"); - String timeToNextMark = format - .format(boat.getEstimateTimeAtNextMark() - StreamParser.getCurrentTimeLong()); - estTimeToNextMarkObject = new Text("Next mark: " + timeToNextMark); - if (boat.getMarkRoundingTime() != null) { - String elapsedTime = format - .format(StreamParser.getCurrentTimeLong() - boat.getMarkRoundingTime()); - legTimeObject = new Text("Last mark: " + elapsedTime); - } else { - legTimeObject = new Text("Last mark: -"); - } - velocityObject.setCache(true); - velocityObject.setCacheHint(CacheHint.SPEED); + teamNameObject = getTextObject(boat.getShortName(), textColor); + velocityObject = getTextObject(boat.getVelocity().toString(), textColor); teamNameObject.setX(TEAMNAME_X_OFFSET); teamNameObject.setY(TEAMNAME_Y_OFFSET); @@ -117,14 +129,22 @@ public class BoatGroup extends Group{ velocityObject.setY(VELOCITY_Y_OFFSET); velocityObject.relocate(velocityObject.getX(), velocityObject.getY()); - estTimeToNextMarkObject.setX(ESTTIMETONEXTMARK_X_OFFSET); - estTimeToNextMarkObject.setY(ESTTIMETONEXTMARK_Y_OFFSET); - estTimeToNextMarkObject - .relocate(estTimeToNextMarkObject.getX(), estTimeToNextMarkObject.getY()); + updateLastMarkRoundingTime(); + updateTimeTillNextMark(); - legTimeObject.setX(LEGTIME_X_OFFSET); - legTimeObject.setY(LEGTIME_Y_OFFSET); - legTimeObject.relocate(legTimeObject.getX(), legTimeObject.getY()); + if (estTimeToNextMarkObject != null){ + estTimeToNextMarkObject.setX(ESTTIMETONEXTMARK_X_OFFSET); + estTimeToNextMarkObject.setY(ESTTIMETONEXTMARK_Y_OFFSET); + estTimeToNextMarkObject + .relocate(estTimeToNextMarkObject.getX(), estTimeToNextMarkObject.getY()); + } + + if (legTimeObject != null){ + legTimeObject.setX(LEGTIME_X_OFFSET); + legTimeObject.setY(LEGTIME_Y_OFFSET); + legTimeObject.relocate(legTimeObject.getX(), legTimeObject.getY()); + + } wake = new Wake(0, -BOAT_HEIGHT); super.getChildren() @@ -148,7 +168,7 @@ public class BoatGroup extends Group{ * @param dx The amount to move the X coordinate by * @param dy The amount to move the Y coordinate by */ - public void moveGroupBy(double dx, double dy) { + private void moveGroupBy(double dx, double dy) { boatPoly.setLayoutX(boatPoly.getLayoutX() + dx); boatPoly.setLayoutY(boatPoly.getLayoutY() + dy); teamNameObject.setLayoutX(teamNameObject.getLayoutX() + dx); @@ -159,6 +179,8 @@ public class BoatGroup extends Group{ estTimeToNextMarkObject.setLayoutY(estTimeToNextMarkObject.getLayoutY() + dy); legTimeObject.setLayoutX(legTimeObject.getLayoutX() + dx); legTimeObject.setLayoutY(legTimeObject.getLayoutY() + dy); + wake.setLayoutX(wake.getLayoutX() + dx); + wake.setLayoutY(wake.getLayoutY() + dy); } @@ -167,7 +189,7 @@ public class BoatGroup extends Group{ * @param x The X coordinate to move the boat to * @param y The Y coordinate to move the boat to */ - public void moveTo (double x, double y, double rotation) { + private void moveTo(double x, double y, double rotation) { rotateTo(rotation); boatPoly.setLayoutX(x); boatPoly.setLayoutY(y); @@ -179,20 +201,113 @@ public class BoatGroup extends Group{ estTimeToNextMarkObject.setLayoutY(y); legTimeObject.setLayoutX(x); legTimeObject.setLayoutY(y); + wake.setLayoutX(x); + wake.setLayoutY(y); + wake.rotate(rotation); } - public void rotateTo (double rotation) { + private void rotateTo(double rotation) { boatPoly.getTransforms().setAll(new Rotate(rotation)); } + /** + * Updates the time until next mark label, will create a label if one doesn't exist + */ + private void updateTimeTillNextMark(){ + if (estTimeToNextMarkObject == null){ + estTimeToNextMarkObject = getTextObject("", textColor); + } + if (boat.getEstimateTimeAtNextMark() != null){ + DateFormat format = new SimpleDateFormat("mm:ss"); + String timeToNextMark = format + .format(boat.getEstimateTimeAtNextMark() - StreamParser.getCurrentTimeLong()); + estTimeToNextMarkObject.setText("Next mark: " + timeToNextMark); + } else { + estTimeToNextMarkObject.setText("Next mark: -"); + } + } + + /** + * Updates the time since last mark rounding, will create a label if one doesn't exist + */ + private void updateLastMarkRoundingTime(){ + if (legTimeObject == null){ + legTimeObject = getTextObject("", textColor); + } + + if (boat.getMarkRoundingTime() != null){ + DateFormat format = new SimpleDateFormat("mm:ss"); + String elapsedTime = format + .format(StreamParser.getCurrentTimeLong() - boat.getMarkRoundingTime()); + legTimeObject.setText("Last mark: " + elapsedTime); + } + else{ + legTimeObject.setText("Last mark: -"); + + } + } + public void move() { + double dx = xIncrement * framesToMove; + double dy = yIncrement * framesToMove; + + distanceTravelled += Math.abs(dx) + Math.abs(dy); moveGroupBy(xIncrement, yIncrement); framesToMove = framesToMove - 1; + if (framesToMove <= 0){ isStopped = true; } + + if (distanceTravelled > 70){ + distanceTravelled = 0d; + + if (lastPoint != null){ + Line l = new Line( + lastPoint.getX(), + lastPoint.getY(), + boatPoly.getLayoutX(), + boatPoly.getLayoutY() + ); + l.getStrokeDashArray().setAll(3d, 7d); + l.setStroke(boat.getColour()); + l.setCache(true); + l.setCacheHint(CacheHint.SPEED); + lineGroup.getChildren().add(l); + } + + if (destinationSet){ + lastPoint = new Point2D(boatPoly.getLayoutX(), boatPoly.getLayoutY()); + } + } + + wake.updatePosition(1000/60); } + /** + * Calculates the rotational velocity required to reach the rotationalGoal from the currentRotation. + */ + protected Double calculateRotationalVelocity (Double rotationalGoal) { + Double rotationalVelocity = 0.0; + + if (Math.abs(rotationalGoal - lastRotation) > 180) { + if (rotationalGoal - lastRotation >= 0.0) { + rotationalVelocity = ((rotationalGoal - lastRotation) - 360) / 200; + } else { + rotationalVelocity = (360 + (rotationalGoal - lastRotation)) / 200; + } + } else { + rotationalVelocity = (rotationalGoal - lastRotation) / 200; + } + + //Sometimes the rotation is too large to be realistic. In that case just do it instantly. + if (Math.abs(rotationalVelocity) > 1) { + rotationalVelocity = 0.0; + } + + return rotationalVelocity; + } + /** * Sets the destination of the boat and the headng it should have once it reaches * @param newXValue The X co-ordinate the boat needs to move to. @@ -208,13 +323,30 @@ public class BoatGroup extends Group{ framesToMove = Math.round((frameRate/(1000.0f/(timeValid-lastTimeValid)))); double dx = newXValue - boatPoly.getLayoutX(); double dy = newYValue - boatPoly.getLayoutY(); + xIncrement = dx/framesToMove; yIncrement = dy/framesToMove; + + destinationSet = true; + + Double rotationalVelocity = calculateRotationalVelocity(rotation); + + updateTimeTillNextMark(); + updateLastMarkRoundingTime(); + + if (Math.abs(rotationalVelocity) > 0.075) { + rotationalVelocity = 0.0; + wake.rotate(rotation); + } + rotateTo(rotation); + wake.setRotationalVelocity(rotationalVelocity, groundSpeed); velocityObject.setText(String.format("%.2f m/s", groundSpeed)); lastTimeValid = timeValid; isStopped = false; + + lastRotation = rotation; } diff --git a/src/main/java/seng302/models/Colors.java b/src/main/java/seng302/models/Colors.java index 23ef8f4e..8d1b9c25 100644 --- a/src/main/java/seng302/models/Colors.java +++ b/src/main/java/seng302/models/Colors.java @@ -6,7 +6,7 @@ import javafx.scene.paint.Color; * Created by ryan_ on 16/03/2017. */ public enum Colors { - RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE; + RED, PERU, SEAGREEN, GREEN, BLUE, PURPLE; static Integer index = 0; diff --git a/src/main/java/seng302/models/Wake.java b/src/main/java/seng302/models/Wake.java index 55d4381c..886dfba8 100644 --- a/src/main/java/seng302/models/Wake.java +++ b/src/main/java/seng302/models/Wake.java @@ -1,30 +1,34 @@ package seng302.models; +import javafx.scene.CacheHint; import javafx.scene.Group; import javafx.scene.paint.Color; import javafx.scene.shape.Arc; import javafx.scene.shape.ArcType; +import javafx.scene.shape.StrokeLineCap; import javafx.scene.transform.Rotate; /** - * By default wake is a group containing 5 arcs. Each arc starts from the same point. Each arc is larger and more - * transparent than the last. On calling updatePositions() arcs rotate at velocities given by setRotationalVelocity(). - * The larger and more transparent an arc is the longer the delay before it rotates at the latest velocity. It is - * assumed that rotationalVelocities() are set regularly as wakes do not stop rotating and an array of velocities needs - * to be populated for the class to work as expected. + * A group containing objects used to represent wakes onscreen. Contains functionality for their animation. */ class Wake extends Group { - private int numWakes = 5; - private double[] velocities = new double[13]; + //The number of wakes + private int numWakes = 8; + //The total possible difference between the first wake and the last. Increasing/Decreasing this will make wakes fan out more/less. + private final double MAX_DIFF = 75; + //Increasing/decreasing this will alter the speed that wakes converge when the heading stop changing. Anything over about 1500 may cause oscillation. + private final int UNIFICATION_SPEED = 750; + + private Arc[] arcs = new Arc[numWakes]; + private double[] rotationalVelocities = new double[numWakes]; private double[] rotations = new double[numWakes]; - private int[] velocityIndices = new int[numWakes]; - private double sum = 0; - private static double max; + private double baseRad; /** * Create a wake at the given location. + * * @param startingX x location where the tip of wake arcs will be. * @param startingY y location where the tip of wake arcs will be. */ @@ -34,74 +38,77 @@ class Wake extends Group { Arc arc; for (int i = 0; i < numWakes; i++) { //Default triangle is -110 deg out of phase with a default wake and has angle of 40 deg. - arc = new Arc(0,0,0,0,-110,40); - //Opacity increases from 0.5 -> 0 evenly over the 5 wake arcs. - arc.setFill(new Color(0.18, 0.7, 1.0, 1.0 + -0.175 * i)); - arc.setType(ArcType.ROUND); + arc = new Arc(0, 0, 0, 0, -110, 40); + arc.setCache(true); + arc.setCacheHint(CacheHint.SPEED); + arc.setType(ArcType.OPEN); + arc.setStroke(new Color(0.18, 0.7, 1.0, 1.0 + (-0.99 / numWakes * i))); + arc.setStrokeWidth(3.0); + arc.setStrokeLineCap(StrokeLineCap.ROUND); + arc.setFill(new Color(0.0, 0.0, 0.0, 0.0)); + baseRad = (20 / numWakes); arcs[i] = arc; } super.getChildren().addAll(arcs); } /** - * Sets the rotationalVelocity of each arc. Each arc is 3 velocities behind the next smallest arc. The smallest uses - * the latest given velocity. + * Sets the rotationalVelocity of each arc. + * * @param rotationalVelocity The rotationalVelocity the wake should move at. - * @param rotationGoal Where the wake will rotate to if the wake is calculated to be on a straight section. This is - * used to prevent desynchronisation with the Boat polygon. - * @param velocity The real world velocity of the boat in m/s. + * @param velocity The real world velocity of the boat in m/s. */ - void setRotationalVelocity (double rotationalVelocity, double rotationGoal, double velocity) { - sum -= Math.abs(velocities[(velocityIndices[0] + 10) % 13]); - sum += Math.abs(rotationalVelocity); - max = Math.max(max, rotationalVelocity); - if (sum < (max / 3)) - rotate (rotationGoal); //In relatively straight segments the wake snaps to match the boats current position. - //This stops the wake from eventually becoming out of sync with the boat. - //This accounts for rogue rotations that are greater than what would be realistic. Value is kinda rough. - //Basically just for our internal mock. - if (Math.abs(rotationalVelocity) > 0.05) { - rotationalVelocity = 0; - rotate(rotationGoal); - } - //Update the index of the array of recent velocities that each wake uses. Each wake is 3 velocities behind the - //next smallest wake. - velocityIndices[0] = (13 + (velocityIndices[0] - 1) % 13) % 13; - velocities[velocityIndices[0]] = rotationalVelocity; - for (int i = 1; i < numWakes; i++) - velocityIndices[i] = (velocityIndices[0] + 3 * i) % 13; + void setRotationalVelocity(double rotationalVelocity, double velocity) { + rotationalVelocities[0] = rotationalVelocity; + for (int i = 1; i < numWakes; i++) { + double wakeSeparationRad = Math.toRadians(rotations[i - 1] - rotations[i]); + double shortestDistance = Math.atan2( + Math.sin(wakeSeparationRad), + Math.cos(wakeSeparationRad) + ); + double distDeg = Math.toDegrees(shortestDistance); - //Scale wakes based on velocity. - double baseRad = 20; - double rad; - for (Arc arc :arcs) { - rad = baseRad + velocity; + if (rotationalVelocities[i - 1] < 0.01 && rotationalVelocities[i - 1] > -0.01) { + rotationalVelocities[i] = distDeg / UNIFICATION_SPEED * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes); + + } else { + if (distDeg < (MAX_DIFF / numWakes)) + rotationalVelocities[i] = rotationalVelocities[i - 1] * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes); + else + rotationalVelocities[i] = rotationalVelocities[i - 1]; + } + } + + double rad = baseRad + velocity; + for (Arc arc : arcs) { arc.setRadiusX(rad); arc.setRadiusY(rad); - baseRad += 5 + (velocity / 2); + rad += (20 / numWakes) + (velocity / 2); } } /** * Arcs rotate based on the distance they would have travelled over the supplied time interval. + * * @param timeInterval the time interval, in microseconds, that the wake should move. */ - void updatePosition (long timeInterval) { + void updatePosition(long timeInterval) { for (int i = 0; i < numWakes; i++) { - rotations[i] = rotations[i] + velocities[velocityIndices[i]] * timeInterval; + rotations[i] = rotations[i] + rotationalVelocities[i] * timeInterval; arcs[i].getTransforms().setAll(new Rotate(rotations[i])); } } /** * Rotate all wakes to the given rotation. + * * @param rotation the from north angle in degrees to rotate to. */ - void rotate (double rotation) { + void rotate(double rotation) { for (int i = 0; i < arcs.length; i++) { rotations[i] = rotation; + rotationalVelocities[i] = 0; arcs[i].getTransforms().setAll(new Rotate(rotation)); } } - } diff --git a/src/main/java/seng302/models/Yacht.java b/src/main/java/seng302/models/Yacht.java index 23ba616a..8ae5d76c 100644 --- a/src/main/java/seng302/models/Yacht.java +++ b/src/main/java/seng302/models/Yacht.java @@ -151,7 +151,7 @@ public class Yacht { this.colour = colour; } - public double getVelocity() { + public Double getVelocity() { return velocity; } diff --git a/src/test/java/seng302/ColorsTest.java b/src/test/java/seng302/ColorsTest.java index 6fc07b41..03bc9ad2 100644 --- a/src/test/java/seng302/ColorsTest.java +++ b/src/test/java/seng302/ColorsTest.java @@ -8,14 +8,11 @@ import seng302.models.Colors; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -/** - * Created by ryan_ on 16/03/2017. - */ public class ColorsTest { @Test public void testNextColor() { - Color expectedColors[] = {Color.RED, Color.ORANGE, Color.YELLOW, Color.GREEN, Color.BLUE, Color.PURPLE}; + Color expectedColors[] = {Color.RED, Color.PERU, Color.SEAGREEN, Color.GREEN, Color.BLUE, Color.PURPLE}; for (int i = 0; i<6; i++) { Assert.assertEquals(expectedColors[i], Colors.getColor());