diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index a9f11770..c598eb05 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -8,6 +8,7 @@ import network.Messages.BoatLocation; import network.Messages.BoatStatus; import network.Messages.Enums.BoatStatusEnum; import network.Messages.Enums.RaceStatusEnum; +import network.Utils.AC35UnitConverter; import shared.dataInput.BoatDataSource; import shared.dataInput.RaceDataSource; import shared.dataInput.RegattaDataSource; @@ -524,12 +525,22 @@ public class MockRace extends RaceState { 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()) { case 0://hasn't started rounding + System.out.println("portside? " + boat.isPortSide(roundingMark));//TEMP REMOVE + 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(roundingMark.getPosition(), - roundingChecks.get(0), boat.getPosition(), legBearing) && - gateCheck && boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(0)))) { + GPSCoordinate.passesLine( + roundingMark.getPosition(), + roundingChecks.get(0), + boat.getPosition(), + legBearing) && + gateCheck && + boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(0)))) { boat.increaseRoundingStatus(); if (boat.getCurrentLeg().getLegNumber() + 2 >= getLegs().size()){ //boat has finished race @@ -539,8 +550,10 @@ public class MockRace extends RaceState { break; case 1://has been parallel to the mark; if (boat.isPortSide(roundingMark) && - GPSCoordinate.passesLine(roundingMark.getPosition(), - roundingChecks.get(1), boat.getPosition(), + GPSCoordinate.passesLine( + roundingMark.getPosition(), + roundingChecks.get(1), + boat.getPosition(), Bearing.fromDegrees(legBearing.degrees() - 90)) &&//negative 90 from bearing because of port rounding boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(1)))) { boat.increaseRoundingStatus(); @@ -568,6 +581,8 @@ public class MockRace extends RaceState { 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()) { case 0://hasn't started rounding if (boat.isStarboardSide(roundingMark) && @@ -606,19 +621,27 @@ public class MockRace extends RaceState { */ 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 epsilonNauticalMiles = boat.getCurrentLeg().getEndCompoundMark().getRoundingDistance(); //250 meters. + 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; - if (boat.calculateDistanceToNextMarker() < epsilonNauticalMiles) { + //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().getMark1Position(); - GPSCoordinate endDirectionLinePoint = boat.getCurrentLeg().getEndCompoundMark().getMark1Position(); + 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; @@ -626,25 +649,32 @@ public class MockRace extends RaceState { bearingToAdd = -90; } - GPSCoordinate roundCheck1 = GPSCoordinate.calculateNewPosition(endDirectionLinePoint, - epsilonNauticalMiles, Azimuth.fromDegrees(bearingOfDirectionLine.degrees() + bearingToAdd)); + //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().getMark1Position(); - GPSCoordinate endNextDirectionLinePoint = nextLeg.getEndCompoundMark().getMark1Position(); + GPSCoordinate startNextDirectionLinePoint = nextLeg.getStartCompoundMark().getAverageGPSCoordinate(); + GPSCoordinate endNextDirectionLinePoint = nextLeg.getEndCompoundMark().getAverageGPSCoordinate(); Bearing bearingOfNextDirectionLine = GPSCoordinate.calculateBearing(startNextDirectionLinePoint, endNextDirectionLinePoint); - roundCheck2 = GPSCoordinate.calculateNewPosition(endDirectionLinePoint, - epsilonNauticalMiles, Azimuth.fromDegrees(bearingOfNextDirectionLine.degrees() + bearingToAdd)); + 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 roundingChecks = new ArrayList(Arrays.asList(roundCheck1, roundCheck2)); + List 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 @@ -657,6 +687,8 @@ public class MockRace extends RaceState { break; } + System.out.println("resultant boat rounding state: " + boat.getRoundingStatus());//TEMP REMOVE + //Check if the boat has finished or stopped racing. if (this.isLastLeg(boat.getCurrentLeg())) { diff --git a/racevisionGame/src/main/java/mock/model/SourceIdAllocator.java b/racevisionGame/src/main/java/mock/model/SourceIdAllocator.java index 17a7e85f..ed36093c 100644 --- a/racevisionGame/src/main/java/mock/model/SourceIdAllocator.java +++ b/racevisionGame/src/main/java/mock/model/SourceIdAllocator.java @@ -36,10 +36,10 @@ public class SourceIdAllocator { */ public synchronized int allocateSourceID() throws SourceIDAllocationException { - if (!((mockRace.getRaceStatusEnum() == RaceStatusEnum.PRESTART) + /*if (!((mockRace.getRaceStatusEnum() == RaceStatusEnum.PRESTART) || (mockRace.getRaceStatusEnum() == RaceStatusEnum.WARNING))) { throw new SourceIDAllocationException("Could not allocate a source ID. Can only allocate during pre-start period. It is currently: " + mockRace.getRaceStatusEnum()); - } + }*///TEMP DISABLED FOR TESTING - RE-ENABLE THIS List allocatedIDs = mockRace.getRaceDataSource().getParticipants(); List allIDs = new ArrayList<>(mockRace.getBoatDataSource().getBoats().keySet()); diff --git a/racevisionGame/src/main/java/shared/model/CompoundMark.java b/racevisionGame/src/main/java/shared/model/CompoundMark.java index 4fa2599c..57d9a32b 100644 --- a/racevisionGame/src/main/java/shared/model/CompoundMark.java +++ b/racevisionGame/src/main/java/shared/model/CompoundMark.java @@ -187,7 +187,7 @@ public class CompoundMark extends XMLCompoundMark{ } //finds the mark furthest west and east - if(this.getMark1Position().getLongitude() > this.getMark2Position().getLongitude()){ + if(this.getMark1Position().getLongitude() < this.getMark2Position().getLongitude()){ westMostMark = this.mark1; eastMostMark = this.mark2; }else{ diff --git a/racevisionGame/src/main/java/shared/model/Constants.java b/racevisionGame/src/main/java/shared/model/Constants.java index bb7ec598..247b4c9b 100644 --- a/racevisionGame/src/main/java/shared/model/Constants.java +++ b/racevisionGame/src/main/java/shared/model/Constants.java @@ -33,7 +33,7 @@ public class Constants { * Frame periods are multiplied by this to get the amount of time a single frame represents. * E.g., frame period = 20ms, scale = 5, frame represents 20 * 5 = 100ms, and so boats are simulated for 100ms, even though only 20ms actually occurred. */ - public static final int RaceTimeScale = 2;//10; + public static final int RaceTimeScale = 10;//10; /** * The race pre-start time, in milliseconds. 3 minutes. diff --git a/racevisionGame/src/main/java/shared/model/MarkRoundingSequence.java b/racevisionGame/src/main/java/shared/model/MarkRoundingSequence.java new file mode 100644 index 00000000..48fc3999 --- /dev/null +++ b/racevisionGame/src/main/java/shared/model/MarkRoundingSequence.java @@ -0,0 +1,78 @@ +package shared.model; + +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +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. + */ +public class MarkRoundingSequence { + + + /** + * Legs in the race. + */ + private List legs; + + /** + * For each leg, a sequence of rounding points. + */ + private Map> roundingPoints; + + + + public MarkRoundingSequence(List legs) { + this.legs = legs; + generateRoundingPoints(); + } + + + /** + * Generates the rounding points for all legs in the race. + */ + private void generateRoundingPoints() { + this.roundingPoints = new HashMap<>(this.legs.size()); + + for (int i = 0; i < this.legs.size(); i++) { + Leg currentLeg = this.legs.get(i); + Optional nextLeg = Optional.ofNullable(this.legs.get(i + 1)); + + generateRoundingPoint(currentLeg, nextLeg); + } + + } + + + /** + * Generates the rounding points for a specific leg. + * @param currentLeg The leg to generate rounding points for. + * @param nextLeg The following leg, used to help generate rounding points. + */ + private void generateRoundingPoint(Leg currentLeg, Optional nextLeg) { + + + + + + + + //Rounding points: + + //each mark/gate has a specific mark to round. Call this ROUNDINGMARK + // with a mark, it is the mark + // with a gate, it depends if it is a starboard or port gate. + // it is the mark that allows the boat to enter between both marks of the gate, whilst obeying the starboard/port requirement. + + //let the bearing between start of leg and end of leg be called LEGBEARING + + //the first rounding point is ROUNDINGDISTANCE units away from the ROUNDINGMARK, on an angle perpendicular to LEGBEARING. + // the angle means that the rounding mark is at the center of a gate, for gates. + + //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. + } + +} diff --git a/racevisionGame/src/main/java/visualiser/model/ResizableRaceCanvas.java b/racevisionGame/src/main/java/visualiser/model/ResizableRaceCanvas.java index 03e86e27..3f475e63 100644 --- a/racevisionGame/src/main/java/visualiser/model/ResizableRaceCanvas.java +++ b/racevisionGame/src/main/java/visualiser/model/ResizableRaceCanvas.java @@ -325,18 +325,6 @@ public class ResizableRaceCanvas extends ResizableCanvas { } - - -/* - //If the race hasn't started, we set the time since last mark to the current time, to ensure we don't start counting until the race actually starts. - if ((boat.getStatus() != BoatStatusEnum.RACING) && (boat.getStatus() == BoatStatusEnum.FINISHED)) { - boat.setTimeAtLastMark(visualiserRace.getVisualiserRaceState().getRaceClock().getCurrentTime()); - } -*/ - - - - } /** @@ -504,6 +492,87 @@ public class ResizableRaceCanvas extends ResizableCanvas { } + private void drawRoundingLines() { + + + + + //ugly hack + //rounding lines + + //Boat is within an acceptable distance from the mark. + VisualiserBoat boat = null; + for (VisualiserBoat visualiserBoat : new ArrayList<>(raceState.getBoats())) { + if (visualiserBoat.isClientBoat()) { + boat = visualiserBoat; + } + } + if (boat == null) { + return; + } + + 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; + 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. + GraphCoordinate legEnd = map.convertGPS(endDirectionLinePoint); + GraphCoordinate round1 = map.convertGPS(roundCheck1); + GraphCoordinate round2 = map.convertGPS(roundCheck2); + + + + gc.strokeLine( + legEnd.getX(), + legEnd.getY(), + round1.getX(), + round1.getY() ); + + gc.strokeLine( + legEnd.getX(), + legEnd.getY(), + round2.getX(), + round2.getY() ); + + } + /** * Draws all of the {@link Mark}s on the canvas. @@ -513,6 +582,7 @@ public class ResizableRaceCanvas extends ResizableCanvas { for (Mark mark : new ArrayList<>(raceState.getMarks())) { drawMark(mark); } + } @@ -621,6 +691,9 @@ public class ResizableRaceCanvas extends ResizableCanvas { //Marks. drawMarks(); + //TEMP + drawRoundingLines(); + } /** @@ -655,7 +728,7 @@ public class ResizableRaceCanvas extends ResizableCanvas { //finds the direction of the current leg as a bearing startDirectionLinePoint = legStartPoint; - GPSCoordinate tempEndDirectionLinePoint = legs.get(index).getEndCompoundMark().getAverageGPSCoordinate(); + GPSCoordinate tempEndDirectionLinePoint = legs.get(index).getEndCompoundMark().getMark1Position(); bearingOfDirectionLine = GPSCoordinate.calculateBearing(startDirectionLinePoint, tempEndDirectionLinePoint); @@ -826,4 +899,4 @@ public class ResizableRaceCanvas extends ResizableCanvas { -} \ No newline at end of file +}