Merge branch 'master'

Conflicts:
	src/main/java/seng302/controllers/CanvasController.java
	src/main/resources/RaceView.fxml
This commit is contained in:
Peter
2017-03-23 15:16:11 +13:00
20 changed files with 661 additions and 133 deletions
@@ -1,18 +1,18 @@
package seng302.controllers;
import javafx.animation.AnimationTimer;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.animation.*;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.TextArea;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.util.Duration;
import seng302.models.Boat;
import seng302.models.Event;
@@ -22,13 +22,13 @@ import seng302.models.mark.GateMark;
import seng302.models.mark.Mark;
import seng302.models.mark.MarkType;
import seng302.models.mark.SingleMark;
import seng302.models.parsers.ConfigParser;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import static java.lang.Math.abs;
/**
* Created by ptg19 on 15/03/17.
* Modified by Haoming Yin (hyi25) on 20/3/2017.
@@ -42,13 +42,94 @@ public class CanvasController {
private Race race;
private GraphicsContext gc;
private HashMap<Boat, TimelineInfo> timelineInfos;
private final double VIEW_CORNER_LAT = 32.280808;
private final double VIEW_CORNER_LON = -64.858401;
private AnchorPane raceResults;
private final double ORIGIN_LAT = 32.320504;
private final double ORIGIN_LON = -64.857063;
@FXML
private AnchorPane contentAnchorPane;
@FXML
private Text windArrowText, windDirectionText;
@FXML Pane raceTimer;
private Animation.Status raceStatus = Animation.Status.PAUSED;
/**
* Display the list of boats in the order they finished the race
*/
private void loadRaceResultView() {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/FinishView.fxml"));
loader.setController(new RaceResultController(race));
try {
contentAnchorPane.getChildren().removeAll();
contentAnchorPane.getChildren().clear();
contentAnchorPane.getChildren().addAll((Pane) loader.load());
} catch (javafx.fxml.LoadException e) {
System.err.println(e.getCause());
} catch (IOException e) {
System.err.println(e);
}
}
/**
* Load the race timer
*/
private void loadTimerView(){
FXMLLoader loader = new FXMLLoader(getClass().getResource("/raceTimer.fxml"));
loader.setController(new RaceTimerController(race));
try{
raceTimer.getChildren().clear();
raceTimer.getChildren().removeAll();
raceTimer.getChildren().addAll((Pane) loader.load());
}
catch(javafx.fxml.LoadException e){
System.out.println(e);
}
catch(IOException e){
System.out.println(e);
}
}
/**
* Play each boats timeline
*/
private void playTimelines(){
for (TimelineInfo timelineInfo : timelineInfos.values()){
Timeline timeline = timelineInfo.getTimeline();
if (timeline.getStatus() == Animation.Status.PAUSED){
timeline.play();
}
}
raceStatus = Animation.Status.RUNNING;
}
/**
* Pause each boats timeline
*/
private void pauseTimelines(){
for (TimelineInfo timelineInfo : timelineInfos.values()){
Timeline timeline = timelineInfo.getTimeline();
if (timeline.getStatus() == Animation.Status.RUNNING){
timeline.pause();
}
}
raceStatus = Animation.Status.PAUSED;
}
/**
* Initialize the controller
*/
public void initialize() {
gc = canvas.getGraphicsContext2D();
gc.scale(22, 22);
gc.scale(15, 15);
RaceController raceController = new RaceController();
raceController.initializeRace();
race = raceController.getRace();
@@ -62,17 +143,53 @@ public class CanvasController {
drawCourse();
drawBoats();
// If race has started, draw the boats and play the timeline
if (race.getRaceTime() > 1){
playTimelines();
}
// Race has not started, pause the timelines
else if (race.getRaceTime() < 1 || raceStatus == Animation.Status.RUNNING){
pauseTimelines();
}
}
};
generateTimeline();
// starts the timer and reads events from each boat's time line
timer.start();
loadTimerView();
Double maxDuration = 0.0;
Timeline maxTimeline = null;
for (TimelineInfo timelineInfo : timelineInfos.values()) {
Timeline timeline = timelineInfo.getTimeline();
System.out.println();
if (timeline.getTotalDuration().toMillis() >= maxDuration) {
maxDuration = timeline.getTotalDuration().toMillis();
maxTimeline = timeline;
}
// Timelines are paused by default
timeline.play();
timeline.pause();
raceStatus = Animation.Status.RUNNING;
}
maxTimeline.setOnFinished(event -> {
race.setRaceFinished();
loadRaceResultView();
});
//set wind direction!!!!!!! can't find another place to put my code --haoming
double windDirection = new ConfigParser("doc/examples/config.xml").getWindDirection();
windDirectionText.setText(String.format("%.1f°", windDirection));
windArrowText.setRotate(windDirection);
}
/**
@@ -97,13 +214,22 @@ public class CanvasController {
// iterates all events and convert each event to keyFrame, then add them into a list
for (Event event : events) {
keyFrames.add(
new KeyFrame(Duration.seconds(event.getTime() / 60 / 60 / 5),
onFinished,
new KeyValue(x, event.getThisMark().getLatitude()),
new KeyValue(y, event.getThisMark().getLongitude())
)
);
if (event.getIsFinishingEvent()) {
keyFrames.add(
new KeyFrame(Duration.seconds(event.getTime() / 60 / 60 / 5),
event1 -> race.setBoatFinished(boat),
new KeyValue(x, event.getThisMark().getLatitude()),
new KeyValue(y, event.getThisMark().getLongitude())
)
);
} else {
keyFrames.add(
new KeyFrame(Duration.seconds(event.getTime() / 60 / 60 / 5),
new KeyValue(x, event.getThisMark().getLatitude()),
new KeyValue(y, event.getThisMark().getLongitude())
)
);
}
}
// uses the lists generated above to create a Timeline for the boat.
@@ -121,22 +247,6 @@ public class CanvasController {
}
}
/**
* @return the distance between the two marks
*/
public double getDistanceBetweenMarks(Mark mark1, Mark mark2) {
double earth_radius = 6378.137;
double dLat = mark2.getLatitude() * Math.PI / 180 - mark1.getLatitude() * Math.PI / 180;
double dLon = mark2.getLongitude() * Math.PI / 180 - mark1.getLongitude() * Math.PI / 180;
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(mark1.getLatitude() * Math.PI / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double d = earth_radius * c;
return d * 1000;
}
/**
* Draws a boat with given (x, y) position in the given color
*
@@ -146,13 +256,11 @@ public class CanvasController {
*/
private void drawBoat(double lat, double lon, Color color) {
// Latitude
//Double x = (MAP_WIDTH / 360.0) * (180 + lon);
//Double y = (MAP_HEIGHT / 180.0) * (80 - lat);
double x = (lat - VIEW_CORNER_LAT) * 1000; // to prevent negative longitude
double y = (lon - VIEW_CORNER_LON) * 1000; // to prevent negative latitude
double x = (lon - ORIGIN_LON) * 1000;
double y = (ORIGIN_LAT - lat) * 1000;
double diameter = 0.5;
gc.setFill(color);
gc.fillOval(x, y, diameter, diameter);
}
@@ -163,7 +271,7 @@ public class CanvasController {
private void drawCourse() {
for (Mark mark : race.getCourse()) {
if (mark.getMarkType() == MarkType.SINGLE_MARK) {
drawSingleMark((SingleMark) mark);
drawSingleMark((SingleMark) mark, Color.BLACK);
} else if (mark.getMarkType() == MarkType.GATE_MARK) {
drawGateMark((GateMark) mark);
}
@@ -175,12 +283,12 @@ public class CanvasController {
*
* @param singleMark
*/
private void drawSingleMark(SingleMark singleMark) {
double x = (singleMark.getLatitude() - VIEW_CORNER_LAT) * 1000; // to prevent negative longitude
double y = (singleMark.getLongitude() - VIEW_CORNER_LON) * 1000; // to prevent negative latitude
private void drawSingleMark(SingleMark singleMark, Color color) {
double x = (singleMark.getLongitude() - ORIGIN_LON) * 1000;
double y = (ORIGIN_LAT - singleMark.getLatitude()) * 1000;
gc.setFill(Color.BLACK);
gc.fillOval(x, y, 0.5, 0.5);
gc.setFill(color);
gc.fillRect(x,y,0.5,0.5);
}
/**
@@ -189,7 +297,35 @@ public class CanvasController {
* @param gateMark
*/
private void drawGateMark(GateMark gateMark) {
drawSingleMark(gateMark.getSingleMark1());
drawSingleMark(gateMark.getSingleMark2());
Color color = Color.BLUE;
if (gateMark.getName().equals("Start")){
color = Color.RED;
}
if (gateMark.getName().equals("Finish")){
color = Color.GREEN;
}
drawSingleMark(gateMark.getSingleMark1(), color);
drawSingleMark(gateMark.getSingleMark2(), color);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setStroke(color);
// Convert lat/lon to x,y
double x1 = (gateMark.getSingleMark1().getLongitude()- ORIGIN_LON) * 1000;
double y1 = (ORIGIN_LAT - gateMark.getSingleMark1().getLatitude()) * 1000;
double x2 = (gateMark.getSingleMark2().getLongitude() - ORIGIN_LON) * 1000;
double y2 = (ORIGIN_LAT - gateMark.getSingleMark2().getLatitude()) * 1000;
gc.setLineWidth(0.07);
gc.strokeLine(x1, y1, x2, y2);
}
}
public Race getRace(){
return this.race;
}
}
@@ -1,18 +0,0 @@
package seng302.controllers;
import javafx.fxml.FXML;
import javafx.scene.Parent;
import javafx.scene.layout.AnchorPane;
/**
* Created by ptg19 on 20/03/17.
*/
public class Controller {
@FXML private AnchorPane window;
@FXML private Parent raceView;
@FXML private RaceController raceViewController;
//^ this is automatic fxml linking based off http://blog.buildpath.de/fxml-composition-how-to-get-the-controller-of-an-included-fxml-view-nested-controllers/
// From googling it's probably better to just add a child however you did that in your 301 michael, it kinda depends on how we are going to
// make an event for changing the screen.
}
@@ -0,0 +1,38 @@
package seng302.controllers;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
/**
* Created by michaelrausch on 21/03/17.
*/
public class MasterViewController implements Initializable {
@FXML
private AnchorPane contentPane;
private void setContentPane(String jfxUrl){
try{
contentPane.getChildren().removeAll();
contentPane.getChildren().clear();
contentPane.getChildren().addAll((Pane) FXMLLoader.load(getClass().getResource(jfxUrl)));
}
catch(javafx.fxml.LoadException e){
System.err.println(e.getCause());
}
catch(IOException e){
System.err.println(e);
}
}
@Override
public void initialize(URL location, ResourceBundle resources) {
setContentPane("/RaceView.fxml");
}
}
@@ -2,9 +2,8 @@ package seng302.controllers;
import seng302.models.Boat;
import seng302.models.OldFileParser;
import seng302.models.parsers.*;
import seng302.models.mark.*;
import seng302.models.Race;
import seng302.models.parsers.CourseParser;
import java.io.FileNotFoundException;
import java.lang.reflect.Array;
@@ -20,6 +19,7 @@ import java.util.Random;
*/
public class RaceController {
Race race = null;
public void initializeRace() {
String raceConfigFile;
raceConfigFile = "doc/examples/config.json";
@@ -55,13 +55,18 @@ public class RaceController {
//get race size
int numberOfBoats = (int) fp.getRaceSize();
int boatsAdded = 0;
//get time scale
double timeScale = fp.getTimeScale();
race.setTimeScale(timeScale);
for (Map<String, Object> team : teams) {
boatNames.add((String) team.get("team-name"));
if (boatsAdded < numberOfBoats){
boatNames.add((String) team.get("team-name"));
race.addBoat(new Boat(team.get("team-name").toString(), (Double) (team.get("velocity"))));
}
boatsAdded++;
}
// Shuffle team names
@@ -72,11 +77,6 @@ public class RaceController {
return null;
}
// Add boats to the race
for (int i = 0; i < numberOfBoats; i++) {
race.addBoat(new Boat(boatNames.get(i), (Double) (teams.get(i).get("velocity"))));
}
CourseParser cp = new CourseParser("doc/examples/course.xml");
race.addCourse(cp.getCourse());
@@ -0,0 +1,37 @@
package seng302.controllers;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import seng302.models.Race;
import java.net.URL;
import java.util.ResourceBundle;
/**
* Created by ptg19 on 20/03/17.
*/
public class RaceResultController implements Initializable{
@FXML private AnchorPane window;
@FXML private VBox resultsVBox;
private Race race;
RaceResultController(Race race){
this.race = race;
}
@Override
public void initialize(URL location, ResourceBundle resources) {
int boatPosition = this.race.getFinishedBoats().length;
for (int i = this.race.getFinishedBoats().length - 1; i >= 0; i--){
resultsVBox.getChildren().add(0, new Text(boatPosition + ": " + this.race.getFinishedBoats()[i].getTeamName()));
boatPosition--;
}
}
}
@@ -0,0 +1,78 @@
package seng302.controllers;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.text.Text;
import javafx.util.Duration;
import seng302.models.Race;
import java.net.URL;
import java.util.ResourceBundle;
public class RaceTimerController implements Initializable{
private Timeline timeline;
private Race race;
@FXML
private Text timerLabel;
/**
* Convert seconds to a string of the format mm:ss
* @param time the time in seconds
* @return a formatted string
*/
public String convertTimeToMinutesSeconds(int time){
if (time < 0){
return String.format("-%02d:%02d", (time * -1) / 60, (time * -1)% 60);
}
return String.format("%02d:%02d", time / 60, time % 60);
}
/**
* Controller to control the race timer
* @param race the race the timer is timing
*/
public RaceTimerController(Race race){
this.race = race;
}
@Override
public void initialize(URL location, ResourceBundle resources) {
timeline = new Timeline();
timeline.setCycleCount(Timeline.INDEFINITE);
// Run timer update every second
timeline.getKeyFrames().add(
new KeyFrame(Duration.seconds(1),
event -> {
// Stop timer if race is finished
if (this.race.isRaceFinished()){
this.timeline.stop();
}
else{
timerLabel.setText(convertTimeToMinutesSeconds(race.getRaceTime()));
this.race.incrementRaceTime();
}
})
);
// Start the timer
timeline.playFromStart();
}
/**
* Stop the race timer
*/
public void stop(){
timeline.stop();
}
/**
* Start the race timer
*/
public void start(){
timeline.play();
}
}