Started to implement the group over the canvas in the code. Removed basic boat redrawing and timeline and replaced with boats being placed into a group and given coordinates.

This commit is contained in:
Kusal Ekanayake
2017-04-11 17:46:02 +12:00
parent 9817fc9093
commit 15ded667fe
4 changed files with 232 additions and 57 deletions
@@ -1,7 +1,9 @@
package seng302.controllers; package seng302.controllers;
import javafx.animation.*; import javafx.animation.*;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.Group;
import javafx.scene.canvas.Canvas; import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext; import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
@@ -14,6 +16,7 @@ import seng302.models.mark.Mark;
import seng302.models.mark.MarkType; import seng302.models.mark.MarkType;
import seng302.models.mark.SingleMark; import seng302.models.mark.SingleMark;
import java.sql.Time;
import java.util.*; import java.util.*;
/** /**
@@ -27,6 +30,7 @@ public class CanvasController {
private RaceViewController raceViewController; private RaceViewController raceViewController;
private ResizableCanvas canvas; private ResizableCanvas canvas;
private Group group;
private GraphicsContext gc; private GraphicsContext gc;
private final double ORIGIN_LAT = 32.321504; private final double ORIGIN_LAT = 32.321504;
@@ -38,30 +42,51 @@ public class CanvasController {
} }
public void initialize() { public void initialize() {
raceViewController = new RaceViewController();
canvas = new ResizableCanvas(); canvas = new ResizableCanvas();
group = new Group();
canvasPane.getChildren().add(canvas); canvasPane.getChildren().add(canvas);
canvasPane.getChildren().add(group);
// Bind canvas size to stack pane size. // Bind canvas size to stack pane size.
canvas.widthProperty().bind(canvasPane.widthProperty()); canvas.widthProperty().bind(new SimpleDoubleProperty(1000));
canvas.heightProperty().bind(canvasPane.heightProperty()); canvas.heightProperty().bind(new SimpleDoubleProperty(1000));
group.minWidth(1000);
group.minHeight(1000);
// canvas.widthProperty().bind(canvasPane.widthProperty());
// canvas.heightProperty().bind(canvasPane.heightProperty());
// group.minWidth(canvas.getWidth());
// group.minHeight(canvas.getHeight());
}
public void setUpBoats(){
gc = canvas.getGraphicsContext2D(); gc = canvas.getGraphicsContext2D();
gc.save();
gc.setFill(Color.SKYBLUE);
gc.fillRect(0,0, 1000,1000);
gc.restore();
drawBoats();
drawCourse();
drawFps(12);
// overriding the handle so that it can clean canvas and redraw boats and course marks // overriding the handle so that it can clean canvas and redraw boats and course marks
AnimationTimer timer = new AnimationTimer() { AnimationTimer timer = new AnimationTimer() {
private long lastUpdate = 0; private long lastUpdate = 0;
private long lastFpsUpdate = 0; private long lastFpsUpdate = 0;
private int lastFpsCount = 0; private int lastFpsCount = 0;
private int fpsCount = 0; private int fpsCount = 0;
boolean done = true;
@Override @Override
public void handle(long now) { public void handle(long now) {
if (true){ //if statement for limiting refresh rate if needed if (true){ //if statement for limiting refresh rate if needed
gc.clearRect(0, 0, canvas.getWidth(),canvas.getHeight()); // gc.clearRect(0, 0, canvas.getWidth(),canvas.getHeight());
gc.setFill(Color.SKYBLUE); // gc.setFill(Color.SKYBLUE);
gc.fillRect(0,0,canvas.getWidth(),canvas.getHeight()); // gc.fillRect(0,0,canvas.getWidth(),canvas.getHeight());
drawCourse();
drawBoats();
drawFps(lastFpsCount);
// If race has started, draw the boats and play the timeline // If race has started, draw the boats and play the timeline
if (raceViewController.getRace().getRaceTime() > 1){ if (raceViewController.getRace().getRaceTime() > 1){
@@ -129,14 +154,26 @@ public class CanvasController {
* Draws all the boats. * Draws all the boats.
*/ */
private void drawBoats() { private void drawBoats() {
Map<Boat, TimelineInfo> timelineInfos = raceViewController.getTimelineInfos(); // Map<Boat, TimelineInfo> timelineInfos = raceViewController.getTimelineInfos();
for (Boat boat : timelineInfos.keySet()) { ArrayList<Boat> boats = raceViewController.getStartingBoats();
TimelineInfo timelineInfo = timelineInfos.get(boat); Double startingY = (ORIGIN_LAT - raceViewController.getRace().getCourse().get(0).getLatitude()) * SCALE;
Double startingX = (ORIGIN_LON - raceViewController.getRace().getCourse().get(0).getLongitude()) * SCALE;
boat.setLocation(timelineInfo.getY().doubleValue(), timelineInfo.getX().doubleValue()); for (Boat boat : boats) {
boat.moveBoatTo(startingX, startingY);
drawBoat(boat.getLongitude(), boat.getLatitude(), boat.getColor(), boat.getShortName(), boat.getSpeedInKnots(), boat.getHeading()); group.getChildren().add(boat.getWake());
group.getChildren().add(boat.getBoatObject());
group.getChildren().add(boat.getTeamNameObject());
group.getChildren().add(boat.getVelocityObject());
// drawBoat(boat.getLongitude(), boat.getLatitude(), boat.getColor(), boat.getShortName(), boat.getSpeedInKnots(), boat.getHeading());
} }
// for (Boat boat : timelineInfos.keySet()) {
// TimelineInfo timelineInfo = timelineInfos.get(boat);
//
// boat.setLocation(timelineInfo.getY().doubleValue(), timelineInfo.getX().doubleValue());
//
// drawBoat(boat.getLongitude(), boat.getLatitude(), boat.getColor(), boat.getShortName(), boat.getSpeedInKnots(), boat.getHeading());
// }
} }
/** /**
@@ -42,6 +42,7 @@ public class RaceViewController {
@FXML @FXML
private CanvasController includedCanvasController; private CanvasController includedCanvasController;
private ArrayList<Boat> startingBoats = new ArrayList<>();
private boolean displayAnnotations; private boolean displayAnnotations;
private boolean displayFps; private boolean displayFps;
private Timeline timerTimeline; private Timeline timerTimeline;
@@ -50,24 +51,29 @@ public class RaceViewController {
private Race race; private Race race;
public void initialize() { public void initialize() {
includedCanvasController.setup(this);
RaceController raceController = new RaceController(); RaceController raceController = new RaceController();
raceController.initializeRace(); raceController.initializeRace();
race = raceController.getRace(); race = raceController.getRace();
for (Boat boat : race.getBoats()) {
startingBoats.add(boat);
}
// try{
// initializeTimelines();
// }
// catch (Exception e){
// e.printStackTrace();
// }
includedCanvasController.setup(this);
includedCanvasController.setUpBoats();
initializeTimer(); initializeTimer();
initializeSettings(); initializeSettings();
try{
initializeTimelines();
}
catch (Exception e){
e.printStackTrace();
}
//set wind direction!!!!!!! can't find another place to put my code --haoming //set wind direction!!!!!!! can't find another place to put my code --haoming
double windDirection = new ConfigParser("/config/config.xml").getWindDirection(); double windDirection = new ConfigParser("/config/config.xml").getWindDirection();
windDirectionText.setText(String.format("%.1f°", windDirection)); windDirectionText.setText(String.format("%.1f°", windDirection));
windArrowText.setRotate(windDirection); windArrowText.setRotate(windDirection);
} }
private void initializeSettings(){ private void initializeSettings(){
@@ -114,39 +120,39 @@ public class RaceViewController {
*/ */
private void initializeTimelines() { private void initializeTimelines() {
HashMap<Boat, List> boat_events = race.getEvents(); HashMap<Boat, List> boat_events = race.getEvents();
for (Boat boat : boat_events.keySet()) { for (Boat boat : boat_events.keySet()) {
// x, y are the real time coordinates startingBoats.add(boat);
DoubleProperty x = new SimpleDoubleProperty(); // // x, y are the real time coordinates
DoubleProperty y = new SimpleDoubleProperty(); // DoubleProperty x = new SimpleDoubleProperty();
// DoubleProperty y = new SimpleDoubleProperty();
List<KeyFrame> keyFrames = new ArrayList<>(); //
List<Event> events = boat_events.get(boat); // List<KeyFrame> keyFrames = new ArrayList<>();
// List<Event> events = boat_events.get(boat);
// iterates all events and convert each event to keyFrame, then add them into a list //
for (Event event : events) { // // iterates all events and convert each event to keyFrame, then add them into a list
if (event.getIsFinishingEvent()) { // for (Event event : events) {
keyFrames.add( // if (event.getIsFinishingEvent()) {
new KeyFrame(Duration.seconds(event.getTime()), // keyFrames.add(
onFinished -> {race.setBoatFinished(boat); handleEvent(event);}, // new KeyFrame(Duration.seconds(event.getTime()),
new KeyValue(x, event.getThisMark().getLatitude()), // onFinished -> {race.setBoatFinished(boat); handleEvent(event);},
new KeyValue(y, event.getThisMark().getLongitude()) // new KeyValue(x, event.getThisMark().getLatitude()),
) // new KeyValue(y, event.getThisMark().getLongitude())
); // )
} else { // );
keyFrames.add( // } else {
new KeyFrame(Duration.seconds(event.getTime()), // keyFrames.add(
onFinished ->{ // new KeyFrame(Duration.seconds(event.getTime()),
handleEvent(event); // onFinished ->{
boat.setHeading(event.getBoatHeading()); // handleEvent(event);
}, // boat.setHeading(event.getBoatHeading());
new KeyValue(x, event.getThisMark().getLatitude()), // },
new KeyValue(y, event.getThisMark().getLongitude()) // new KeyValue(x, event.getThisMark().getLatitude()),
) // new KeyValue(y, event.getThisMark().getLongitude())
); // )
} // );
} // }
timelineInfos.put(boat, new TimelineInfo(new Timeline(keyFrames.toArray(new KeyFrame[keyFrames.size()])), x, y)); // }
// timelineInfos.put(boat, new TimelineInfo(new Timeline(keyFrames.toArray(new KeyFrame[keyFrames.size()])), x, y));
} }
setRaceDuration(); setRaceDuration();
} }
@@ -277,4 +283,8 @@ public class RaceViewController {
public Map<Boat, TimelineInfo> getTimelineInfos() { public Map<Boat, TimelineInfo> getTimelineInfos() {
return timelineInfos; return timelineInfos;
} }
public ArrayList<Boat> getStartingBoats(){
return startingBoats;
}
} }
+102
View File
@@ -1,12 +1,24 @@
package seng302.models; package seng302.models;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.shape.Polygon;
import javafx.scene.text.Text;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
/** /**
* Represents a boat in the race. * Represents a boat in the race.
*/ */
public class Boat { public class Boat {
private static final double TEAMNAME_X_OFFSET = 15d;
private static final double TEAMNAME_Y_OFFSET = -20d;
private static final double VELOCITY_X_OFFSET = 15d;
private static final double VELOCITY_Y_OFFSET = -10d;
private static final double VELOCITY_WAKE_RATIO = 2d; //Ratio for deciding how long the wake will be wrt velocity
private static final double BOAT_HEIGHT = 15d;
private static final double BOAT_WIDTH = 10d;
private String teamName; // The name of the team, this is also the name of the boat private String teamName; // The name of the team, this is also the name of the boat
private double velocity; // In meters/second private double velocity; // In meters/second
private double lat; // Boats position private double lat; // Boats position
@@ -17,6 +29,12 @@ public class Boat {
private double heading; private double heading;
private String shortName; private String shortName;
//Graphical
private Polygon boatObject;
private Polygon wake;
private Text teamNameObject;
private Text velocityObject;
public Boat(String teamName) { public Boat(String teamName) {
this.teamName = teamName; this.teamName = teamName;
this.velocity = 10; // Default velocity this.velocity = 10; // Default velocity
@@ -39,6 +57,13 @@ public class Boat {
this.distanceToNextMark = 0.0; this.distanceToNextMark = 0.0;
this.color = Colors.getColor(); this.color = Colors.getColor();
this.shortName = shortName; this.shortName = shortName;
this.boatObject = new Polygon();
this.boatObject.getPoints().addAll(BOAT_WIDTH /2,0.0,
BOAT_WIDTH, BOAT_HEIGHT,
0.0, BOAT_HEIGHT);
createWake();
this.teamNameObject = new Text(shortName);
this.velocityObject = new Text(Double.toString(boatVelocity) + "ms");
} }
/** /**
@@ -117,6 +142,11 @@ public class Boat {
} }
public void setHeading(double heading){ public void setHeading(double heading){
boatObject.getTransforms().clear();
wake.getTransforms().clear();
wake.getTransforms().add(new Translate(0, BOAT_HEIGHT));
wake.getTransforms().add(new Rotate(heading, BOAT_WIDTH/2, -BOAT_HEIGHT));
boatObject.getTransforms().add(new Rotate(heading, BOAT_WIDTH/2, 0));
this.heading = heading; this.heading = heading;
} }
@@ -127,4 +157,76 @@ public class Boat {
public String getShortName(){ public String getShortName(){
return this.shortName; return this.shortName;
} }
/**
* Moves the boat and its children annotations from its current coordinates by specified amounts.
* @param x The amount to move the X coordinate by
* @param y The amount to move the Y coordinate by
*/
void moveBoatBy(Double x, Double y) {
boatObject.setLayoutX(boatObject.getLayoutX() + x);
boatObject.setLayoutY(boatObject.getLayoutY() + y);
boatObject.relocate(boatObject.getLayoutX(), boatObject.getLayoutY());
teamNameObject.setX(teamNameObject.getX() + x);
teamNameObject.setY(teamNameObject.getY() + y);
teamNameObject.relocate(teamNameObject.getX(), teamNameObject.getY());
velocityObject.setX(velocityObject.getX() + x);
velocityObject.setY(velocityObject.getY() + y);
velocityObject.relocate(velocityObject.getX(), velocityObject.getY());
wake.setLayoutX(wake.getLayoutX() + x);
wake.setLayoutY(wake.getLayoutY() + y);
wake.relocate(wake.getLayoutX(), wake.getLayoutY());
}
/**
* Moves the boat and its children annotations to coordinates specified
* @param x The X coordinate to move the boat to
* @param y The Y coordinate to move the boat to
*/
public void moveBoatTo(Double x, Double y) {
boatObject.setLayoutX(x);
boatObject.setLayoutY(y);
boatObject.relocate(boatObject.getLayoutX(), boatObject.getLayoutY());
teamNameObject.setX(x + TEAMNAME_X_OFFSET);
teamNameObject.setY(y + TEAMNAME_Y_OFFSET);
teamNameObject.relocate(teamNameObject.getX(), teamNameObject.getY());
velocityObject.setX(x + VELOCITY_X_OFFSET);
velocityObject.setY(y + VELOCITY_Y_OFFSET);
velocityObject.relocate(velocityObject.getX(), velocityObject.getY());
wake.setLayoutX(x);
wake.setLayoutY(y);
wake.relocate(wake.getLayoutX(), wake.getLayoutY());
}
private void createWake(){
wake = new Polygon();
wake.setFill(Color.LIGHTSKYBLUE);
wake.getPoints().addAll(5.0,0.0,
10.0, velocity * VELOCITY_WAKE_RATIO,
0.0, velocity * VELOCITY_WAKE_RATIO);
}
public Polygon getWake() {
return wake;
}
public Polygon getBoatObject() {
return boatObject;
}
public Text getTeamNameObject() {
return teamNameObject;
}
public Text getVelocityObject() {
return velocityObject;
}
} }
+28 -2
View File
@@ -16,7 +16,7 @@ public class Event {
private Mark mark1; // This mark private Mark mark1; // This mark
private Mark mark2; // Next mark private Mark mark2; // Next mark
private int markPosInRace; // the position of the current mark in the race course private int markPosInRace; // the position of the current mark in the race course
private double heading;
private final double ORIGIN_LAT = 32.320504; private final double ORIGIN_LAT = 32.320504;
private final double ORIGIN_LON = -64.857063; private final double ORIGIN_LON = -64.857063;
private final double SCALE = 16000; private final double SCALE = 16000;
@@ -36,6 +36,8 @@ public class Event {
this.mark1 = mark1; this.mark1 = mark1;
this.mark2 = mark2; this.mark2 = mark2;
this.markPosInRace = markPosInRace; this.markPosInRace = markPosInRace;
this.heading = angleFromCoordinate(mark1, mark2);
} }
/** /**
@@ -92,7 +94,7 @@ public class Event {
if (this.isFinishingEvent) { if (this.isFinishingEvent) {
return (this.getTimeString() + ", " + this.getBoat().getTeamName() + " finished the race"); return (this.getTimeString() + ", " + this.getBoat().getTeamName() + " finished the race");
} }
System.out.println(this.getDistanceBetweenMarks()); // System.out.println(this.getDistanceBetweenMarks());
return (this.getTimeString() + ", " + this.getBoat().getTeamName() + " passed " + this.mark1.getName() + " going heading " + this.getBoatHeading() + "°"); return (this.getTimeString() + ", " + this.getBoat().getTeamName() + " passed " + this.mark1.getName() + " going heading " + this.getBoatHeading() + "°");
} }
@@ -138,6 +140,30 @@ public class Event {
} }
/**
* Calculates the angle between to angular co-ordinates on a sphere.
*
* @param geoPointOne first geographical location
* @param geoPointTwo second geographical location
* @return the angle from point one to point two
*/
private Double angleFromCoordinate(Mark geoPointOne, Mark geoPointTwo) {
if (geoPointTwo == null)
return null;
double x1 = geoPointOne.getLatitude();
double y1 = -geoPointOne.getLongitude();
double x2 = geoPointTwo.getLatitude();
double y2 = -geoPointTwo.getLongitude();
return Math.toDegrees(Math.atan2(x2-x1, y2-y1));
}
public double getHeading() {
return heading;
}
public Mark getThisMark() { public Mark getThisMark() {
return this.mark1; return this.mark1;
} }