mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-17 01:31:01 +00:00
I think I've finally fixed the spikes.
This commit is contained in:
1 parent
8728d87a16
commit
8e98eae938
3 files changed
+205
-106
No files matched your search
+1
-1
@@ -6,7 +6,7 @@ import com.sap.sailing.domain.common.impl.RadianPosition;
|
||||
|
||||
public class AbstractPosition implements Position {
|
||||
|
||||
private static final double TRESHOLD_DEGREES = 30.0;
|
||||
private static final double TRESHOLD_DEGREES = 25.0;
|
||||
private static final long serialVersionUID = -3057027562787541064L;
|
||||
|
||||
@Override
|
||||
|
||||
+23
-24
@@ -8,15 +8,13 @@ public class TwoDSegment {
|
||||
private TwoDPoint firstPoint = null;
|
||||
private TwoDPoint secondPoint = null;
|
||||
|
||||
public TwoDSegment(final TwoDPoint p1, final TwoDPoint p2) {
|
||||
public TwoDSegment(TwoDPoint p1, TwoDPoint p2) {
|
||||
this(p1.getX(), p1.getY(), p2.getX(), p2.getY());
|
||||
}
|
||||
|
||||
public TwoDSegment(final double x1, final double y1, final double x2, final double y2) {
|
||||
public TwoDSegment(double x1, double y1, double x2, double y2) {
|
||||
if (x1 == x2) {
|
||||
// FIXME:
|
||||
// this.slope = Double.MAX_VALUE;
|
||||
|
||||
} else {
|
||||
this.lineSlope = (y2 - y1) / (x2 - x1);
|
||||
this.lineIntercept = (x2 * y1 - x1 * y2) / (x2 - x1);
|
||||
@@ -34,15 +32,15 @@ public class TwoDSegment {
|
||||
return this.lineIntercept;
|
||||
}
|
||||
|
||||
public TwoDPoint projectionOfPointOnLine(final TwoDPoint p) {
|
||||
final double x = (this.lineSlope * p.getY() + p.getX() - this.lineSlope * this.lineIntercept)
|
||||
public TwoDPoint projectionOfPointOnLine(TwoDPoint p) {
|
||||
double x = (this.lineSlope * p.getY() + p.getX() - this.lineSlope * this.lineIntercept)
|
||||
/ (this.lineSlope * this.lineSlope + 1);
|
||||
final double y = this.lineSlope * x + this.lineIntercept;
|
||||
double y = this.lineSlope * x + this.lineIntercept;
|
||||
|
||||
return new TwoDPoint(x, y);
|
||||
}
|
||||
|
||||
public double distanceToLine(final TwoDPoint p) {
|
||||
public double distanceToLine(TwoDPoint p) {
|
||||
return TwoDPoint.distanceBetween(p, this.projectionOfPointOnLine(p));
|
||||
}
|
||||
|
||||
@@ -63,36 +61,37 @@ public class TwoDSegment {
|
||||
return new TwoDVector(this.firstPoint, this.secondPoint);
|
||||
}
|
||||
|
||||
public static boolean areIntersecting(final TwoDSegment s1, final TwoDSegment s2) {
|
||||
public static boolean areIntersecting(TwoDSegment s1, TwoDSegment s2) {
|
||||
return TwoDPoint.areIntersecting(s1.firstPoint, s1.secondPoint, s2.firstPoint, s2.secondPoint);
|
||||
}
|
||||
|
||||
public TwoDPoint intersectionPointWith(final TwoDSegment segment2) {
|
||||
return getIntersectionPoint(this, segment2);
|
||||
public TwoDPoint intersectionPointWith(TwoDSegment segment2) {
|
||||
return getIntersection(this, segment2);
|
||||
}
|
||||
|
||||
public static TwoDPoint getIntersectionPoint(final TwoDSegment segment1, final TwoDSegment segment2) {
|
||||
public static TwoDPoint getIntersection(TwoDSegment segment1, TwoDSegment segment2) {
|
||||
|
||||
final double x1 = segment1.firstPoint.getX();
|
||||
final double x2 = segment1.secondPoint.getX();
|
||||
final double x3 = segment2.firstPoint.getX();
|
||||
final double x4 = segment2.secondPoint.getX();
|
||||
double xA = segment1.firstPoint.getX();
|
||||
double yA = segment1.firstPoint.getY();
|
||||
|
||||
final double y1 = segment1.firstPoint.getY();
|
||||
final double y2 = segment1.secondPoint.getY();
|
||||
final double y3 = segment2.firstPoint.getY();
|
||||
final double y4 = segment2.secondPoint.getY();
|
||||
double xC = segment2.firstPoint.getX();
|
||||
double yC = segment2.firstPoint.getY();
|
||||
|
||||
final double temp1 = (y2 - y1) / (x2 - x1);
|
||||
final double temp2 = (y4 - y3) / (x4 - x3);
|
||||
double m1 = segment1.lineSlope;
|
||||
double m2 = segment2.lineSlope;
|
||||
|
||||
final double x = (y3 - y1 + x1 * temp1 - x3 * temp2) / (temp1 - temp2);
|
||||
final double y = x * temp1 + y1 - x1 * temp1;
|
||||
double x = (yC - yA + m1 * xA - m2 * xC) / (m1 - m2);
|
||||
double y = (yC - yA + m2 * (xA - xC)) * m1 / (m1 - m2) + yA;
|
||||
|
||||
return new TwoDPoint(x, y);
|
||||
}
|
||||
|
||||
public boolean contains(TwoDPoint point) {
|
||||
|
||||
if (Math.abs(point.getY() - (this.lineSlope * point.getX() + this.lineIntercept)) > 0.00001) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double maxX = Math.max(this.firstPoint.getX(), this.secondPoint.getX());
|
||||
double minX = Math.min(this.firstPoint.getX(), this.secondPoint.getX());
|
||||
|
||||
|
||||
+181
-81
@@ -17,9 +17,7 @@ import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.sap.sailing.gwt.ui.client.ErrorReporter;
|
||||
import com.sap.sailing.gwt.ui.client.SimulatorServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.shared.PositionDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.Request1TurnerDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RequestTotalTimeDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.Response1TurnerDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.ResponseTotalTimeDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.racemap.TwoDPoint;
|
||||
@@ -61,6 +59,9 @@ public class PathPolyline {
|
||||
private static int STEP_DURATION_MILLISECONDS = 2000;
|
||||
private static boolean USE_REAL_AVERAGE_WIND = true;
|
||||
|
||||
private static boolean FIX_CUT_SPIKES = true;
|
||||
private static boolean FIX_CUT_TRIANGLES = true;
|
||||
|
||||
public static PathPolyline createPathPolyline(List<SimulatorWindDTO> pathPoints, ErrorReporter errorReporter, SimulatorServiceAsync simulatorService,
|
||||
MapWidget map, SimulatorMap simulatorMap, int boatClassID) {
|
||||
|
||||
@@ -112,6 +113,17 @@ public class PathPolyline {
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void printTurnPoints() {
|
||||
System.out.println("-----------------------------");
|
||||
|
||||
for (int index = 0; index < this.turnPoints.length; index++) {
|
||||
System.out.println("Point no. " + index + " at " + this.turnPoints[index].toString());
|
||||
}
|
||||
|
||||
System.out.println("-----------------------------");
|
||||
}
|
||||
|
||||
private void drawPolylineOnMap() {
|
||||
if (this.polyline != null) {
|
||||
this.map.removeOverlay(this.polyline);
|
||||
@@ -130,16 +142,17 @@ public class PathPolyline {
|
||||
if (indexOfMovedPoint == 0 || indexOfMovedPoint == noOfPoints - 1) {
|
||||
// start and end points cannot be moved!
|
||||
} else {
|
||||
// System.err.println("INDEX OF MOVED POINT = " + indexOfMovedPoint);
|
||||
|
||||
TwoDPoint newOrigin = computeNewOrigin(indexOfMovedPoint);
|
||||
final boolean projectionOnBeforeLine = projectedToBeforeLine(indexOfMovedPoint);
|
||||
|
||||
SpecialCaseResult specialCase = null;
|
||||
|
||||
if (indexOfMovedPoint == 1) {
|
||||
turnPoints[indexOfMovedPoint + 1] = toLatLng(computeAfterNewOrigin(indexOfMovedPoint, newOrigin));
|
||||
} else if (indexOfMovedPoint == noOfPoints - 2) {
|
||||
turnPoints[indexOfMovedPoint - 1] = toLatLng(computeBeforeNewOrigin(indexOfMovedPoint, newOrigin));
|
||||
} else {
|
||||
specialCase = isSepecialCase(indexOfMovedPoint, newOrigin);
|
||||
turnPoints[indexOfMovedPoint - 1] = toLatLng(computeBeforeNewOrigin(indexOfMovedPoint, newOrigin));
|
||||
turnPoints[indexOfMovedPoint + 1] = toLatLng(computeAfterNewOrigin(indexOfMovedPoint, newOrigin));
|
||||
}
|
||||
@@ -148,87 +161,58 @@ public class PathPolyline {
|
||||
|
||||
LatLng newOriginLng = turnPoints[indexOfMovedPoint];
|
||||
|
||||
// start of "cut the triangles" fix
|
||||
if (noOfPoints > 3) {
|
||||
LatLng first = null;
|
||||
LatLng second = null;
|
||||
LatLng third = null;
|
||||
LatLng fourth = null;
|
||||
if (FIX_CUT_SPIKES) {
|
||||
fix_cutSpikes(specialCase, indexOfMovedPoint);
|
||||
}
|
||||
|
||||
for (int index = 0; (index + 3) < noOfPoints; index++) {
|
||||
first = turnPoints[index];
|
||||
second = turnPoints[index + 1];
|
||||
third = turnPoints[index + 2];
|
||||
fourth = turnPoints[index + 3];
|
||||
|
||||
if (TwoDPoint.areIntersecting(toTwoDPoint(first), toTwoDPoint(second), toTwoDPoint(third), toTwoDPoint(fourth))) {
|
||||
|
||||
TwoDSegment firstSegment = new TwoDSegment(toTwoDPoint(first), toTwoDPoint(second));
|
||||
TwoDSegment secondSegment = new TwoDSegment(toTwoDPoint(third), toTwoDPoint(fourth));
|
||||
List<LatLng> newTurnPoints = new ArrayList<LatLng>();
|
||||
|
||||
for (int index2 = 0; index2 < noOfPoints; index2++) {
|
||||
if (index2 == index + 1) {
|
||||
continue;
|
||||
} else if (index2 == index + 2) {
|
||||
newTurnPoints.add(toLatLng(firstSegment.intersectionPointWith(secondSegment)));
|
||||
} else {
|
||||
newTurnPoints.add(LatLng.newInstance(turnPoints[index2].getLatitude(), turnPoints[index2].getLongitude()));
|
||||
}
|
||||
}
|
||||
|
||||
turnPoints = null;
|
||||
turnPoints = newTurnPoints.toArray(new LatLng[0]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (FIX_CUT_TRIANGLES) {
|
||||
fix_cutTriangles();
|
||||
}
|
||||
// end of "cut the triangles" fix
|
||||
|
||||
// start of "optimal towards wind" fix
|
||||
SimulatorWindDTO firstPoint = toSimulatorWindDTO(originalOriginLng);
|
||||
PositionDTO secondPoint = toPositionDTO(newOriginLng);
|
||||
|
||||
Request1TurnerDTO requestData = new Request1TurnerDTO(boatClassID, allPoints, firstPoint, secondPoint, USE_REAL_AVERAGE_WIND,
|
||||
STEP_DURATION_MILLISECONDS, !projectionOnBeforeLine);
|
||||
|
||||
simulatorService.get1Turner(requestData, new AsyncCallback<Response1TurnerDTO>() {
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable error) {
|
||||
System.out.println("eroare");
|
||||
errorReporter.reportError("Failed to initialize boat classes!\r\n" + error.getMessage());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Response1TurnerDTO receiveData) {
|
||||
System.out.println("succes");
|
||||
|
||||
SimulatorWindDTO oneTurner = receiveData.oneTurner;
|
||||
List<LatLng> newTurnPoints = new ArrayList<LatLng>();
|
||||
|
||||
for (int index2 = 0; index2 < noOfPoints; index2++) {
|
||||
if (index2 == indexOfMovedPoint) {
|
||||
if (projectionOnBeforeLine) {
|
||||
newTurnPoints.add(originalOriginLng);
|
||||
newTurnPoints.add(LatLng.newInstance(oneTurner.position.latDeg, oneTurner.position.lngDeg));
|
||||
newTurnPoints.add(turnPoints[index2]);
|
||||
} else {
|
||||
newTurnPoints.add(turnPoints[index2]);
|
||||
newTurnPoints.add(LatLng.newInstance(oneTurner.position.latDeg, oneTurner.position.lngDeg));
|
||||
newTurnPoints.add(originalOriginLng);
|
||||
}
|
||||
} else {
|
||||
newTurnPoints.add(turnPoints[index2]);
|
||||
}
|
||||
}
|
||||
|
||||
turnPoints = null;
|
||||
turnPoints = newTurnPoints.toArray(new LatLng[0]);
|
||||
}
|
||||
});
|
||||
// SimulatorWindDTO firstPoint = toSimulatorWindDTO(originalOriginLng);
|
||||
// PositionDTO secondPoint = toPositionDTO(newOriginLng);
|
||||
//
|
||||
// Request1TurnerDTO requestData = new Request1TurnerDTO(boatClassID, allPoints, firstPoint,
|
||||
// secondPoint, USE_REAL_AVERAGE_WIND,
|
||||
// STEP_DURATION_MILLISECONDS, !projectionOnBeforeLine);
|
||||
//
|
||||
// simulatorService.get1Turner(requestData, new AsyncCallback<Response1TurnerDTO>() {
|
||||
//
|
||||
// @Override
|
||||
// public void onFailure(Throwable error) {
|
||||
// System.out.println("eroare");
|
||||
// errorReporter.reportError("Failed to initialize boat classes!\r\n" + error.getMessage());
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onSuccess(Response1TurnerDTO receiveData) {
|
||||
// System.out.println("succes");
|
||||
//
|
||||
// SimulatorWindDTO oneTurner = receiveData.oneTurner;
|
||||
// List<LatLng> newTurnPoints = new ArrayList<LatLng>();
|
||||
//
|
||||
// for (int index2 = 0; index2 < noOfPoints; index2++) {
|
||||
// if (index2 == indexOfMovedPoint) {
|
||||
// if (projectionOnBeforeLine) {
|
||||
// newTurnPoints.add(originalOriginLng);
|
||||
// newTurnPoints.add(LatLng.newInstance(oneTurner.position.latDeg, oneTurner.position.lngDeg));
|
||||
// newTurnPoints.add(turnPoints[index2]);
|
||||
// } else {
|
||||
// newTurnPoints.add(turnPoints[index2]);
|
||||
// newTurnPoints.add(LatLng.newInstance(oneTurner.position.latDeg, oneTurner.position.lngDeg));
|
||||
// newTurnPoints.add(originalOriginLng);
|
||||
// }
|
||||
// } else {
|
||||
// newTurnPoints.add(turnPoints[index2]);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// turnPoints = null;
|
||||
// turnPoints = newTurnPoints.toArray(new LatLng[0]);
|
||||
// }
|
||||
// });
|
||||
// end of "optimal towards wind" fix
|
||||
}
|
||||
|
||||
@@ -244,6 +228,77 @@ public class PathPolyline {
|
||||
this.getTotalTime_new();
|
||||
}
|
||||
|
||||
private void fix_cutSpikes(SpecialCaseResult specialCase, int indexOfMovedPoint) {
|
||||
|
||||
int noOfPoints = this.turnPoints.length;
|
||||
|
||||
if (specialCase != null && specialCase.isSpecialCase) {
|
||||
List<LatLng> newTurnPoints = new ArrayList<LatLng>();
|
||||
|
||||
if (specialCase.isBefore) {
|
||||
for (int index2 = 0; index2 < noOfPoints; index2++) {
|
||||
if (index2 == indexOfMovedPoint + 1) {
|
||||
continue;
|
||||
} else {
|
||||
newTurnPoints.add(LatLng.newInstance(this.turnPoints[index2].getLatitude(), this.turnPoints[index2].getLongitude()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int index2 = 0; index2 < noOfPoints; index2++) {
|
||||
if (index2 == indexOfMovedPoint - 1) {
|
||||
continue;
|
||||
} else {
|
||||
newTurnPoints.add(LatLng.newInstance(this.turnPoints[index2].getLatitude(), this.turnPoints[index2].getLongitude()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.turnPoints[indexOfMovedPoint] = toLatLng(specialCase.intersection);
|
||||
|
||||
this.turnPoints = null;
|
||||
this.turnPoints = newTurnPoints.toArray(new LatLng[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void fix_cutTriangles() {
|
||||
int noOfPoints = this.turnPoints.length;
|
||||
if (noOfPoints > 3) {
|
||||
LatLng first = null;
|
||||
LatLng second = null;
|
||||
LatLng third = null;
|
||||
LatLng fourth = null;
|
||||
|
||||
for (int index = 0; (index + 3) < noOfPoints; index++) {
|
||||
first = this.turnPoints[index];
|
||||
second = this.turnPoints[index + 1];
|
||||
third = this.turnPoints[index + 2];
|
||||
fourth = this.turnPoints[index + 3];
|
||||
|
||||
if (TwoDPoint.areIntersecting(toTwoDPoint(first), toTwoDPoint(second), toTwoDPoint(third), toTwoDPoint(fourth))) {
|
||||
|
||||
TwoDSegment firstSegment = new TwoDSegment(toTwoDPoint(first), toTwoDPoint(second));
|
||||
TwoDSegment secondSegment = new TwoDSegment(toTwoDPoint(third), toTwoDPoint(fourth));
|
||||
List<LatLng> newTurnPoints = new ArrayList<LatLng>();
|
||||
|
||||
for (int index2 = 0; index2 < noOfPoints; index2++) {
|
||||
if (index2 == index + 1) {
|
||||
continue;
|
||||
} else if (index2 == index + 2) {
|
||||
newTurnPoints.add(toLatLng(firstSegment.intersectionPointWith(secondSegment)));
|
||||
} else {
|
||||
newTurnPoints.add(LatLng.newInstance(this.turnPoints[index2].getLatitude(), this.turnPoints[index2].getLongitude()));
|
||||
}
|
||||
}
|
||||
|
||||
this.turnPoints = null;
|
||||
this.turnPoints = newTurnPoints.toArray(new LatLng[0]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SimulatorWindDTO toSimulatorWindDTO(LatLng latLng) {
|
||||
|
||||
int index = 0;
|
||||
@@ -260,6 +315,51 @@ public class PathPolyline {
|
||||
return temp;
|
||||
}
|
||||
|
||||
private class SpecialCaseResult {
|
||||
boolean isSpecialCase = false;
|
||||
TwoDPoint intersection = null;
|
||||
boolean isBefore = false;
|
||||
}
|
||||
|
||||
private SpecialCaseResult isSepecialCase(int indexOfMovedPoint, TwoDPoint newC) {
|
||||
|
||||
TwoDPoint A = toTwoDPoint(this.turnPoints[indexOfMovedPoint - 2]);
|
||||
TwoDPoint B = toTwoDPoint(this.turnPoints[indexOfMovedPoint - 1]);
|
||||
TwoDPoint C = toTwoDPoint(this.turnPoints[indexOfMovedPoint]);
|
||||
TwoDPoint D = toTwoDPoint(this.turnPoints[indexOfMovedPoint + 1]);
|
||||
TwoDPoint E = toTwoDPoint(this.turnPoints[indexOfMovedPoint + 2]);
|
||||
|
||||
TwoDSegment BC = new TwoDSegment(B, C);
|
||||
TwoDSegment DE = new TwoDSegment(D, E);
|
||||
TwoDPoint intersection_BC_and_DE = TwoDSegment.getIntersection(BC, DE);
|
||||
|
||||
TwoDSegment AB = new TwoDSegment(A, B);
|
||||
TwoDSegment CD = new TwoDSegment(C, D);
|
||||
TwoDPoint intersection_AB_and_CD = TwoDSegment.getIntersection(AB, CD);
|
||||
|
||||
SpecialCaseResult result = new SpecialCaseResult();
|
||||
|
||||
if (BC.contains(intersection_BC_and_DE)) {
|
||||
TwoDSegment firstPartOfBC = new TwoDSegment(B, intersection_BC_and_DE);
|
||||
if (firstPartOfBC.contains(newC)) {
|
||||
result.isSpecialCase = true;
|
||||
result.isBefore = true;
|
||||
result.intersection = intersection_BC_and_DE;
|
||||
return result;
|
||||
}
|
||||
} else if (CD.contains(intersection_AB_and_CD)) {
|
||||
TwoDSegment secondPartOfCD = new TwoDSegment(intersection_AB_and_CD, D);
|
||||
if (secondPartOfCD.contains(newC)) {
|
||||
result.isSpecialCase = true;
|
||||
result.isBefore = false;
|
||||
result.intersection = intersection_AB_and_CD;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private TwoDPoint computeNewOrigin(int indexOfMovedPoint) {
|
||||
|
||||
double distance = (map.getZoomLevel() - 11) * DEFAULT_DISTANCE_PX;
|
||||
|
||||
Reference in new issue
Block a user