Finished MarkRoundingSequence class. Added MarkRoundingData class.
RaceState has a MarkRoundingSequenceMember. The temporary rounding line drawing in canvas uses MarkRoundingSequence. MockRace now uses MarkRoundingSequence. #story[1185]
This commit is contained in:
@@ -1,19 +1,13 @@
|
|||||||
package mock.model;
|
package mock.model;
|
||||||
|
|
||||||
import mock.model.wind.WindGenerator;
|
import mock.model.wind.WindGenerator;
|
||||||
import javafx.animation.AnimationTimer;
|
|
||||||
import mock.model.collider.ColliderRegistry;
|
import mock.model.collider.ColliderRegistry;
|
||||||
import mock.xml.*;
|
|
||||||
import network.Messages.BoatLocation;
|
|
||||||
import network.Messages.BoatStatus;
|
|
||||||
import network.Messages.Enums.BoatStatusEnum;
|
import network.Messages.Enums.BoatStatusEnum;
|
||||||
import network.Messages.Enums.RaceStatusEnum;
|
import network.Messages.Enums.RaceStatusEnum;
|
||||||
import network.Utils.AC35UnitConverter;
|
|
||||||
import shared.dataInput.BoatDataSource;
|
import shared.dataInput.BoatDataSource;
|
||||||
import shared.dataInput.RaceDataSource;
|
import shared.dataInput.RaceDataSource;
|
||||||
import shared.dataInput.RegattaDataSource;
|
import shared.dataInput.RegattaDataSource;
|
||||||
import shared.exceptions.BoatNotFoundException;
|
import shared.exceptions.BoatNotFoundException;
|
||||||
import shared.enums.RoundingType;
|
|
||||||
import shared.model.*;
|
import shared.model.*;
|
||||||
import shared.model.Bearing;
|
import shared.model.Bearing;
|
||||||
|
|
||||||
@@ -358,8 +352,6 @@ public class MockRace extends RaceState {
|
|||||||
|
|
||||||
if (!finish && totalElapsedMilliseconds >= updatePeriodMilliseconds && boat.isSailsOut()) {
|
if (!finish && totalElapsedMilliseconds >= updatePeriodMilliseconds && boat.isSailsOut()) {
|
||||||
|
|
||||||
checkPosition(boat, totalElapsedMilliseconds);
|
|
||||||
|
|
||||||
if (boat.getCurrentSpeed() == 0) {
|
if (boat.getCurrentSpeed() == 0) {
|
||||||
newOptimalVMG(boat);
|
newOptimalVMG(boat);
|
||||||
boat.setBearing(boat.calculateBearingToNextMarker());
|
boat.setBearing(boat.calculateBearingToNextMarker());
|
||||||
@@ -373,6 +365,7 @@ public class MockRace extends RaceState {
|
|||||||
//Scale it.
|
//Scale it.
|
||||||
distanceTravelledMeters = distanceTravelledMeters * this.scaleFactor;
|
distanceTravelledMeters = distanceTravelledMeters * this.scaleFactor;
|
||||||
|
|
||||||
|
checkPosition(boat, totalElapsedMilliseconds);
|
||||||
|
|
||||||
//Move the boat forwards that many meters, and advances its time counters by enough milliseconds.
|
//Move the boat forwards that many meters, and advances its time counters by enough milliseconds.
|
||||||
boat.moveForwards(distanceTravelledMeters);
|
boat.moveForwards(distanceTravelledMeters);
|
||||||
@@ -515,32 +508,28 @@ public class MockRace extends RaceState {
|
|||||||
/**
|
/**
|
||||||
* Checks to be run on boats rounding marks on the port side
|
* Checks to be run on boats rounding marks on the port side
|
||||||
* @param boat the boat that is rounding a mark
|
* @param boat the boat that is rounding a mark
|
||||||
* @param roundingChecks the checks to run
|
* @param roundingData The data for the current leg's rounding.
|
||||||
* @param legBearing the direction of the leg
|
|
||||||
*/
|
*/
|
||||||
private void boatRoundingCheckPort(MockBoat boat, List<GPSCoordinate> roundingChecks, Bearing legBearing) {
|
private void boatRoundingCheckPort(MockBoat boat, MarkRoundingData roundingData) {
|
||||||
//boats must pass all checks in order to round a mark
|
//boats must pass all checks in order to round a mark
|
||||||
|
|
||||||
//boolean for if boat has to/needs to pass through a gate
|
//boolean for if boat has to/needs to pass through a gate
|
||||||
boolean gateCheck = boat.getCurrentLeg().getEndCompoundMark().getMark2() == null || boat.isBetweenGate(boat.getCurrentLeg().getEndCompoundMark());
|
boolean gateCheck = boat.getCurrentLeg().getEndCompoundMark().getMark2() == null || boat.isBetweenGate(boat.getCurrentLeg().getEndCompoundMark());
|
||||||
Mark roundingMark = boat.getCurrentLeg().getEndCompoundMark().getMarkForRounding(legBearing);
|
|
||||||
|
|
||||||
System.out.println("boat rounding state: " + boat.getRoundingStatus());//TEMP REMOVE
|
|
||||||
|
|
||||||
switch (boat.getRoundingStatus()) {
|
switch (boat.getRoundingStatus()) {
|
||||||
case 0://hasn't started rounding
|
case 0://hasn't started rounding
|
||||||
System.out.println("portside? " + boat.isPortSide(roundingMark));//TEMP REMOVE
|
if (boat.isPortSide(roundingData.getMarkToRound()) &&
|
||||||
System.out.println("passes line? " + GPSCoordinate.passesLine(roundingMark.getPosition(), roundingChecks.get(0), boat.getPosition(), legBearing));//TEMP REMOVE
|
|
||||||
System.out.println("gatecheck? " + gateCheck);//TEMP REMOVE
|
|
||||||
System.out.println("between gates? " + boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(0))));//TEMP REMOVE
|
|
||||||
if (boat.isPortSide(roundingMark) &&
|
|
||||||
GPSCoordinate.passesLine(
|
GPSCoordinate.passesLine(
|
||||||
roundingMark.getPosition(),
|
roundingData.getMarkToRound().getPosition(),
|
||||||
roundingChecks.get(0),
|
roundingData.getRoundCheck1(),
|
||||||
boat.getPosition(),
|
boat.getPosition(),
|
||||||
legBearing) &&
|
roundingData.getLegBearing()) &&
|
||||||
gateCheck &&
|
gateCheck &&
|
||||||
boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(0)))) {
|
boat.isBetweenGate(
|
||||||
|
roundingData.getMarkToRound(),
|
||||||
|
Mark.tempMark(roundingData.getRoundCheck1())) ) {
|
||||||
boat.increaseRoundingStatus();
|
boat.increaseRoundingStatus();
|
||||||
if (boat.getCurrentLeg().getLegNumber() + 2 >= getLegs().size()){
|
if (boat.getCurrentLeg().getLegNumber() + 2 >= getLegs().size()){
|
||||||
//boat has finished race
|
//boat has finished race
|
||||||
@@ -549,13 +538,18 @@ public class MockRace extends RaceState {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1://has been parallel to the mark;
|
case 1://has been parallel to the mark;
|
||||||
if (boat.isPortSide(roundingMark) &&
|
if (boat.isPortSide(roundingData.getMarkToRound()) &&
|
||||||
GPSCoordinate.passesLine(
|
GPSCoordinate.passesLine(
|
||||||
roundingMark.getPosition(),
|
roundingData.getMarkToRound().getPosition(),
|
||||||
roundingChecks.get(1),
|
roundingData.getRoundCheck2(),
|
||||||
boat.getPosition(),
|
boat.getPosition(),
|
||||||
Bearing.fromDegrees(legBearing.degrees() - 90)) &&//negative 90 from bearing because of port rounding
|
Bearing.fromDegrees(
|
||||||
boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(1)))) {
|
GPSCoordinate.calculateBearing(
|
||||||
|
roundingData.getMarkToRound().getPosition(),
|
||||||
|
roundingData.getRoundCheck2()).degrees() - 90)) &&//negative 90 from bearing because of port rounding
|
||||||
|
boat.isBetweenGate(
|
||||||
|
roundingData.getMarkToRound(),
|
||||||
|
Mark.tempMark(roundingData.getRoundCheck2()))) {
|
||||||
boat.increaseRoundingStatus();
|
boat.increaseRoundingStatus();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -571,25 +565,27 @@ public class MockRace extends RaceState {
|
|||||||
/**
|
/**
|
||||||
* Checks to be run on boats rounding marks on the starboard side
|
* Checks to be run on boats rounding marks on the starboard side
|
||||||
* @param boat the boat that is rounding a mark
|
* @param boat the boat that is rounding a mark
|
||||||
* @param roundingChecks the checks to run
|
* @param roundingData The data for the current leg's rounding.
|
||||||
* @param legBearing the direction of the leg
|
|
||||||
*/
|
*/
|
||||||
private void boatRoundingCheckStarboard(MockBoat boat, List<GPSCoordinate> roundingChecks, Bearing legBearing){
|
private void boatRoundingCheckStarboard(MockBoat boat, MarkRoundingData roundingData){
|
||||||
//boats must pass all checks in order to round a mark
|
//boats must pass all checks in order to round a mark
|
||||||
|
|
||||||
//boolean for if boat has to/needs to pass through a gate
|
//boolean for if boat has to/needs to pass through a gate
|
||||||
boolean gateCheck = boat.getCurrentLeg().getEndCompoundMark().getMark2() == null || boat.isBetweenGate(boat.getCurrentLeg().getEndCompoundMark());
|
boolean gateCheck = boat.getCurrentLeg().getEndCompoundMark().getMark2() == null || boat.isBetweenGate(boat.getCurrentLeg().getEndCompoundMark());
|
||||||
Mark roundingMark = boat.getCurrentLeg().getEndCompoundMark().getMarkForRounding(legBearing);
|
|
||||||
|
|
||||||
System.out.println("boat rounding state: " + boat.getRoundingStatus());//TEMP REMOVE
|
|
||||||
|
|
||||||
switch (boat.getRoundingStatus()) {
|
switch (boat.getRoundingStatus()) {
|
||||||
case 0://hasn't started rounding
|
case 0://hasn't started rounding
|
||||||
if (boat.isStarboardSide(roundingMark) &&
|
if (boat.isStarboardSide(roundingData.getMarkToRound()) &&
|
||||||
GPSCoordinate.passesLine(roundingMark.getPosition(),
|
GPSCoordinate.passesLine(
|
||||||
roundingChecks.get(0), boat.getPosition(), legBearing) &&
|
roundingData.getMarkToRound().getPosition(),
|
||||||
|
roundingData.getRoundCheck1(),
|
||||||
|
boat.getPosition(),
|
||||||
|
roundingData.getLegBearing()) &&
|
||||||
gateCheck &&
|
gateCheck &&
|
||||||
boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(0)))) {
|
boat.isBetweenGate(
|
||||||
|
roundingData.getMarkToRound(),
|
||||||
|
Mark.tempMark(roundingData.getRoundCheck1()))) {
|
||||||
boat.increaseRoundingStatus();
|
boat.increaseRoundingStatus();
|
||||||
if (boat.getCurrentLeg().getLegNumber() + 2 >= getLegs().size()){
|
if (boat.getCurrentLeg().getLegNumber() + 2 >= getLegs().size()){
|
||||||
//boat has finished race
|
//boat has finished race
|
||||||
@@ -598,10 +594,18 @@ public class MockRace extends RaceState {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1://has been parallel to the mark
|
case 1://has been parallel to the mark
|
||||||
if (boat.isStarboardSide(roundingMark) &&
|
if (boat.isStarboardSide(roundingData.getMarkToRound()) &&
|
||||||
GPSCoordinate.passesLine(roundingMark.getPosition(),
|
GPSCoordinate.passesLine(
|
||||||
roundingChecks.get(1), boat.getPosition(), Bearing.fromDegrees(legBearing.degrees() + 90)) && //positive 90 from bearing because of starboard rounding
|
roundingData.getMarkToRound().getPosition(),
|
||||||
boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(1)))) {
|
roundingData.getRoundCheck2(),
|
||||||
|
boat.getPosition(),
|
||||||
|
Bearing.fromDegrees(
|
||||||
|
GPSCoordinate.calculateBearing(
|
||||||
|
roundingData.getMarkToRound().getPosition(),
|
||||||
|
roundingData.getRoundCheck2() ).degrees() + 90)) && //positive 90 from bearing because of starboard rounding
|
||||||
|
boat.isBetweenGate(
|
||||||
|
roundingData.getMarkToRound(),
|
||||||
|
Mark.tempMark(roundingData.getRoundCheck2())) ) {
|
||||||
boat.increaseRoundingStatus();
|
boat.increaseRoundingStatus();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -620,75 +624,22 @@ public class MockRace extends RaceState {
|
|||||||
* @param timeElapsed The total time, in milliseconds, that has elapsed since the race started.
|
* @param timeElapsed The total time, in milliseconds, that has elapsed since the race started.
|
||||||
*/
|
*/
|
||||||
protected void checkPosition(MockBoat boat, long timeElapsed) {
|
protected void checkPosition(MockBoat boat, long timeElapsed) {
|
||||||
//The distance, in nautical miles, within which the boat needs to get in order to consider that it has reached the marker.
|
|
||||||
double epsilonMeters = boat.getCurrentLeg().getEndCompoundMark().getRoundingDistance(); //250 meters.
|
|
||||||
//System.out.println("epsilon dist meters: " + epsilonMeters);//TEMP REMOVE
|
|
||||||
|
|
||||||
//System.out.println("boat dist to next point NM: " + boat.calculateDistanceToNextMarker());//TEMP REMOVE
|
|
||||||
|
|
||||||
double epsilonNM = epsilonMeters / Constants.NMToMetersConversion;
|
|
||||||
|
|
||||||
//System.out.println("epsilon NM: " + epsilonNM);//TEMP REMOVE
|
|
||||||
|
|
||||||
if (boat.calculateDistanceToNextMarker() < epsilonNM) {
|
|
||||||
//Boat is within an acceptable distance from the mark.
|
|
||||||
|
|
||||||
GPSCoordinate startDirectionLinePoint = boat.getCurrentLeg().getStartCompoundMark().getAverageGPSCoordinate();
|
|
||||||
GPSCoordinate endDirectionLinePoint = boat.getCurrentLeg().getEndCompoundMark().getAverageGPSCoordinate();
|
|
||||||
Bearing bearingOfDirectionLine = GPSCoordinate.calculateBearing(startDirectionLinePoint, endDirectionLinePoint);
|
|
||||||
|
|
||||||
//use the direction line to create three invisible points that act as crossover lines a boat must cross
|
|
||||||
//to round a mark
|
|
||||||
|
|
||||||
double bearingToAdd;
|
|
||||||
//System.out.println("leg: " + boat.getCurrentLeg().getName() + " has bearing DL of: " + bearingOfDirectionLine.degrees());//TEMP REMOVE
|
|
||||||
if (boat.getCurrentLeg().getEndCompoundMark().getRoundingType() == RoundingType.Port ||
|
|
||||||
boat.getCurrentLeg().getEndCompoundMark().getRoundingType() == RoundingType.SP){
|
|
||||||
bearingToAdd = 90;
|
|
||||||
}else{
|
|
||||||
bearingToAdd = -90;
|
|
||||||
}
|
|
||||||
|
|
||||||
//System.out.println("so new bearing is " + (bearingToAdd + bearingOfDirectionLine.degrees()));//TEMP REMOVE
|
|
||||||
|
|
||||||
GPSCoordinate roundCheck1 = GPSCoordinate.calculateNewPosition(
|
|
||||||
endDirectionLinePoint,
|
|
||||||
epsilonMeters,
|
|
||||||
Azimuth.fromDegrees(bearingOfDirectionLine.degrees() + bearingToAdd) );
|
|
||||||
//System.out.println("this has a rounding coordinate of (" + roundCheck1.getLongitude() + ", " + roundCheck1.getLatitude() + ")");//TEMP REMOVE
|
|
||||||
|
|
||||||
GPSCoordinate roundCheck2;
|
|
||||||
try{
|
|
||||||
Leg nextLeg = getLegs().get(getLegs().indexOf(boat.getCurrentLeg()) + 1);
|
|
||||||
|
|
||||||
GPSCoordinate startNextDirectionLinePoint = nextLeg.getStartCompoundMark().getAverageGPSCoordinate();
|
|
||||||
GPSCoordinate endNextDirectionLinePoint = nextLeg.getEndCompoundMark().getAverageGPSCoordinate();
|
|
||||||
Bearing bearingOfNextDirectionLine = GPSCoordinate.calculateBearing(startNextDirectionLinePoint, endNextDirectionLinePoint);
|
|
||||||
|
|
||||||
roundCheck2 = GPSCoordinate.calculateNewPosition(
|
|
||||||
endDirectionLinePoint,
|
|
||||||
epsilonMeters,
|
|
||||||
Azimuth.fromDegrees(bearingOfNextDirectionLine.degrees() + bearingToAdd) );
|
|
||||||
}catch(NullPointerException e){
|
|
||||||
//this is caused by the last leg not being having a leg after it
|
|
||||||
roundCheck2 = roundCheck1;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<GPSCoordinate> roundingChecks = new ArrayList<>(Arrays.asList(roundCheck1, roundCheck2));
|
|
||||||
|
|
||||||
switch (boat.getCurrentLeg().getEndCompoundMark().getRoundingType()) {
|
switch (boat.getCurrentLeg().getEndCompoundMark().getRoundingType()) {
|
||||||
case SP://Not yet implemented so these gates will be rounded port side
|
case SP://Not yet implemented so these gates will be rounded port side
|
||||||
case Port:
|
case Port:
|
||||||
boatRoundingCheckPort(boat, roundingChecks, bearingOfDirectionLine);
|
boatRoundingCheckPort(
|
||||||
|
boat,
|
||||||
|
getMarkRoundingSequence().getRoundingData(boat.getCurrentLeg()) );
|
||||||
break;
|
break;
|
||||||
case PS://not yet implemented so these gates will be rounded starboard side
|
case PS://not yet implemented so these gates will be rounded starboard side
|
||||||
case Starboard:
|
case Starboard:
|
||||||
boatRoundingCheckStarboard(boat, roundingChecks, bearingOfDirectionLine);
|
boatRoundingCheckStarboard(
|
||||||
|
boat,
|
||||||
|
getMarkRoundingSequence().getRoundingData(boat.getCurrentLeg()) );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("resultant boat rounding state: " + boat.getRoundingStatus());//TEMP REMOVE
|
|
||||||
|
|
||||||
|
|
||||||
//Check if the boat has finished or stopped racing.
|
//Check if the boat has finished or stopped racing.
|
||||||
if (this.isLastLeg(boat.getCurrentLeg())) {
|
if (this.isLastLeg(boat.getCurrentLeg())) {
|
||||||
@@ -701,8 +652,6 @@ public class MockRace extends RaceState {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package shared.model;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains data related to mark rounding for a specific leg.
|
||||||
|
*/
|
||||||
|
public class MarkRoundingData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The leg this relates to.
|
||||||
|
*/
|
||||||
|
private Leg leg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mark that should be rounded.
|
||||||
|
*/
|
||||||
|
private Mark markToRound;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bearing of the leg.
|
||||||
|
*/
|
||||||
|
private Bearing legBearing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The bearing of the next leg.
|
||||||
|
*/
|
||||||
|
private Bearing nextLegBearing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The location of the first rounding check point.
|
||||||
|
*/
|
||||||
|
private GPSCoordinate roundCheck1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The location of the second rounding check point.
|
||||||
|
*/
|
||||||
|
private GPSCoordinate roundCheck2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A halfway point between mark to round and roundCheck1.
|
||||||
|
*/
|
||||||
|
private GPSCoordinate roundCheck1Halfway;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A halfway point between mark to round and roundCheck2.
|
||||||
|
*/
|
||||||
|
private GPSCoordinate roundCheck2Halfway;
|
||||||
|
|
||||||
|
|
||||||
|
public MarkRoundingData() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Leg getLeg() {
|
||||||
|
return leg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLeg(Leg leg) {
|
||||||
|
this.leg = leg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Mark getMarkToRound() {
|
||||||
|
return markToRound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMarkToRound(Mark markToRound) {
|
||||||
|
this.markToRound = markToRound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bearing getLegBearing() {
|
||||||
|
return legBearing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLegBearing(Bearing legBearing) {
|
||||||
|
this.legBearing = legBearing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bearing getNextLegBearing() {
|
||||||
|
return nextLegBearing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNextLegBearing(Bearing nextLegBearing) {
|
||||||
|
this.nextLegBearing = nextLegBearing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GPSCoordinate getRoundCheck1() {
|
||||||
|
return roundCheck1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoundCheck1(GPSCoordinate roundCheck1) {
|
||||||
|
this.roundCheck1 = roundCheck1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GPSCoordinate getRoundCheck2() {
|
||||||
|
return roundCheck2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoundCheck2(GPSCoordinate roundCheck2) {
|
||||||
|
this.roundCheck2 = roundCheck2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GPSCoordinate getRoundCheck1Halfway() {
|
||||||
|
return roundCheck1Halfway;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoundCheck1Halfway(GPSCoordinate roundCheck1Halfway) {
|
||||||
|
this.roundCheck1Halfway = roundCheck1Halfway;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GPSCoordinate getRoundCheck2Halfway() {
|
||||||
|
return roundCheck2Halfway;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRoundCheck2Halfway(GPSCoordinate roundCheck2Halfway) {
|
||||||
|
this.roundCheck2Halfway = roundCheck2Halfway;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
package shared.model;
|
package shared.model;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import java.util.*;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import static shared.enums.RoundingType.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class contains a sequence of points that describe the mark rounding order for a course.
|
* This class contains a sequence of points that describe the mark rounding order for a course.
|
||||||
@@ -19,9 +16,9 @@ public class MarkRoundingSequence {
|
|||||||
private List<Leg> legs;
|
private List<Leg> legs;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For each leg, a sequence of rounding points.
|
* For each leg, mark rounding information.
|
||||||
*/
|
*/
|
||||||
private Map<Leg, List<GPSCoordinate>> roundingPoints;
|
private Map<Leg, MarkRoundingData> roundingPoints;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -31,6 +28,16 @@ public class MarkRoundingSequence {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the rounding points for a given leg.
|
||||||
|
* @param leg Leg to check.
|
||||||
|
* @return Rounding points for leg.
|
||||||
|
*/
|
||||||
|
public MarkRoundingData getRoundingData(Leg leg) {
|
||||||
|
return roundingPoints.get(leg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the rounding points for all legs in the race.
|
* Generates the rounding points for all legs in the race.
|
||||||
*/
|
*/
|
||||||
@@ -39,7 +46,11 @@ public class MarkRoundingSequence {
|
|||||||
|
|
||||||
for (int i = 0; i < this.legs.size(); i++) {
|
for (int i = 0; i < this.legs.size(); i++) {
|
||||||
Leg currentLeg = this.legs.get(i);
|
Leg currentLeg = this.legs.get(i);
|
||||||
Optional<Leg> nextLeg = Optional.ofNullable(this.legs.get(i + 1));
|
|
||||||
|
Optional<Leg> nextLeg = Optional.empty();
|
||||||
|
if (i < legs.size() - 1) {
|
||||||
|
nextLeg = Optional.of(this.legs.get(i + 1));
|
||||||
|
}
|
||||||
|
|
||||||
generateRoundingPoint(currentLeg, nextLeg);
|
generateRoundingPoint(currentLeg, nextLeg);
|
||||||
}
|
}
|
||||||
@@ -50,14 +61,85 @@ public class MarkRoundingSequence {
|
|||||||
/**
|
/**
|
||||||
* Generates the rounding points for a specific leg.
|
* Generates the rounding points for a specific leg.
|
||||||
* @param currentLeg The leg to generate rounding points for.
|
* @param currentLeg The leg to generate rounding points for.
|
||||||
* @param nextLeg The following leg, used to help generate rounding points.
|
* @param nextLeg The following leg, used to help generate rounding points. Final leg of race doesn't have a following leg.
|
||||||
*/
|
*/
|
||||||
private void generateRoundingPoint(Leg currentLeg, Optional<Leg> nextLeg) {
|
private void generateRoundingPoint(Leg currentLeg, Optional<Leg> nextLeg) {
|
||||||
|
|
||||||
|
Bearing bearingToAddFirstPoint = calculateBearingToAdd(currentLeg);
|
||||||
|
|
||||||
|
GPSCoordinate startCoord = currentLeg.getStartCompoundMark().getAverageGPSCoordinate();
|
||||||
|
GPSCoordinate endCoord = currentLeg.getEndCompoundMark().getAverageGPSCoordinate();
|
||||||
|
Bearing legBearing = GPSCoordinate.calculateBearing(startCoord, endCoord);
|
||||||
|
Bearing nextBearing = legBearing;
|
||||||
|
|
||||||
|
Mark markToRound = currentLeg.getEndCompoundMark().getMarkForRounding(legBearing);
|
||||||
|
|
||||||
|
GPSCoordinate roundCheck1;
|
||||||
|
if (currentLeg.getEndCompoundMark().getMark2() == null) {
|
||||||
|
//End is a single mark.
|
||||||
|
roundCheck1 = calculateRoundingCheckPoint(
|
||||||
|
currentLeg,
|
||||||
|
markToRound,
|
||||||
|
legBearing,
|
||||||
|
bearingToAddFirstPoint);
|
||||||
|
} else {
|
||||||
|
//End is a gate.
|
||||||
|
if (markToRound == currentLeg.getEndCompoundMark().getMark1()) {
|
||||||
|
roundCheck1 = currentLeg.getEndCompoundMark().getMark2().getPosition();
|
||||||
|
} else {
|
||||||
|
roundCheck1 = currentLeg.getEndCompoundMark().getMark1().getPosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO the halfway points currently haven't been done properly.
|
||||||
|
|
||||||
|
GPSCoordinate roundCheck1Halfway = calculateRoundingCheckPoint(
|
||||||
|
currentLeg,
|
||||||
|
markToRound,
|
||||||
|
legBearing,
|
||||||
|
bearingToAddFirstPoint);
|
||||||
|
|
||||||
|
|
||||||
|
GPSCoordinate roundCheck2 = roundCheck1;
|
||||||
|
GPSCoordinate roundCheck2Halfway = roundCheck1Halfway;
|
||||||
|
if (nextLeg.isPresent()) {
|
||||||
|
|
||||||
|
Bearing bearingToAddSecondPoint = bearingToAddFirstPoint;//calculateBearingToAdd(nextLeg.get());
|
||||||
|
|
||||||
|
GPSCoordinate startCoord2 = nextLeg.get().getStartCompoundMark().getAverageGPSCoordinate();
|
||||||
|
GPSCoordinate endCoord2 = nextLeg.get().getEndCompoundMark().getAverageGPSCoordinate();
|
||||||
|
nextBearing = GPSCoordinate.calculateBearing(startCoord2, endCoord2);
|
||||||
|
|
||||||
|
roundCheck2 = calculateRoundingCheckPoint(
|
||||||
|
currentLeg,
|
||||||
|
markToRound,
|
||||||
|
nextBearing,
|
||||||
|
bearingToAddSecondPoint);
|
||||||
|
|
||||||
|
roundCheck2Halfway = calculateRoundingCheckPoint(
|
||||||
|
currentLeg,
|
||||||
|
markToRound,
|
||||||
|
nextBearing,
|
||||||
|
bearingToAddSecondPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MarkRoundingData roundingData = new MarkRoundingData();
|
||||||
|
roundingData.setLeg(currentLeg);
|
||||||
|
|
||||||
|
roundingData.setLegBearing(legBearing);
|
||||||
|
roundingData.setNextLegBearing(nextBearing);
|
||||||
|
|
||||||
|
roundingData.setMarkToRound(markToRound);
|
||||||
|
|
||||||
|
roundingData.setRoundCheck1(roundCheck1);
|
||||||
|
roundingData.setRoundCheck1Halfway(roundCheck1Halfway);
|
||||||
|
|
||||||
|
roundingData.setRoundCheck2(roundCheck2);
|
||||||
|
roundingData.setRoundCheck2Halfway(roundCheck2Halfway);
|
||||||
|
|
||||||
|
|
||||||
|
this.roundingPoints.put(currentLeg, roundingData);
|
||||||
|
|
||||||
|
|
||||||
//Rounding points:
|
//Rounding points:
|
||||||
@@ -75,4 +157,54 @@ public class MarkRoundingSequence {
|
|||||||
//the second rounding point is the same as the first, except LEGBEARING is the bearing between end of current leg, and start of next leg.
|
//the second rounding point is the same as the first, except LEGBEARING is the bearing between end of current leg, and start of next leg.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the location of the rounding check point, which together with the mark to round, forms a line that the boat must cross to round the mark.
|
||||||
|
* @param leg Leg of race to check.
|
||||||
|
* @param markToRound Mark at end of leg to round.
|
||||||
|
* @param legBearing The bearing of the nearest leg. For the first rounding point this is the leg's bearing, for the second rounding point it is the next leg's bearing.
|
||||||
|
* @param bearingToAdd The bearing to add to the leg bearing to get a perpendicular bearing.
|
||||||
|
* @return The location of the rounding point, which together with the mark to round forms a line the boat must cross.
|
||||||
|
*/
|
||||||
|
private GPSCoordinate calculateRoundingCheckPoint(Leg leg, Mark markToRound, Bearing legBearing, Bearing bearingToAdd) {
|
||||||
|
|
||||||
|
|
||||||
|
double roundingDistanceMeters = leg.getEndCompoundMark().getRoundingDistance();
|
||||||
|
|
||||||
|
|
||||||
|
//We project from rounding mark to get the second point which forms the line the boat must cross.
|
||||||
|
/*
|
||||||
|
c2
|
||||||
|
|
|
||||||
|
|
|
||||||
|
r------c1
|
||||||
|
b
|
||||||
|
*/
|
||||||
|
GPSCoordinate roundCheck = GPSCoordinate.calculateNewPosition(
|
||||||
|
markToRound.getPosition(),
|
||||||
|
roundingDistanceMeters,
|
||||||
|
Azimuth.fromDegrees(legBearing.degrees() + bearingToAdd.degrees()) );
|
||||||
|
|
||||||
|
return roundCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the bearing that must be added to a leg's bearing to calculate a perpendicular bearing, used for finding rounding points.
|
||||||
|
* @param leg Leg to check.
|
||||||
|
* @return Bearing to add. Will be either +90 or -90.
|
||||||
|
*/
|
||||||
|
private Bearing calculateBearingToAdd(Leg leg) {
|
||||||
|
|
||||||
|
if (leg.getEndCompoundMark().getRoundingType() == Port ||
|
||||||
|
leg.getEndCompoundMark().getRoundingType() == SP) {
|
||||||
|
return Bearing.fromDegrees(90);
|
||||||
|
} else {
|
||||||
|
return Bearing.fromDegrees(-90);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,12 @@ public abstract class RaceState {
|
|||||||
private ObservableList<Leg> legs;
|
private ObservableList<Leg> legs;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The sequence of rounding points for each leg/mark.
|
||||||
|
*/
|
||||||
|
private MarkRoundingSequence markRoundingSequence;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The clock which tracks the race's start time, current time, and elapsed duration.
|
* The clock which tracks the race's start time, current time, and elapsed duration.
|
||||||
@@ -102,10 +108,15 @@ public abstract class RaceState {
|
|||||||
*/
|
*/
|
||||||
protected void useLegsList(List<Leg> legs) {
|
protected void useLegsList(List<Leg> legs) {
|
||||||
this.legs.setAll(legs);
|
this.legs.setAll(legs);
|
||||||
|
|
||||||
|
//We create this before adding the extra finish leg, as it doesn't contain compound marks.
|
||||||
|
this.markRoundingSequence = new MarkRoundingSequence(getLegs());
|
||||||
|
|
||||||
//We add a "dummy" leg at the end of the race.
|
//We add a "dummy" leg at the end of the race.
|
||||||
if (getLegs().size() > 0) {
|
if (getLegs().size() > 0) {
|
||||||
getLegs().add(new Leg("Finish", getLegs().size()));
|
getLegs().add(new Leg("Finish", getLegs().size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -367,6 +378,11 @@ public abstract class RaceState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the rounding sequences for each leg.
|
||||||
|
* @return Rounding sequence for each leg.
|
||||||
|
*/
|
||||||
|
public MarkRoundingSequence getMarkRoundingSequence() {
|
||||||
|
return markRoundingSequence;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -495,8 +495,6 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
private void drawRoundingLines() {
|
private void drawRoundingLines() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//ugly hack
|
//ugly hack
|
||||||
//rounding lines
|
//rounding lines
|
||||||
|
|
||||||
@@ -511,52 +509,16 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GPSCoordinate startDirectionLinePoint = boat.getCurrentLeg().getStartCompoundMark().getAverageGPSCoordinate();
|
Leg currentLeg = boat.getCurrentLeg();
|
||||||
GPSCoordinate endDirectionLinePoint = boat.getCurrentLeg().getEndCompoundMark().getAverageGPSCoordinate();
|
|
||||||
Bearing bearingOfDirectionLine = GPSCoordinate.calculateBearing(startDirectionLinePoint, endDirectionLinePoint);
|
|
||||||
|
|
||||||
//use the direction line to create three invisible points that act as crossover lines a boat must cross
|
MarkRoundingData roundingData = raceState.getMarkRoundingSequence().getRoundingData(currentLeg);
|
||||||
//to round a mark
|
|
||||||
|
|
||||||
double bearingToAdd;
|
|
||||||
if (boat.getCurrentLeg().getEndCompoundMark().getRoundingType() == RoundingType.Port ||
|
|
||||||
boat.getCurrentLeg().getEndCompoundMark().getRoundingType() == RoundingType.SP) {
|
|
||||||
bearingToAdd = 90;
|
|
||||||
}else{
|
|
||||||
bearingToAdd = -90;
|
|
||||||
}
|
|
||||||
|
|
||||||
double epsilonMeters = boat.getCurrentLeg().getEndCompoundMark().getRoundingDistance();
|
|
||||||
|
|
||||||
GPSCoordinate endMarkPos = boat.getCurrentLeg().getEndCompoundMark().getMarkForRounding(bearingOfDirectionLine).getPosition();
|
|
||||||
|
|
||||||
GPSCoordinate roundCheck1 = GPSCoordinate.calculateNewPosition(
|
|
||||||
endMarkPos,
|
|
||||||
epsilonMeters,
|
|
||||||
Azimuth.fromDegrees(bearingOfDirectionLine.degrees() + bearingToAdd) );
|
|
||||||
|
|
||||||
GPSCoordinate roundCheck2;
|
|
||||||
try{
|
|
||||||
Leg nextLeg = raceState.getLegs().get(raceState.getLegs().indexOf(boat.getCurrentLeg()) + 1);
|
|
||||||
|
|
||||||
GPSCoordinate startNextDirectionLinePoint = nextLeg.getStartCompoundMark().getAverageGPSCoordinate();
|
|
||||||
GPSCoordinate endNextDirectionLinePoint = nextLeg.getEndCompoundMark().getAverageGPSCoordinate();
|
|
||||||
Bearing bearingOfNextDirectionLine = GPSCoordinate.calculateBearing(startNextDirectionLinePoint, endNextDirectionLinePoint);
|
|
||||||
|
|
||||||
roundCheck2 = GPSCoordinate.calculateNewPosition(
|
|
||||||
endDirectionLinePoint,
|
|
||||||
epsilonMeters,
|
|
||||||
Azimuth.fromDegrees(bearingOfNextDirectionLine.degrees() + bearingToAdd) );
|
|
||||||
} catch(NullPointerException e) {
|
|
||||||
//this is caused by the last leg not being having a leg after it
|
|
||||||
roundCheck2 = roundCheck1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//To screen coords.
|
//To screen coords.
|
||||||
GraphCoordinate legEnd = map.convertGPS(endDirectionLinePoint);
|
GraphCoordinate legEnd = map.convertGPS(roundingData.getMarkToRound().getPosition());
|
||||||
GraphCoordinate round1 = map.convertGPS(roundCheck1);
|
GraphCoordinate round1 = map.convertGPS(roundingData.getRoundCheck1());
|
||||||
GraphCoordinate round2 = map.convertGPS(roundCheck2);
|
GraphCoordinate round2 = map.convertGPS(roundingData.getRoundCheck2());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
gc.strokeLine(
|
gc.strokeLine(
|
||||||
@@ -571,6 +533,9 @@ public class ResizableRaceCanvas extends ResizableCanvas {
|
|||||||
round2.getX(),
|
round2.getX(),
|
||||||
round2.getY() );
|
round2.getY() );
|
||||||
|
|
||||||
|
drawCircle(round1, 12, Color.ORANGE);
|
||||||
|
drawCircle(round2, 12, Color.GREEN);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user