mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 14:28:43 +00:00
Wake calculation now changed to be based off of the separation between wakes.
This allows wakes to auto correct their position better and stops the system reliance on "realistic data". Wakes have several options for behaviour until the ideal settings are decided upon. Note that MarkGroup position updating is currently disabled. #implement #refactor #issue[1] #story[923]
This commit is contained in:
@@ -62,7 +62,8 @@ public class App extends Application
|
|||||||
}
|
}
|
||||||
//Change the StreamReceiver in this else block to change the default data source.
|
//Change the StreamReceiver in this else block to change the default data source.
|
||||||
else{
|
else{
|
||||||
sr = new StreamReceiver("localhost", 4949, "RaceStream");
|
// sr = new StreamReceiver("localhost", 4949, "RaceStream");
|
||||||
|
sr = new StreamReceiver("csse-s302staff.canterbury.ac.nz", 4941, "RaceStream");
|
||||||
}
|
}
|
||||||
|
|
||||||
sr.start();
|
sr.start();
|
||||||
|
|||||||
@@ -204,25 +204,23 @@ public class BoatGroup extends RaceObject{
|
|||||||
double dx = newXValue - boatPoly.getLayoutX();
|
double dx = newXValue - boatPoly.getLayoutX();
|
||||||
double dy = newYValue - boatPoly.getLayoutY();
|
double dy = newYValue - boatPoly.getLayoutY();
|
||||||
//Check movement is reasonable. Assumes a 1000 * 1000 canvas
|
//Check movement is reasonable. Assumes a 1000 * 1000 canvas
|
||||||
if (Math.abs(dx) > 50 || Math.abs(dy) > 50) {
|
// if (Math.abs(dx) > 50 || Math.abs(dy) > 50) {
|
||||||
dx = 0;
|
// dx = 0;
|
||||||
dy = 0;
|
// dy = 0;
|
||||||
moveTo(newXValue, newYValue);
|
// moveTo(newXValue, newYValue);
|
||||||
}
|
// }
|
||||||
|
|
||||||
pixelVelocityX = dx / expectedUpdateInterval;
|
pixelVelocityX = dx / expectedUpdateInterval;
|
||||||
pixelVelocityY = dy / expectedUpdateInterval;
|
pixelVelocityY = dy / expectedUpdateInterval;
|
||||||
rotationalGoal = rotation;
|
rotationalGoal = rotation;
|
||||||
calculateRotationalVelocity();
|
calculateRotationalVelocity();
|
||||||
|
if (Math.abs(rotationalVelocity) > 0.075) {
|
||||||
if (wakeGenerationDelay > 0) {
|
System.out.println("rotationalVelocity = " + rotationalVelocity);
|
||||||
wake.rotate(rotationalGoal);
|
|
||||||
rotateTo(rotationalGoal); //Need to test with this removed.
|
|
||||||
rotationalVelocity = 0;
|
rotationalVelocity = 0;
|
||||||
wakeGenerationDelay--;
|
rotateTo(rotationalGoal);
|
||||||
} else {
|
wake.rotate(rotationalGoal);
|
||||||
wake.setRotationalVelocity(rotationalVelocity, rotationalGoal, boat.getVelocity());
|
|
||||||
}
|
}
|
||||||
|
wake.setRotationalVelocity(rotationalVelocity, boat.getVelocity());
|
||||||
velocityObject.setText(String.format("%.2f m/s", boat.getVelocity()));
|
velocityObject.setText(String.format("%.2f m/s", boat.getVelocity()));
|
||||||
} else {
|
} else {
|
||||||
setToInitialLocation = true;
|
setToInitialLocation = true;
|
||||||
@@ -347,7 +345,7 @@ public class BoatGroup extends RaceObject{
|
|||||||
App.start() -> Controller.setContentPane -> RaceViewController -> CanvasController
|
App.start() -> Controller.setContentPane -> RaceViewController -> CanvasController
|
||||||
*/
|
*/
|
||||||
this.stage = stage;
|
this.stage = stage;
|
||||||
this.stage.iconifiedProperty().addListener(e -> {
|
this.stage.iconifiedProperty().addListener( e -> {
|
||||||
isMaximized = !stage.isIconified();
|
isMaximized = !stage.isIconified();
|
||||||
if (!lineStorage.isEmpty()) {
|
if (!lineStorage.isEmpty()) {
|
||||||
lineGroup.getChildren().addAll(lineStorage);
|
lineGroup.getChildren().addAll(lineStorage);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import javafx.scene.Group;
|
|||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
import javafx.scene.shape.Arc;
|
import javafx.scene.shape.Arc;
|
||||||
import javafx.scene.shape.ArcType;
|
import javafx.scene.shape.ArcType;
|
||||||
|
import javafx.scene.shape.StrokeLineCap;
|
||||||
import javafx.scene.transform.Rotate;
|
import javafx.scene.transform.Rotate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,13 +16,22 @@ import javafx.scene.transform.Rotate;
|
|||||||
*/
|
*/
|
||||||
class Wake extends Group {
|
class Wake extends Group {
|
||||||
|
|
||||||
private int numWakes = 5;
|
//Wake Settings. Should probably be hard coded in when the final values are decided upon.
|
||||||
private double[] velocities = new double[13];
|
private enum functionType {LINEAR, LOGARITHMIC, POWER, ROOT, POWOUT_LOGIN}
|
||||||
|
private functionType wakeFunction = functionType.LOGARITHMIC;
|
||||||
|
private ArcType arcType = ArcType.OPEN;
|
||||||
|
private int numWakes = 10;
|
||||||
|
private double offSet = 0;
|
||||||
|
private final double MAX_DIFF = 75.0;
|
||||||
|
private final int UNIFICATION_SPEED = 500;
|
||||||
|
private final int POWER = 2;
|
||||||
|
|
||||||
private Arc[] arcs = new Arc[numWakes];
|
private Arc[] arcs = new Arc[numWakes];
|
||||||
|
private double[] rotationalVelocities = new double[numWakes];
|
||||||
private double[] rotations = new double[numWakes];
|
private double[] rotations = new double[numWakes];
|
||||||
private int[] velocityIndices = new int[numWakes];
|
private double baseRad;
|
||||||
private double sum = 0;
|
private boolean spawnNewWake = false;
|
||||||
private static double max;
|
private int count = 10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a wake at the given location.
|
* Create a wake at the given location.
|
||||||
@@ -34,12 +44,23 @@ class Wake extends Group {
|
|||||||
Arc arc;
|
Arc arc;
|
||||||
for (int i = 0; i < numWakes; i++) {
|
for (int i = 0; i < numWakes; i++) {
|
||||||
//Default triangle is -110 deg out of phase with a default wake and has angle of 40 deg.
|
//Default triangle is -110 deg out of phase with a default wake and has angle of 40 deg.
|
||||||
arc = new Arc(0,0,0,0,-110,40);
|
arc = new Arc(0, 0, 0, 0, -110, 40);
|
||||||
//Opacity increases from 0.5 -> 0 evenly over the 5 wake arcs.
|
|
||||||
arc.setFill(new Color(0.18, 0.7, 1.0, 1.0 + -0.175 * i));
|
if (arcType == ArcType.ROUND) {
|
||||||
|
arc.setFill(new Color(0.18, 0.7, 1.0, 0.4 + (-0.35 / numWakes * i)));
|
||||||
arc.setType(ArcType.ROUND);
|
arc.setType(ArcType.ROUND);
|
||||||
|
baseRad = 10;
|
||||||
|
|
||||||
|
} else if (arcType == ArcType.OPEN) {
|
||||||
|
arc.setType(ArcType.OPEN);
|
||||||
|
arc.setStroke(new Color(0.18, 0.7, 1.0, 1.0 + (-0.99 / numWakes * i)));
|
||||||
|
arc.setStrokeWidth(3.0);
|
||||||
|
arc.setStrokeLineCap(StrokeLineCap.ROUND);
|
||||||
|
arc.setFill(new Color(0.0, 0.0, 0.0, 0.0));
|
||||||
|
baseRad = (20 / numWakes);
|
||||||
arcs[i] = arc;
|
arcs[i] = arc;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
super.getChildren().addAll(arcs);
|
super.getChildren().addAll(arcs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,38 +68,91 @@ class Wake extends Group {
|
|||||||
* Sets the rotationalVelocity of each arc. Each arc is 3 velocities behind the next smallest arc. The smallest uses
|
* Sets the rotationalVelocity of each arc. Each arc is 3 velocities behind the next smallest arc. The smallest uses
|
||||||
* the latest given velocity.
|
* the latest given velocity.
|
||||||
* @param rotationalVelocity The rotationalVelocity the wake should move at.
|
* @param rotationalVelocity The rotationalVelocity the wake should move at.
|
||||||
* @param rotationGoal Where the wake will rotate to if the wake is calculated to be on a straight section. This is
|
|
||||||
* used to prevent desynchronisation with the Boat polygon.
|
|
||||||
* @param velocity The real world velocity of the boat in m/s.
|
* @param velocity The real world velocity of the boat in m/s.
|
||||||
*/
|
*/
|
||||||
void setRotationalVelocity (double rotationalVelocity, double rotationGoal, double velocity) {
|
void setRotationalVelocity (double rotationalVelocity, double velocity) {
|
||||||
sum -= Math.abs(velocities[(velocityIndices[0] + 10) % 13]);
|
rotationalVelocities[0] = rotationalVelocity;
|
||||||
sum += Math.abs(rotationalVelocity);
|
for (int i = 1; i < numWakes; i++) {
|
||||||
max = Math.max(max, rotationalVelocity);
|
double difference = Math.atan2(
|
||||||
if (sum < (max / 3))
|
Math.sin(
|
||||||
rotate (rotationGoal); //In relatively straight segments the wake snaps to match the boats current position.
|
Math.toRadians(
|
||||||
//This stops the wake from eventually becoming out of sync with the boat.
|
rotations[i - 1] - rotations[i]
|
||||||
//This accounts for rogue rotations that are greater than what would be realistic. Value is kinda rough.
|
)
|
||||||
//Basically just for our internal mock.
|
),
|
||||||
if (Math.abs(rotationalVelocity) > 0.05) {
|
Math.cos(
|
||||||
rotationalVelocity = 0;
|
Math.toRadians(
|
||||||
rotate(rotationGoal);
|
rotations[i - 1] - rotations[i]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
difference = Math.toDegrees(difference);
|
||||||
|
|
||||||
|
if (wakeFunction == functionType.LOGARITHMIC) {
|
||||||
|
if (rotationalVelocities[i-1] < 0.01 && rotationalVelocities[i-1] > -0.01) {
|
||||||
|
rotationalVelocities[i] = (MAX_DIFF / numWakes) / UNIFICATION_SPEED * Math.log(Math.abs(difference) + 1) / Math.log(MAX_DIFF / numWakes) * 1.5;
|
||||||
|
if (difference < 0)
|
||||||
|
{
|
||||||
|
rotationalVelocities[i] = -rotationalVelocities[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rotationalVelocities[i] = rotationalVelocities[i-1] * Math.log(Math.abs(difference) + 1) / Math.log(MAX_DIFF / numWakes);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (wakeFunction == functionType.LINEAR) {
|
||||||
|
if (rotationalVelocities[i - 1] < 0.01 && rotationalVelocities[i - 1] > -0.01) {
|
||||||
|
rotationalVelocities[i] = difference / UNIFICATION_SPEED * 2;
|
||||||
|
} else {
|
||||||
|
if (difference < (MAX_DIFF / numWakes))
|
||||||
|
rotationalVelocities[i] = rotationalVelocities[i - 1] * difference / (MAX_DIFF / numWakes);
|
||||||
|
else
|
||||||
|
rotationalVelocities[i] = rotationalVelocities[i - 1];
|
||||||
|
}
|
||||||
|
} else if (wakeFunction == functionType.POWER) {
|
||||||
|
if (rotationalVelocities[i - 1] < 0.01 && rotationalVelocities[i - 1] > -0.01) {
|
||||||
|
rotationalVelocities[i] = difference / UNIFICATION_SPEED * Math.pow(difference, POWER) / Math.pow((MAX_DIFF / numWakes), POWER);
|
||||||
|
} else {
|
||||||
|
if (difference < (MAX_DIFF / numWakes))
|
||||||
|
rotationalVelocities[i] = rotationalVelocities[i - 1] * Math.pow(difference, POWER) / Math.pow((MAX_DIFF / numWakes), POWER);
|
||||||
|
else
|
||||||
|
rotationalVelocities[i] = rotationalVelocities[i - 1];
|
||||||
|
}
|
||||||
|
} else if (wakeFunction == functionType.ROOT) {
|
||||||
|
if (rotationalVelocities[i - 1] < 0.01 && rotationalVelocities[i - 1] > -0.01) {
|
||||||
|
rotationalVelocities[i] = (MAX_DIFF / numWakes) / UNIFICATION_SPEED * Math.sqrt(Math.abs(difference)) / Math.sqrt(MAX_DIFF / numWakes);
|
||||||
|
} else {
|
||||||
|
if (difference < (MAX_DIFF / numWakes))
|
||||||
|
rotationalVelocities[i] = rotationalVelocities[i - 1] * Math.sqrt(Math.abs(difference)) / Math.sqrt(MAX_DIFF / numWakes);
|
||||||
|
else
|
||||||
|
rotationalVelocities[i] = rotationalVelocities[i - 1];
|
||||||
|
}
|
||||||
|
if (difference < 0)
|
||||||
|
rotationalVelocities[i] = -rotationalVelocities[i];
|
||||||
|
} else if (wakeFunction == functionType.POWOUT_LOGIN) {
|
||||||
|
if (rotationalVelocities[i - 1] < 0.01 && rotationalVelocities[i - 1] > -0.01) {
|
||||||
|
rotationalVelocities[i] = difference / UNIFICATION_SPEED * Math.log(Math.abs(difference) + 1) / Math.log(MAX_DIFF / numWakes);
|
||||||
|
} else {
|
||||||
|
if (difference < (MAX_DIFF / numWakes))
|
||||||
|
rotationalVelocities[i] = rotationalVelocities[i - 1] * Math.pow(difference, POWER) / Math.pow((MAX_DIFF / numWakes), POWER);
|
||||||
|
else
|
||||||
|
rotationalVelocities[i] = rotationalVelocities[i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
//Update the index of the array of recent velocities that each wake uses. Each wake is 3 velocities behind the
|
|
||||||
//next smallest wake.
|
|
||||||
velocityIndices[0] = (13 + (velocityIndices[0] - 1) % 13) % 13;
|
|
||||||
velocities[velocityIndices[0]] = rotationalVelocity;
|
|
||||||
for (int i = 1; i < numWakes; i++)
|
|
||||||
velocityIndices[i] = (velocityIndices[0] + 3 * i) % 13;
|
|
||||||
|
|
||||||
//Scale wakes based on velocity.
|
//Scale wakes based on velocity.
|
||||||
double baseRad = 20;
|
// if (count-- == 0)
|
||||||
double rad;
|
// {
|
||||||
|
// count = 10;
|
||||||
|
// offSet = 0;
|
||||||
|
// } else {
|
||||||
|
// offSet += baseRad / 5;
|
||||||
|
// }
|
||||||
|
double rad = baseRad + velocity + offSet;
|
||||||
for (Arc arc :arcs) {
|
for (Arc arc :arcs) {
|
||||||
rad = baseRad + velocity;
|
|
||||||
arc.setRadiusX(rad);
|
arc.setRadiusX(rad);
|
||||||
arc.setRadiusY(rad);
|
arc.setRadiusY(rad);
|
||||||
baseRad += 5 + (velocity / 2);
|
rad += (20 / numWakes) + (velocity / 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +162,7 @@ class Wake extends Group {
|
|||||||
*/
|
*/
|
||||||
void updatePosition (long timeInterval) {
|
void updatePosition (long timeInterval) {
|
||||||
for (int i = 0; i < numWakes; i++) {
|
for (int i = 0; i < numWakes; i++) {
|
||||||
rotations[i] = rotations[i] + velocities[velocityIndices[i]] * timeInterval;
|
rotations[i] = rotations[i] + rotationalVelocities[i] * timeInterval;
|
||||||
arcs[i].getTransforms().setAll(new Rotate(rotations[i]));
|
arcs[i].getTransforms().setAll(new Rotate(rotations[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,6 +174,7 @@ class Wake extends Group {
|
|||||||
void rotate (double rotation) {
|
void rotate (double rotation) {
|
||||||
for (int i = 0; i < arcs.length; i++) {
|
for (int i = 0; i < arcs.length; i++) {
|
||||||
rotations[i] = rotation;
|
rotations[i] = rotation;
|
||||||
|
rotationalVelocities[i] = 0;
|
||||||
arcs[i].getTransforms().setAll(new Rotate(rotation));
|
arcs[i].getTransforms().setAll(new Rotate(rotation));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,42 +126,42 @@ public class MarkGroup extends RaceObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updatePosition (long timeInterval) {
|
public void updatePosition (long timeInterval) {
|
||||||
Circle markCircle = (Circle) super.getChildren().get(0);
|
// Circle markCircle = (Circle) super.getChildren().get(0);
|
||||||
|
//
|
||||||
if (nodePixelVelocitiesX[0] > 0 && markCircle.getCenterX() > nodeDestinations[0].getX() ||
|
// if (nodePixelVelocitiesX[0] > 0 && markCircle.getCenterX() > nodeDestinations[0].getX() ||
|
||||||
nodePixelVelocitiesX[0] < 0 && markCircle.getCenterX() < nodeDestinations[0].getY())
|
// nodePixelVelocitiesX[0] < 0 && markCircle.getCenterX() < nodeDestinations[0].getY())
|
||||||
nodePixelVelocitiesX[0] = 0;
|
// nodePixelVelocitiesX[0] = 0;
|
||||||
else if (nodePixelVelocitiesX[0] != 0)
|
// else if (nodePixelVelocitiesX[0] != 0)
|
||||||
markCircle.setCenterX(markCircle.getCenterX() + nodePixelVelocitiesX[0] * timeInterval);
|
// markCircle.setCenterX(markCircle.getCenterX() + nodePixelVelocitiesX[0] * timeInterval);
|
||||||
|
//
|
||||||
if (nodePixelVelocitiesY[0] > 0 && markCircle.getCenterY() > nodeDestinations[0].getY() ||
|
// if (nodePixelVelocitiesY[0] > 0 && markCircle.getCenterY() > nodeDestinations[0].getY() ||
|
||||||
nodePixelVelocitiesY[0] < 0 && markCircle.getCenterY() < nodeDestinations[0].getY())
|
// nodePixelVelocitiesY[0] < 0 && markCircle.getCenterY() < nodeDestinations[0].getY())
|
||||||
nodePixelVelocitiesY[0] = 0;
|
// nodePixelVelocitiesY[0] = 0;
|
||||||
else if (nodePixelVelocitiesY[0] != 0)
|
// else if (nodePixelVelocitiesY[0] != 0)
|
||||||
markCircle.setCenterY(markCircle.getCenterY() + nodePixelVelocitiesY[0] * timeInterval);
|
// markCircle.setCenterY(markCircle.getCenterY() + nodePixelVelocitiesY[0] * timeInterval);
|
||||||
|
//
|
||||||
if (mainMark.getMarkType() != MarkType.SINGLE_MARK) {
|
// if (mainMark.getMarkType() != MarkType.SINGLE_MARK) {
|
||||||
|
//
|
||||||
Line line = (Line) super.getChildren().get(2);
|
// Line line = (Line) super.getChildren().get(2);
|
||||||
line.setStartX(markCircle.getCenterX());
|
// line.setStartX(markCircle.getCenterX());
|
||||||
line.setStartY(markCircle.getCenterY());
|
// line.setStartY(markCircle.getCenterY());
|
||||||
|
//
|
||||||
markCircle = (Circle) super.getChildren().get(1);
|
// markCircle = (Circle) super.getChildren().get(1);
|
||||||
|
//
|
||||||
if (nodePixelVelocitiesX[1] > 0 && markCircle.getCenterX() >= nodeDestinations[1].getX() ||
|
// if (nodePixelVelocitiesX[1] > 0 && markCircle.getCenterX() >= nodeDestinations[1].getX() ||
|
||||||
nodePixelVelocitiesX[1] < 0 && markCircle.getCenterX() <= nodeDestinations[1].getX())
|
// nodePixelVelocitiesX[1] < 0 && markCircle.getCenterX() <= nodeDestinations[1].getX())
|
||||||
nodePixelVelocitiesX[1] = 0;
|
// nodePixelVelocitiesX[1] = 0;
|
||||||
else if (nodePixelVelocitiesX[1] != 0)
|
// else if (nodePixelVelocitiesX[1] != 0)
|
||||||
markCircle.setCenterX(markCircle.getCenterX() + nodePixelVelocitiesX[1] * timeInterval);
|
// markCircle.setCenterX(markCircle.getCenterX() + nodePixelVelocitiesX[1] * timeInterval);
|
||||||
|
//
|
||||||
if (nodePixelVelocitiesY[1] > 0 && markCircle.getCenterY() > nodeDestinations[1].getY() ||
|
// if (nodePixelVelocitiesY[1] > 0 && markCircle.getCenterY() > nodeDestinations[1].getY() ||
|
||||||
nodePixelVelocitiesY[1] < 0 && markCircle.getCenterY() < nodeDestinations[1].getY())
|
// nodePixelVelocitiesY[1] < 0 && markCircle.getCenterY() < nodeDestinations[1].getY())
|
||||||
nodePixelVelocitiesY[1] = 0;
|
// nodePixelVelocitiesY[1] = 0;
|
||||||
else if (nodePixelVelocitiesY[1] != 0)
|
// else if (nodePixelVelocitiesY[1] != 0)
|
||||||
markCircle.setCenterY(markCircle.getCenterY() + nodePixelVelocitiesY[1] * timeInterval);
|
// markCircle.setCenterY(markCircle.getCenterY() + nodePixelVelocitiesY[1] * timeInterval);
|
||||||
line.setEndX(markCircle.getCenterX());
|
// line.setEndX(markCircle.getCenterX());
|
||||||
line.setEndY(markCircle.getCenterY());
|
// line.setEndY(markCircle.getCenterY());
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
public void moveGroupBy (double x, double y, double rotation) {
|
public void moveGroupBy (double x, double y, double rotation) {
|
||||||
|
|||||||
Reference in New Issue
Block a user