Merge branch 'Story40_Zooming' into 'develop'

Story40 zooming

# Zooming and Tracking
## Changes
* Boats can now be selected by clicking and selecting on the drop down menu
* Selected boats can now be tracked when the zoom level is increased
* Zoom in with the 'z' key and zoom out with the 'x' key

## Testing
* Manual testing logged in doc
* No cucumber tests made for this story

## Bug Fixes
* Fixed the sails animation bug

See merge request !62
This commit is contained in:
Peter Galloway
2017-08-16 18:25:58 +12:00
8 changed files with 157 additions and 77 deletions
+1 -1
View File
@@ -40,7 +40,7 @@ public class ClientYacht extends Observable {
private String country;
private Long estimateTimeAtFinish;
private Boolean sailIn = false;
private Boolean sailIn = true;
private Integer currentMarkSeqID = 0;
private Long markRoundTime;
private Long timeTillNext;
@@ -365,14 +365,15 @@ public class GameClient {
socketThread.sendBoatAction(BoatAction.TACK_GYBE); break;
//TODO Allow a zoom in and zoom out methods
case Z: // zoom in
System.out.println("Zoom in");
raceView.getGameView().zoomIn();
break;
case X: // zoom out
System.out.println("Zoom out");
raceView.getGameView().zoomOut();
break;
}
}
private void keyReleased(KeyEvent e) {
switch (e.getCode()) {
//TODO 12/07/17 Determine the sail state and send the appropriate packet (eg. if sails are in, send a sail out packet)
+48 -17
View File
@@ -71,6 +71,7 @@ public class GameView extends Pane {
private Map<ClientYacht, BoatObject> boatObjects = new HashMap<>();
private Map<ClientYacht, AnnotationBox> annotations = new HashMap<>();
private ObservableList<Node> gameObjects;
private BoatObject selectedBoat = null;
private Group annotationsGroup = new Group();
private Group wakesGroup = new Group();
private Group boatObjectGroup = new Group();
@@ -93,18 +94,18 @@ public class GameView extends Pane {
double scaleFactor = 1;
public void zoomOut() {
scaleFactor = 0.95;
for (Node child : getChildren()) {
child.setScaleX(child.getScaleX() * scaleFactor);
child.setScaleY(child.getScaleY() * scaleFactor);
scaleFactor = 0.1;
if (this.getScaleX() > 0.5) {
this.setScaleX(this.getScaleX() - scaleFactor);
this.setScaleY(this.getScaleY() - scaleFactor);
}
}
public void zoomIn() {
scaleFactor = 1.05;
for (Node child : getChildren()) {
child.setScaleX(child.getScaleX() * scaleFactor);
child.setScaleY(child.getScaleY() * scaleFactor);
scaleFactor = 0.10;
if (this.getScaleX() < 2.5) {
this.setScaleX(this.getScaleX() + scaleFactor);
this.setScaleY(this.getScaleY() + scaleFactor);
}
}
@@ -113,14 +114,25 @@ public class GameView extends Pane {
VERTICAL
}
private void trackBoat() {
if (selectedBoat != null) {
double x = selectedBoat.getBoatLayoutX();
double y = selectedBoat.getBoatLayoutY();
double displacementX = this.getWidth();
double displacementY = this.getHeight();
this.setLayoutX((-x + (displacementX / 2.0)) * this.getScaleX());
this.setLayoutY((-y + (displacementY / 2.0)) * this.getScaleY());
} else {
this.setLayoutX(0);
this.setLayoutY(0);
}
}
public GameView () {
gameObjects = this.getChildren();
// create image view for map, bind panel size to image
gameObjects.add(mapImage);
fpsDisplay.setLayoutX(5);
fpsDisplay.setLayoutY(20);
fpsDisplay.setStrokeWidth(2);
gameObjects.add(fpsDisplay);
gameObjects.add(raceBorder);
gameObjects.add(markers);
initializeTimer();
@@ -138,6 +150,7 @@ public class GameView extends Pane {
@Override
public void handle(long now) {
trackBoat();
if (lastTime == 0) {
lastTime = now;
} else {
@@ -161,9 +174,7 @@ public class GameView extends Pane {
lastTime = now;
}
}
// Platform.runLater(() ->
boatObjects.forEach((boat, boatObject) -> boatObject.updateLocation());
// );
}
};
}
@@ -343,6 +354,22 @@ public class GameView extends Pane {
// drawGoogleMap();
}
private void setSelectedBoat(BoatObject bo, Boolean isSelected) {
if (this.selectedBoat == bo && !isSelected) {
this.selectedBoat = null;
boatObjects.forEach((boat, group) ->
group.setIsSelected(false)
);
} else if (isSelected) {
this.selectedBoat = bo;
for (BoatObject group : boatObjects.values()) {
if (group != bo) {
group.setIsSelected(false);
}
}
}
}
/**
* Draws all the boats.
* @param clientYachts The yachts to set in the race
@@ -353,6 +380,7 @@ public class GameView extends Pane {
for (ClientYacht clientYacht : clientYachts) {
Paint colour = Colors.getColor();
newBoat = new BoatObject();
newBoat.addSelectedBoatListener(this::setSelectedBoat);
newBoat.setFill(colour);
boatObjects.put(clientYacht, newBoat);
createAndBindAnnotationBox(clientYacht, colour);
@@ -365,9 +393,6 @@ public class GameView extends Pane {
BoatObject bo = boatObjects.get(boat);
Point2D p2d = findScaledXY(lat, lon);
bo.moveTo(p2d.getX(), p2d.getY(), heading, velocity, sailIn, windDir);
// annotations.get(boat).setLayoutX(p2d.getX());
// annotations.get(boat).setLayoutY(p2d.getY());
// annotations.get(boat).setLocation(100d, 100d);
annotations.get(boat).setLocation(p2d.getX(), p2d.getY());
bo.setTrajectory(
heading,
@@ -630,6 +655,7 @@ public class GameView extends Pane {
public void setBoatAsPlayer (ClientYacht playerYacht) {
this.playerYacht = playerYacht;
playerYacht.toggleSail();
boatObjects.get(playerYacht).setAsPlayer();
annotations.get(playerYacht).addAnnotation(
"velocity",
@@ -679,4 +705,9 @@ public class GameView extends Pane {
timeline.setOnFinished(event -> gameObjects.remove(circle));
});
}
public void setFrameRateFXText(Text fpsDisplay) {
this.fpsDisplay = null;
this.fpsDisplay = fpsDisplay;
}
}
@@ -71,7 +71,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
private Button selectAnnotationBtn;
@FXML
private ComboBox<ClientYacht> yachtSelectionComboBox;
@FXML
private Text fpsDisplay;
//Race Data
private Map<Integer, ClientYacht> participants;
private Map<Integer, CompoundMark> markers;
@@ -115,7 +116,8 @@ public class RaceViewController extends Thread implements ImportantAnnotationDel
initialiseSparkLine();
gameView = new GameView();
Platform.runLater(() -> contentAnchorPane.getChildren().add(gameView));
gameView.setFrameRateFXText(fpsDisplay);
Platform.runLater(() -> contentAnchorPane.getChildren().add(0, gameView));
gameView.setBoats(new ArrayList<>(participants.values()));
gameView.updateBorder(raceData.getCourseLimit());
gameView.updateCourse(
@@ -95,7 +95,7 @@ public class AnnotationBox extends Group {
background.setStroke(theme);
background.setStrokeWidth(2);
background.setCache(true);
background.setCacheHint(CacheHint.SPEED);
background.setCacheHint(CacheHint.SCALE);
this.getChildren().add(background);
}
@@ -213,7 +213,7 @@ public class AnnotationBox extends Group {
Text text = new Text();
text.setFill(theme);
text.setStrokeWidth(2);
text.setCacheHint(CacheHint.SPEED);
// text.setCacheHint(CacheHint.QUALITY);
text.setCache(true);
return text;
}
@@ -1,9 +1,9 @@
package seng302.visualiser.fxObjects;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Platform;
import javafx.geometry.Point2D;
import javafx.scene.CacheHint;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.paint.Color;
@@ -23,6 +23,12 @@ import javafx.scene.transform.Rotate;
*/
public class BoatObject extends Group {
@FunctionalInterface
public interface SelectedBoatListener {
void notifySelected(BoatObject boatObject, Boolean isSelected);
}
//Constants for drawing
private static final double BOAT_HEIGHT = 15d;
private static final double BOAT_WIDTH = 10d;
@@ -41,9 +47,11 @@ public class BoatObject extends Group {
private double distanceTravelled, lastRotation;
private Point2D lastPoint;
private Paint colour = Color.BLACK;
private Boolean isSelected, destinationSet; //All boats are initialised as selected
private Boolean isSelected = false, destinationSet; //All boats are initialised as selected
private boolean isPlayer = false;
private List<SelectedBoatListener> selectedBoatListenerListeners = new ArrayList<>();
/**
* Creates a BoatGroup with the default triangular boat polygon.
*/
@@ -85,7 +93,7 @@ public class BoatObject extends Group {
});
boatPoly.setOnMouseClicked(event -> setIsSelected(!isSelected));
boatPoly.setCache(true);
boatPoly.setCacheHint(CacheHint.SPEED);
// boatPoly.setCacheHint(CacheHint.SPEED);
// annotationBox = new AnnotationBox();
// annotationBox.setFill(colour);
@@ -287,6 +295,7 @@ public class BoatObject extends Group {
// }
public void setIsSelected(Boolean isSelected) {
updateListener(isSelected);
this.isSelected = isSelected;
setLineGroupVisible(isSelected);
setWakeVisible(isSelected);
@@ -365,6 +374,10 @@ public class BoatObject extends Group {
lastHeading = heading;
}
public Boolean getSelected() {
return isSelected;
}
public void setTrajectory(double heading, double velocity, double scaleFactorX, double scaleFactorY) {
// wake.setRotation(lastHeading - heading, velocity);
// rotateTo(heading);
@@ -372,4 +385,14 @@ public class BoatObject extends Group {
// yVelocity = Math.sin(Math.toRadians(heading)) * velocity * scaleFactorY;
lastHeading = heading;
}
private void updateListener(Boolean isSelected) {
for (SelectedBoatListener sbl : selectedBoatListenerListeners) {
sbl.notifySelected(this, isSelected);
}
}
public void addSelectedBoatListener(SelectedBoatListener sbl) {
selectedBoatListenerListeners.add(sbl);
}
}
+34 -11
View File
@@ -1,5 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.chart.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.shape.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.chart.CategoryAxis?>
<?import javafx.scene.chart.LineChart?>
<?import javafx.scene.chart.NumberAxis?>
@@ -17,18 +23,38 @@
<?import javafx.scene.shape.Circle?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<GridPane prefHeight="960.0" prefWidth="1530.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.RaceViewController">
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="998.0" prefWidth="1530.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="seng302.visualiser.controllers.RaceViewController">
<children>
<AnchorPane layoutX="322.0" layoutY="130.0" prefHeight="998.0" prefWidth="1281.0"
style="-fx-background-color: skyblue;" AnchorPane.bottomAnchor="0.0"
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<GridPane prefHeight="998.0" prefWidth="1281.0" AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"
AnchorPane.topAnchor="0.0">
<columnConstraints>
<ColumnConstraints maxWidth="246.0" minWidth="246.0" prefWidth="246.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="1034.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="630.0" minWidth="10.0"
prefWidth="68.0"/>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1213.0" minWidth="10.0"
prefWidth="1213.0"/>
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="500.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints />
<RowConstraints />
<RowConstraints maxHeight="489.0" minHeight="1.0" prefHeight="24.0"
vgrow="SOMETIMES"/>
<RowConstraints maxHeight="997.0" minHeight="10.0" prefHeight="974.0"
vgrow="SOMETIMES"/>
</rowConstraints>
<children>
<AnchorPane prefHeight="960.0" prefWidth="250.0" style="-fx-background-color: #2C2c36;" GridPane.rowSpan="3">
<AnchorPane fx:id="contentAnchorPane" prefHeight="200.0" prefWidth="200.0"
GridPane.columnSpan="2" GridPane.rowSpan="2"/>
<Text fx:id="fpsDisplay" strokeType="OUTSIDE" strokeWidth="0.0" text="60 FPS"
GridPane.halignment="CENTER" GridPane.valignment="CENTER"/>
</children>
</GridPane>
</children>
</AnchorPane>
<AnchorPane prefHeight="960.0" prefWidth="250.0" style="-fx-background-color: #2C2c36;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Label layoutX="11.0" layoutY="283.0" text="Team Position" textFill="WHITE" />
<Label layoutX="13.0" layoutY="432.0" text="Settings" textFill="WHITE" />
@@ -76,8 +102,5 @@
</LineChart>
</children>
</AnchorPane>
<AnchorPane fx:id="contentAnchorPane" style="-fx-background-color: skyblue;" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" GridPane.columnIndex="1" GridPane.halignment="LEFT" GridPane.rowSpan="2147483647" GridPane.valignment="TOP">
</AnchorPane>
</children>
</GridPane>
</AnchorPane>
@@ -21,9 +21,9 @@ public class BoatSailAnimationToggleTest {
@Test
public void sailToggleTest() throws Exception {
assertFalse(yacht.getSailIn());
yacht.toggleSail();
assertTrue(yacht.getSailIn());
yacht.toggleSail();
assertFalse(yacht.getSailIn());
}
}