Added a minimap to the race view.

#implement #story[1273]
This commit is contained in:
Calum
2017-09-28 03:17:33 +13:00
parent 00ddf117b2
commit 9075d2a909
8 changed files with 236 additions and 539 deletions
@@ -2,7 +2,6 @@ package seng302.visualiser;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -24,7 +23,11 @@ import javafx.scene.transform.Scale;
import javafx.scene.transform.Translate; import javafx.scene.transform.Translate;
import org.fxyz3d.scene.Skybox; import org.fxyz3d.scene.Skybox;
import seng302.gameServer.messages.RoundingSide; import seng302.gameServer.messages.RoundingSide;
import seng302.model.*; import seng302.model.ClientYacht;
import seng302.model.GameKeyBind;
import seng302.model.KeyAction;
import seng302.model.Limit;
import seng302.model.ScaledPoint;
import seng302.model.mark.CompoundMark; import seng302.model.mark.CompoundMark;
import seng302.model.mark.Corner; import seng302.model.mark.Corner;
import seng302.model.mark.Mark; import seng302.model.mark.Mark;
@@ -47,7 +50,7 @@ import seng302.visualiser.fxObjects.assets_3D.ModelType;
* Collection of animated3D assets that displays a race. * Collection of animated3D assets that displays a race.
*/ */
public class GameView3D extends GameView{ public class GameView3D extends GameView {
private final double FOV = 60; private final double FOV = 60;
private final double DEFAULT_CAMERA_X = 0; private final double DEFAULT_CAMERA_X = 0;
@@ -29,7 +29,7 @@ import seng302.visualiser.fxObjects.assets_2D.Marker2D;
public class MapPreview extends GameView { public class MapPreview extends GameView {
private Polygon raceBorder = new CourseBoundary(); private Polygon raceBorder = new CourseBoundary();
private Map<Mark, Marker2D> markerObjects; protected Map<Mark, Marker2D> markerObjects;
public MapPreview(List<CompoundMark> marks, List<Corner> course, List<Limit> border) { public MapPreview(List<CompoundMark> marks, List<Corner> course, List<Limit> border) {
this.compoundMarks = marks; this.compoundMarks = marks;
@@ -0,0 +1,82 @@
package seng302.visualiser;
import java.util.HashMap;
import java.util.List;
import javafx.application.Platform;
import javafx.geometry.Point2D;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import seng302.model.ClientYacht;
import seng302.model.Limit;
import seng302.model.mark.CompoundMark;
import seng302.model.mark.Corner;
import seng302.model.mark.Mark;
import seng302.utilities.Sounds;
/**
* Created by cir27 on 28/09/17.
*/
public class MiniMap extends MapPreview {
private HashMap<ClientYacht, Circle> boatIcons = new HashMap<>();
private Polygon playerBoat;
private double playerRotation;
private List<ClientYacht> boats;
private ClientYacht player;
public MiniMap (List<CompoundMark> marks, List<Corner> course, List<Limit> border, List<ClientYacht> boats, ClientYacht player) {
super(marks, course, border);
this.boats = boats;
this.player = player;
setBoats(boats);
// player.addMarkRoundingListener(this::updateMarkArrows);
}
public void setBoats(List<ClientYacht> yachts) {
for (ClientYacht yacht : yachts) {
Circle boatIcon = new Circle(0, 0, 4);
boatIcon.setStroke(Color.BLACK);
boatIcon.setFill(Color.GRAY);
boatIcon.setFill(yacht.getColour());
boatIcon.setFill(yacht.getColour());
boatIcons.put(yacht, boatIcon);
yacht.addLocationListener((boat, lat, lon, heading, sailIn, velocity) -> {
Circle bi = boatIcons.get(boat);
Point2D p2d = scaledPoint.findScaledXY(lat, lon);
bi.setCenterX(p2d.getX());
bi.setCenterY(p2d.getY());
});
}
Platform.runLater(() -> {
gameObjects.getChildren().addAll(boatIcons.values());
});
}
private void updateMarkArrows (ClientYacht yacht, int legNumber) {
CompoundMark compoundMark;
if (legNumber - 1 >= 0) {
Sounds.playMarkRoundingSound();
compoundMark = course.get(legNumber-1);
for (Mark mark : compoundMark.getMarks()) {
markerObjects.get(mark).showNextExitArrow();
}
}
CompoundMark nextMark = null;
if (legNumber < course.size() - 1) {
Sounds.playMarkRoundingSound();
nextMark = course.get(legNumber);
for (Mark mark : nextMark.getMarks()) {
markerObjects.get(mark).showNextEnterArrow();
}
}
if (legNumber - 2 >= 0) {
CompoundMark lastMark = course.get(Math.max(0, legNumber - 2));
if (lastMark != nextMark) {
for (Mark mark : lastMark.getMarks()) {
markerObjects.get(mark).hideAllArrows();
}
}
}
}
}
@@ -4,67 +4,38 @@ import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXDialog; import com.jfoenix.controls.JFXDialog;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import javafx.animation.RotateTransition; import javafx.animation.RotateTransition;
import javafx.animation.Timeline;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.ReadOnlyBooleanProperty; import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.SubScene; import javafx.scene.SubScene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint; import javafx.scene.paint.Paint;
import javafx.scene.shape.Line;
import javafx.scene.shape.Polyline;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration; import javafx.util.Duration;
import seng302.model.ClientYacht; import seng302.model.ClientYacht;
import seng302.model.RaceState; import seng302.model.RaceState;
import seng302.model.mark.CompoundMark;
import seng302.model.mark.Mark;
import seng302.model.stream.xml.parser.RaceXMLData; import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.token.TokenType; import seng302.model.token.TokenType;
import seng302.utilities.Sounds; import seng302.utilities.Sounds;
import seng302.visualiser.GameView3D; import seng302.visualiser.GameView3D;
import seng302.visualiser.controllers.annotations.ImportantAnnotationController; import seng302.visualiser.MiniMap;
import seng302.visualiser.controllers.annotations.ImportantAnnotationDelegate;
import seng302.visualiser.controllers.annotations.ImportantAnnotationsState;
import seng302.visualiser.controllers.dialogs.FinishDialogController; import seng302.visualiser.controllers.dialogs.FinishDialogController;
import seng302.visualiser.fxObjects.ChatHistory; import seng302.visualiser.fxObjects.ChatHistory;
import seng302.visualiser.fxObjects.assets_2D.WindArrow;
import seng302.visualiser.fxObjects.assets_3D.BoatObject;
/** /**
* Controller class that manages the display of a race * Controller class that manages the display of a race
*/ */
public class RaceViewController extends Thread implements ImportantAnnotationDelegate { public class RaceViewController extends Thread {
private final int CHAT_LIMIT = 128; private final int CHAT_LIMIT = 128;
private static final Double ICON_BLINK_TIMEOUT_RATIO = 0.6; private static final Double ICON_BLINK_TIMEOUT_RATIO = 0.6;
@@ -75,39 +46,17 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
@FXML @FXML
private ImageView loadingScreen; private ImageView loadingScreen;
@FXML @FXML
private Pane basePane;
@FXML
private JFXButton chatSend; private JFXButton chatSend;
@FXML @FXML
private Pane chatHistoryHolder; private Pane chatHistoryHolder;
@FXML @FXML
private TextField chatInput; private TextField chatInput;
@FXML @FXML
private LineChart<String, Double> raceSparkLine;
@FXML
private NumberAxis sparklineYAxis;
@FXML
private VBox positionVbox;
@FXML
private CheckBox toggleFps;
@FXML
private Label timerLabel; private Label timerLabel;
@FXML @FXML
private StackPane contentStackPane; private StackPane contentStackPane;
private GridPane contentGridPane;
@FXML @FXML
private AnchorPane rvAnchorPane; private Pane miniMapPane;
@FXML
private AnchorPane windArrowHolder;
@FXML
private Slider annotationSlider;
@FXML
private Button selectAnnotationBtn;
@FXML
private ComboBox<ClientYacht> yachtSelectionComboBox;
@FXML
private Text fpsDisplay;
@FXML @FXML
private ImageView windImageView; private ImageView windImageView;
@FXML @FXML
@@ -118,33 +67,24 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
private Label positionLabel, boatSpeedLabel, boatHeadingLabel; private Label positionLabel, boatSpeedLabel, boatHeadingLabel;
@FXML @FXML
private ImageView velocityIcon, handlingIcon, windWalkerIcon, bumperIcon, badRandomIcon; private ImageView velocityIcon, handlingIcon, windWalkerIcon, bumperIcon, badRandomIcon;
@FXML
private JFXButton miniMapButton;
//Race Data
private Map<Integer, ClientYacht> participants;
private Map<Integer, CompoundMark> markers;
private RaceXMLData courseData;
private GameView3D gameView; private GameView3D gameView;
private RaceState raceState; private RaceState raceState;
private ChatHistory chatHistory; private ChatHistory chatHistory;
private Timeline timerTimeline;
private Timer timer = new Timer(); private Timer timer = new Timer();
private List<Series<String, Double>> sparkLineData = new ArrayList<>();
private ImportantAnnotationsState importantAnnotations;
private Polyline windArrow = new WindArrow(Color.LIGHTGRAY);
private ObservableList<ClientYacht> selectionComboBoxList = FXCollections.observableArrayList();
private ClientYacht player; private ClientYacht player;
private JFXDialog finishScreenDialog; private JFXDialog finishScreenDialog;
private FinishDialogController finishDialogController; private FinishDialogController finishDialogController;
//Icon stuff
private Timer blinkingTimer = new Timer(); private Timer blinkingTimer = new Timer();
private ImageView iconToDisplay; private ImageView iconToDisplay;
private Double lastWindDirection; private Double lastWindDirection;
private MiniMap miniMap;
public void initialize() { public void initialize() {
miniMapPane.setVisible(false);
miniMapButton.setVisible(false);
contentStackPane.setVisible(false); contentStackPane.setVisible(false);
Image loadingImage = new Image("PP.png"); Image loadingImage = new Image("PP.png");
loadingScreen.setImage(loadingImage); loadingScreen.setImage(loadingImage);
@@ -204,7 +144,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
public void showView(){ public void showView(){
loadingScreenPane.setVisible(false); loadingScreenPane.setVisible(false);
contentStackPane.setVisible(true); contentStackPane.setVisible(true);
miniMapPane.setVisible(true);
miniMapButton.setVisible(true);
Platform.runLater(new Runnable() { Platform.runLater(new Runnable() {
@Override @Override
public void run() { public void run() {
@@ -238,31 +179,35 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
Map<Integer, ClientYacht> participants, RaceXMLData raceData, RaceState raceState, Map<Integer, ClientYacht> participants, RaceXMLData raceData, RaceState raceState,
ClientYacht player) { ClientYacht player) {
this.participants = participants;
this.courseData = raceData;
this.markers = raceData.getCompoundMarks();
this.raceState = raceState; this.raceState = raceState;
this.player = player; this.player = player;
raceState.getPlayerPositions().addListener((ListChangeListener<ClientYacht>) c -> {
while (c.next()) {
if (c.wasPermutated()) {
updateOrder(raceState.getPlayerPositions());
}
}
});
player.addPowerUpListener(this::displayPowerUpIcon); player.addPowerUpListener(this::displayPowerUpIcon);
player.addPowerDownListener(this::removeIcon); player.addPowerDownListener(this::removeIcon);
updateOrder(raceState.getPlayerPositions());
gameView = new GameView3D(); gameView = new GameView3D();
miniMap = new MiniMap(
new ArrayList<>(raceData.getCompoundMarks().values()),
raceData.getMarkSequence(), raceData.getCourseLimit(),
new ArrayList<>(participants.values()), player
);
miniMapButton.setOnMouseClicked((event) -> {
if (miniMapPane.visibleProperty().get()) {
miniMapPane.setVisible(false);
miniMapButton.setText("");
} else {
miniMapPane.setVisible(true);
miniMapButton.setText("");
}
});
Platform.runLater(() -> { Platform.runLater(() -> {
contentStackPane.getChildren().add(0, gameView.getAssets()); contentStackPane.getChildren().add(0, gameView.getAssets());
((SubScene) gameView.getAssets()).widthProperty() ((SubScene) gameView.getAssets()).widthProperty()
.bind(ViewManager.getInstance().getStage().widthProperty()); .bind(ViewManager.getInstance().getStage().widthProperty());
((SubScene) gameView.getAssets()).heightProperty() ((SubScene) gameView.getAssets()).heightProperty()
.bind(ViewManager.getInstance().getStage().heightProperty()); .bind(ViewManager.getInstance().getStage().heightProperty());
miniMapPane.getChildren().add(miniMap.getAssets());
}); });
gameView.setBoats(new ArrayList<>(participants.values())); gameView.setBoats(new ArrayList<>(participants.values()));
gameView.updateBorder(raceData.getCourseLimit()); gameView.updateBorder(raceData.getCourseLimit());
@@ -350,45 +295,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
} }
} }
/**
* The important annotations have been changed, update this view
*
* @param importantAnnotationsState The current state of the selected annotations
*/
public void importantAnnotationsChanged(ImportantAnnotationsState importantAnnotationsState) {
this.importantAnnotations = importantAnnotationsState;
setAnnotations((int) annotationSlider.getValue()); // Refresh the displayed annotations
}
/**
* Loads the "select annotations" view in a new window
*/
private void loadSelectAnnotationView() {
try {
FXMLLoader fxmlLoader = new FXMLLoader();
Stage stage = new Stage();
// Set controller
ImportantAnnotationController controller = new ImportantAnnotationController(
this, stage
);
fxmlLoader.setController(controller);
// Load FXML and set CSS
fxmlLoader.setLocation(
getClass().getResource("/views/importantAnnotationSelectView.fxml")
);
Scene scene = new Scene(fxmlLoader.load(), 469, 298);
scene.getStylesheets().add(getClass().getResource("/css/master.css").toString());
stage.initStyle(StageStyle.UNDECORATED);
stage.setScene(scene);
stage.show();
controller.loadState(importantAnnotations);
} catch (IOException e) {
e.printStackTrace();
}
}
/** /**
* Initialises a timer which updates elements of the RaceView such as wind direction, yacht * Initialises a timer which updates elements of the RaceView such as wind direction, yacht
* orderings etc.. which are dependent on the info from the stream parser constantly. * orderings etc.. which are dependent on the info from the stream parser constantly.
@@ -406,35 +312,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
}, 0, 1000); }, 0, 1000);
} }
/**
* Iterates over all corners until ones SeqID matches with the yachts current leg number.
* Then it gets the compoundMarkID of that corner and uses it to fetch the appropriate mark
* Returns null if no next mark found.
* @param bg The BoatGroup to find the next mark of
* @return The next Mark or null if none found
*/
private Mark getNextMark(BoatObject bg) {
// TODO: 1/08/17 Move to GameView
//
// Integer legNumber = bg.getClientYacht().getLegNumber();
// List<Corner> markSequence = courseData.getMarkSequence();
//
// if (legNumber == 0) {
// return null;
// } else if (legNumber == markSequence.size() - 1) {
// return null;
// }
//
// for (Corner corner : markSequence) {
// if (legNumber + 2 == corner.getSeqID()) {
// return courseData.getCompoundMarks().get(corner.getCompoundMarkID());
// }
// }
// return null;
return null;
}
/** /**
* Updates the wind direction arrow and text as from info from the StreamParser * Updates the wind direction arrow and text as from info from the StreamParser
* @param direction the from north angle of the wind. * @param direction the from north angle of the wind.
@@ -516,226 +393,6 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
boatHeadingLabel.setText(String.format("Boat Heading:\n%.1f°", player.getHeading())); boatHeadingLabel.setText(String.format("Boat Heading:\n%.1f°", player.getHeading()));
} }
/**
* Updates the order of the yachts as from the StreamParser and sets them in the yacht order
* section
*/
private void updateOrder(ObservableList<ClientYacht> yachts) {
// List<Text> vboxEntries = new ArrayList<>();
//
// for (int i = 0; i < yachts.size(); i++) {
//// System.out.println("yacht == null " + String.valueOf(yacht == null));
// if (yachts.get(i).getBoatStatus() == BoatStatus.FINISHED
// .getCode()) { // 3 is finish status
// Text textToAdd = new Text(i + 1 + ". " +
// yachts.get(i).getShortName() + " (Finished)");
// textToAdd.setFill(Paint.valueOf("#d3d3d3"));
// vboxEntries.add(textToAdd);
//
// } else {
// Text textToAdd = new Text(i + 1 + ". " +
// yachts.get(i).getShortName() + " ");
// textToAdd.setFill(Paint.valueOf("#d3d3d3"));
// textToAdd.setStyle("");
// vboxEntries.add(textToAdd);
// }
// }
// Platform.runLater(() ->
// positionVbox.getChildren().setAll(vboxEntries)
// );
}
private void updateLaylines(BoatObject bg) {
// TODO: 1/08/17 move to GameView
//
// Mark nextMark = getNextMark(bg);
// Boolean isUpwind = null;
// // Can only calc leg direction if there is a next mark and it is a gate mark
// if (nextMark != null) {
// if (nextMark instanceof GateMark) {
// if (bg.isUpwindLeg(gameViewController, nextMark)) {
// isUpwind = true;
// } else {
// isUpwind = false;
// }
//
// for(MarkObject mg : gameViewController.getMarkGroups()) {
//
// mg.removeLaylines();
//
// if (mg.getMainMark().getId() == nextMark.getId()) {
//
// SingleMark singleMark1 = ((GateMark) nextMark).getSingleMark1();
// SingleMark singleMark2 = ((GateMark) nextMark).getSingleMark2();
// Point2D markPoint1 = gameViewController
// .findScaledXY(singleMark1.getLatitude(), singleMark1.getLongitude());
// Point2D markPoint2 = gameViewController
// .findScaledXY(singleMark2.getLatitude(), singleMark2.getLongitude());
// HashMap<Double, Double> angleAndSpeed;
// if (isUpwind) {
// angleAndSpeed = PolarTable.getOptimalUpwindVMG(StreamParser.getWindSpeed());
// } else {
// angleAndSpeed = PolarTable.getOptimalDownwindVMG(StreamParser.getWindSpeed());
// }
//
// Double resultingAngle = angleAndSpeed.keySet().iterator().next();
//
//
// Point2D yachtCurrentPos = new Point2D(bg.getBoatLayoutX(), bg.getBoatLayoutY());
// Point2D gateMidPoint = markPoint1.midpoint(markPoint2);
// Integer lineFuncResult = GeoUtility.lineFunction(yachtCurrentPos, gateMidPoint, markPoint2);
// Line rightLayline = new Line();
// Line leftLayline = new Line();
// if (lineFuncResult == 1) {
// rightLayline = makeRightLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection());
// leftLayline = makeLeftLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection());
// } else if (lineFuncResult == -1) {
// rightLayline = makeRightLayline(markPoint1, 180 - resultingAngle, StreamParser.getWindDirection());
// leftLayline = makeLeftLayline(markPoint2, 180 - resultingAngle, StreamParser.getWindDirection());
// }
//
// leftLayline.setStrokeWidth(0.5);
// leftLayline.setStroke(bg.getBoat().getColour());
//
// rightLayline.setStrokeWidth(0.5);
// rightLayline.setStroke(bg.getBoat().getColour());
//
// bg.setLaylines(leftLayline, rightLayline);
// mg.addLaylines(leftLayline, rightLayline);
//
// }
// }
// }
// }
}
private Point2D getPointRotation(Point2D ref, Double distance, Double angle) {
Double newX = ref.getX() + (ref.getX() + distance - ref.getX()) * Math.cos(angle)
- (ref.getY() + distance - ref.getY()) * Math.sin(angle);
Double newY = ref.getY() + (ref.getX() + distance - ref.getX()) * Math.sin(angle)
+ (ref.getY() + distance - ref.getY()) * Math.cos(angle);
return new Point2D(newX, newY);
}
public Line makeLeftLayline(Point2D startPoint, Double layLineAngle, Double baseAngle) {
Point2D ep = getPointRotation(startPoint, 50.0, baseAngle + layLineAngle);
Line line = new Line(startPoint.getX(), startPoint.getY(), ep.getX(), ep.getY());
return line;
}
public Line makeRightLayline(Point2D startPoint, Double layLineAngle, Double baseAngle) {
Point2D ep = getPointRotation(startPoint, 50.0, baseAngle - layLineAngle);
Line line = new Line(startPoint.getX(), startPoint.getY(), ep.getX(), ep.getY());
return line;
}
/**
* Initialised the combo box with any yachts currently in the race and adds the required listener
* for the combobox to take action upon selection
*/
private void initialiseBoatSelectionComboBox() {
// yachtSelectionComboBox.setItems(
// FXCollections.observableArrayList(participants.values())
// );
// //Null check is if the listener is fired but nothing selected
// yachtSelectionComboBox.valueProperty().addListener((obs, lastSelection, selectedBoat) -> {
// if (selectedBoat != null) {
// gameView.selectBoat(selectedBoat);
// }
// });
//TODO uncomment out
// selectionComboBoxList.setAll(participants.values());
// yachtSelectionComboBox.setItems(selectionComboBoxList);
// yachtSelectionComboBox.valueProperty().addListener((obs, lastSelection, selectedBoat) -> {
// if (selectedBoat != null) {
// gameView.selectBoat(selectedBoat);
// }
// });
}
/**
* Display the list of yachts in the order they finished the race
*/
private void loadRaceResultView() {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/FinishView.fxml"));
try {
contentGridPane.getChildren().removeAll();
contentGridPane.getChildren().clear();
contentGridPane.getChildren().addAll((Pane) loader.load());
} catch (javafx.fxml.LoadException e) {
System.err.println(e.getCause().toString());
} catch (IOException e) {
System.err.println(e.toString());
}
}
private String getMillisToFormattedTime(long milliseconds) {
return String.format("%02d:%02d:%02d",
TimeUnit.MILLISECONDS.toHours(milliseconds),
TimeUnit.MILLISECONDS.toMinutes(milliseconds) % 60, //Modulus 60 minutes per hour
TimeUnit.MILLISECONDS.toSeconds(milliseconds) % 60 //Modulus 60 seconds per minute
);
}
private void setAnnotations(Integer annotationLevel) {
// switch (annotationLevel) {
// // No Annotations
// case 0:
// gameView.setAnnotationVisibilities(
// false, false, false, false, false, false
// );
// break;
// // Important Annotations
// case 1:
// gameView.setAnnotationVisibilities(
// importantAnnotations.getAnnotationState(Annotation.NAME),
// importantAnnotations.getAnnotationState(Annotation.SPEED),
// importantAnnotations.getAnnotationState(Annotation.ESTTIMETONEXTMARK),
// importantAnnotations.getAnnotationState(Annotation.LEGTIME),
// importantAnnotations.getAnnotationState(Annotation.TRACK),
// importantAnnotations.getAnnotationState(Annotation.WAKE)
// );
// break;
// // All Annotations
// case 2:
// gameView.setAnnotationVisibilities(
// true, true, true, true, true, true
// );
// break;
// }
}
/**
* Sets all the annotations of the selected yacht to be visible and all others to be hidden
*
* @param yacht The yacht for which we want to view all annotations
*/
private void setSelectedBoat(ClientYacht yacht) {
// for (BoatObject bg : gameViewController.getBoatGroups()) {
// //We need to iterate over all race groups to get the matching yacht group belonging to this yacht if we
// //are to toggle its annotations, there is no other backwards knowledge of a yacht to its yachtgroup.
// if (bg.getBoat().getHullID().equals(yacht.getHullID())) {
//// updateLaylines(bg);
// bg.setIsSelected(true);
//// selectedBoat = yacht;
// } else {
// bg.setIsSelected(false);
// }
// }
}
public void updateTokens(RaceXMLData raceData) { public void updateTokens(RaceXMLData raceData) {
gameView.updateTokens(raceData.getTokens()); gameView.updateTokens(raceData.getTokens());
@@ -29,11 +29,11 @@ public class MarkArrowFactory {
STARBOARD, STARBOARD,
} }
public static final double MARK_ARROW_SEPARATION = 15; public static final double MARK_ARROW_SEPARATION = 8;
public static final double ARROW_LENGTH = 75; public static final double ARROW_LENGTH = 20;
public static final double ARROW_HEAD_DEPTH = 10; public static final double ARROW_HEAD_DEPTH = 5;
public static final double ARROW_HEAD_WIDTH = 6; public static final double ARROW_HEAD_WIDTH = 3;
public static final double STROKE_WIDTH = 3; public static final double STROKE_WIDTH = 1;
public static Model constructEntryArrow3D ( public static Model constructEntryArrow3D (
RoundingSide roundingSide, double angle, ModelType type) { RoundingSide roundingSide, double angle, ModelType type) {
@@ -60,8 +60,10 @@ public class BoatModel extends Model {
*/ */
public void changeColour(Color newColour) { public void changeColour(Color newColour) {
changeColourChild(HULL_INDEX, newColour); changeColourChild(HULL_INDEX, newColour);
if (meshType != BoatMeshType.PARROT) {
changeColourChild(MAST_INDEX, newColour); changeColourChild(MAST_INDEX, newColour);
} }
}
private void changeColourChild(int index, Color newColour) { private void changeColourChild(int index, Color newColour) {
MeshView meshView = getMeshViewChild(index); MeshView meshView = getMeshViewChild(index);
@@ -117,12 +117,7 @@ public class ModelFactory {
Group boatAssets = new Group(); Group boatAssets = new Group();
MeshView hull = importSTL(boatType.hullFile); MeshView hull = importSTL(boatType.hullFile);
hull.setMaterial(new PhongMaterial(primaryColour)); hull.setMaterial(new PhongMaterial(primaryColour));
MeshView sail = importSTL(boatType.sailFile); boatAssets.getChildren().addAll(hull);
sail.setMaterial(
new PhongMaterial(boatType == BoatMeshType.PARROT ? Color.BLACK : Color.WHITE)
);
boatAssets.getChildren().addAll(hull, sail);
if (boatType.mastFile != null) { if (boatType.mastFile != null) {
MeshView mast = importSTL(boatType.mastFile); MeshView mast = importSTL(boatType.mastFile);
@@ -130,11 +125,17 @@ public class ModelFactory {
boatAssets.getChildren().add(mast); boatAssets.getChildren().add(mast);
} }
if (boatType.jibFile != null) { MeshView sail = importSTL(boatType.sailFile);
MeshView jib = importSTL(boatType.jibFile);
sail.setMaterial( sail.setMaterial(
new PhongMaterial(boatType == BoatMeshType.PARROT ? Color.DARKGRAY : Color.WHITE) new PhongMaterial(boatType == BoatMeshType.PARROT ? Color.DARKGRAY : Color.WHITE)
); );
boatAssets.getChildren().addAll(sail);
if (boatType.jibFile != null) {
MeshView jib = importSTL(boatType.jibFile);
jib.setMaterial(
new PhongMaterial(boatType == BoatMeshType.PARROT ? Color.BLACK : Color.WHITE)
);
boatAssets.getChildren().add(jib); boatAssets.getChildren().add(jib);
} }
+95 -143
View File
@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.*?>
<?import javafx.scene.shape.*?>
<?import com.jfoenix.controls.*?> <?import com.jfoenix.controls.*?>
<?import java.lang.*?> <?import java.lang.*?>
<?import javafx.geometry.*?> <?import javafx.geometry.*?>
@@ -25,267 +23,206 @@
<StackPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="800.0" prefWidth="1200.0" style="-fx-background-color: skyblue;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.RaceViewController"> <StackPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="800.0" prefWidth="1200.0" style="-fx-background-color: skyblue;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.RaceViewController">
<children> <children>
<StackPane fx:id="contentStackPane" maxHeight="1.7976931348623157E308" <StackPane fx:id="contentStackPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="800.0" prefWidth="1200.0" style="-fx-background-color: skyblue;" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="800.0"
prefWidth="1200.0" style="-fx-background-color: skyblue;" xmlns="http://javafx.com/javafx/8.0.111"
xmlns:fx="http://javafx.com/fxml/1">
<children> <children>
<GridPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" <GridPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="800.0" prefWidth="1200.0">
prefHeight="800.0" prefWidth="1200.0">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="250.0" minWidth="250.0" <ColumnConstraints hgrow="SOMETIMES" maxWidth="250.0" minWidth="250.0" prefWidth="250.0" />
prefWidth="250.0"/> <ColumnConstraints hgrow="SOMETIMES" maxWidth="1.7976931348623157E308" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1.7976931348623157E308"/> <ColumnConstraints hgrow="SOMETIMES" maxWidth="-Infinity" minWidth="400.0" prefWidth="400.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="-Infinity" minWidth="400.0" <ColumnConstraints />
prefWidth="400.0"/>
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="70.0" minHeight="70.0" prefHeight="70.0" <RowConstraints maxHeight="70.0" minHeight="70.0" prefHeight="70.0" vgrow="SOMETIMES" />
vgrow="SOMETIMES"/> <RowConstraints maxHeight="1.7976931348623157E308" vgrow="SOMETIMES" />
<RowConstraints maxHeight="1.7976931348623157E308" vgrow="SOMETIMES"/> <RowConstraints maxHeight="250.0" minHeight="250.0" prefHeight="250.0" valignment="BOTTOM" vgrow="SOMETIMES" />
<RowConstraints maxHeight="250.0" minHeight="250.0" prefHeight="250.0"
valignment="BOTTOM" vgrow="SOMETIMES"/>
</rowConstraints> </rowConstraints>
<children> <children>
<GridPane id="timerGrid" fx:id="timerGrid" prefWidth="192.0" styleClass="timer"> <GridPane id="timerGrid" fx:id="timerGrid" prefWidth="192.0" styleClass="timer">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="50.0" minWidth="50.0" <ColumnConstraints hgrow="SOMETIMES" maxWidth="50.0" minWidth="50.0" prefWidth="50.0" />
prefWidth="50.0"/> <ColumnConstraints hgrow="SOMETIMES" maxWidth="135.0" minWidth="135.0" prefWidth="135.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="135.0" minWidth="135.0"
prefWidth="135.0"/>
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints> </rowConstraints>
<opaqueInsets> <opaqueInsets>
<Insets/> <Insets />
</opaqueInsets> </opaqueInsets>
<GridPane.margin> <GridPane.margin>
<Insets left="10.0" right="200.0" top="10.0"/> <Insets left="10.0" right="200.0" top="10.0" />
</GridPane.margin> </GridPane.margin>
<children> <children>
<ImageView fitHeight="40.0" fitWidth="40.0" pickOnBounds="true" <ImageView fitHeight="40.0" fitWidth="40.0" pickOnBounds="true" preserveRatio="true" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
preserveRatio="true" GridPane.halignment="CENTER"
GridPane.valignment="CENTER">
<image> <image>
<Image url="@../images/timer.png"/> <Image url="@../images/timer.png" />
</image> </image>
<GridPane.margin> <GridPane.margin>
<Insets/> <Insets />
</GridPane.margin> </GridPane.margin>
</ImageView> </ImageView>
<Label fx:id="timerLabel" text="00:03:34" GridPane.columnIndex="1" <Label fx:id="timerLabel" text="00:03:34" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
GridPane.halignment="CENTER" GridPane.valignment="CENTER">
<font> <font>
<Font size="21.0"/> <Font size="21.0" />
</font> </font>
<GridPane.margin> <GridPane.margin>
<Insets/> <Insets />
</GridPane.margin> </GridPane.margin>
</Label> </Label>
</children> </children>
</GridPane> </GridPane>
<GridPane GridPane.columnIndex="2"> <GridPane GridPane.columnIndex="2">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints> </rowConstraints>
</GridPane> </GridPane>
<GridPane fx:id="chatGridPane" GridPane.columnIndex="2" GridPane.rowIndex="2"> <GridPane fx:id="chatGridPane" GridPane.columnIndex="2" GridPane.rowIndex="2">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="390.0" minWidth="390.0" <ColumnConstraints hgrow="SOMETIMES" maxWidth="390.0" minWidth="390.0" prefWidth="390.0" />
prefWidth="390.0"/>
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="1.7976931348623157E308" vgrow="SOMETIMES"/> <RowConstraints maxHeight="1.7976931348623157E308" vgrow="SOMETIMES" />
<RowConstraints maxHeight="60.0" minHeight="60.0" prefHeight="60.0" <RowConstraints maxHeight="60.0" minHeight="60.0" prefHeight="60.0" vgrow="SOMETIMES" />
vgrow="SOMETIMES"/>
</rowConstraints> </rowConstraints>
<children> <children>
<Pane fx:id="chatHistoryHolder" prefHeight="200.0" prefWidth="200.0" <Pane fx:id="chatHistoryHolder" prefHeight="200.0" prefWidth="200.0" GridPane.hgrow="ALWAYS" GridPane.valignment="BOTTOM" GridPane.vgrow="ALWAYS">
GridPane.hgrow="ALWAYS" GridPane.valignment="BOTTOM"
GridPane.vgrow="ALWAYS">
<GridPane.margin> <GridPane.margin>
<Insets/> <Insets />
</GridPane.margin> </GridPane.margin>
<padding> <padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/> <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding> </padding>
</Pane> </Pane>
<GridPane fx:id="chatInputHolder" GridPane.rowIndex="1"> <GridPane fx:id="chatInputHolder" GridPane.rowIndex="1">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
prefWidth="100.0"/> <ColumnConstraints hgrow="SOMETIMES" maxWidth="-Infinity" minWidth="90.0" prefWidth="90.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="-Infinity"
minWidth="90.0" prefWidth="90.0"/>
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" <RowConstraints maxHeight="50.0" minHeight="50.0" prefHeight="50.0" valignment="CENTER" vgrow="SOMETIMES" />
valignment="CENTER" vgrow="SOMETIMES"/>
</rowConstraints> </rowConstraints>
<children> <children>
<JFXButton fx:id="chatSend" alignment="CENTER" buttonType="RAISED" <JFXButton fx:id="chatSend" alignment="CENTER" buttonType="RAISED" focusTraversable="false" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="35.0" text="SEND" GridPane.columnIndex="1">
focusTraversable="false" maxHeight="-Infinity"
maxWidth="1.7976931348623157E308" minHeight="-Infinity"
minWidth="-Infinity" prefHeight="35.0" text="SEND"
GridPane.columnIndex="1">
<GridPane.margin> <GridPane.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/> <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</GridPane.margin> </GridPane.margin>
</JFXButton> </JFXButton>
<JFXTextField fx:id="chatInput" focusTraversable="false" <JFXTextField fx:id="chatInput" focusTraversable="false" maxHeight="35.0" minHeight="-Infinity" prefHeight="35.0">
maxHeight="35.0" minHeight="-Infinity" prefHeight="35.0">
<GridPane.margin> <GridPane.margin>
<Insets bottom="10.0" left="20.0" right="10.0"/> <Insets bottom="10.0" left="20.0" right="10.0" />
</GridPane.margin> </GridPane.margin>
<padding> <padding>
<Insets right="15.0"/> <Insets right="15.0" />
</padding> </padding>
</JFXTextField> </JFXTextField>
</children> </children>
<GridPane.margin> <GridPane.margin>
<Insets top="10.0"/> <Insets top="10.0" />
</GridPane.margin> </GridPane.margin>
</GridPane> </GridPane>
</children> </children>
<GridPane.margin> <GridPane.margin>
<Insets bottom="10.0" right="10.0"/> <Insets bottom="10.0" right="10.0" />
</GridPane.margin> </GridPane.margin>
</GridPane> </GridPane>
<GridPane fx:id="windGridPane" maxHeight="-Infinity" maxWidth="-Infinity" <GridPane fx:id="windGridPane" maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="150.0" prefWidth="240.0" GridPane.halignment="CENTER" GridPane.rowIndex="2" GridPane.valignment="BOTTOM">
prefHeight="150.0" prefWidth="240.0" GridPane.halignment="CENTER"
GridPane.rowIndex="2" GridPane.valignment="BOTTOM">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="110.0" minWidth="110.0" <ColumnConstraints hgrow="SOMETIMES" maxWidth="110.0" minWidth="110.0" prefWidth="110.0" />
prefWidth="110.0"/> <ColumnConstraints hgrow="SOMETIMES" maxWidth="132.0" minWidth="10.0" prefWidth="132.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="132.0" minWidth="10.0"
prefWidth="132.0"/>
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="120.0" minHeight="120.0" prefHeight="120.0" <RowConstraints maxHeight="120.0" minHeight="120.0" prefHeight="120.0" vgrow="SOMETIMES" />
vgrow="SOMETIMES"/> <RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0"
vgrow="SOMETIMES"/>
</rowConstraints> </rowConstraints>
<children> <children>
<Label fx:id="positionLabel" text="Position:" GridPane.columnIndex="1" <Label fx:id="positionLabel" text="Position:" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowSpan="2" GridPane.valignment="TOP">
GridPane.halignment="LEFT" GridPane.rowSpan="2" GridPane.valignment="TOP">
<padding> <padding>
<Insets bottom="5.0" left="10.0" right="5.0" top="5.0"/> <Insets bottom="5.0" left="10.0" right="5.0" top="5.0" />
</padding> </padding>
</Label> </Label>
<Label fx:id="boatSpeedLabel" text="Boat Speed:" GridPane.columnIndex="1" <Label fx:id="boatSpeedLabel" text="Boat Speed:" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowSpan="2" GridPane.valignment="CENTER">
GridPane.halignment="LEFT" GridPane.rowSpan="2"
GridPane.valignment="CENTER">
<opaqueInsets> <opaqueInsets>
<Insets/> <Insets />
</opaqueInsets> </opaqueInsets>
<padding> <padding>
<Insets bottom="5.0" left="10.0" right="5.0" top="5.0"/> <Insets bottom="5.0" left="10.0" right="5.0" top="5.0" />
</padding> </padding>
</Label> </Label>
<Label fx:id="boatHeadingLabel" text="Boat Heading:" <Label fx:id="boatHeadingLabel" text="Boat Heading:" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowSpan="2" GridPane.valignment="BOTTOM">
GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowSpan="2"
GridPane.valignment="BOTTOM">
<padding> <padding>
<Insets bottom="5.0" left="10.0" right="5.0" top="5.0"/> <Insets bottom="5.0" left="10.0" right="5.0" top="5.0" />
</padding> </padding>
</Label> </Label>
<GridPane fx:id="windHolder" GridPane.rowSpan="2"> <GridPane fx:id="windHolder" GridPane.rowSpan="2">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
prefWidth="100.0"/>
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="120.0" minHeight="120.0" <RowConstraints maxHeight="120.0" minHeight="120.0" prefHeight="120.0" vgrow="SOMETIMES" />
prefHeight="120.0" vgrow="SOMETIMES"/> <RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0"
vgrow="SOMETIMES"/>
</rowConstraints> </rowConstraints>
<children> <children>
<ImageView fx:id="windImageView" fitHeight="92.0" fitWidth="109.0" <ImageView fx:id="windImageView" fitHeight="92.0" fitWidth="109.0" pickOnBounds="true" preserveRatio="true" GridPane.halignment="CENTER" GridPane.rowSpan="2" GridPane.valignment="CENTER" />
pickOnBounds="true" preserveRatio="true" <Label fx:id="windSpeedLabel" text="0.0 Knots" GridPane.halignment="RIGHT" GridPane.rowIndex="1" GridPane.valignment="CENTER">
GridPane.halignment="CENTER" GridPane.rowSpan="2"
GridPane.valignment="CENTER"/>
<Label fx:id="windSpeedLabel" text="0.0 Knots"
GridPane.halignment="RIGHT" GridPane.rowIndex="1"
GridPane.valignment="CENTER">
<GridPane.margin> <GridPane.margin>
<Insets right="5.0"/> <Insets right="5.0" />
</GridPane.margin> </GridPane.margin>
</Label> </Label>
<Label fx:id="windDirectionLabel" text="180.0°" <Label fx:id="windDirectionLabel" text="180.0°" GridPane.halignment="LEFT" GridPane.rowIndex="1" GridPane.valignment="CENTER">
GridPane.halignment="LEFT" GridPane.rowIndex="1"
GridPane.valignment="CENTER">
<GridPane.margin> <GridPane.margin>
<Insets left="5.0"/> <Insets left="5.0" />
</GridPane.margin> </GridPane.margin>
</Label> </Label>
</children> </children>
</GridPane> </GridPane>
</children> </children>
<opaqueInsets> <opaqueInsets>
<Insets/> <Insets />
</opaqueInsets> </opaqueInsets>
<GridPane.margin> <GridPane.margin>
<Insets bottom="10.0" left="10.0" top="40.0"/> <Insets bottom="10.0" left="10.0" top="40.0" />
</GridPane.margin> </GridPane.margin>
</GridPane> </GridPane>
<GridPane GridPane.columnIndex="1" GridPane.rowIndex="2"> <GridPane GridPane.columnIndex="1" GridPane.rowIndex="2">
<columnConstraints> <columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/> <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="152.0" minHeight="10.0" prefHeight="152.0" <RowConstraints maxHeight="152.0" minHeight="10.0" prefHeight="152.0" vgrow="SOMETIMES" />
vgrow="SOMETIMES"/> <RowConstraints maxHeight="118.0" minHeight="10.0" prefHeight="98.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="118.0" minHeight="10.0" prefHeight="98.0"
vgrow="SOMETIMES"/>
</rowConstraints> </rowConstraints>
<children> <children>
<ImageView fx:id="velocityIcon" fitHeight="88.0" fitWidth="106.0" <ImageView fx:id="velocityIcon" fitHeight="88.0" fitWidth="106.0" pickOnBounds="true" preserveRatio="true" visible="false" GridPane.halignment="CENTER" GridPane.rowIndex="1">
pickOnBounds="true" preserveRatio="true" visible="false"
GridPane.halignment="CENTER" GridPane.rowIndex="1">
<image> <image>
<Image url="@../icons/velocity.png"/> <Image url="@../icons/velocity.png" />
</image> </image>
</ImageView> </ImageView>
<ImageView fx:id="handlingIcon" fitHeight="87.0" fitWidth="98.0" <ImageView fx:id="handlingIcon" fitHeight="87.0" fitWidth="98.0" pickOnBounds="true" preserveRatio="true" visible="false" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1">
pickOnBounds="true" preserveRatio="true" visible="false"
GridPane.columnIndex="1" GridPane.halignment="CENTER"
GridPane.rowIndex="1">
<image> <image>
<Image url="@../icons/handlingIcon.png"/> <Image url="@../icons/handlingIcon.png" />
</image> </image>
</ImageView> </ImageView>
<ImageView fx:id="windWalkerIcon" fitHeight="83.0" fitWidth="100.0" <ImageView fx:id="windWalkerIcon" fitHeight="83.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true" visible="false" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="1">
pickOnBounds="true" preserveRatio="true" visible="false"
GridPane.columnIndex="2" GridPane.halignment="CENTER"
GridPane.rowIndex="1">
<image> <image>
<Image url="@../icons/windWalkerIcon.png"/> <Image url="@../icons/windWalkerIcon.png" />
</image> </image>
</ImageView> </ImageView>
<ImageView fx:id="bumperIcon" fitHeight="83.0" fitWidth="88.0" <ImageView fx:id="bumperIcon" fitHeight="83.0" fitWidth="88.0" pickOnBounds="true" preserveRatio="true" visible="false" GridPane.columnIndex="3" GridPane.halignment="CENTER" GridPane.rowIndex="1">
pickOnBounds="true" preserveRatio="true" visible="false"
GridPane.columnIndex="3" GridPane.halignment="CENTER"
GridPane.rowIndex="1">
<image> <image>
<Image url="@../icons/bumperIcon.png"/> <Image url="@../icons/bumperIcon.png" />
</image> </image>
</ImageView> </ImageView>
<ImageView fx:id="badRandomIcon" fitHeight="69.0" fitWidth="103.0" <ImageView fx:id="badRandomIcon" fitHeight="69.0" fitWidth="103.0" pickOnBounds="true" preserveRatio="true" visible="false" GridPane.columnIndex="4" GridPane.halignment="CENTER" GridPane.rowIndex="1" GridPane.valignment="CENTER">
pickOnBounds="true" preserveRatio="true" visible="false"
GridPane.columnIndex="4" GridPane.halignment="CENTER"
GridPane.rowIndex="1" GridPane.valignment="CENTER">
<image> <image>
<Image url="@../icons/slowedIcon.png"/> <Image url="@../icons/slowedIcon.png" />
</image> </image>
</ImageView> </ImageView>
</children> </children>
@@ -294,15 +231,30 @@
</GridPane> </GridPane>
</children> </children>
</StackPane> </StackPane>
<Pane fx:id="miniMapPane" maxHeight="200.0" maxWidth="200.0" minHeight="200.0" minWidth="200.0" prefHeight="200.0" prefWidth="200.0" style="-fx-background-color: white; -fx-opacity: 0.45; -fx-background-radius: 10;" StackPane.alignment="TOP_RIGHT">
<StackPane.margin>
<Insets right="15.0" top="15.0" />
</StackPane.margin>
</Pane>
<JFXButton fx:id="miniMapButton" style="-fx-background-color: white; -fx-opacity: 0.45; -fx-background-radius: 10;" text="✓" StackPane.alignment="TOP_RIGHT">
<font>
<Font size="15.0" />
</font>
<StackPane.margin>
<Insets right="15.0" top="15.0" />
</StackPane.margin>
</JFXButton>
</children> </children>
<children>
<AnchorPane fx:id="loadingScreenPane"> <AnchorPane fx:id="loadingScreenPane">
<children> <children>
<ImageView fx:id="loadingScreen" fitHeight="672.0" fitWidth="1200.0" pickOnBounds="true" preserveRatio="true" /> <ImageView fx:id="loadingScreen" fitHeight="672.0" fitWidth="1200.0" pickOnBounds="true" preserveRatio="true" />
<JFXSpinner layoutX="566.0" layoutY="692.0" radius="30.0" /> <JFXSpinner layoutX="566.0" layoutY="692.0" radius="30.0" />
</children> </children>
</AnchorPane> </AnchorPane>
</children>
<stylesheets> <stylesheets>
<String fx:value="/css/Master.css"/> <String fx:value="/css/Master.css" />
<String fx:value="/css/RaceView.css"/> <String fx:value="/css/RaceView.css" />
</stylesheets> </stylesheets>
</StackPane> </StackPane>