mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 06:18:44 +00:00
Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
package seng302.gameServer;
|
package seng302.gameServer;
|
||||||
|
|
||||||
import seng302.gameServer.messages.*;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import seng302.gameServer.messages.BoatLocationMessage;
|
import seng302.gameServer.messages.BoatLocationMessage;
|
||||||
@@ -25,9 +24,6 @@ import seng302.model.token.Token;
|
|||||||
import seng302.model.token.TokenType;
|
import seng302.model.token.TokenType;
|
||||||
import seng302.utilities.XMLGenerator;
|
import seng302.utilities.XMLGenerator;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Class for interfacing between the data we have in the GameState to the messages we need to send
|
* A Class for interfacing between the data we have in the GameState to the messages we need to send
|
||||||
* through the MainServerThread.
|
* through the MainServerThread.
|
||||||
@@ -77,9 +73,6 @@ public class MessageFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void updateBoats(List<ServerYacht> yachts) {
|
public static void updateBoats(List<ServerYacht> yachts) {
|
||||||
// for (ServerYacht serverYacht : yachts) {
|
|
||||||
// System.out.println(serverYacht);
|
|
||||||
// }
|
|
||||||
xmlGenerator.getRace().setBoats(yachts);
|
xmlGenerator.getRace().setBoats(yachts);
|
||||||
String xmlStr = xmlGenerator.getBoatsAsXml();
|
String xmlStr = xmlGenerator.getBoatsAsXml();
|
||||||
MessageFactory.boats = new XMLMessage(xmlStr, XMLMessageSubType.BOAT, xmlStr.length());
|
MessageFactory.boats = new XMLMessage(xmlStr, XMLMessageSubType.BOAT, xmlStr.length());
|
||||||
|
|||||||
@@ -1,14 +1,5 @@
|
|||||||
package seng302.visualiser;
|
package seng302.visualiser;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.util.Pair;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import seng302.gameServer.messages.*;
|
|
||||||
import seng302.model.stream.packets.PacketType;
|
|
||||||
import seng302.model.stream.packets.StreamPacket;
|
|
||||||
import seng302.utilities.XMLParser;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -43,7 +34,7 @@ import seng302.model.stream.xml.generator.RaceXMLTemplate;
|
|||||||
import seng302.model.stream.xml.generator.RegattaXMLTemplate;
|
import seng302.model.stream.xml.generator.RegattaXMLTemplate;
|
||||||
import seng302.utilities.XMLGenerator;
|
import seng302.utilities.XMLGenerator;
|
||||||
import seng302.utilities.XMLParser;
|
import seng302.utilities.XMLParser;
|
||||||
import seng302.visualiser.controllers.ViewManager;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class describing a single connection to a Server for the purposes of sending and receiving on
|
* A class describing a single connection to a Server for the purposes of sending and receiving on
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
package seng302.visualiser;
|
package seng302.visualiser;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javafx.scene.Group;
|
import javafx.scene.Group;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
|
import seng302.model.ClientYacht;
|
||||||
import seng302.model.Limit;
|
import seng302.model.Limit;
|
||||||
import seng302.model.ScaledPoint;
|
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.utilities.Sounds;
|
||||||
|
import seng302.visualiser.fxObjects.Marker;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract class for keeping functionality common between race visualisation.
|
* Abstract class for keeping functionality common between race visualisation.
|
||||||
@@ -24,8 +29,36 @@ public abstract class GameView {
|
|||||||
List<CompoundMark> course = new ArrayList<>();
|
List<CompoundMark> course = new ArrayList<>();
|
||||||
List<CompoundMark> compoundMarks = new ArrayList<>();
|
List<CompoundMark> compoundMarks = new ArrayList<>();
|
||||||
List<Corner> courseOrder = new ArrayList<>();
|
List<Corner> courseOrder = new ArrayList<>();
|
||||||
|
HashMap<Mark, Marker> markerObjects = new HashMap<>();
|
||||||
|
|
||||||
public abstract Node getAssets();
|
public abstract Node getAssets();
|
||||||
public abstract void updateCourse(List<CompoundMark> newCourse, List<Corner> sequence);
|
public abstract void updateCourse(List<CompoundMark> newCourse, List<Corner> sequence);
|
||||||
public abstract void updateBorder(List<Limit> border);
|
public abstract void updateBorder(List<Limit> border);
|
||||||
|
|
||||||
|
void updateMarkArrows (ClientYacht yacht, int legNumber) {
|
||||||
|
CompoundMark compoundMark;
|
||||||
|
if (legNumber - 1 >= 0 && legNumber-1 < course.size()) {
|
||||||
|
Sounds.playMarkRoundingSound();
|
||||||
|
compoundMark = course.get(legNumber-1);
|
||||||
|
for (Mark mark : compoundMark.getMarks()) {
|
||||||
|
markerObjects.get(mark).showNextExitArrow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CompoundMark nextMark = null;
|
||||||
|
if (legNumber < course.size()) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import seng302.model.mark.Corner;
|
|||||||
import seng302.model.mark.Mark;
|
import seng302.model.mark.Mark;
|
||||||
import seng302.model.token.Token;
|
import seng302.model.token.Token;
|
||||||
import seng302.utilities.GeoUtility;
|
import seng302.utilities.GeoUtility;
|
||||||
import seng302.utilities.Sounds;
|
|
||||||
import seng302.visualiser.cameras.ChaseCamera;
|
import seng302.visualiser.cameras.ChaseCamera;
|
||||||
import seng302.visualiser.cameras.IsometricCamera;
|
import seng302.visualiser.cameras.IsometricCamera;
|
||||||
import seng302.visualiser.cameras.RaceCamera;
|
import seng302.visualiser.cameras.RaceCamera;
|
||||||
@@ -55,7 +54,7 @@ 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;
|
||||||
private final double DEFAULT_CAMERA_Y = 100;
|
private final double DEFAULT_CAMERA_Y = 160;
|
||||||
|
|
||||||
private Group root3D;
|
private Group root3D;
|
||||||
private SubScene view;
|
private SubScene view;
|
||||||
@@ -66,11 +65,6 @@ public class GameView3D extends GameView {
|
|||||||
private PerspectiveCamera isometricCam;
|
private PerspectiveCamera isometricCam;
|
||||||
private PerspectiveCamera topDownCam;
|
private PerspectiveCamera topDownCam;
|
||||||
private PerspectiveCamera chaseCam;
|
private PerspectiveCamera chaseCam;
|
||||||
|
|
||||||
/* Note that if either of these is null then values for it have not been added and the other
|
|
||||||
should be used as the limits of the map. */
|
|
||||||
private Map<Mark, Marker3D> markerObjects;
|
|
||||||
|
|
||||||
private BoatObject playerBoat;
|
private BoatObject playerBoat;
|
||||||
private Map<ClientYacht, BoatObject> boatObjects = new HashMap<>();
|
private Map<ClientYacht, BoatObject> boatObjects = new HashMap<>();
|
||||||
private Group wakesGroup = new Group();
|
private Group wakesGroup = new Group();
|
||||||
@@ -535,31 +529,4 @@ public class GameView3D extends GameView {
|
|||||||
public void setWindDir(double windDir) {
|
public void setWindDir(double windDir) {
|
||||||
this.windDir = windDir;
|
this.windDir = windDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -108,10 +108,6 @@ public class MapMaker {
|
|||||||
return mapPreviews.get(index).getAssets();
|
return mapPreviews.get(index).getAssets();
|
||||||
}
|
}
|
||||||
|
|
||||||
public RaceXMLData getCurrentRace() {
|
|
||||||
return races.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RegattaXMLData getCurrentRegatta() {
|
public RegattaXMLData getCurrentRegatta() {
|
||||||
return regattas.get(index);
|
return regattas.get(index);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package seng302.visualiser;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.geometry.Point2D;
|
import javafx.geometry.Point2D;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
@@ -19,6 +18,7 @@ import seng302.model.mark.Corner;
|
|||||||
import seng302.model.mark.Mark;
|
import seng302.model.mark.Mark;
|
||||||
import seng302.utilities.GeoUtility;
|
import seng302.utilities.GeoUtility;
|
||||||
import seng302.visualiser.fxObjects.MarkArrowFactory;
|
import seng302.visualiser.fxObjects.MarkArrowFactory;
|
||||||
|
import seng302.visualiser.fxObjects.Marker;
|
||||||
import seng302.visualiser.fxObjects.assets_2D.CourseBoundary;
|
import seng302.visualiser.fxObjects.assets_2D.CourseBoundary;
|
||||||
import seng302.visualiser.fxObjects.assets_2D.Gate;
|
import seng302.visualiser.fxObjects.assets_2D.Gate;
|
||||||
import seng302.visualiser.fxObjects.assets_2D.Marker2D;
|
import seng302.visualiser.fxObjects.assets_2D.Marker2D;
|
||||||
@@ -29,7 +29,6 @@ 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();
|
||||||
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;
|
||||||
@@ -240,7 +239,7 @@ public class MapPreview extends GameView {
|
|||||||
* @param colour The desired colour of the gate.
|
* @param colour The desired colour of the gate.
|
||||||
* @return the new gate.
|
* @return the new gate.
|
||||||
*/
|
*/
|
||||||
private Gate makeAndBindGate(Marker2D m1, Marker2D m2, Paint colour) {
|
private Gate makeAndBindGate(Marker m1, Marker m2, Paint colour) {
|
||||||
Gate gate = new Gate(colour);
|
Gate gate = new Gate(colour);
|
||||||
gate.startXProperty().bind(
|
gate.startXProperty().bind(
|
||||||
m1.layoutXProperty()
|
m1.layoutXProperty()
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ import seng302.model.ClientYacht;
|
|||||||
import seng302.model.Limit;
|
import seng302.model.Limit;
|
||||||
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.utilities.Sounds;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class converts a map preview to a minimap by adding boats.
|
* Class converts a map preview to a minimap by adding boats.
|
||||||
@@ -20,15 +18,9 @@ import seng302.utilities.Sounds;
|
|||||||
public class MiniMap extends MapPreview {
|
public class MiniMap extends MapPreview {
|
||||||
|
|
||||||
private HashMap<ClientYacht, Polygon> boatIcons = new HashMap<>();
|
private HashMap<ClientYacht, Polygon> 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) {
|
public MiniMap (List<CompoundMark> marks, List<Corner> course, List<Limit> border, List<ClientYacht> boats, ClientYacht player) {
|
||||||
super(marks, course, border);
|
super(marks, course, border);
|
||||||
this.boats = boats;
|
|
||||||
this.player = player;
|
|
||||||
setBoats(boats);
|
setBoats(boats);
|
||||||
player.addMarkRoundingListener(this::updateMarkArrows);
|
player.addMarkRoundingListener(this::updateMarkArrows);
|
||||||
}
|
}
|
||||||
@@ -56,31 +48,4 @@ public class MiniMap extends MapPreview {
|
|||||||
gameObjects.getChildren().addAll(boatIcons.values());
|
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
package seng302.visualiser;
|
package seng302.visualiser;
|
||||||
|
|
||||||
import seng302.gameServer.ServerAdvertiser;
|
import static seng302.gameServer.ServerAdvertiser.getLocalHostIp;
|
||||||
import seng302.gameServer.ServerDescription;
|
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
import javax.jmdns.JmDNS;
|
import javax.jmdns.JmDNS;
|
||||||
import javax.jmdns.ServiceEvent;
|
import javax.jmdns.ServiceEvent;
|
||||||
import javax.jmdns.ServiceListener;
|
import javax.jmdns.ServiceListener;
|
||||||
import javax.jmdns.impl.JmDNSImpl;
|
import seng302.gameServer.ServerAdvertiser;
|
||||||
import java.io.IOException;
|
import seng302.gameServer.ServerDescription;
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import static seng302.gameServer.ServerAdvertiser.getLocalHostIp;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listens for servers on the local network
|
* Listens for servers on the local network
|
||||||
@@ -58,7 +58,7 @@ public class ServerListener{
|
|||||||
servers.remove(toRemove);
|
servers.remove(toRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate.serverRemoved(new ArrayList<ServerDescription>(servers));
|
delegate.serverRemoved(new ArrayList<>(servers));
|
||||||
|
|
||||||
// Get all other servers with the same name to respond if they are up
|
// Get all other servers with the same name to respond if they are up
|
||||||
jmdns.requestServiceInfo(ServerAdvertiser.SERVICE_TYPE, serverName);
|
jmdns.requestServiceInfo(ServerAdvertiser.SERVICE_TYPE, serverName);
|
||||||
@@ -94,13 +94,6 @@ public class ServerListener{
|
|||||||
|
|
||||||
listener = new GameServeMonitor();
|
listener = new GameServeMonitor();
|
||||||
jmdns.addServiceListener(ServerAdvertiser.SERVICE_TYPE, listener);
|
jmdns.addServiceListener(ServerAdvertiser.SERVICE_TYPE, listener);
|
||||||
|
|
||||||
/*new Timer().schedule(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
}, 50, SERVICE_REFRESH_INTERVAL);*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ServerListener getInstance() throws IOException {
|
public static ServerListener getInstance() throws IOException {
|
||||||
@@ -134,7 +127,7 @@ public class ServerListener{
|
|||||||
for (ServerDescription server : servers){
|
for (ServerDescription server : servers){
|
||||||
if (server.serverShouldBeRemoved()){
|
if (server.serverShouldBeRemoved()){
|
||||||
listener.servers.remove(server);
|
listener.servers.remove(server);
|
||||||
delegate.serverRemoved(new ArrayList<ServerDescription>(listener.servers));
|
delegate.serverRemoved(new ArrayList<>(listener.servers));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ public class IsometricCamera extends PerspectiveCamera implements RaceCamera {
|
|||||||
private final Double MAX_Y = 170.0;
|
private final Double MAX_Y = 170.0;
|
||||||
|
|
||||||
private final Double PAN_LIMIT = 160.0;
|
private final Double PAN_LIMIT = 160.0;
|
||||||
private final Double NEAR_ZOOM_LIMIT = -50.0;
|
private final Double NEAR_ZOOM_LIMIT = -30.0;
|
||||||
private final Double FAR_ZOOM_LIMIT = -160.0;
|
private final Double FAR_ZOOM_LIMIT = -180.0;
|
||||||
|
|
||||||
private Double horizontalPan;
|
private Double horizontalPan;
|
||||||
private Double verticalPan;
|
private Double verticalPan;
|
||||||
@@ -29,7 +29,7 @@ public class IsometricCamera extends PerspectiveCamera implements RaceCamera {
|
|||||||
super(true);
|
super(true);
|
||||||
transforms = this.getTransforms();
|
transforms = this.getTransforms();
|
||||||
|
|
||||||
zoomFactor = (FAR_ZOOM_LIMIT + NEAR_ZOOM_LIMIT) / 2.0;
|
zoomFactor = FAR_ZOOM_LIMIT;
|
||||||
horizontalPan = cameraStartX;
|
horizontalPan = cameraStartX;
|
||||||
verticalPan = cameraStartY;
|
verticalPan = cameraStartY;
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import seng302.visualiser.fxObjects.assets_3D.BoatObject;
|
|||||||
|
|
||||||
public class TopDownCamera extends PerspectiveCamera implements RaceCamera {
|
public class TopDownCamera extends PerspectiveCamera implements RaceCamera {
|
||||||
|
|
||||||
private final Double PAN_LIMIT = 30.0;
|
private final Double PAN_LIMIT = 40d;
|
||||||
private final Double NEAR_ZOOM_LIMIT = -30.0;
|
private final Double NEAR_ZOOM_LIMIT = -20.0;
|
||||||
private final Double FAR_ZOOM_LIMIT = -130.0;
|
private final Double FAR_ZOOM_LIMIT = -145d;
|
||||||
private final Double ZOOM_STEP = 2.5;
|
private final Double ZOOM_STEP = 2.5;
|
||||||
|
|
||||||
private ObservableList<Transform> transforms;
|
private ObservableList<Transform> transforms;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import javafx.scene.layout.StackPane;
|
|||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The pre loading screen before launch the start view
|
||||||
* Created by Kusal on 26-Sep-17.
|
* Created by Kusal on 26-Sep-17.
|
||||||
*/
|
*/
|
||||||
public class SplashScreenController implements Initializable{
|
public class SplashScreenController implements Initializable{
|
||||||
@@ -26,9 +27,7 @@ public class SplashScreenController implements Initializable{
|
|||||||
public void run(){
|
public void run(){
|
||||||
try {
|
try {
|
||||||
Thread.sleep(3000);
|
Thread.sleep(3000);
|
||||||
Platform.runLater(new Runnable() {
|
Platform.runLater(() -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
try {
|
||||||
Stage stage = new Stage();
|
Stage stage = new Stage();
|
||||||
ViewManager.getInstance().initialStartView(stage);
|
ViewManager.getInstance().initialStartView(stage);
|
||||||
@@ -36,7 +35,6 @@ public class SplashScreenController implements Initializable{
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
rootPane.getScene().getWindow().hide();
|
rootPane.getScene().getWindow().hide();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package seng302.visualiser.fxObjects;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import javafx.scene.Group;
|
||||||
|
import seng302.visualiser.fxObjects.MarkArrowFactory.RoundingSide;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by cir27 on 28/09/17.
|
||||||
|
*/
|
||||||
|
public abstract class Marker extends Group{
|
||||||
|
|
||||||
|
protected List<Group> enterArrows = new ArrayList<>();
|
||||||
|
protected List<Group> exitArrows = new ArrayList<>();
|
||||||
|
protected int enterArrowIndex = 0;
|
||||||
|
protected int exitArrowIndex = 0;
|
||||||
|
|
||||||
|
public abstract void addArrows(RoundingSide roundingSide, double entryAngle, double exitAngle);
|
||||||
|
/**
|
||||||
|
* Shows the next EnterArrow. Does nothing if there are no more enter arrows. Other arrows become hidden.
|
||||||
|
*/
|
||||||
|
public void showNextEnterArrow() {
|
||||||
|
showArrow(enterArrows, enterArrowIndex);
|
||||||
|
enterArrowIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the next ExitArrow. Does nothing if there are no more enter arrows. Other arrows become hidden.
|
||||||
|
*/
|
||||||
|
public void showNextExitArrow() {
|
||||||
|
showArrow(exitArrows, exitArrowIndex);
|
||||||
|
exitArrowIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void showArrow(List<Group> arrowList, int arrowListIndex);
|
||||||
|
|
||||||
|
public abstract void hideAllArrows();
|
||||||
|
}
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
package seng302.visualiser.fxObjects.assets_2D;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
|
||||||
import javafx.scene.CacheHint;
|
|
||||||
import javafx.scene.Group;
|
|
||||||
import javafx.scene.paint.Color;
|
|
||||||
import javafx.scene.paint.Paint;
|
|
||||||
import javafx.scene.shape.Rectangle;
|
|
||||||
import javafx.scene.text.Text;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Grouping of string objects over a semi transparent background.
|
|
||||||
*/
|
|
||||||
public class AnnotationBox extends Group {
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface AnnotationFormatter<T> {
|
|
||||||
String transformString (T input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class stores a text object and relationship for updating the text object if needed
|
|
||||||
*
|
|
||||||
* @param <T> The type of observable value passed to the annotation, if there is one.
|
|
||||||
*/
|
|
||||||
public class Annotation<T> {
|
|
||||||
private Text text;
|
|
||||||
private ObservableValue<T> source;
|
|
||||||
private AnnotationFormatter<T> format;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for observing annotation
|
|
||||||
* @param textObject the javaFX text object the annotation is displayed in
|
|
||||||
* @param source observable value that the annotation is taken from
|
|
||||||
* @param formatter interface describing how to format the source data if needed
|
|
||||||
*/
|
|
||||||
public Annotation (Text textObject, ObservableValue<T> source, AnnotationFormatter<T> formatter) {
|
|
||||||
this.text = textObject;
|
|
||||||
this.source = source;
|
|
||||||
this.format = formatter;
|
|
||||||
source.addListener((obs, oldVal, newVal) ->
|
|
||||||
Platform.runLater(() -> text.setText(format.transformString(newVal)))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for a static annotation
|
|
||||||
* @param textObject the javaFX text object the annotation is displayed in
|
|
||||||
* @param annotationText the static value of the test object
|
|
||||||
*/
|
|
||||||
public Annotation (Text textObject, String annotationText) {
|
|
||||||
textObject.setText(annotationText);
|
|
||||||
text = textObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Text getText () {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Text offset constants
|
|
||||||
private static final double X_OFFSET_TEXT = 20d;
|
|
||||||
private static final double Y_OFFSET_TEXT_INIT = -35d;
|
|
||||||
private static final double Y_OFFSET_PER_TEXT = 12d;
|
|
||||||
//Background constants
|
|
||||||
private static final double TEXT_BUFFER = 3;
|
|
||||||
private static final double BACKGROUND_X = X_OFFSET_TEXT - TEXT_BUFFER;
|
|
||||||
private static final double BACKGROUND_Y = Y_OFFSET_TEXT_INIT - TEXT_BUFFER;
|
|
||||||
private static final double BACKGROUND_H_PER_TEXT = 9.5d;
|
|
||||||
private static final double BACKGROUND_ARC_SIZE = 10;
|
|
||||||
|
|
||||||
private int visibleAnnotations = 0;
|
|
||||||
private double backgroundWidth = 145d;
|
|
||||||
|
|
||||||
private Rectangle background = new Rectangle();
|
|
||||||
private Paint theme = Color.BLACK;
|
|
||||||
|
|
||||||
private Map<String, Annotation> annotationsByName = new HashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an empty annotation box. The box is offset from (0,0) by (17, -38).
|
|
||||||
*/
|
|
||||||
public AnnotationBox() {
|
|
||||||
this.setCache(true);
|
|
||||||
background.setX(BACKGROUND_X);
|
|
||||||
background.setY(BACKGROUND_Y);
|
|
||||||
background.setWidth(backgroundWidth);
|
|
||||||
background.setHeight(Math.abs(BACKGROUND_X) + TEXT_BUFFER + BACKGROUND_H_PER_TEXT * 4);
|
|
||||||
background.setArcHeight(BACKGROUND_ARC_SIZE);
|
|
||||||
background.setArcWidth(BACKGROUND_ARC_SIZE);
|
|
||||||
background.setFill(new Color(1, 1, 1, 0.75));
|
|
||||||
background.setStroke(theme);
|
|
||||||
background.setStrokeWidth(2);
|
|
||||||
background.setCache(true);
|
|
||||||
background.setCacheHint(CacheHint.SCALE);
|
|
||||||
this.getChildren().add(background);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an annotation to the box. Use the name to reference the annotation for removal or\
|
|
||||||
* changing visibility.
|
|
||||||
* @param annotationName the name of the annotation.
|
|
||||||
* @param annotation the annotation.
|
|
||||||
*/
|
|
||||||
public void addAnnotation (String annotationName, Annotation annotation) {
|
|
||||||
annotationsByName.put(annotationName, annotation);
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
this.getChildren().add(annotation.getText());
|
|
||||||
visibleAnnotations++;
|
|
||||||
update();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an annotation with a constant text.
|
|
||||||
* @param annotationName The name of the annotation. Will be used to reference it later.
|
|
||||||
* @param annotationText The desired text.
|
|
||||||
*/
|
|
||||||
public void addAnnotation (String annotationName, String annotationText) {
|
|
||||||
Text text = getTextObject();
|
|
||||||
addAnnotation(annotationName, new Annotation(text, annotationText));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an annotation with the given name. The annotation will contain the value of the given
|
|
||||||
* ObservableValue. The formatter should return a String and takes an object of the same type as
|
|
||||||
* the ObservableValue as a parameter. The String is how you want the annotation to look.
|
|
||||||
* @param annotationName The annotation name.
|
|
||||||
* @param observable The observable value the annotation will display.
|
|
||||||
* @param formatter A formatting function for the observable value.
|
|
||||||
* @param <E> The type of ObservableValue.
|
|
||||||
*/
|
|
||||||
public <E> void addAnnotation (String annotationName, ObservableValue<E> observable,
|
|
||||||
AnnotationFormatter<E> formatter) {
|
|
||||||
Text newText = getTextObject();
|
|
||||||
addAnnotation(annotationName, new Annotation<>(newText, observable, formatter));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the visibility of the annotation with the given name if it exists.
|
|
||||||
* @param annotationName The name of the annotation
|
|
||||||
* @param visibility the desired visibility
|
|
||||||
*/
|
|
||||||
public void setAnnotationVisibility (String annotationName, boolean visibility) {
|
|
||||||
if (annotationsByName.containsKey(annotationName)) {
|
|
||||||
Text textField = annotationsByName.get(annotationName).text;
|
|
||||||
boolean currentState = textField.visibleProperty().get();
|
|
||||||
if (visibility != currentState) {
|
|
||||||
if (visibility)
|
|
||||||
visibleAnnotations++;
|
|
||||||
else
|
|
||||||
visibleAnnotations--;
|
|
||||||
}
|
|
||||||
textField.setVisible(visibility);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the annotation with the given name if it exits.
|
|
||||||
* @param annotationName The name given when the annotation was created.
|
|
||||||
*/
|
|
||||||
public void removeAnnotation (String annotationName) {
|
|
||||||
if (annotationName.contains(annotationName)) {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
this.getChildren().remove(annotationsByName.remove(annotationName).getText());
|
|
||||||
visibleAnnotations--;
|
|
||||||
update();
|
|
||||||
});
|
|
||||||
annotationsByName.remove(annotationName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moves the annotation.
|
|
||||||
* @param x x location
|
|
||||||
* @param y y location
|
|
||||||
*/
|
|
||||||
public void setLocation (double x, double y) {
|
|
||||||
Platform.runLater(()-> this.relocate(x + BACKGROUND_X, y + BACKGROUND_Y));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes the width of the annotation box. Default is 145.
|
|
||||||
* @param width new width.
|
|
||||||
*/
|
|
||||||
public void setWidth (double width) {
|
|
||||||
backgroundWidth = width;
|
|
||||||
Platform.runLater(() -> background.setWidth(backgroundWidth));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void update () {
|
|
||||||
background.setVisible(visibleAnnotations != 0);
|
|
||||||
background.setHeight(Math.abs(BACKGROUND_X) + TEXT_BUFFER + BACKGROUND_H_PER_TEXT * visibleAnnotations);
|
|
||||||
for (int i = 1; i <= visibleAnnotations; i++) {
|
|
||||||
Text text = (Text) this.getChildren().get(i);
|
|
||||||
if (text.visibleProperty().get()) {
|
|
||||||
text.setX(X_OFFSET_TEXT);
|
|
||||||
text.setY(Y_OFFSET_TEXT_INIT + Y_OFFSET_PER_TEXT * i);
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a text object for an annotation.
|
|
||||||
* @return The text object
|
|
||||||
*/
|
|
||||||
private Text getTextObject() {
|
|
||||||
Text text = new Text();
|
|
||||||
text.setFill(theme);
|
|
||||||
text.setStrokeWidth(2);
|
|
||||||
// text.setCacheHint(CacheHint.QUALITY);
|
|
||||||
text.setCache(true);
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the colour of the annotation box's border and text colour.
|
|
||||||
* @param value desired colour.
|
|
||||||
*/
|
|
||||||
public void setFill (Paint value) {
|
|
||||||
theme = value;
|
|
||||||
background.setStroke(theme);
|
|
||||||
annotationsByName.forEach((name, annotation) -> annotation.getText().setFill(theme));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package seng302.visualiser.fxObjects.assets_2D;
|
package seng302.visualiser.fxObjects.assets_2D;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.scene.Group;
|
import javafx.scene.Group;
|
||||||
@@ -8,18 +7,15 @@ import javafx.scene.paint.Color;
|
|||||||
import javafx.scene.paint.Paint;
|
import javafx.scene.paint.Paint;
|
||||||
import javafx.scene.shape.Circle;
|
import javafx.scene.shape.Circle;
|
||||||
import seng302.visualiser.fxObjects.MarkArrowFactory;
|
import seng302.visualiser.fxObjects.MarkArrowFactory;
|
||||||
|
import seng302.visualiser.fxObjects.Marker;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visual object for a mark. Contains a coloured circle and any specified arrows.
|
* Visual object for a mark. Contains a coloured circle and any specified arrows.
|
||||||
*/
|
*/
|
||||||
public class Marker2D extends Group {
|
public class Marker2D extends Marker {
|
||||||
|
|
||||||
private Circle mark = new Circle();
|
private Circle mark = new Circle();
|
||||||
private Paint colour = Color.BLACK;
|
private Paint colour = Color.BLACK;
|
||||||
private List<Group> enterArrows = new ArrayList<>();
|
|
||||||
private List<Group> exitArrows = new ArrayList<>();
|
|
||||||
private int enterArrowIndex = 0;
|
|
||||||
private int exitArrowIndex = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Marker containing only a circle. The default colour is black.
|
* Creates a new Marker containing only a circle. The default colour is black.
|
||||||
@@ -79,7 +75,8 @@ public class Marker2D extends Group {
|
|||||||
exitArrowIndex++;
|
exitArrowIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showArrow(List<Group> arrowList, int arrowListIndex) {
|
@Override
|
||||||
|
protected void showArrow(List<Group> arrowList, int arrowListIndex) {
|
||||||
if (arrowListIndex < arrowList.size()) {
|
if (arrowListIndex < arrowList.size()) {
|
||||||
Platform.runLater(() ->
|
Platform.runLater(() ->
|
||||||
this.getChildren().setAll(mark, arrowList.get(arrowListIndex))
|
this.getChildren().setAll(mark, arrowList.get(arrowListIndex))
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ public class Wake extends Group {
|
|||||||
|
|
||||||
//The number of wakes
|
//The number of wakes
|
||||||
private int numWakes = 8;
|
private int numWakes = 8;
|
||||||
//The total possible difference between the first wake and the last. Increasing/Decreasing this will make wakes fan out more/less.
|
|
||||||
private final double MAX_DIFF = 75;
|
|
||||||
//Increasing/decreasing this will alter the speed that wakes converge when the heading stop changing. Anything over about 1500 may cause oscillation.
|
|
||||||
private final int UNIFICATION_SPEED = 45;
|
|
||||||
|
|
||||||
|
|
||||||
private Arc[] arcs = new Arc[numWakes];
|
private Arc[] arcs = new Arc[numWakes];
|
||||||
@@ -69,34 +65,6 @@ public class Wake extends Group {
|
|||||||
rad += (14 / numWakes) + (velocity / 2.5);
|
rad += (14 / numWakes) + (velocity / 2.5);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// } else {
|
|
||||||
// rotations[0] = rotation;
|
|
||||||
// ((Rotate) arcs[0].getTransforms().get(0)).setAngle(rotation);
|
|
||||||
// for (int i = 1; i < numWakes; i++) {
|
|
||||||
// double wakeSeparationRad = Math.toRadians(rotations[i - 1] - rotations[i]);
|
|
||||||
// double shortestDistance = Math.atan2(
|
|
||||||
// Math.sin(wakeSeparationRad),
|
|
||||||
// Math.cos(wakeSeparationRad)
|
|
||||||
// );
|
|
||||||
// double distDeg = Math.toDegrees(shortestDistance);
|
|
||||||
// if (rotationalVelocities[i - 1] < 0.01 && rotationalVelocities[i - 1] > -0.01) {
|
|
||||||
// rotationalVelocities[i] = distDeg / UNIFICATION_SPEED * 2 * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes);
|
|
||||||
//
|
|
||||||
// } else {
|
|
||||||
// if (distDeg < (MAX_DIFF / numWakes)) {
|
|
||||||
// rotationalVelocities[i] = distDeg / UNIFICATION_SPEED * Math.log(Math.abs(distDeg) + 1) / Math.log(MAX_DIFF / numWakes);
|
|
||||||
// } else
|
|
||||||
// rotationalVelocities[i] = rotationalVelocities[i - 1];
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// double rad = (14 / numWakes) + velocity;
|
|
||||||
// for (Arc arc : arcs) {
|
|
||||||
// arc.setRadiusX(rad);
|
|
||||||
// arc.setRadiusY(rad);
|
|
||||||
// rad += (14 / numWakes) + (velocity / 2.5);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
package seng302.visualiser.fxObjects.assets_2D;
|
|
||||||
|
|
||||||
import javafx.scene.paint.Paint;
|
|
||||||
import javafx.scene.shape.Polyline;
|
|
||||||
import javafx.scene.shape.StrokeLineCap;
|
|
||||||
import javafx.scene.shape.StrokeLineJoin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by cir27 on 5/09/17.
|
|
||||||
*/
|
|
||||||
public class WindArrow extends Polyline {
|
|
||||||
public WindArrow(Paint fill) {
|
|
||||||
this.getPoints().addAll(
|
|
||||||
-10d, 15d,
|
|
||||||
0d, 25d,
|
|
||||||
0d, -25d,
|
|
||||||
0d, 25d,
|
|
||||||
10d, 15d
|
|
||||||
);
|
|
||||||
this.setStrokeLineCap(StrokeLineCap.ROUND);
|
|
||||||
this.setStroke(fill);
|
|
||||||
this.setStrokeWidth(5);
|
|
||||||
this.setStrokeLineJoin(StrokeLineJoin.ROUND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +1,17 @@
|
|||||||
package seng302.visualiser.fxObjects.assets_3D;
|
package seng302.visualiser.fxObjects.assets_3D;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.scene.Group;
|
import javafx.scene.Group;
|
||||||
import seng302.visualiser.fxObjects.MarkArrowFactory;
|
import seng302.visualiser.fxObjects.MarkArrowFactory;
|
||||||
import seng302.visualiser.fxObjects.MarkArrowFactory.RoundingSide;
|
import seng302.visualiser.fxObjects.MarkArrowFactory.RoundingSide;
|
||||||
|
import seng302.visualiser.fxObjects.Marker;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visual object for a mark. Contains a coloured circle and any specified arrows.
|
* Visual object for a mark. Contains a coloured circle and any specified arrows.
|
||||||
*/
|
*/
|
||||||
public class Marker3D extends Group {
|
public class Marker3D extends Marker {
|
||||||
|
|
||||||
private Model mark;
|
private Model mark;
|
||||||
private List<Group> enterArrows = new ArrayList<>();
|
|
||||||
private List<Group> exitArrows = new ArrayList<>();
|
|
||||||
private int enterArrowIndex = 0;
|
|
||||||
private int exitArrowIndex = 0;
|
|
||||||
private ModelType markType;
|
private ModelType markType;
|
||||||
private ModelType arrowType;
|
private ModelType arrowType;
|
||||||
|
|
||||||
@@ -60,23 +55,8 @@ public class Marker3D extends Group {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Shows the next EnterArrow. Does nothing if there are no more enter arrows. Other arrows become hidden.
|
protected void showArrow(List<Group> arrowList, int arrowListIndex) {
|
||||||
*/
|
|
||||||
public void showNextEnterArrow() {
|
|
||||||
showArrow(enterArrows, enterArrowIndex);
|
|
||||||
enterArrowIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shows the next ExitArrow. Does nothing if there are no more enter arrows. Other arrows become hidden.
|
|
||||||
*/
|
|
||||||
public void showNextExitArrow() {
|
|
||||||
showArrow(exitArrows, exitArrowIndex);
|
|
||||||
exitArrowIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showArrow(List<Group> arrowList, int arrowListIndex) {
|
|
||||||
if (arrowListIndex < arrowList.size()) {
|
if (arrowListIndex < arrowList.size()) {
|
||||||
Platform.runLater(() ->
|
Platform.runLater(() ->
|
||||||
this.getChildren().setAll(mark.getAssets(), arrowList.get(arrowListIndex))
|
this.getChildren().setAll(mark.getAssets(), arrowList.get(arrowListIndex))
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
package seng302.visualiser.map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Boundary class represents a rectangle territorial boundary on a map. It
|
|
||||||
* contains four extremity double values(N, E, S, W). N and S are represented as
|
|
||||||
* latitudes in radians. E and W are represented as longitudes in radians.
|
|
||||||
*
|
|
||||||
* Created by Haoming on 10/5/17
|
|
||||||
*/
|
|
||||||
public class Boundary {
|
|
||||||
|
|
||||||
private double northLat, eastLng, southLat, westLng;
|
|
||||||
|
|
||||||
public Boundary(double northLat, double eastLng, double southLat, double westLng) {
|
|
||||||
this.northLat = northLat;
|
|
||||||
this.eastLng = eastLng;
|
|
||||||
this.southLat = southLat;
|
|
||||||
this.westLng = westLng;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getCentreLat() {
|
|
||||||
return (northLat + southLat) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getCentreLng() {
|
|
||||||
return (eastLng + westLng) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getNorthLat() {
|
|
||||||
return northLat;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getEastLng() {
|
|
||||||
return eastLng;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getSouthLat() {
|
|
||||||
return southLat;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getWestLng() {
|
|
||||||
return westLng;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
package seng302.visualiser.map;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
import javafx.geometry.Point2D;
|
|
||||||
import javafx.scene.image.Image;
|
|
||||||
import javax.net.ssl.HttpsURLConnection;
|
|
||||||
import seng302.model.GeoPoint;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CanvasMap retrieves a map image with given geo boundary from Google Map server.
|
|
||||||
* By passing a rectangle like geo boundary, it returns a map image with the
|
|
||||||
* highest resolution. However, due to free quote account usage limit, the maximum
|
|
||||||
* resolution is only 1280 * 1280.
|
|
||||||
*
|
|
||||||
* Created by Haoming on 15/5/2017
|
|
||||||
*/
|
|
||||||
public class CanvasMap {
|
|
||||||
|
|
||||||
private Boundary boundary;
|
|
||||||
private long width, height; // desired image size
|
|
||||||
private int zoom;
|
|
||||||
|
|
||||||
private String KEY = "AIzaSyC-5oOShMCY5Oy_9L7guYMPUPFHDMr37wE";
|
|
||||||
|
|
||||||
public CanvasMap(Boundary boundary) {
|
|
||||||
this.boundary = boundary;
|
|
||||||
calculateOptimalMapSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Image getMapImage() {
|
|
||||||
try {
|
|
||||||
URL url = new URL(getRequest());
|
|
||||||
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
|
|
||||||
|
|
||||||
return new Image(connection.getInputStream());
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.out.println("[CanvasMap] Exception");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getRequest() {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("https://maps.googleapis.com/maps/api/staticmap?");
|
|
||||||
sb.append(String.format("center=%f,%f", boundary.getCentreLat(), boundary.getCentreLng()));
|
|
||||||
sb.append(String.format("&zoom=%d", zoom));
|
|
||||||
sb.append(String.format("&size=%dx%d&scale=2", width, height));
|
|
||||||
sb.append("&style=feature:all|element:labels|visibility:off"); // hide all labels on map
|
|
||||||
// sb.append(String.format("&markers=%f,%f", boundary.getSouthLat(), boundary.getWestLng()));
|
|
||||||
// sb.append(String.format("&key=%s", KEY));
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void calculateOptimalMapSize() {
|
|
||||||
for (int z = 20; z > 0; z--) {
|
|
||||||
MapSize mapSize = getMapSize(z, boundary);
|
|
||||||
zoom = z;
|
|
||||||
width = mapSize.width;
|
|
||||||
height = mapSize.height;
|
|
||||||
// if map size is valid, exit the loop as we have the highest resolution
|
|
||||||
if (mapSize.isValid()) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private MapSize getMapSize(int zoom, Boundary boundary) {
|
|
||||||
double scale = Math.pow(2, zoom);
|
|
||||||
GeoPoint geoSW = new GeoPoint(boundary.getSouthLat(), boundary.getWestLng());
|
|
||||||
GeoPoint geoNE = new GeoPoint(boundary.getNorthLat(), boundary.getEastLng());
|
|
||||||
Point2D pointSW = MercatorProjection.toMapPoint(geoSW);
|
|
||||||
Point2D pointNE = MercatorProjection.toMapPoint(geoNE);
|
|
||||||
return new MapSize(Math.abs(pointNE.getX() - pointSW.getX()) * scale,
|
|
||||||
Math.abs(pointNE.getY() - pointSW.getY()) * scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
class MapSize {
|
|
||||||
long width, height;
|
|
||||||
|
|
||||||
MapSize(double width, double height) {
|
|
||||||
this.width = Math.round(width);
|
|
||||||
this.height = Math.round(height);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map size is valid when width and height are both less than 640 pixels
|
|
||||||
* @return true if both dimensions are less than 640px
|
|
||||||
*/
|
|
||||||
boolean isValid() {
|
|
||||||
return Math.max(width, height) <= 640;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getWidth() {
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getHeight() {
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getZoom() {
|
|
||||||
return zoom;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
package seng302.visualiser.map;
|
|
||||||
|
|
||||||
import javafx.geometry.Point2D;
|
|
||||||
import seng302.model.GeoPoint;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An utility class useful to convert between Geo locations and Mercator projection
|
|
||||||
* planar coordinates.
|
|
||||||
* Created by Haoming on 15/5/2017
|
|
||||||
*/
|
|
||||||
public class MercatorProjection {
|
|
||||||
|
|
||||||
private static final double MERCATOR_RANGE = 256;
|
|
||||||
private static final double pixelsPerLngDegree = MERCATOR_RANGE / 360.0;
|
|
||||||
private static final double pixelsPerLngRadian = MERCATOR_RANGE / (2 * Math.PI);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A help function keeps the value in bound between -0.9999 and 0.9999.
|
|
||||||
* @param value in bound value
|
|
||||||
* @return the value in bound
|
|
||||||
*/
|
|
||||||
private static double bound(double value) {
|
|
||||||
return Math.min(Math.max(value, -0.9999), 0.9999);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Projects a Geo Location (lat, lng) on a planar
|
|
||||||
* @param geo GeoPoint (lat, lng) location to be projected
|
|
||||||
* @return the projection Point2D (x, y) on planar
|
|
||||||
*/
|
|
||||||
public static Point2D toMapPoint(GeoPoint geo) {
|
|
||||||
double x, y;
|
|
||||||
Point2D origin = new Point2D(MERCATOR_RANGE / 2.0, MERCATOR_RANGE / 2.0);
|
|
||||||
x = (origin.getX() + geo.getLng() * pixelsPerLngDegree);
|
|
||||||
|
|
||||||
// NOTE(appleton): Truncating to 0.9999 effectively limits latitude to
|
|
||||||
// 89.189. This is about a third of a tile past the edge of the world tile.
|
|
||||||
double sinY = bound(Math.sin(Math.toRadians(geo.getLat())));
|
|
||||||
y = origin.getY() + 0.5 * Math.log((1 + sinY) / (1 - sinY)) * (-pixelsPerLngRadian);
|
|
||||||
return new Point2D(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the planar projection (x, y) back to Geo Location (lat, lng)
|
|
||||||
* @param point Point2D (x, y) to be converted back
|
|
||||||
* @return the original Geo location converted from the given projection point
|
|
||||||
*/
|
|
||||||
public static GeoPoint toMapGeo(Point2D point) {
|
|
||||||
Point2D origin = new Point2D(MERCATOR_RANGE / 2.0, MERCATOR_RANGE / 2.0);
|
|
||||||
double lng = (point.getX() - origin.getX()) / pixelsPerLngDegree;
|
|
||||||
double latRadians = (point.getY() - origin.getY()) / (-pixelsPerLngRadian);
|
|
||||||
double lat = Math.toDegrees(2 * Math.atan(Math.exp(latRadians)) - Math.PI / 2.0);
|
|
||||||
return new GeoPoint(lat, lng);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package seng302.visualiser.map;
|
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ResourceBundle;
|
|
||||||
import javafx.fxml.FXML;
|
|
||||||
import javafx.fxml.Initializable;
|
|
||||||
import javafx.scene.canvas.Canvas;
|
|
||||||
import javafx.scene.canvas.GraphicsContext;
|
|
||||||
|
|
||||||
public class TestMapController implements Initializable{
|
|
||||||
|
|
||||||
@FXML
|
|
||||||
private Canvas mapCanvas;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
|
||||||
GraphicsContext gc = mapCanvas.getGraphicsContext2D();
|
|
||||||
Boundary bound = new Boundary(57.662943, 11.848501, 57.673945, 11.824966);
|
|
||||||
CanvasMap canvasMap = new CanvasMap(bound);
|
|
||||||
gc.drawImage(canvasMap.getMapImage(), 0, 0, canvasMap.getWidth(), canvasMap.getHeight());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
@@ -1,43 +0,0 @@
|
|||||||
package seng302.visualiser.map;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import seng302.model.GeoPoint;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unit test for Mercator Project class.
|
|
||||||
* Created by hyi25 on 15/05/17.
|
|
||||||
*/
|
|
||||||
public class MercatorProjectionTest {
|
|
||||||
@Test
|
|
||||||
public void toMapPoint() throws Exception {
|
|
||||||
GeoPoint geo1 = new GeoPoint(12.485394, 19.38947);
|
|
||||||
javafx.geometry.Point2D actualPoint1 = MercatorProjection.toMapPoint(geo1);
|
|
||||||
javafx.geometry.Point2D expectedPoint1 = new javafx.geometry.Point2D(141.78806755555556, 119.0503853635612);
|
|
||||||
assertEquals(expectedPoint1.getX(), actualPoint1.getX(), 0.0001);
|
|
||||||
assertEquals(expectedPoint1.getY(), actualPoint1.getY(), 0.0001);
|
|
||||||
|
|
||||||
GeoPoint geo2 = new GeoPoint(77.456432, -23.456462);
|
|
||||||
javafx.geometry.Point2D actualPoint2 = MercatorProjection.toMapPoint(geo2);
|
|
||||||
javafx.geometry.Point2D expectedPoint2 = new javafx.geometry.Point2D(111.31984924444444, 38.03143323746788);
|
|
||||||
assertEquals(expectedPoint2.getX(), actualPoint2.getX(), 0.0001);
|
|
||||||
assertEquals(expectedPoint2.getY(), actualPoint2.getY(), 0.0001);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void toMapGeo() throws Exception {
|
|
||||||
javafx.geometry.Point2D point1 = new javafx.geometry.Point2D(123.1234, 25.4565);
|
|
||||||
GeoPoint actualGeo1 = MercatorProjection.toMapGeo(point1);
|
|
||||||
GeoPoint expectedGeo1 = new GeoPoint(80.77043127275441, -6.857718749999995);
|
|
||||||
assertEquals(expectedGeo1.getLat(), actualGeo1.getLat(), 0.0001);
|
|
||||||
assertEquals(expectedGeo1.getLng(), actualGeo1.getLng(), 0.0001);
|
|
||||||
|
|
||||||
javafx.geometry.Point2D point2 = new javafx.geometry.Point2D(1.235, 255.4565);
|
|
||||||
GeoPoint actualGeo2 = MercatorProjection.toMapGeo(point2);
|
|
||||||
GeoPoint expectedGeo2 = new GeoPoint(-84.98475532898011, -178.26328125);
|
|
||||||
assertEquals(expectedGeo2.getLat(), actualGeo2.getLat(), 0.0001);
|
|
||||||
assertEquals(expectedGeo2.getLng(), actualGeo2.getLng(), 0.0001);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user