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]
main
fjc40 8 years ago
parent 346aa148ef
commit 3fa6b9200d

@ -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,84 +624,29 @@ 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()) {
case SP://Not yet implemented so these gates will be rounded port side
case Port:
boatRoundingCheckPort(boat, roundingChecks, bearingOfDirectionLine);
break;
case PS://not yet implemented so these gates will be rounded starboard side
case Starboard:
boatRoundingCheckStarboard(boat, roundingChecks, bearingOfDirectionLine);
break;
}
System.out.println("resultant boat rounding state: " + boat.getRoundingStatus());//TEMP REMOVE
switch (boat.getCurrentLeg().getEndCompoundMark().getRoundingType()) {
case SP://Not yet implemented so these gates will be rounded port side
case Port:
boatRoundingCheckPort(
boat,
getMarkRoundingSequence().getRoundingData(boat.getCurrentLeg()) );
break;
case PS://not yet implemented so these gates will be rounded starboard side
case Starboard:
boatRoundingCheckStarboard(
boat,
getMarkRoundingSequence().getRoundingData(boat.getCurrentLeg()) );
break;
}
//Check if the boat has finished or stopped racing.
if (this.isLastLeg(boat.getCurrentLeg())) {
//Boat has finished.
boat.setTimeFinished(timeElapsed);
boat.setCurrentSpeed(0);
boat.setStatus(BoatStatusEnum.FINISHED);
} //Check if the boat has finished or stopped racing.
if (this.isLastLeg(boat.getCurrentLeg())) {
//Boat has finished.
boat.setTimeFinished(timeElapsed);
boat.setCurrentSpeed(0);
boat.setStatus(BoatStatusEnum.FINISHED);
} }

@ -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
//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(); MarkRoundingData roundingData = raceState.getMarkRoundingSequence().getRoundingData(currentLeg);
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);
} }

Loading…
Cancel
Save