Actually added sub modules this time
#story[778]
This commit is contained in:
@@ -0,0 +1,410 @@
|
||||
package seng302.Model;
|
||||
|
||||
|
||||
import javafx.scene.canvas.Canvas;
|
||||
import javafx.scene.canvas.GraphicsContext;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.paint.Paint;
|
||||
import javafx.scene.transform.Rotate;
|
||||
import seng302.*;
|
||||
import seng302.Controllers.RaceController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This creates a JavaFX Canvas that is fills it's parent.
|
||||
* Cannot be downsized.
|
||||
* Created by fwy13 on 17/03/17.
|
||||
*/
|
||||
public class ResizableRaceCanvas extends Canvas {
|
||||
private GraphicsContext gc;
|
||||
private RaceMap map;
|
||||
private List<BoatInRace> boats;
|
||||
private boolean raceAnno = true;
|
||||
private boolean annoName = true;
|
||||
private boolean annoAbbrev = true;
|
||||
private boolean annoSpeed = true;
|
||||
private boolean annoPath = true;
|
||||
private ArrayList<GPSCoordinate> raceBoundaries;
|
||||
double[] xpoints = {}, ypoints = {};
|
||||
|
||||
public ResizableRaceCanvas(RaceDataSource raceData) {
|
||||
gc = this.getGraphicsContext2D();
|
||||
// Redraw canvas when size changes.
|
||||
widthProperty().addListener(evt -> drawRaceMap());
|
||||
heightProperty().addListener(evt -> drawRaceMap());
|
||||
|
||||
double lat1 = raceData.getMapTopLeft().getLatitude();
|
||||
double long1 = raceData.getMapTopLeft().getLongitude();
|
||||
double lat2 = raceData.getMapBottomRight().getLatitude();
|
||||
double long2 = raceData.getMapBottomRight().getLongitude();
|
||||
setMap(new RaceMap(lat1, long1, lat2, long2, (int) getWidth(), (int) getHeight()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the boats that are to be displayed in this race.
|
||||
*
|
||||
* @param boats in race
|
||||
*/
|
||||
public void setBoats(List<BoatInRace> boats) {
|
||||
this.boats = boats;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the RaceMap that the RaceCanvas is to be displaying for.
|
||||
*
|
||||
* @param map race map
|
||||
*/
|
||||
public void setMap(RaceMap map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the mark of a race as a circle.
|
||||
*
|
||||
* @param graphCoordinate Latitude and Logintude in GraphCoordinate that it is to be displayed as.
|
||||
* @param paint Colour the mark is to be coloured.
|
||||
* @see GraphCoordinate
|
||||
* @see Color
|
||||
* @see Paint
|
||||
*/
|
||||
public void displayMark(GraphCoordinate graphCoordinate, Paint paint) {
|
||||
double d = 25;
|
||||
gc.setFill(paint);
|
||||
gc.fillOval(graphCoordinate.getX() - (d / 2), graphCoordinate.getY() - (d / 2), d, d);
|
||||
}
|
||||
|
||||
public void displayBoat(BoatInRace boat, double angle) {
|
||||
GraphCoordinate pos = this.map.convertGPS(boat.getCurrentPosition());
|
||||
Paint paint = boat.getColour();
|
||||
|
||||
double[] x = {pos.getX() - 6, pos.getX(), pos.getX() + 6};
|
||||
double[] y = {pos.getY() + 12, pos.getY() - 12, pos.getY() + 12};
|
||||
gc.setFill(paint);
|
||||
|
||||
gc.save();
|
||||
rotate(angle, pos.getX(), pos.getY());
|
||||
gc.fillPolygon(x, y, 3);
|
||||
gc.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a line on the map with rectangles on the starting and ending point of the line.
|
||||
*
|
||||
* @param graphCoordinateA Starting Point of the line in GraphCoordinate.
|
||||
* @param graphCoordinateB End Point of the line in GraphCoordinate.
|
||||
* @param paint Colour the line is to coloured.
|
||||
* @see GraphCoordinate
|
||||
* @see Color
|
||||
* @see Paint
|
||||
*/
|
||||
private void displayLine(GraphCoordinate graphCoordinateA, GraphCoordinate graphCoordinateB, Paint paint) {
|
||||
gc.setStroke(paint);
|
||||
gc.setFill(paint);
|
||||
gc.fillOval(graphCoordinateA.getX() - 3, graphCoordinateA.getY() - 3, 6, 6);
|
||||
gc.fillOval(graphCoordinateB.getX() - 3, graphCoordinateB.getY() - 3, 6, 6);
|
||||
gc.strokeLine(graphCoordinateA.getX(), graphCoordinateA.getY(), graphCoordinateB.getX(), graphCoordinateB.getY());
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a point on the Canvas
|
||||
*
|
||||
* @param graphCoordinate Coordinate that the point is to be displayed at.
|
||||
* @param paint Colour that the boat is to be coloured.
|
||||
* @see GraphCoordinate
|
||||
* @see Paint
|
||||
* @see Color
|
||||
*/
|
||||
private void displayPoint(GraphCoordinate graphCoordinate, Paint paint) {
|
||||
gc.setFill(paint);
|
||||
gc.fillOval(graphCoordinate.getX(), graphCoordinate.getY(), 10, 10);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Displays an arrow on the Canvas
|
||||
*
|
||||
* @param coordinate Coordinate that the arrow is to be displayed at.
|
||||
* @param angle Angle that the arrow is to be facing in degrees 0 degrees = North (Up).
|
||||
* @see GraphCoordinate
|
||||
*/
|
||||
private void displayArrow(GraphCoordinate coordinate, int angle) {
|
||||
gc.save();
|
||||
rotate(angle, coordinate.getX(), coordinate.getY());
|
||||
gc.setFill(Color.BLACK);
|
||||
gc.fillPolygon(new double[]{coordinate.getX() - 12, coordinate.getX() - 6, coordinate.getX(), coordinate.getX() - 4, coordinate.getX() - 4, coordinate.getX() - 8, coordinate.getX() - 8},
|
||||
new double[]{coordinate.getY() - 5, coordinate.getY() - 20, coordinate.getY() - 5, coordinate.getY() - 5, coordinate.getY() + 20, coordinate.getY() + 20, coordinate.getY() - 5},
|
||||
7);
|
||||
gc.restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates things on the canvas Note: this must be called in between gc.save() and gc.restore() else they will rotate everything
|
||||
*
|
||||
* @param angle Bearing angle to rotate at in degrees
|
||||
* @param px Pivot point x of rotation.
|
||||
* @param py Pivot point y of rotation.
|
||||
*/
|
||||
private void rotate(double angle, double px, double py) {
|
||||
Rotate r = new Rotate(angle, px, py);
|
||||
gc.setTransform(r.getMxx(), r.getMyx(), r.getMxy(), r.getMyy(), r.getTx(), r.getTy());
|
||||
}
|
||||
|
||||
/**
|
||||
* Display given name and speed of boat at a graph coordinate
|
||||
*
|
||||
* @param name name of the boat
|
||||
* @param abbrev abbreviation of the boat name
|
||||
* @param speed speed of the boat
|
||||
* @param coordinate coordinate the text appears
|
||||
*/
|
||||
private void displayText(String name, String abbrev, double speed, GraphCoordinate coordinate) {
|
||||
String text = "";
|
||||
//Check name toggle value
|
||||
if (annoName){
|
||||
text += String.format("%s ", name);
|
||||
}
|
||||
//Check abbreviation toggle value
|
||||
if (annoAbbrev){
|
||||
text += String.format("%s ", abbrev);
|
||||
}
|
||||
//Check speed toggle value
|
||||
if (annoSpeed){
|
||||
text += String.format("%.2fkn", speed);
|
||||
}
|
||||
//String text = String.format("%s, %2$.2fkn", name, speed);
|
||||
long xCoord = coordinate.getX() + 20;
|
||||
long yCoord = coordinate.getY();
|
||||
if (xCoord + (text.length() * 7) >= getWidth()) {
|
||||
xCoord -= text.length() * 7;
|
||||
}
|
||||
if (yCoord - (text.length() * 2) <= 0) {
|
||||
yCoord += 30;
|
||||
}
|
||||
gc.fillText(text, xCoord, yCoord);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws race map with up to date data.
|
||||
*/
|
||||
public void update() {
|
||||
this.drawRaceMap();
|
||||
this.updateBoats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw boundary of the race.
|
||||
*/
|
||||
public void drawBoundaries() {
|
||||
if (this.raceBoundaries == null) {
|
||||
return;
|
||||
}
|
||||
gc.setFill(Color.AQUA);
|
||||
setRaceBoundCoordinates();
|
||||
gc.fillPolygon(xpoints, ypoints, xpoints.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the Race Map
|
||||
*/
|
||||
public void drawRaceMap() {
|
||||
|
||||
double width = getWidth();
|
||||
double height = getHeight();
|
||||
|
||||
gc.clearRect(0, 0, width, height);
|
||||
|
||||
if (map == null) {
|
||||
return;//TODO this should return a exception in the future
|
||||
}
|
||||
this.map.setHeight((int) height);
|
||||
this.map.setWidth((int) width);
|
||||
|
||||
//finish line
|
||||
gc.setLineWidth(2);
|
||||
drawBoundaries();
|
||||
GraphCoordinate finishLineCoord1 = this.map.convertGPS(Constants.finishLineMarker1);
|
||||
GraphCoordinate finishLineCoord2 = this.map.convertGPS(Constants.finishLineMarker2);
|
||||
displayLine(finishLineCoord1, finishLineCoord2, Color.DARKRED);
|
||||
//marks
|
||||
GraphCoordinate markCoord = this.map.convertGPS(Constants.mark1);
|
||||
GraphCoordinate windwardGate1 = this.map.convertGPS(Constants.windwardGate1);
|
||||
GraphCoordinate windwardGate2 = this.map.convertGPS(Constants.windwardGate2);
|
||||
GraphCoordinate leewardGate1 = this.map.convertGPS(Constants.leewardGate1);
|
||||
GraphCoordinate leewardGate2 = this.map.convertGPS(Constants.leewardGate2);
|
||||
displayMark(markCoord, Color.GOLD);
|
||||
displayLine(windwardGate1, windwardGate2, Color.DARKCYAN);
|
||||
displayLine(leewardGate1, leewardGate2, Color.DARKVIOLET);
|
||||
//start line
|
||||
GraphCoordinate startline1 = this.map.convertGPS(Constants.startLineMarker1);
|
||||
GraphCoordinate startline2 = this.map.convertGPS(Constants.startLineMarker2);
|
||||
|
||||
displayLine(startline1, startline2, Color.GREEN);
|
||||
|
||||
updateBoats();
|
||||
|
||||
//display wind direction arrow - specify origin point and angle - angle now set to random angle
|
||||
displayArrow(new GraphCoordinate((int) getWidth() - 40, 40), 150);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a boat at a certain GPSCoordinate
|
||||
*
|
||||
* @param colour Colour to colour boat.
|
||||
* @param gpsCoordinates GPScoordinate that the boat is to be drawn at.
|
||||
* @see GPSCoordinate
|
||||
* @see Color
|
||||
*/
|
||||
public void drawBoat(Color colour, GPSCoordinate gpsCoordinates) {
|
||||
GraphCoordinate graphCoordinate = this.map.convertGPS(gpsCoordinates);
|
||||
displayPoint(graphCoordinate, colour);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the raceAnno value
|
||||
*/
|
||||
public void toggleAnnotations() {
|
||||
if (raceAnno) {
|
||||
raceAnno = false;
|
||||
} else {
|
||||
raceAnno = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle name display in annotation
|
||||
*/
|
||||
public void toggleAnnoName() {
|
||||
if (annoName) {
|
||||
annoName = false;
|
||||
} else {
|
||||
annoName = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void toggleBoatPath() {
|
||||
if (annoPath) {
|
||||
annoPath = false;
|
||||
} else {
|
||||
annoPath = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle abbreviation display in annotation
|
||||
*/
|
||||
public void toggleAnnoAbbrev() {
|
||||
if (annoAbbrev) {
|
||||
annoAbbrev = false;
|
||||
} else {
|
||||
annoAbbrev = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle speed display in annotation
|
||||
*/
|
||||
public void toggleAnnoSpeed() {
|
||||
if (annoSpeed) {
|
||||
annoSpeed = false;
|
||||
} else {
|
||||
annoSpeed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws boats while race in progress, when leg heading is set.
|
||||
*/
|
||||
public void updateBoats() {
|
||||
if (boats != null) {
|
||||
for (BoatInRace boat : boats) {
|
||||
boolean finished = boat.getCurrentLeg().getName().equals("Finish") || boat.getCurrentLeg().getName().equals("DNF");
|
||||
boolean isStart = boat.isStarted();
|
||||
if (!finished && isStart) {
|
||||
displayBoat(boat, boat.getHeading());
|
||||
GraphCoordinate wakeFrom = this.map.convertGPS(boat.getCurrentPosition());
|
||||
GraphCoordinate wakeTo = this.map.convertGPS(boat.getWake());
|
||||
displayLine(wakeFrom, wakeTo, boat.getColour());
|
||||
} else if (!isStart) {
|
||||
displayBoat(boat, boat.getHeading());
|
||||
} else {
|
||||
displayBoat(boat, 0);
|
||||
}
|
||||
|
||||
if (raceAnno)
|
||||
displayText(boat.toString(), boat.getAbbrev(), boat.getVelocity(), this.map.convertGPS(boat.getCurrentPosition()));
|
||||
|
||||
if(boat.isTrackVisible()) drawTrack(boat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws all track points for a given boat. Colour is set by boat, opacity by track point.
|
||||
* @param boat whose track is displayed
|
||||
* @see seng302.Model.TrackPoint
|
||||
*/
|
||||
private void drawTrack(BoatInRace boat) {
|
||||
if (annoPath && raceAnno) {
|
||||
for (TrackPoint point : boat.getTrack()) {
|
||||
GraphCoordinate scaledCoordinate = this.map.convertGPS(point.getCoordinate());
|
||||
Color boatColour = boat.getColour();
|
||||
gc.setFill(new Color(boatColour.getRed(), boatColour.getGreen(), boatColour.getBlue(), point.getAlpha()));
|
||||
gc.fillOval(scaledCoordinate.getX(), scaledCoordinate.getY(), 5, 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setRaceBoundaries(List<GPSCoordinate> boundaries) {
|
||||
this.raceBoundaries = new ArrayList<>();
|
||||
for (GPSCoordinate bound : boundaries) {
|
||||
raceBoundaries.add(bound);
|
||||
}
|
||||
setRaceBoundCoordinates();
|
||||
}
|
||||
|
||||
public void setRaceBoundCoordinates() {
|
||||
xpoints = new double[this.raceBoundaries.size()];
|
||||
ypoints = new double[this.raceBoundaries.size()];
|
||||
for (int i = 0; i < raceBoundaries.size(); i++) {
|
||||
GraphCoordinate coord = map.convertGPS(raceBoundaries.get(i));
|
||||
xpoints[i] = coord.getX();
|
||||
ypoints[i] = coord.getY();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Canvas to resizable.
|
||||
*
|
||||
* @return That the Canvas is resizable.
|
||||
*/
|
||||
@Override
|
||||
public boolean isResizable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred width of the Canvas
|
||||
*
|
||||
* @param width of canvas
|
||||
* @return Returns the width of the Canvas
|
||||
*/
|
||||
@Override
|
||||
public double prefWidth(double width) {
|
||||
return getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred height of the Canvas
|
||||
*
|
||||
* @param height of canvas
|
||||
* @return Returns the height of the Canvas
|
||||
*/
|
||||
@Override
|
||||
public double prefHeight(double height) {
|
||||
return getHeight();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user