Merge remote-tracking branch 'remotes/origin/master' into scoreTable

main
hba56 8 years ago
commit c17900a461

@ -219,7 +219,6 @@ public class Event {
long millisecondsToAdd = racePreStartTime + racePreparatoryTime;
long secondsToAdd = millisecondsToAdd / 1000;
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ");

@ -32,7 +32,7 @@ public class NewPolars {
public static void addPolars(double trueWindSpeed, Bearing trueWindAngle, double boatSpeed){
double tws = trueWindSpeed;
double bs = boatSpeed;
double twa = trueWindAngle.degrees();
double twa = trueWindAngle.degrees() + 180;
if (!polars.containsKey(tws)){
polars.put(tws, new TreeMap<>());
}

@ -12,6 +12,7 @@ import network.Messages.Enums.RaceStatusEnum;
import network.Messages.LatestMessages;
import shared.model.RunnableWithFramePeriod;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;
@ -139,6 +140,8 @@ public class RaceLogic implements RunnableWithFramePeriod, Observer {
*/
private void raceLoop() {
ArrayList<MockBoat> collisionBoats = new ArrayList<>();
long previousFrameTime = System.currentTimeMillis();
while (race.getRaceStatusEnum() != RaceStatusEnum.FINISHED && loopBool) {
@ -168,9 +171,17 @@ public class RaceLogic implements RunnableWithFramePeriod, Observer {
//If it is still racing, update its position.
if (boat.getStatus() == BoatStatusEnum.RACING) {
race.updatePosition(boat, framePeriod, race.getRaceClock().getDurationMilli());
race.getColliderRegistry().rayCast(boat);
if(race.getColliderRegistry().rayCast(boat)){
//System.out.println("Collision!");
//Add boat to list
collisionBoats.add(boat);
}
//System.out.println(race.getColliderRegistry().rayCast(boat));
}
}
} else {
@ -183,9 +194,14 @@ public class RaceLogic implements RunnableWithFramePeriod, Observer {
// Change wind direction
race.changeWindDirection();
//Pass collision boats in
server.parseBoatCollisions(collisionBoats);
//Parse the race snapshot.
server.parseSnapshot();
collisionBoats.clear();
//Update the last frame time.
previousFrameTime = currentTime;
}

@ -3,6 +3,7 @@ package mock.model;
import network.AckSequencer;
import network.Messages.*;
import network.Messages.Enums.BoatLocationDeviceEnum;
import network.Messages.Enums.YachtEventEnum;
import network.Messages.Enums.XMLMessageType;
import shared.model.Bearing;
import shared.model.CompoundMark;
@ -24,6 +25,7 @@ public class RaceServer {
private MockRace race;
private LatestMessages latestMessages;
private static RaceServer server;
private List<YachtEvent> collisionEvents = new ArrayList<>();
/**
@ -75,9 +77,19 @@ public class RaceServer {
//Parse the race status.
snapshotMessages.add(parseRaceStatus());
//Parse collisions
if(collisionEvents.size()>0){
snapshotMessages.addAll(collisionEvents);
}
latestMessages.setSnapshot(snapshotMessages);
updateXMLFiles();
//Reset collision list
collisionEvents.clear();
//System.out.println(collisionEvents.size());
}
/**
@ -314,4 +326,28 @@ public class RaceServer {
return message;
}
/**
* Parse the yacht event and return it
* @param boat yacht with event
* @param event event that happened
* @return yacht event
*/
private YachtEvent parseYachtEvent(MockBoat boat, YachtEventEnum event){
YachtEvent yachtEvent = new YachtEvent(
System.currentTimeMillis(),
this.boatLocationSequenceNumber,
race.getRaceId(),
boat.getSourceID(),
1337,
event);
return yachtEvent;
}
public void parseBoatCollisions(ArrayList<MockBoat> boats){
for (MockBoat boat : boats){
collisionEvents.add(parseYachtEvent(boat, YachtEventEnum.COLLISION));
}
}
}

@ -22,6 +22,7 @@ public class CollisionCommand extends ObserverCommand {
*/
public CollisionCommand(MockRace race, MockBoat boat) {
super(race, boat);
race.addObserver(this);
}
@Override

@ -29,12 +29,14 @@ public class CommandFactory {
}
switch(action.getBoatAction()) {
case AUTO_PILOT: return new VMGCommand(race, boat);
case VMG: return new VMGCommand(race, boat);
case TACK_GYBE: return new TackGybeCommand(race, boat);
case UPWIND: return new WindCommand(race, boat, true);
case DOWNWIND: return new WindCommand(race, boat, false);
case SAILS_OUT: return new SailsCommand(race, boat, true);
case SAILS_IN: return new SailsCommand(race, boat, false);
case ZOOM_IN: return null;
case ZOOM_OUT: return null;
default: throw new CommandConstructionException("Could not create command for BoatAction: " + action + ". Unknown BoatAction.");
}

@ -1,7 +1,7 @@
package mock.model.commandFactory;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;
/**
* Wraps multiple commands into a composite to execute queued commands during a frame.
@ -10,15 +10,15 @@ public class CompositeCommand implements Command {
private Queue<Command> commands;
public CompositeCommand() {
this.commands = new ArrayDeque<>();
this.commands = new ConcurrentLinkedDeque<>();
}
public void addCommand(Command command) {
commands.add(command);
commands.offer(command);
}
@Override
public void execute() {
while(!commands.isEmpty()) commands.remove().execute();
while(commands.peek() != null) commands.poll().execute();
}
}

@ -103,7 +103,6 @@ public class RaceXMLCreator {
}
}
/**
* Rotate the features in a race such as the boundary, and the marks.
* @param race the race to alter

@ -12,12 +12,15 @@ public enum BoatActionEnum {
/**
* Autopilot = auto VMG.
*/
AUTO_PILOT(1),
VMG(1),
SAILS_IN(2),
SAILS_OUT(3),
TACK_GYBE(4),
UPWIND(5),
DOWNWIND(6);
DOWNWIND(6),
ZOOM_IN(7),
ZOOM_OUT(8),
TOGGLE_SAILS(9);
private byte value;

@ -0,0 +1,46 @@
package visualiser.Commands.VisualiserRaceCommands;
import javafx.scene.media.AudioClip;
import mock.model.commandFactory.Command;
import network.Messages.YachtEvent;
import shared.exceptions.BoatNotFoundException;
import visualiser.model.VisualiserBoat;
import visualiser.model.VisualiserRaceState;
/**
* Created by zwu18 on 4/09/17.
*/
public class BoatCollisionCommand implements Command {
YachtEvent yachtEvent;
VisualiserRaceState visualiserRace;
public BoatCollisionCommand(YachtEvent yachtEvent, VisualiserRaceState visualiserRace){
this.yachtEvent = yachtEvent;
this.visualiserRace = visualiserRace;
}
@Override
public void execute() {
if(visualiserRace.getPlayerBoatID()==yachtEvent.getSourceID()){
//System.out.println("I crashed!");
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/collision.wav").toExternalForm());
sound.play();
} else {
//System.out.println("Someone else crashed!");
AudioClip sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/quietcollision.wav").toExternalForm());
sound.play();
}
try {
VisualiserBoat boat = visualiserRace.getBoat(yachtEvent.getSourceID());
boat.setHasCollided(true);
} catch (BoatNotFoundException e) {
e.printStackTrace();
}
}
}

@ -1,5 +1,6 @@
package visualiser.Commands.VisualiserRaceCommands;
import javafx.scene.media.AudioClip;
import mock.model.commandFactory.Command;
import network.Messages.BoatStatus;
import network.Messages.Enums.BoatStatusEnum;
@ -176,6 +177,10 @@ public class RaceStatusCommand implements Command {
//Record order in which boat finished leg.
visualiserRace.getLegCompletionOrder().get(boat.getCurrentLeg()).add(boat);
//play sound
AudioClip sound = new AudioClip(getClass().getResource("/visualiser/sounds/passmark.wav").toExternalForm());
sound.play();
//Update boat.
boat.setCurrentLeg(leg);
boat.setTimeAtLastMark(visualiserRace.getRaceClock().getCurrentTime());

@ -23,7 +23,9 @@ public class VisualiserRaceCommandFactory {
switch (message.getType()) {
case BOATLOCATION: return new BoatLocationCommand((BoatLocation) message, visualiserRace);
case BOATLOCATION:
//System.out.println("Boat location received");
return new BoatLocationCommand((BoatLocation) message, visualiserRace);
case RACESTATUS: return new RaceStatusCommand((RaceStatus) message, visualiserRace);
@ -31,6 +33,11 @@ public class VisualiserRaceCommandFactory {
case ASSIGN_PLAYER_BOAT: return new AssignPlayerBoatCommand((AssignPlayerBoat) message, visualiserRace);
case YACHTEVENTCODE:
return new BoatCollisionCommand((YachtEvent) message, visualiserRace);
default: throw new CommandConstructionException("Could not create VisualiserRaceCommand. Unrecognised or unsupported MessageType: " + message.getType());
}

@ -80,6 +80,9 @@ public class HostGameController extends Controller {
*/
public void alertMatchBrowser(){
try{
if (matchBrowserInterface == null){
return;//private game
}
matchBrowserInterface.startSendingHostData(App.game.getHostedGameData(), udpSocket);
}catch (IOException e){
System.err.println("failed to send out hosted game info");

@ -138,11 +138,13 @@ public class InGameLobbyController extends Controller {
playerContainer.add(playerBoatToSet, (count % 3) , row);
playerContainer.setMargin(playerBoatToSet, new Insets(10, 10, 10, 10));
SeaSurface sea = new SeaSurface(750, 200, 250, 0, 210);
subjects.add(sea.getSurface());
SeaSurface sea = new SeaSurface(750, 200);
sea.setX(250);
sea.setZ(210);
subjects.add(sea);
MeshView mesh = new MeshView(importer.getImport());
Subject3D subject = new Subject3D(mesh);
Subject3D subject = new Subject3D(mesh,0);
subjects.add(subject);
playerBoatToSet.setDistance(50);
@ -321,7 +323,7 @@ public class InGameLobbyController extends Controller {
* @param interval Countdown interval to check
*/
private void countdownText(long interval){
countdownTenPane.setVisible(false);
//Do nothing if 5 seconds or less to go
if (interval <=5000){
countdownTenPane.setVisible(false);

@ -7,6 +7,8 @@ import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.media.AudioClip;
import network.Messages.HostGame;
import visualiser.app.MatchBrowserSingleton;
import visualiser.model.RaceConnection;
@ -34,6 +36,8 @@ public class LobbyController extends Controller {
private ObservableList<RaceConnection> connections;
private ObservableList<RaceConnection> customConnections;
private AudioClip sound;
//the socket for match browser
private DatagramSocket udpSocket;
private MatchBrowserLobbyInterface matchBrowserLobbyInterface;
@ -67,6 +71,8 @@ public class LobbyController extends Controller {
* Refreshes the connections in the lobby
*/
public void refreshBtnPressed(){
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
sound.play();
addServerGames();
for(RaceConnection connection: connections) {
connection.check();
@ -92,6 +98,8 @@ public class LobbyController extends Controller {
}
public void menuBtnPressed() throws IOException {
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
sound.play();
matchBrowserLobbyInterface.closeSocket();
loadScene("title.fxml");
}
@ -100,6 +108,8 @@ public class LobbyController extends Controller {
* adds a new connection
*/
public void addConnectionPressed(){
sound = new AudioClip(this.getClass().getResource("/visualiser/sounds/buttonpress.wav").toExternalForm());
sound.play();
String hostName = addressFld.getText();
String portString = portFld.getText();
try {

@ -3,30 +3,38 @@ package visualiser.Controllers;
import com.interactivemesh.jfx.importer.stl.StlMeshImporter;
import javafx.animation.AnimationTimer;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList;
import javafx.fxml.FXML;
import javafx.scene.AmbientLight;
import javafx.scene.PointLight;
import javafx.scene.chart.LineChart;
import javafx.scene.control.*;
import javafx.scene.effect.Light;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.Sphere;
import javafx.scene.paint.Color;
import javafx.scene.paint.Material;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.*;
import javafx.scene.transform.Translate;
import javafx.util.Callback;
import network.Messages.Enums.RaceStatusEnum;
import shared.dataInput.RaceDataSource;
import shared.model.Leg;
import shared.model.Mark;
import shared.exceptions.BoatNotFoundException;
import shared.model.*;
import visualiser.app.App;
import visualiser.enums.TutorialState;
import visualiser.gameController.ControllerClient;
import visualiser.gameController.Keys.ControlKey;
import visualiser.layout.*;
import visualiser.model.*;
import visualiser.gameController.Keys.KeyFactory;
import visualiser.layout.Subject3D;
import visualiser.layout.View3D;
@ -62,6 +70,15 @@ public class RaceViewController extends Controller {
private View3D view3D;
private ObservableList<Subject3D> viewSubjects;
/**
* Arrow pointing to next mark in third person
*/
private Subject3D nextMarkArrow;
/**
* Animation loop for rotating mark arrow
*/
private AnimationTimer pointToMark;
// note: it says it's not used but it is! do not remove :)
private @FXML ArrowController arrowController;
private @FXML GridPane canvasBase;
@ -123,9 +140,8 @@ public class RaceViewController extends Controller {
}
}
/**
* Sets up the listener and actions that occur when a key is pressed.
*/
private AnimationTimer arrowToNextMark;
private void initKeypressHandler() {
newRacePane.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
String codeString = event.getCode().toString();
@ -191,10 +207,23 @@ public class RaceViewController extends Controller {
* Initialises the various UI components to listen to the {@link #visualiserRace}.
*/
private void initialiseRaceVisuals() {
// Import arrow mesh
URL asset = this.getClass().getClassLoader().getResource("assets/arrow V1.0.4.stl");
StlMeshImporter importer = new StlMeshImporter();
importer.read(asset);
MeshView arrow = new MeshView(importer.getImport());
PhongMaterial arrowMat = new PhongMaterial(Color.RED);
arrow.setMaterial(arrowMat);
this.nextMarkArrow = new Annotation3D(arrow);
this.nextMarkArrow.setScale(0.1);
// initialise displays
initialiseFps();
initialiseInfoTable();
initialiseView3D();
initialiseView3D(this.visualiserRace);
initialiseRaceClock();
raceTimer(); // start the timer
new Sparkline(this.raceState, this.sparklineChart);
@ -202,55 +231,122 @@ public class RaceViewController extends Controller {
arrowController.setWindProperty(this.raceState.windProperty());
}
private void initialiseView3D() {
private void initialiseView3D(VisualiserRaceEvent race) {
viewSubjects = FXCollections.observableArrayList();
AmbientLight ambientLight = new AmbientLight(Color.web("#CCCCFF"));
ambientLight.setTranslateX(250);
ambientLight.setTranslateZ(210);
ambientLight.setLightOn(true);
PointLight pointLight = new PointLight(Color.web("#AAAAFF"));
pointLight.setTranslateX(250);
pointLight.setTranslateZ(210);
pointLight.setLightOn(true);
// Import boat mesh
URL asset = RaceViewController.class.getClassLoader().getResource("assets/V1.2 " +
"Complete Boat.stl");
URL asset = RaceViewController.class.getClassLoader().getResource("assets/V1.2 Complete Boat.stl");
StlMeshImporter importer = new StlMeshImporter();
importer.read(asset);
// Configure camera angles and control
view3D = new View3D();
URL markerAsset = RaceViewController.class.getClassLoader().getResource("assets/Bouy V1.1.stl");
StlMeshImporter importerMark = new StlMeshImporter();
importerMark.read(markerAsset);
URL alternateBoatAsset = RaceViewController.class.getClassLoader().getResource("assets/V1.3 BurgerBoat.stl");
StlMeshImporter importerBurgerBoat = new StlMeshImporter();
importerBurgerBoat.read(alternateBoatAsset);
view3D = new View3D(false);
view3D.setItems(viewSubjects);
view3D.setDistance(1050);
view3D.setYaw(0);
view3D.setPitch(60);
view3D.setBirdsEye();
view3D.enableTracking();
view3D.addAmbientLight(ambientLight);
view3D.addPointLight(pointLight);
canvasBase.add(view3D, 0, 0);
// Set up projection from GPS to view
RaceDataSource raceData = raceState.getRaceDataSource();
RaceDataSource raceData = visualiserRace.getVisualiserRaceState().getRaceDataSource();
final GPSConverter gpsConverter = new GPSConverter(raceData, 450, 450);
view3D.setItems(viewSubjects);
SkyBox skyBox = new SkyBox(750, 200, 250, 0, 210);
viewSubjects.addAll(skyBox.getSkyBoxPlanes());
// Set up sea surface
SeaSurface sea = new SeaSurface(750, 200);
sea.setX(250);
sea.setZ(210);
viewSubjects.add(sea);
// Set up sea surface overlay
SeaSurface seaOverlay = new SeaSurface(4000, 200);
seaOverlay.setX(250);
seaOverlay.setZ(210);
viewSubjects.add(seaOverlay);
Boundary3D boundary3D = new Boundary3D(visualiserRace.getVisualiserRaceState().getRaceDataSource().getBoundary(), gpsConverter);
for (Subject3D subject3D: boundary3D.getBoundaryNodes()){
viewSubjects.add(subject3D);
}
// Position and add each mark to view
for (Mark mark : raceState.getMarks()) {
Subject3D subject = new Subject3D(new Sphere(2));
subject.setX(gpsConverter.convertGPS(mark.getPosition()).getX());
subject.setZ(gpsConverter.convertGPS(mark.getPosition()).getY());
for(Mark mark: race.getVisualiserRaceState().getMarks()) {
MeshView mesh = new MeshView(importerMark.getImport());
Subject3D markModel = new Subject3D(mesh, mark.getSourceID());
viewSubjects.add(subject);
markModel.setX(gpsConverter.convertGPS(mark.getPosition()).getX());
markModel.setZ(gpsConverter.convertGPS(mark.getPosition()).getY());
viewSubjects.add(markModel);
}
// Position and add each boat to view
for (VisualiserBoat boat : raceState.getBoats()) {
MeshView mesh = new MeshView(importer.getImport());
Subject3D subject = new Subject3D(mesh);
viewSubjects.add(subject);
for(VisualiserBoat boat: race.getVisualiserRaceState().getBoats()) {
MeshView mesh;
if(boat.getSourceID() == race.getVisualiserRaceState().getPlayerBoatID()) {
mesh = new MeshView(importer.getImport());
} else {
mesh = new MeshView(importerBurgerBoat.getImport());
}
Subject3D boatModel = new Subject3D(mesh, boat.getSourceID());
viewSubjects.add(boatModel);
// Track this boat's movement with the new subject
AnimationTimer trackBoat = new AnimationTimer() {
@Override public void handle(long now) {
subject.setHeading(boat.getBearing().degrees());
subject.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
subject.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
@Override
public void handle(long now) {
boatModel.setHeading(boat.getBearing().degrees());
boatModel.setX(gpsConverter.convertGPS(boat.getPosition()).getX());
boatModel.setZ(gpsConverter.convertGPS(boat.getPosition()).getY());
}
};
trackBoat.start();
Material markColor = new PhongMaterial(new Color(0.15,0.9,0.2,1));
CompoundMark nextMark = boat.getCurrentLeg().getEndCompoundMark();
view3D.getShape(nextMark.getMark1().getSourceID()).setMaterial(markColor);
if(nextMark.getMark2() != null) {
view3D.getShape(nextMark.getMark2().getSourceID()).setMaterial(markColor);
}
Subject3D shockwave = new Shockwave(10);
viewSubjects.add(shockwave);
boat.legProperty().addListener((o, prev, curr) -> swapColours(curr));
boat.hasCollidedProperty().addListener((o, prev, curr) -> showCollision(boatModel, shockwave));
}
// Fix initial bird's-eye position
view3D.updatePivot(new Translate(250, 0, 210));
view3D.targetProperty().addListener((o, prev, curr)-> {
if(curr != null && visualiserRace.getVisualiserRaceState().isVisualiserBoat(curr.getSourceID())) {
addThirdPersonAnnotations(curr);
} else {
removeThirdPersonAnnotations(prev);
}
});
// Bind zooming to scrolling
view3D.setOnScroll(e -> {
view3D.updateDistance(e.getDeltaY());
@ -296,6 +392,112 @@ public class RaceViewController extends Controller {
});
}
private void showCollision(Subject3D boat, Subject3D shockwave) {
AnimationTimer shockFront = new AnimationTimer() {
double opacity = 1;
@Override
public void handle(long now) {
shockwave.setX(boat.getPosition().getX());
shockwave.setY(boat.getPosition().getY());
shockwave.setZ(boat.getPosition().getZ());
if(opacity <= 0) {
shockwave.getMesh().setMaterial(new PhongMaterial(new Color(1,0,0,0)));
this.stop();
}
else {
shockwave.getMesh().setMaterial(new PhongMaterial(new Color(1,0,0,opacity)));
opacity -= 0.1;
}
}
};
shockFront.start();
}
private void addThirdPersonAnnotations(Subject3D subject3D) {
viewSubjects.add(nextMarkArrow);
final VisualiserBoat boat;
try {
boat = visualiserRace.getVisualiserRaceState().getBoat(subject3D.getSourceID());
} catch (BoatNotFoundException e) {
e.printStackTrace();
return;
}
arrowToNextMark = new AnimationTimer() {
@Override
public void handle(long now) {
CompoundMark target = boat.getCurrentLeg().getEndCompoundMark();
Bearing headingToMark = GPSCoordinate.calculateBearing(boat.getPosition(), target.getAverageGPSCoordinate());
nextMarkArrow.setX(view3D.getPivot().getX());
nextMarkArrow.setY(view3D.getPivot().getY());
nextMarkArrow.setZ(view3D.getPivot().getZ() + 15);
nextMarkArrow.setHeading(headingToMark.degrees());
}
};
arrowToNextMark.start();
/*
try {
VisualiserBoat boat = visualiserRace.getVisualiserRaceState().getBoat(subject3D.getSourceID());
this.pointToMark = new AnimationTimer() {
@Override
public void handle(long now) {
CompoundMark target = boat.getCurrentLeg().getEndCompoundMark();
Bearing headingToMark = GPSCoordinate.calculateBearing(boat.getPosition(), target.getAverageGPSCoordinate());
nextMarkArrow.setX(view3D.getPivot().getX());
nextMarkArrow.setY(view3D.getPivot().getY());
nextMarkArrow.setZ(view3D.getPivot().getZ() + 10);
nextMarkArrow.setHeading(headingToMark.degrees());
}
};
pointToMark.start();
} catch (BoatNotFoundException e) {
e.printStackTrace();
}*/
}
private void removeThirdPersonAnnotations(Subject3D subject3D) {
viewSubjects.remove(nextMarkArrow);
arrowToNextMark.stop();
/*
try {
VisualiserBoat boat = visualiserRace.getVisualiserRaceState().getBoat(subject3D.getSourceID());
boat.positionProperty().removeListener(pointToMark);
} catch (BoatNotFoundException e) {
e.printStackTrace();
}*/
//pointToMark.stop();
}
/**
* Swap the colour of the next mark to pass with the last mark passed
* @param leg boat has started on
*/
private void swapColours(Leg leg) {
CompoundMark start = leg.getStartCompoundMark();
CompoundMark end = leg.getEndCompoundMark();
Shape3D start1 = view3D.getShape(start.getMark1().getSourceID());
Shape3D end1 = view3D.getShape(end.getMark1().getSourceID());
Material nextMark = start1.getMaterial();
Material lastMark = end1.getMaterial();
start1.setMaterial(lastMark);
if(start.getMark2() != null) {
Shape3D start2 = view3D.getShape(start.getMark2().getSourceID());
start2.setMaterial(lastMark);
}
end1.setMaterial(nextMark);
if(end.getMark2() != null) {
Shape3D end2 = view3D.getShape(end.getMark2().getSourceID());
end2.setMaterial(nextMark);
}
}
/**
* Initialises the frame rate functionality. This allows for toggling the
* frame rate, and connect the fps label to the race's fps property.

@ -6,11 +6,20 @@ import javafx.scene.control.RadioButton;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.media.AudioClip;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.stage.Modality;
import mock.exceptions.EventConstructionException;
import visualiser.app.App;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Controller for the opening title window.

@ -25,7 +25,7 @@ public enum TutorialState {
/**
* State for vmg in tutorial
*/
VMG(BoatActionEnum.AUTO_PILOT),
VMG(BoatActionEnum.VMG),
/**
* State for sails-in in tutorial

@ -12,7 +12,7 @@ public class SailsToggleKey extends ControlKey {
* Constructor for Control
*/
public SailsToggleKey() {
super("Toggle Sails", BoatActionEnum.NOT_A_STATUS);
super("Toggle Sails", BoatActionEnum.TOGGLE_SAILS);
}
/**

@ -11,7 +11,7 @@ public class VMGKey extends ControlKey{
* Constructor for Control
*/
public VMGKey() {
super("VMG", BoatActionEnum.AUTO_PILOT);
super("VMG", BoatActionEnum.VMG);
}
@Override

@ -1,12 +1,14 @@
package visualiser.gameController.Keys;
import network.Messages.Enums.BoatActionEnum;
/**
* key to zoom into the game
*/
public class ZoomInKey extends ControlKey {
public ZoomInKey() {
super("Zoom In");
super("Zoom In", BoatActionEnum.ZOOM_IN);
}
@Override

@ -1,5 +1,7 @@
package visualiser.gameController.Keys;
import network.Messages.Enums.BoatActionEnum;
/**
* Key to zoom out of the game.
*/
@ -9,7 +11,7 @@ public class ZoomOutKey extends ControlKey{
* Constructor for Control
*/
public ZoomOutKey() {
super("Zoom Out");
super("Zoom Out", BoatActionEnum.ZOOM_OUT);
}
@Override

@ -0,0 +1,24 @@
package visualiser.layout;
import javafx.scene.shape.Shape3D;
/**
* Created by connortaylorbrown on 13/09/17.
*/
public class Annotation3D extends Subject3D {
/**
* Constructor for view subject wrapper
*
* @param mesh to be rendered
*/
public Annotation3D(Shape3D mesh) {
super(mesh, 0);
}
/**
* Prevent rescaling of this subject
* @param scale ignored
*/
@Override
public void setScale(double scale) {}
}

@ -0,0 +1,89 @@
package visualiser.layout;
import com.sun.corba.se.impl.orbutil.graph.Graph;
import javafx.scene.shape.Box;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Sphere;
import shared.model.GPSCoordinate;
import visualiser.model.GraphCoordinate;
import visualiser.utils.GPSConverter;
import java.util.ArrayList;
import java.util.List;
/**
* Class that creates a 3d boundary based on gps coordinates
*/
public class Boundary3D {
public static double thickness = 1;
private List<Subject3D> boundaryNodes = new ArrayList<>();
private List<Subject3D> boundaryConnectors = new ArrayList<>();
private GPSConverter gpsConverter;
public Boundary3D(List<GPSCoordinate> points, GPSConverter gpsConverter){
this.gpsConverter = gpsConverter;
setBoundaryNodes(points);
}
/**
* Splits up the list so that it generates a edge of the boundary
* @param points boundary gpscoordinate
*/
private void setBoundaryNodes(List<GPSCoordinate> points){
if (points.size() < 2){
return;
}
System.out.println(points.size());
for (int i = 0; i < points.size(); i++){
if (i + 1 != points.size()){
addBound(points.get(i), points.get(i + 1));
} else {
addBound(points.get(i), points.get(0));
}
}
}
/**
* Add a two point boundary this will create a sphere at coord1 and a line to coord 2
* this is to reduce double up (2 spheres in one point).
* @param coord1 point to make sphere and start the line.
* @param coord2 point to end the line.
*/
private void addBound(GPSCoordinate coord1, GPSCoordinate coord2){
GraphCoordinate graphCoord1 = gpsConverter.convertGPS(coord1);
GraphCoordinate graphCoord2 = gpsConverter.convertGPS(coord2);
GraphCoordinate avgCoord = new GraphCoordinate((graphCoord1.getX() + graphCoord2.getX()) / 2,
(graphCoord1.getY() + graphCoord2.getY()) / 2);
double a = (graphCoord1.getX() - graphCoord2.getX());
double b = (graphCoord1.getY() - graphCoord2.getY());
double c = Math.sqrt(a * a + b * b);
Subject3D bound1 = new Annotation3D(new Sphere(thickness * 2));
bound1.setX(graphCoord1.getX());
bound1.setZ(graphCoord1.getY());
boundaryNodes.add(bound1);
Subject3D connector = new Annotation3D(new Box(c, thickness, thickness));
connector.setX(avgCoord.getX());
connector.setZ(avgCoord.getY());
double angle = 90 + Math.toDegrees(GPSConverter.getAngle(graphCoord2, graphCoord1));
connector.setHeading(angle);
boundaryConnectors.add(connector);
}
/**
* get the 3d objects to draw
* @return 3d boundary to draw
*/
public List<Subject3D> getBoundaryNodes(){
//these two must be concatenated with nodes after connectors else the spheres will not overlap the lines
ArrayList<Subject3D> result = new ArrayList<>(boundaryConnectors);
result.addAll(boundaryNodes);
return result;
}
}

@ -1,5 +1,13 @@
package visualiser.layout;
import com.sun.javafx.geom.PickRay;
import com.sun.javafx.scene.input.PickResultChooser;
import com.sun.javafx.sg.prism.NGNode;
import javafx.scene.Node;
import javafx.scene.shape.*;
import java.util.ArrayList;
import java.util.Arrays;
import javafx.scene.shape.TriangleMesh;
import java.util.ArrayList;
@ -92,6 +100,7 @@ public class Plane3D extends TriangleMesh{
float x = this.getPoints().get(i);
float y = this.getPoints().get(i + 1);
float z = this.getPoints().get(i + 2);
System.out.println(String.format("Point at %d is x:%f, y:%f, z:%f", index, x, y, z));
}
/**

@ -1,65 +1,64 @@
package visualiser.layout;
import javafx.geometry.Point3D;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Box;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.Shape3D;
import javafx.scene.shape.TriangleMesh;
import visualiser.utils.PerlinNoiseGenerator;
/**
* Creates a SeaSurface
*/
public class SeaSurface {
private float[][] noiseArray;
private Subject3D surface;
public class SeaSurface extends Subject3D {
private static Image image;
/**
* Sea Surface Constructor
*
* @param size size of the sea surface (has to be square for simplicity's sake)
* @param freq frequency the perlin noise is to be generated at
* @param x offset that the sea should be set at position-wise
* @param y offset that the sea should be set at position-wise
* @param z offset that the sea should be set at position-wise
*/
public SeaSurface(int size, double freq, double x, double y, double z){
noiseArray = PerlinNoiseGenerator.createNoise(size, freq);
createSurface();
surface.setZ(z);
surface.setY(y);
surface.setX(x);
public SeaSurface(int size, double freq) {
super(createSurface(size, freq), 0);
image = new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/ThickCloudsWaterDown2048.png"));
}
/**
* Creates the sea surface
*/
private void createSurface(){
Image diffuseMap = createImage(noiseArray.length, noiseArray);
private static Shape3D createSurface(int size, double freq) {
float[][] noiseArray = PerlinNoiseGenerator.createNoise(size, freq);
Image diffuseMap = createImage(noiseArray.length, noiseArray); //image
PhongMaterial material = new PhongMaterial();
material.setDiffuseColor(Color.web("#FFFFFF"));
material.setSpecularColor(Color.web("#000000"));
material.setDiffuseMap(diffuseMap);
//material.setSpecularColor(Color.WHITE);
Plane3D seaPlane = new Plane3D(noiseArray.length, noiseArray.length, 10, 10);
MeshView seaSurface = new MeshView(seaPlane);
// Box seaSurface = new Box(noiseArray.length, 0.1, noiseArray.length);
seaSurface.setMaterial(material);
seaSurface.setMouseTransparent(true);
seaSurface.toFront();
//seaSurface.setRotationAxis(new Point3D(1, 0, 0));
//seaSurface.setRotate(90);
surface = new Subject3D(seaSurface);
return seaSurface;
}
/**
* Create texture for uv mapping
*
* @param size size of the image to make
* @param noise array of noise
* @return image that is created
*/
private Image createImage(double size, float[][] noise) {
private static Image createImage(double size, float[][] noise) {
int width = (int) size;
int height = (int) size;
@ -94,6 +93,7 @@ public class SeaSurface {
/**
* Nomalises the values so that the colours are correct
*
* @param value value to normalise
* @param min current min
* @param max current max
@ -109,6 +109,7 @@ public class SeaSurface {
/**
* clamps a value between a min and max
*
* @param value value to clamp
* @param min minimum value it can be
* @param max maximum value it can be
@ -126,11 +127,11 @@ public class SeaSurface {
}
/**
* Get surface
* @return the surface so it can be drawn
* Prevent rescaling of sea surface
*
* @param scale ignored
*/
public Subject3D getSurface(){
return surface;
@Override
public void setScale(double scale) {
}
}

@ -0,0 +1,17 @@
package visualiser.layout;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Cylinder;
import javafx.scene.transform.Rotate;
/**
* Created by cbt24 on 14/09/17.
*/
public class Shockwave extends Subject3D {
public Shockwave(double radius) {
super(new Cylinder(radius,0),0);
getMesh().getTransforms().add(new Rotate(-90, Rotate.X_AXIS));
getMesh().setMaterial(new PhongMaterial(new Color(0,0,0,0)));
}
}

@ -0,0 +1,163 @@
package visualiser.layout;
import javafx.geometry.Point3D;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.MeshView;
import javafx.scene.transform.Rotate;
import java.util.ArrayList;
import java.util.List;
/**
* Creates a skyBox
*/
public class SkyBox {
private int size;
private double x;
private double y;
private double z;
private double freq;
private double yshift;
private double clipOverlap;
private List<Subject3D> skyBoxPlanes = new ArrayList<>();
public SkyBox(int edgeLength, double freq, double x, double y, double z) {
this.size = edgeLength;
this.x = x;
this.y = y;
this.z = z;
this.freq = freq;
this.yshift = -size/64;
clipOverlap = 0;
makeSkyBox();
}
private void makeSkyBox() {
addTop();
addFront();
addBack();
addLeft();
addRight();
//addSeaOverlay();
}
private void addTop() {
MeshView surface = makeSurface(new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/ThickCloudsWaterUp2048.png")), size);
surface.setRotationAxis(new Point3D(0, 0, 1));
surface.setRotate(180);
surface.setTranslateX(x);
surface.setTranslateY(y - size + 1);
surface.setTranslateZ(z);
Subject3D top = new SkyBoxPlane(surface,0);
skyBoxPlanes.add(top);
}
private void addRight() {
MeshView surface = makeSurface(new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/ThickCloudsWaterRight2048.png")), size + 1);
surface.setTranslateX(size/2);
surface.setTranslateY(size/2);
surface.setRotationAxis(new Point3D(1, 0, 0));
surface.setRotate(90);
surface.setTranslateX(-size/2);
surface.setTranslateY(-size/2);
surface.setTranslateX(x);
surface.setTranslateY(y + yshift);
surface.setTranslateZ(z + size/2 - clipOverlap);
Subject3D right = new SkyBoxPlane(surface,0);
skyBoxPlanes.add(right);
}
private void addLeft() {
MeshView surface = makeSurface(new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/ThickCloudsWaterLeft2048.png")), size + 1);
surface.setTranslateX(size/2);
surface.setTranslateY(size/2);
surface.setRotationAxis(new Point3D(1, 0, 0));
surface.setRotate(-90);
surface.setTranslateX(-size/2);
surface.setTranslateY(-size/2);
surface.setScaleX(-1);
surface.setScaleZ(-1);
surface.setTranslateX(x);
surface.setTranslateY(y + yshift);
surface.setTranslateZ(z - size/2 + clipOverlap);
Subject3D left = new SkyBoxPlane(surface,0);
skyBoxPlanes.add(left);
}
private void addBack() {
MeshView surface = makeSurface(new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/ThickCloudsWaterBack2048.png")), size);
surface.getTransforms().add(new Rotate(90, 0, 0));
surface.setRotationAxis(new Point3D(1, 0, 0));
surface.setRotate(-90);
surface.setScaleY(-1);
surface.setScaleZ(-1);
surface.setTranslateX(x - size/2 + clipOverlap);
surface.setTranslateY(y + yshift);
surface.setTranslateZ(z);
Subject3D back = new SkyBoxPlane(surface,0);
skyBoxPlanes.add(back);
}
private void addFront() {
MeshView surface = makeSurface(new Image(getClass().getClassLoader().getResourceAsStream("images/skybox/ThickCloudsWaterFront2048.png")), size);
surface.setTranslateX(size/2);
surface.setTranslateY(size/2);
surface.setRotationAxis(new Point3D(0, 0, 1));
surface.setRotate(-90);
surface.setTranslateX(-size/2);
surface.setTranslateY(-size/2);
surface.setTranslateX(x + size/2 - clipOverlap);
surface.setTranslateY(y + yshift);
surface.setTranslateZ(z);
Subject3D front = new SkyBoxPlane(surface,0);
skyBoxPlanes.add(front);
}
private MeshView makeSurface(Image diffuseMap, int size) {
PhongMaterial material = new PhongMaterial();
material.setDiffuseColor(Color.web("#FFFFFF"));
material.setSpecularColor(Color.web("#000000"));
material.setDiffuseMap(diffuseMap);
Plane3D plane = new Plane3D(size, size, 10, 10);
MeshView surface = new MeshView(plane);
surface.setMaterial(material);
surface.setMouseTransparent(true);
return surface;
}
private void addSeaOverlay() {
SeaSurface seaSurface = new SeaSurface(size * 3, freq);
seaSurface.setX(x);
seaSurface.setY(y - size/4 + 1);
seaSurface.setZ(z);
skyBoxPlanes.add(seaSurface);
}
public List<Subject3D> getSkyBoxPlanes() {
return skyBoxPlanes;
}
}

@ -0,0 +1,14 @@
package visualiser.layout;
import javafx.scene.shape.Shape3D;
public class SkyBoxPlane extends Subject3D {
public SkyBoxPlane(Shape3D mesh, int sourceID) {
super(mesh,sourceID);
}
@Override
public void setScale(double scale) {
}
}

@ -2,6 +2,7 @@ package visualiser.layout;
import javafx.scene.shape.Shape3D;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Translate;
/**
@ -12,6 +13,10 @@ public class Subject3D {
* Rendered mesh
*/
private Shape3D mesh;
/**
* Source ID of subject in game model
*/
private int sourceID;
/**
* Position translation updated by state listeners
@ -23,22 +28,29 @@ public class Subject3D {
*/
private Rotate heading;
private Scale scale;
/**
* Constructor for view subject wrapper
* @param mesh to be rendered
*/
public Subject3D(Shape3D mesh) {
public Subject3D(Shape3D mesh, int sourceID) {
this.mesh = mesh;
this.sourceID = sourceID;
this.scale = new Scale();
this.position = new Translate();
this.heading = new Rotate(0, Rotate.Y_AXIS);
this.mesh.getTransforms().addAll(position, heading, new Rotate(90, Rotate.X_AXIS), new Rotate(180, Rotate.Y_AXIS));
this.mesh.getTransforms().addAll(position, scale, heading, new Rotate(90, Rotate.X_AXIS), new Rotate(180, Rotate.Y_AXIS));
}
public Shape3D getMesh() {
return mesh;
}
public int getSourceID() {
return sourceID;
}
public Translate getPosition() {
return this.position;
}
@ -47,6 +59,12 @@ public class Subject3D {
return heading;
}
public void setScale(double scale) {
this.scale.setX(scale);
this.scale.setY(scale);
this.scale.setZ(scale);
}
public void setX(double x) {
position.setX(x);
}

@ -1,11 +1,12 @@
package visualiser.layout;
import javafx.animation.AnimationTimer;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.SubScene;
import javafx.scene.*;
import javafx.scene.input.PickResult;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
@ -33,11 +34,15 @@ public class View3D extends Pane {
/**
* Map for selecting Subject3D from Shape3D
*/
private Map<Shape3D, Subject3D> selectionMap;
private Map<Shape3D, Subject3D> shapeMap;
/**
* Map for selecting Shape3D from source ID
*/
private Map<Integer, Shape3D> sourceMap;
/**
* Subject tracked by camera
*/
private Subject3D target;
private ObjectProperty<Subject3D> target;
/**
* Rendering container for shapes
*/
@ -67,44 +72,43 @@ public class View3D extends Pane {
*/
private Rotate pitch;
/**
* Single listener for subject heading changes
*/
private ChangeListener<? super Number> pivotHeading = (o, prev, curr) -> yaw.setAngle((double)curr);
/**
* Single listener for subject position (x) changes
*/
private ChangeListener<? super Number> pivotX = (o, prev, curr) -> pivot.setX((double)curr);
/**
* Single listener for subject position (y) changes
* Animation loop for camera tracking
*/
private ChangeListener<? super Number> pivotY = (o, prev, curr) -> pivot.setY((double)curr);
private AnimationTimer trackBoat;
/**
* Single listener for subject position (z) changes
* Distance to switch from third person to bird's eye
*/
private ChangeListener<? super Number> pivotZ = (o, prev, curr) -> pivot.setZ((double)curr);
private final double THIRD_PERSON_LIMIT = 100;
/**
* Distance to switch from third person to bird's eye
* Distance to stop zoom
*/
private double THIRD_PERSON_LIMIT = 100;
private final double FIRST_PERSON_LIMIT = 2;
/**
* Default constructor for View3D. Sets up Scene and PerspectiveCamera.
* @param fill whether or not to fill the background of the view.
*/
public View3D() {
public View3D(boolean fill) {
this.world = new Group();
this.selectionMap = new HashMap<>();
this.target = null;
this.shapeMap = new HashMap<>();
this.sourceMap = new HashMap<>();
this.target = new SimpleObjectProperty<>(null);
this.scene = new SubScene(world, 300, 300);
scene.widthProperty().bind(this.widthProperty());
scene.heightProperty().bind(this.heightProperty());
if (fill) {
scene.setFill(new Color(0.2, 0.6, 1, 1));
}
scene.setCamera(buildCamera());
this.getChildren().add(scene);
}
public View3D(){
this(true);
}
/**
* Sets up camera view frustum and binds transformations
* @return perspective camera
@ -140,17 +144,23 @@ public class View3D extends Pane {
if (c.wasRemoved() || c.wasAdded()) {
for (Subject3D shape : c.getRemoved()) {
world.getChildren().remove(shape.getMesh());
selectionMap.remove(shape.getMesh());
shapeMap.remove(shape.getMesh());
sourceMap.remove(shape.getSourceID());
}
for (Subject3D shape : c.getAddedSubList()) {
world.getChildren().add(shape.getMesh());
selectionMap.put(shape.getMesh(), shape);
shapeMap.put(shape.getMesh(), shape);
sourceMap.put(shape.getSourceID(), shape.getMesh());
}
}
}
});
}
public Shape3D getShape(int sourceID) {
return sourceMap.get(sourceID);
}
/**
* Intercept mouse clicks on subjects in view. The applied listener cannot be removed.
*/
@ -158,20 +168,48 @@ public class View3D extends Pane {
scene.setOnMousePressed(e -> {
PickResult result = e.getPickResult();
if(result != null && result.getIntersectedNode() != null && result.getIntersectedNode() instanceof Shape3D) {
trackSubject(selectionMap.get(result.getIntersectedNode()));
untrackSubject();
trackSubject(shapeMap.get(result.getIntersectedNode()));
setThirdPerson();
}
});
}
public ObjectProperty<Subject3D> targetProperty() {
return target;
}
/**
* Configures camera to third person view
*/
public void setThirdPerson() {
this.setDistance(THIRD_PERSON_LIMIT / 2);
this.setPitch(10);
for(Subject3D item: items) {
item.setScale(0.1);
}
}
/**
* Configures camera to bird's eye view
*/
public void setBirdsEye() {
this.setYaw(0);
this.setPitch(60);
for(Subject3D item: items) {
item.setScale(1);
}
}
/**
* Stop camera from following the last selected subject
*/
private void untrackSubject() {
if(target != null) {
target.getPosition().xProperty().removeListener(pivotX);
target.getPosition().yProperty().removeListener(pivotY);
target.getPosition().zProperty().removeListener(pivotZ);
target.getHeading().angleProperty().removeListener(pivotHeading);
if(target.get() != null) {
trackBoat.stop();
target.setValue(null);
}
}
@ -180,19 +218,16 @@ public class View3D extends Pane {
* @param subject to track
*/
private void trackSubject(Subject3D subject) {
untrackSubject();
target = subject;
updatePivot(target.getPosition());
setYaw(target.getHeading().getAngle());
target.getPosition().xProperty().addListener(pivotX);
target.getPosition().yProperty().addListener(pivotY);
target.getPosition().zProperty().addListener(pivotZ);
target.getHeading().angleProperty().addListener(pivotHeading);
target.set(subject);
this.setDistance(THIRD_PERSON_LIMIT);
this.setPitch(20);
this.trackBoat = new AnimationTimer() {
@Override
public void handle(long now) {
updatePivot(target.get().getPosition());
setYaw(target.get().getHeading().getAngle());
}
};
trackBoat.start();
}
public void setNearClip(double nearClip) {
@ -203,6 +238,10 @@ public class View3D extends Pane {
this.farClip = farClip;
}
public Translate getPivot() {
return pivot;
}
/**
* Sets the coordinates of the camera pivot once.
* @param pivot source of coordinates
@ -230,13 +269,12 @@ public class View3D extends Pane {
public void updateDistance(double delta) {
double distance = -this.distance.getZ() + delta;
if(distance <= 0) {
this.setDistance(0);
if(distance <= FIRST_PERSON_LIMIT) {
this.setDistance(FIRST_PERSON_LIMIT);
} else if(distance > THIRD_PERSON_LIMIT) {
untrackSubject();
this.setYaw(0);
this.setPitch(60);
this.setDistance(distance);
untrackSubject();
setBirdsEye();
} else {
this.setDistance(distance);
}
@ -257,4 +295,12 @@ public class View3D extends Pane {
public void setPitch(double pitch) {
this.pitch.setAngle(-pitch);
}
public void addAmbientLight(AmbientLight ambientLight) {
this.world.getChildren().add(ambientLight);
}
public void addPointLight(PointLight pointLight) {
this.world.getChildren().add(pointLight);
}
}

@ -1,6 +1,8 @@
package visualiser.model;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.paint.Color;
import network.Messages.Enums.BoatStatusEnum;
@ -62,6 +64,7 @@ public class VisualiserBoat extends Boat {
private ObjectProperty<GPSCoordinate> positionProperty;
private ObjectProperty<Bearing> bearingProperty;
private BooleanProperty hasCollided;
/**
@ -74,6 +77,7 @@ public class VisualiserBoat extends Boat {
super(boat.getSourceID(), boat.getName(), boat.getCountry());
this.color = color;
this.hasCollided = new SimpleBooleanProperty(false);
}
@ -253,10 +257,6 @@ public class VisualiserBoat extends Boat {
this.positionProperty.set(position);
}
public ObjectProperty<GPSCoordinate> positionProperty() {
return positionProperty;
}
@Override
public Bearing getBearing() {
return bearingProperty.get();
@ -270,7 +270,15 @@ public class VisualiserBoat extends Boat {
this.bearingProperty.set(bearing);
}
public ObjectProperty<Bearing> bearingProperty() {
return bearingProperty;
public boolean hasCollided() {
return hasCollided.get();
}
public BooleanProperty hasCollidedProperty() {
return hasCollided;
}
public void setHasCollided(boolean hasCollided) {
this.hasCollided.set(hasCollided);
}
}

@ -65,7 +65,7 @@ public class VisualiserRaceController implements RunnableWithFramePeriod {
compositeRaceCommand.addCommand(command);
} catch (CommandConstructionException e) {
Logger.getGlobal().log(Level.WARNING, "VisualiserRaceController could not create a command for incoming message.");
//Logger.getGlobal().log(Level.WARNING, "VisualiserRaceController could not create a command for incoming message.");
} catch (InterruptedException e) {
Logger.getGlobal().log(Level.SEVERE, "VisualiserRaceController was interrupted on thread: " + Thread.currentThread() + " while waiting for messages.");

@ -101,4 +101,14 @@ public class GPSConverter {
return convertGPS(coordinate.getLatitude(), coordinate.getLongitude());
}
/**
* Gets the bearing between two coordinates
* @param coord1 coordinate 1
* @param coord2 coordinate 2
* @return return the bearing between the two.
*/
public static double getAngle(GraphCoordinate coord1, GraphCoordinate coord2){
return Math.atan2(coord2.getX() - coord1.getX(), coord2.getY() - coord1.getY());
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Race>
<RaceID>5326</RaceID>
<RaceType>FLEET</RaceType>
<CreationTimeDate>RACE_CREATION_TIME</CreationTimeDate>
<RaceStartTime Postpone="false" Time="RACE_START_TIME"/>
<Participants>
</Participants>
<CompoundMarkSequence>
<Corner SeqID="1" CompoundMarkID="1" Rounding="SP" ZoneSize="3" />
<Corner SeqID="2" CompoundMarkID="2" Rounding="Port" ZoneSize="3" />
<Corner SeqID="3" CompoundMarkID="4" Rounding="Port" ZoneSize="3" />
<Corner SeqID="4" CompoundMarkID="3" Rounding="Starboard" ZoneSize="3" />
<Corner SeqID="5" CompoundMarkID="4" Rounding="Port" ZoneSize="3" />
<Corner SeqID="6" CompoundMarkID="5" Rounding="SP" ZoneSize="3"/>
</CompoundMarkSequence>
<Course>
<CompoundMark CompoundMarkID="1" Name="Start Line">
<Mark SeqId="1" Name="PRO" TargetLat="32.296577" TargetLng="-64.854304" SourceID="101"/>
<Mark SeqId="2" Name="PIN" TargetLat="32.293771" TargetLng="-64.855242" SourceID="102"/>
</CompoundMark>
<CompoundMark CompoundMarkID="2" Name="Marker 1">
<Mark Name="Marker1" TargetLat="32.293039" TargetLng="-64.843983" SourceID="103"/>
</CompoundMark>
<CompoundMark CompoundMarkID="3" Name="Windward Gate">
<Mark Name="WGL" SeqId="1" TargetLat="32.28468" TargetLng="-64.850045" SourceID="104"/>
<Mark Name="WGR" SeqId="2" TargetLat="32.280164" TargetLng="-64.847591" SourceID="105"/>
</CompoundMark>
<CompoundMark CompoundMarkID="4" Name="Leeward Gate">
<Mark Name="LGL" SeqId="1" TargetLat="32.309693" TargetLng="-64.835249" SourceID="106"/>
<Mark Name="LGR" SeqId="2" TargetLat="32.308046" TargetLng="-64.831785" SourceID="107"/>
</CompoundMark>
<CompoundMark CompoundMarkID="5" Name="Finish Line">
<Mark Name="FL" SeqId="1" TargetLat="32.317379" TargetLng="-64.839291" SourceID="108"/>
<Mark Name="FR" SeqId="2" TargetLat="32.317257" TargetLng="-64.83626" SourceID="109"/>
</CompoundMark>
</Course>
<CourseLimit>
<Limit Lat="32.313922" Lon="-64.837168" SeqID="1"/>
<Limit Lat="32.317379" Lon="-64.839291" SeqID="2"/>
<Limit Lat="32.317911" Lon="-64.836996" SeqID="3"/>
<Limit Lat="32.317257" Lon="-64.83626" SeqID="4"/>
<Limit Lat="32.304273" Lon="-64.822834" SeqID="5"/>
<Limit Lat="32.279097" Lon="-64.841545" SeqID="6"/>
<Limit Lat="32.279604" Lon="-64.849871" SeqID="7"/>
<Limit Lat="32.289545" Lon="-64.854162" SeqID="8"/>
<Limit Lat="32.290198" Lon="-64.858711" SeqID="9"/>
<Limit Lat="32.297164" Lon="-64.856394" SeqID="10"/>
<Limit Lat="32.296148" Lon="-64.849184" SeqID="11"/>
</CourseLimit>
</Race>

@ -4,8 +4,6 @@ import network.Exceptions.InvalidMessageException;
import network.MessageEncoders.RaceVisionByteEncoder;
import network.Messages.BoatAction;
import network.Messages.Enums.BoatActionEnum;
import network.Messages.Enums.RequestToJoinEnum;
import network.Messages.RequestToJoin;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
@ -65,7 +63,7 @@ public class BoatActionDecoderTest {
*/
@Test
public void autoPilotTest() throws Exception {
boatActionTypeTest(BoatActionEnum.AUTO_PILOT);
boatActionTypeTest(BoatActionEnum.VMG);
}
/**

Loading…
Cancel
Save