mirror of https://github.com/apache/lucene.git
Refactor and make hierarchical GeoStandardPath. Some tests fail and will need to be researched further.
This commit is contained in:
parent
7b7cb396e5
commit
fc7ce76851
|
@ -179,6 +179,7 @@ class GeoStandardPath extends GeoBasePath {
|
|||
final GeoPoint point = points.get(0);
|
||||
|
||||
// Construct normal plane
|
||||
// TBD - what does this actually do? Why Z??
|
||||
final Plane normalPlane = Plane.constructNormalizedZPlane(upperPoint, lowerPoint, point);
|
||||
|
||||
final CircleSegmentEndpoint onlyEndpoint =
|
||||
|
@ -188,48 +189,53 @@ class GeoStandardPath extends GeoBasePath {
|
|||
new GeoPoint[] {
|
||||
onlyEndpoint.circlePlane.getSampleIntersectionPoint(planetModel, normalPlane)
|
||||
};
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Create segment endpoints. Use an appropriate constructor for the start and end of the
|
||||
// path.
|
||||
for (int i = 0; i < segments.size(); i++) {
|
||||
final PathSegment currentSegment = segments.get(i);
|
||||
|
||||
// Create segment endpoints. Use an appropriate constructor for the start and end of the path.
|
||||
for (int i = 0; i < segments.size(); i++) {
|
||||
final PathSegment currentSegment = segments.get(i);
|
||||
if (i == 0) {
|
||||
// Starting endpoint
|
||||
final SegmentEndpoint startEndpoint =
|
||||
new CutoffSingleCircleSegmentEndpoint(
|
||||
planetModel,
|
||||
null,
|
||||
currentSegment.start,
|
||||
currentSegment.startCutoffPlane,
|
||||
currentSegment.ULHC,
|
||||
currentSegment.LLHC);
|
||||
endPoints.add(startEndpoint);
|
||||
this.edgePoints = new GeoPoint[] {currentSegment.ULHC};
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
// Starting endpoint
|
||||
final SegmentEndpoint startEndpoint =
|
||||
new CutoffSingleCircleSegmentEndpoint(
|
||||
planetModel,
|
||||
null,
|
||||
currentSegment.start,
|
||||
currentSegment.startCutoffPlane,
|
||||
currentSegment.ULHC,
|
||||
currentSegment.LLHC);
|
||||
endPoints.add(startEndpoint);
|
||||
this.edgePoints = new GeoPoint[] {currentSegment.ULHC};
|
||||
continue;
|
||||
}
|
||||
|
||||
// General intersection case
|
||||
final PathSegment prevSegment = segments.get(i - 1);
|
||||
if (prevSegment.endCutoffPlane.isWithin(currentSegment.ULHC)
|
||||
&& prevSegment.endCutoffPlane.isWithin(currentSegment.LLHC)
|
||||
&& currentSegment.startCutoffPlane.isWithin(prevSegment.URHC)
|
||||
&& currentSegment.startCutoffPlane.isWithin(prevSegment.LRHC)) {
|
||||
// The planes are identical. We wouldn't need a circle at all except for the possibility of
|
||||
// backing up, which is hard to detect here.
|
||||
final SegmentEndpoint midEndpoint =
|
||||
new CutoffSingleCircleSegmentEndpoint(
|
||||
planetModel,
|
||||
prevSegment,
|
||||
currentSegment.start,
|
||||
prevSegment.endCutoffPlane,
|
||||
currentSegment.startCutoffPlane,
|
||||
currentSegment.ULHC,
|
||||
currentSegment.LLHC);
|
||||
// don't need a circle at all. Special constructor...
|
||||
endPoints.add(midEndpoint);
|
||||
} else {
|
||||
// General intersection case.
|
||||
// The CutoffDualCircleSegmentEndpoint is advanced enough now to handle
|
||||
// joinings that are perfectly in a line, I believe, so I am disabling
|
||||
// the special code for that situation. TBD (and testing) to remove it.
|
||||
final PathSegment prevSegment = segments.get(i - 1);
|
||||
/*
|
||||
if (prevSegment.endCutoffPlane.isWithin(currentSegment.ULHC)
|
||||
&& prevSegment.endCutoffPlane.isWithin(currentSegment.LLHC)
|
||||
&& currentSegment.startCutoffPlane.isWithin(prevSegment.URHC)
|
||||
&& currentSegment.startCutoffPlane.isWithin(prevSegment.LRHC)) {
|
||||
// The planes are identical. We wouldn't need a circle at all except for the possibility
|
||||
// of
|
||||
// backing up, which is hard to detect here.
|
||||
final SegmentEndpoint midEndpoint =
|
||||
new CutoffSingleCircleSegmentEndpoint(
|
||||
planetModel,
|
||||
prevSegment,
|
||||
currentSegment.start,
|
||||
prevSegment.endCutoffPlane,
|
||||
currentSegment.startCutoffPlane,
|
||||
currentSegment.ULHC,
|
||||
currentSegment.LLHC);
|
||||
// don't need a circle at all. Special constructor...
|
||||
endPoints.add(midEndpoint);
|
||||
} else {
|
||||
*/
|
||||
endPoints.add(
|
||||
new CutoffDualCircleSegmentEndpoint(
|
||||
planetModel,
|
||||
|
@ -241,29 +247,32 @@ class GeoStandardPath extends GeoBasePath {
|
|||
prevSegment.LRHC,
|
||||
currentSegment.ULHC,
|
||||
currentSegment.LLHC));
|
||||
// }
|
||||
}
|
||||
// Do final endpoint
|
||||
final PathSegment lastSegment = segments.get(segments.size() - 1);
|
||||
endPoints.add(
|
||||
new CutoffSingleCircleSegmentEndpoint(
|
||||
planetModel,
|
||||
lastSegment,
|
||||
lastSegment.end,
|
||||
lastSegment.endCutoffPlane,
|
||||
lastSegment.URHC,
|
||||
lastSegment.LRHC));
|
||||
}
|
||||
// Do final endpoint
|
||||
final PathSegment lastSegment = segments.get(segments.size() - 1);
|
||||
endPoints.add(
|
||||
new CutoffSingleCircleSegmentEndpoint(
|
||||
planetModel,
|
||||
lastSegment,
|
||||
lastSegment.end,
|
||||
lastSegment.endCutoffPlane,
|
||||
lastSegment.URHC,
|
||||
lastSegment.LRHC));
|
||||
|
||||
final TreeBuilder treeBuilder = new TreeBuilder(segments.size() + endPoints.size());
|
||||
// Segments will have one less than the number of endpoints.
|
||||
// So, we add the first endpoint, and then do it pairwise.
|
||||
treeBuilder.addComponent(segments.get(0));
|
||||
treeBuilder.addComponent(endPoints.get(0));
|
||||
for (int i = 0; i < segments.size(); i++) {
|
||||
treeBuilder.addComponent(segments.get(i));
|
||||
treeBuilder.addComponent(endPoints.get(i + 1));
|
||||
}
|
||||
|
||||
rootComponent = treeBuilder.getRoot();
|
||||
|
||||
// System.out.println("Root component: "+rootComponent);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -289,134 +298,38 @@ class GeoStandardPath extends GeoBasePath {
|
|||
@Override
|
||||
public double computePathCenterDistance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
// Walk along path and keep track of the closest distance we find
|
||||
double closestDistance = Double.POSITIVE_INFINITY;
|
||||
// Segments first
|
||||
for (PathSegment segment : segments) {
|
||||
final double segmentDistance = segment.pathCenterDistance(distanceStyle, x, y, z);
|
||||
if (segmentDistance < closestDistance) {
|
||||
closestDistance = segmentDistance;
|
||||
}
|
||||
if (rootComponent == null) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
// Now, endpoints
|
||||
for (SegmentEndpoint endpoint : endPoints) {
|
||||
final double endpointDistance = endpoint.pathCenterDistance(distanceStyle, x, y, z);
|
||||
if (endpointDistance < closestDistance) {
|
||||
closestDistance = endpointDistance;
|
||||
}
|
||||
}
|
||||
return closestDistance;
|
||||
return rootComponent.pathCenterDistance(distanceStyle, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double computeNearestDistance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
double currentDistance = 0.0;
|
||||
double minPathCenterDistance = Double.POSITIVE_INFINITY;
|
||||
double bestDistance = Double.POSITIVE_INFINITY;
|
||||
int segmentIndex = 0;
|
||||
|
||||
for (final SegmentEndpoint endpoint : endPoints) {
|
||||
final double endpointPathCenterDistance = endpoint.pathCenterDistance(distanceStyle, x, y, z);
|
||||
if (endpointPathCenterDistance < minPathCenterDistance) {
|
||||
// Use this endpoint
|
||||
minPathCenterDistance = endpointPathCenterDistance;
|
||||
bestDistance = currentDistance;
|
||||
}
|
||||
// Look at the following segment, if any
|
||||
if (segmentIndex < segments.size()) {
|
||||
final PathSegment segment = segments.get(segmentIndex++);
|
||||
final double segmentPathCenterDistance = segment.pathCenterDistance(distanceStyle, x, y, z);
|
||||
if (segmentPathCenterDistance < minPathCenterDistance) {
|
||||
minPathCenterDistance = segmentPathCenterDistance;
|
||||
bestDistance =
|
||||
distanceStyle.aggregateDistances(
|
||||
currentDistance, segment.nearestPathDistance(distanceStyle, x, y, z));
|
||||
}
|
||||
currentDistance =
|
||||
distanceStyle.aggregateDistances(
|
||||
currentDistance, segment.fullPathDistance(distanceStyle));
|
||||
}
|
||||
if (rootComponent == null) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
return bestDistance;
|
||||
return rootComponent.nearestDistance(distanceStyle, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double distance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
// Algorithm:
|
||||
// (1) If the point is within any of the segments along the path, return that value.
|
||||
// (2) If the point is within any of the segment end circles along the path, return that value.
|
||||
// The algorithm loops over the whole path to get the shortest distance
|
||||
double bestDistance = Double.POSITIVE_INFINITY;
|
||||
|
||||
double currentDistance = 0.0;
|
||||
for (final PathSegment segment : segments) {
|
||||
double distance = segment.pathDistance(distanceStyle, x, y, z);
|
||||
if (distance != Double.POSITIVE_INFINITY) {
|
||||
final double thisDistance =
|
||||
distanceStyle.fromAggregationForm(
|
||||
distanceStyle.aggregateDistances(currentDistance, distance));
|
||||
if (thisDistance < bestDistance) {
|
||||
bestDistance = thisDistance;
|
||||
}
|
||||
}
|
||||
currentDistance =
|
||||
distanceStyle.aggregateDistances(
|
||||
currentDistance, segment.fullPathDistance(distanceStyle));
|
||||
if (rootComponent == null) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
int segmentIndex = 0;
|
||||
currentDistance = 0.0;
|
||||
for (final SegmentEndpoint endpoint : endPoints) {
|
||||
double distance = endpoint.pathDistance(distanceStyle, x, y, z);
|
||||
if (distance != Double.POSITIVE_INFINITY) {
|
||||
final double thisDistance =
|
||||
distanceStyle.fromAggregationForm(
|
||||
distanceStyle.aggregateDistances(currentDistance, distance));
|
||||
if (thisDistance < bestDistance) {
|
||||
bestDistance = thisDistance;
|
||||
}
|
||||
}
|
||||
if (segmentIndex < segments.size())
|
||||
currentDistance =
|
||||
distanceStyle.aggregateDistances(
|
||||
currentDistance, segments.get(segmentIndex++).fullPathDistance(distanceStyle));
|
||||
}
|
||||
|
||||
return bestDistance;
|
||||
return rootComponent.distance(distanceStyle, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double deltaDistance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
// Algorithm:
|
||||
// (1) If the point is within any of the segments along the path, return that value.
|
||||
// (2) If the point is within any of the segment end circles along the path, return that value.
|
||||
// Finds best distance
|
||||
double bestDistance = Double.POSITIVE_INFINITY;
|
||||
|
||||
for (final PathSegment segment : segments) {
|
||||
final double distance = segment.pathDeltaDistance(distanceStyle, x, y, z);
|
||||
if (distance != Double.POSITIVE_INFINITY) {
|
||||
final double thisDistance = distanceStyle.fromAggregationForm(distance);
|
||||
if (thisDistance < bestDistance) {
|
||||
bestDistance = thisDistance;
|
||||
}
|
||||
}
|
||||
if (rootComponent == null) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
for (final SegmentEndpoint endpoint : endPoints) {
|
||||
final double distance = endpoint.pathDeltaDistance(distanceStyle, x, y, z);
|
||||
if (distance != Double.POSITIVE_INFINITY) {
|
||||
final double thisDistance = distanceStyle.fromAggregationForm(distance);
|
||||
if (thisDistance < bestDistance) {
|
||||
bestDistance = thisDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestDistance;
|
||||
return distanceStyle.fromAggregationForm(
|
||||
rootComponent.pathDeltaDistance(distanceStyle, x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -429,35 +342,18 @@ class GeoStandardPath extends GeoBasePath {
|
|||
@Override
|
||||
protected double outsideDistance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
double minDistance = Double.POSITIVE_INFINITY;
|
||||
for (final SegmentEndpoint endpoint : endPoints) {
|
||||
final double newDistance = endpoint.outsideDistance(distanceStyle, x, y, z);
|
||||
if (newDistance < minDistance) {
|
||||
minDistance = newDistance;
|
||||
}
|
||||
if (rootComponent == null) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
for (final PathSegment segment : segments) {
|
||||
final double newDistance = segment.outsideDistance(distanceStyle, x, y, z);
|
||||
if (newDistance < minDistance) {
|
||||
minDistance = newDistance;
|
||||
}
|
||||
}
|
||||
return minDistance;
|
||||
return rootComponent.outsideDistance(distanceStyle, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(final double x, final double y, final double z) {
|
||||
for (SegmentEndpoint pathPoint : endPoints) {
|
||||
if (pathPoint.isWithin(x, y, z)) {
|
||||
return true;
|
||||
}
|
||||
if (rootComponent == null) {
|
||||
return false;
|
||||
}
|
||||
for (PathSegment pathSegment : segments) {
|
||||
if (pathSegment.isWithin(x, y, z)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return rootComponent.isWithin(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -478,49 +374,25 @@ class GeoStandardPath extends GeoBasePath {
|
|||
// Well, sort of. We can detect intersections also due to overlap of segments with each other.
|
||||
// But that's an edge case and we won't be optimizing for it.
|
||||
// System.err.println(" Looking for intersection of plane " + plane + " with path " + this);
|
||||
for (final SegmentEndpoint pathPoint : endPoints) {
|
||||
if (pathPoint.intersects(plane, notablePoints, bounds)) {
|
||||
return true;
|
||||
}
|
||||
if (rootComponent == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (final PathSegment pathSegment : segments) {
|
||||
if (pathSegment.intersects(plane, notablePoints, bounds)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return rootComponent.intersects(plane, notablePoints, bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(GeoShape geoShape) {
|
||||
for (final SegmentEndpoint pathPoint : endPoints) {
|
||||
if (pathPoint.intersects(geoShape)) {
|
||||
return true;
|
||||
}
|
||||
if (rootComponent == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (final PathSegment pathSegment : segments) {
|
||||
if (pathSegment.intersects(geoShape)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return rootComponent.intersects(geoShape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getBounds(Bounds bounds) {
|
||||
super.getBounds(bounds);
|
||||
// For building bounds, order matters. We want to traverse
|
||||
// never more than 180 degrees longitude at a pop or we risk having the
|
||||
// bounds object get itself inverted. So do the edges first.
|
||||
for (PathSegment pathSegment : segments) {
|
||||
pathSegment.getBounds(bounds);
|
||||
}
|
||||
for (SegmentEndpoint pathPoint : endPoints) {
|
||||
pathPoint.getBounds(bounds);
|
||||
if (rootComponent != null) {
|
||||
rootComponent.getBounds(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -600,6 +472,33 @@ class GeoStandardPath extends GeoBasePath {
|
|||
*/
|
||||
double fullPathDistance(final DistanceStyle distanceStyle);
|
||||
|
||||
/**
|
||||
* Compute distance measure starting from beginning of the path and including perpendicular
|
||||
* dog-leg to a point within the corridor.
|
||||
*
|
||||
* @param distanceStyle is the distance style
|
||||
* @param x is the x coordinate of the point we want to get the distance to
|
||||
* @param y is the y coordinate of the point we want to get the distance to
|
||||
* @param z is the z coordinate of the point we want to get the distance to
|
||||
* @return the distance from start of path
|
||||
*/
|
||||
double distance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z);
|
||||
|
||||
/**
|
||||
* Compute distance starting from the beginning of the path all along the center of the
|
||||
* corridor, and then for the last section to a point perpendicular to mentioned point, unless
|
||||
* that point is outside of the corridor.
|
||||
*
|
||||
* @param distanceStyle is the distance style
|
||||
* @param x is the x coordinate of the point we want to get the distance to
|
||||
* @param y is the y coordinate of the point we want to get the distance to
|
||||
* @param z is the z coordinate of the point we want to get the distance to
|
||||
* @return the distance from start of path
|
||||
*/
|
||||
double nearestDistance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z);
|
||||
|
||||
/**
|
||||
* Compute path distance.
|
||||
*
|
||||
|
@ -638,7 +537,8 @@ class GeoStandardPath extends GeoBasePath {
|
|||
final DistanceStyle distanceStyle, final double x, final double y, final double z);
|
||||
|
||||
/**
|
||||
* Compute path center distance.
|
||||
* Compute path center distance. Returns POSITIVE_INFINITY if the point is outside of the
|
||||
* bounds.
|
||||
*
|
||||
* @param distanceStyle is the distance style.
|
||||
* @param x is the point x.
|
||||
|
@ -700,6 +600,8 @@ class GeoStandardPath extends GeoBasePath {
|
|||
bounds = new XYZBounds();
|
||||
child1.getBounds(bounds);
|
||||
child2.getBounds(bounds);
|
||||
// System.out.println("Constructed PathNode with child1="+child1+" and child2="+child2+" with
|
||||
// computed bounds "+bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -711,15 +613,27 @@ class GeoStandardPath extends GeoBasePath {
|
|||
public boolean isWithin(final double x, final double y, final double z) {
|
||||
// We computed the bounds for the node already, so use that as an "early-out".
|
||||
// If we don't leave early, we need to check both children.
|
||||
if (x < bounds.getMinimumX() || x > bounds.getMaximumX()) {
|
||||
return false;
|
||||
}
|
||||
if (y < bounds.getMinimumY() || y > bounds.getMaximumY()) {
|
||||
return false;
|
||||
}
|
||||
if (z < bounds.getMinimumZ() || z > bounds.getMaximumZ()) {
|
||||
if (x < bounds.getMinimumX()
|
||||
|| x > bounds.getMaximumX()
|
||||
|| y < bounds.getMinimumY()
|
||||
|| y > bounds.getMaximumY()
|
||||
|| z < bounds.getMinimumZ()
|
||||
|| z > bounds.getMaximumZ()) {
|
||||
// Debugging: check that this really is true.
|
||||
/*
|
||||
if (child1.isWithin(x, y, z) || child2.isWithin(x, y, z)) {
|
||||
System.out.println("XYZBounds of PathNode inconsistent with isWithin of children! XYZBounds="+bounds+" child1="+child1+" child2="+child2+" Point=["+x+", "+y+", "+z+"]");
|
||||
XYZBounds ch1Bounds = new XYZBounds();
|
||||
child1.getBounds(ch1Bounds);
|
||||
XYZBounds ch2Bounds = new XYZBounds();
|
||||
child2.getBounds(ch2Bounds);
|
||||
System.out.println("Child1: Bounds="+ch1Bounds+" isWithin="+child1.isWithin(x,y,z));
|
||||
System.out.println("Child2: Bounds="+ch2Bounds+" isWithin="+child2.isWithin(x,y,z));
|
||||
}
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
return child1.isWithin(x, y, z) || child2.isWithin(x, y, z);
|
||||
}
|
||||
|
||||
|
@ -728,6 +642,30 @@ class GeoStandardPath extends GeoBasePath {
|
|||
return child1.getStartingDistance(distanceStyle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double distance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
if (!isWithin(x, y, z)) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
final double child1Distance = child1.distance(distanceStyle, x, y, z);
|
||||
final double child2Distance = child2.distance(distanceStyle, x, y, z);
|
||||
// System.out.println("In PathNode.distance(), returning child1 distance = "+child1Distance+"
|
||||
// and child2 distance = "+child2Distance);
|
||||
return Math.min(child1Distance, child2Distance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nearestDistance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
if (!isWithin(x, y, z)) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
return Math.min(
|
||||
child1.nearestDistance(distanceStyle, x, y, z),
|
||||
child2.nearestDistance(distanceStyle, x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double fullPathDistance(final DistanceStyle distanceStyle) {
|
||||
return distanceStyle.aggregateDistances(
|
||||
|
@ -809,6 +747,11 @@ class GeoStandardPath extends GeoBasePath {
|
|||
child2.getBounds(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PathNode (" + child1 + ") (" + child2 + ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -827,9 +770,7 @@ class GeoStandardPath extends GeoBasePath {
|
|||
private interface SegmentEndpoint extends PathComponent {}
|
||||
|
||||
/** Base implementation of SegmentEndpoint */
|
||||
private static class BaseSegmentEndpoint implements SegmentEndpoint {
|
||||
/** The planet model */
|
||||
protected final PlanetModel planetModel;
|
||||
private static class BaseSegmentEndpoint extends GeoBaseBounds implements SegmentEndpoint {
|
||||
/** The previous path element */
|
||||
protected final PathComponent previous;
|
||||
/** The center point of the endpoint */
|
||||
|
@ -839,7 +780,7 @@ class GeoStandardPath extends GeoBasePath {
|
|||
|
||||
public BaseSegmentEndpoint(
|
||||
final PlanetModel planetModel, final PathComponent previous, final GeoPoint point) {
|
||||
this.planetModel = planetModel;
|
||||
super(planetModel);
|
||||
this.previous = previous;
|
||||
this.point = point;
|
||||
}
|
||||
|
@ -857,14 +798,36 @@ class GeoStandardPath extends GeoBasePath {
|
|||
@Override
|
||||
public double getStartingDistance(DistanceStyle distanceStyle) {
|
||||
if (previous == null) {
|
||||
return 0.0;
|
||||
return distanceStyle.toAggregationForm(0.0);
|
||||
}
|
||||
return previous.getStartingDistance(distanceStyle);
|
||||
return distanceStyle.aggregateDistances(
|
||||
previous.getStartingDistance(distanceStyle), previous.fullPathDistance(distanceStyle));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double distance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
if (!isWithin(x, y, z)) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
final double startingDistance = getStartingDistance(distanceStyle);
|
||||
final double pathDistance = pathDistance(distanceStyle, x, y, z);
|
||||
return distanceStyle.fromAggregationForm(
|
||||
distanceStyle.aggregateDistances(startingDistance, pathDistance));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nearestDistance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
if (!isWithin(x, y, z)) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
return distanceStyle.fromAggregationForm(getStartingDistance(distanceStyle));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double fullPathDistance(final DistanceStyle distanceStyle) {
|
||||
return 0.0;
|
||||
return distanceStyle.toAggregationForm(0.0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -890,12 +853,18 @@ class GeoStandardPath extends GeoBasePath {
|
|||
@Override
|
||||
public double nearestPathDistance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
if (!isWithin(x, y, z)) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
return distanceStyle.toAggregationForm(0.0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double pathCenterDistance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
if (!isWithin(x, y, z)) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
return distanceStyle.computeDistance(this.point, x, y, z);
|
||||
}
|
||||
|
||||
|
@ -918,6 +887,7 @@ class GeoStandardPath extends GeoBasePath {
|
|||
|
||||
@Override
|
||||
public void getBounds(final Bounds bounds) {
|
||||
super.getBounds(bounds);
|
||||
bounds.addPoint(point);
|
||||
}
|
||||
|
||||
|
@ -937,7 +907,7 @@ class GeoStandardPath extends GeoBasePath {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return point.toString();
|
||||
return "SegmentEndpoint (" + point + ")";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -948,6 +918,21 @@ class GeoStandardPath extends GeoBasePath {
|
|||
/** No notable points from the circle itself */
|
||||
protected static final GeoPoint[] circlePoints = new GeoPoint[0];
|
||||
|
||||
public CircleSegmentEndpoint(
|
||||
final PlanetModel planetModel,
|
||||
final PathComponent previous,
|
||||
final GeoPoint point,
|
||||
final GeoPoint upperPoint,
|
||||
final GeoPoint lowerPoint) {
|
||||
super(planetModel, previous, point);
|
||||
circlePlane = SidedPlane.constructSidedPlaneFromTwoPoints(point, upperPoint, lowerPoint);
|
||||
}
|
||||
|
||||
// Note: we need a method of constructing a plane as follows:
|
||||
// (1) We start with two points (edge points of the adjoining segment)
|
||||
// (2) We construct a plane with those two points through the center of the earth
|
||||
// (3) We construct a plane perpendicular to the first plane that goes through the two points.
|
||||
// TBD
|
||||
/**
|
||||
* Constructor for case (1). Generate a simple circle cutoff plane.
|
||||
*
|
||||
|
@ -1012,6 +997,11 @@ class GeoStandardPath extends GeoBasePath {
|
|||
super.getBounds(bounds);
|
||||
bounds.addPlane(planetModel, circlePlane);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CircleSegmentEndpoint: " + super.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/** Endpoint that's a single circle with cutoff(s). */
|
||||
|
@ -1022,6 +1012,8 @@ class GeoStandardPath extends GeoBasePath {
|
|||
/** Notable points for this segment endpoint */
|
||||
private final GeoPoint[] notablePoints;
|
||||
|
||||
private final SidedPlane cutoffPlane;
|
||||
|
||||
/**
|
||||
* Constructor for case (2). Generate an endpoint, given a single cutoff plane plus upper and
|
||||
* lower edge points.
|
||||
|
@ -1041,83 +1033,20 @@ class GeoStandardPath extends GeoBasePath {
|
|||
final SidedPlane cutoffPlane,
|
||||
final GeoPoint topEdgePoint,
|
||||
final GeoPoint bottomEdgePoint) {
|
||||
super(planetModel, previous, point, cutoffPlane, topEdgePoint, bottomEdgePoint);
|
||||
this.cutoffPlanes = new Membership[] {new SidedPlane(cutoffPlane)};
|
||||
this.notablePoints = new GeoPoint[] {topEdgePoint, bottomEdgePoint};
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for case (2.5). Generate an endpoint, given two cutoff planes plus upper and
|
||||
* lower edge points.
|
||||
*
|
||||
* @param planetModel is the planet model.
|
||||
* @param point is the center.
|
||||
* @param cutoffPlane1 is one adjoining path segment cutoff plane.
|
||||
* @param cutoffPlane2 is another adjoining path segment cutoff plane.
|
||||
* @param topEdgePoint is a point on the cutoffPlane that should be also on the circle plane.
|
||||
* @param bottomEdgePoint is another point on the cutoffPlane that should be also on the circle
|
||||
* plane.
|
||||
*/
|
||||
public CutoffSingleCircleSegmentEndpoint(
|
||||
final PlanetModel planetModel,
|
||||
final PathComponent previous,
|
||||
final GeoPoint point,
|
||||
final SidedPlane cutoffPlane1,
|
||||
final SidedPlane cutoffPlane2,
|
||||
final GeoPoint topEdgePoint,
|
||||
final GeoPoint bottomEdgePoint) {
|
||||
super(planetModel, previous, point, cutoffPlane1, topEdgePoint, bottomEdgePoint);
|
||||
this.cutoffPlanes =
|
||||
new Membership[] {new SidedPlane(cutoffPlane1), new SidedPlane(cutoffPlane2)};
|
||||
super(planetModel, previous, point, topEdgePoint, bottomEdgePoint);
|
||||
this.cutoffPlane = new SidedPlane(cutoffPlane);
|
||||
this.cutoffPlanes = new Membership[] {cutoffPlane};
|
||||
this.notablePoints = new GeoPoint[] {topEdgePoint, bottomEdgePoint};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(final Vector point) {
|
||||
if (!super.isWithin(point)) {
|
||||
return false;
|
||||
}
|
||||
for (final Membership m : cutoffPlanes) {
|
||||
if (!m.isWithin(point)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return cutoffPlane.isWithin(point) && super.isWithin(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(final double x, final double y, final double z) {
|
||||
if (!super.isWithin(x, y, z)) {
|
||||
return false;
|
||||
}
|
||||
for (final Membership m : cutoffPlanes) {
|
||||
if (!m.isWithin(x, y, z)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nearestPathDistance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
for (final Membership m : cutoffPlanes) {
|
||||
if (!m.isWithin(x, y, z)) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
}
|
||||
return super.nearestPathDistance(distanceStyle, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double pathCenterDistance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
for (final Membership m : cutoffPlanes) {
|
||||
if (!m.isWithin(x, y, z)) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
}
|
||||
return super.pathCenterDistance(distanceStyle, x, y, z);
|
||||
return cutoffPlane.isWithin(x, y, z) && super.isWithin(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1131,16 +1060,30 @@ class GeoStandardPath extends GeoBasePath {
|
|||
public boolean intersects(final GeoShape geoShape) {
|
||||
return geoShape.intersects(circlePlane, this.notablePoints, this.cutoffPlanes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getBounds(final Bounds bounds) {
|
||||
super.getBounds(bounds);
|
||||
bounds.addPlane(planetModel, circlePlane, cutoffPlane);
|
||||
bounds.addPlane(planetModel, cutoffPlane, circlePlane);
|
||||
bounds.addIntersection(planetModel, circlePlane, cutoffPlane);
|
||||
bounds.addIntersection(planetModel, cutoffPlane, circlePlane);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CutoffSingleCircleSegmentEndpoint: " + super.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint that's a dual circle with cutoff(s). This SegmentEndpoint is used when we have two
|
||||
* adjoining segments that are not colinear, and when we are on a non-spherical world. (1) We
|
||||
* construct two circles. Each circle uses the two segment endpoints for one of the two segments,
|
||||
* plus the one segment endpoint that is on the other side of the segment's cutoff plane. (2)
|
||||
* isWithin() is computed using both circles, using just the portion that is within both segments'
|
||||
* cutoff planes. If either matches, the point is included. (3) intersects() is computed using
|
||||
* both circles, with similar cutoffs. (4) bounds() uses both circles too.
|
||||
* adjoining segments. (1) We construct two circles. Each circle uses the two segment endpoints
|
||||
* for one of the two segments, plus the one segment endpoint that is on the other side of the
|
||||
* segment's cutoff plane. (2) isWithin() is computed using both circles, using just the portion
|
||||
* that is within both segments' cutoff planes. If either matches, the point is included. (3)
|
||||
* intersects() is computed using both circles, with similar cutoffs. (4) bounds() uses both
|
||||
* circles too.
|
||||
*/
|
||||
private static class CutoffDualCircleSegmentEndpoint extends BaseSegmentEndpoint {
|
||||
|
||||
|
@ -1155,6 +1098,9 @@ class GeoStandardPath extends GeoBasePath {
|
|||
/** Both cutoff planes are included here */
|
||||
protected final Membership[] cutoffPlanes;
|
||||
|
||||
protected final SidedPlane boundaryPlane1;
|
||||
protected final SidedPlane boundaryPlane2;
|
||||
|
||||
public CutoffDualCircleSegmentEndpoint(
|
||||
final PlanetModel planetModel,
|
||||
final PathComponent previous,
|
||||
|
@ -1180,8 +1126,8 @@ class GeoStandardPath extends GeoBasePath {
|
|||
point, prevURHC, prevLRHC, currentLLHC);
|
||||
notablePoints1 = new GeoPoint[] {prevURHC, prevLRHC, currentLLHC};
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Constructing CutoffDualCircleSegmentEndpoint with colinear segments");
|
||||
circlePlane1 = SidedPlane.constructSidedPlaneFromTwoPoints(point, prevURHC, prevLRHC);
|
||||
notablePoints1 = new GeoPoint[] {prevURHC, prevLRHC};
|
||||
}
|
||||
// Second plane consists of current endpoints plus one of the prev endpoints (the one past the
|
||||
// end of the current segment)
|
||||
|
@ -1196,11 +1142,12 @@ class GeoStandardPath extends GeoBasePath {
|
|||
point, currentULHC, currentLLHC, prevLRHC);
|
||||
notablePoints2 = new GeoPoint[] {currentULHC, currentLLHC, prevLRHC};
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Constructing CutoffDualCircleSegmentEndpoint with colinear segments");
|
||||
circlePlane2 = SidedPlane.constructSidedPlaneFromTwoPoints(point, currentULHC, currentLLHC);
|
||||
notablePoints2 = new GeoPoint[] {currentULHC, currentLLHC};
|
||||
}
|
||||
this.cutoffPlanes =
|
||||
new Membership[] {new SidedPlane(prevCutoffPlane), new SidedPlane(nextCutoffPlane)};
|
||||
this.boundaryPlane1 = new SidedPlane(prevCutoffPlane);
|
||||
this.boundaryPlane2 = new SidedPlane(nextCutoffPlane);
|
||||
this.cutoffPlanes = new Membership[] {boundaryPlane1, boundaryPlane2};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1223,28 +1170,6 @@ class GeoStandardPath extends GeoBasePath {
|
|||
return circlePlane1.isWithin(x, y, z) || circlePlane2.isWithin(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nearestPathDistance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
for (final Membership m : cutoffPlanes) {
|
||||
if (!m.isWithin(x, y, z)) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
}
|
||||
return super.nearestPathDistance(distanceStyle, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double pathCenterDistance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
for (final Membership m : cutoffPlanes) {
|
||||
if (!m.isWithin(x, y, z)) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
}
|
||||
return super.pathCenterDistance(distanceStyle, x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(
|
||||
final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds) {
|
||||
|
@ -1263,15 +1188,28 @@ class GeoStandardPath extends GeoBasePath {
|
|||
@Override
|
||||
public void getBounds(final Bounds bounds) {
|
||||
super.getBounds(bounds);
|
||||
bounds.addPlane(planetModel, circlePlane1);
|
||||
bounds.addPlane(planetModel, circlePlane2);
|
||||
// System.out.println("Computing bounds for circlePlane1="+circlePlane1);
|
||||
bounds.addPlane(planetModel, circlePlane1, boundaryPlane1, boundaryPlane2);
|
||||
// System.out.println("Computing bounds for circlePlane2="+circlePlane2);
|
||||
bounds.addPlane(planetModel, circlePlane2, boundaryPlane1, boundaryPlane2);
|
||||
bounds.addPlane(planetModel, boundaryPlane1, circlePlane1, boundaryPlane2);
|
||||
bounds.addPlane(planetModel, boundaryPlane1, circlePlane2, boundaryPlane2);
|
||||
bounds.addPlane(planetModel, boundaryPlane2, circlePlane1, boundaryPlane1);
|
||||
bounds.addPlane(planetModel, boundaryPlane2, circlePlane2, boundaryPlane1);
|
||||
bounds.addIntersection(planetModel, circlePlane1, boundaryPlane1, boundaryPlane2);
|
||||
bounds.addIntersection(planetModel, circlePlane1, boundaryPlane2, boundaryPlane1);
|
||||
bounds.addIntersection(planetModel, circlePlane2, boundaryPlane1, boundaryPlane2);
|
||||
bounds.addIntersection(planetModel, circlePlane2, boundaryPlane2, boundaryPlane1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CutoffDualCircleSegmentEndpoint: " + super.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/** This is the pre-calculated data for a path segment. */
|
||||
private static class PathSegment implements PathComponent {
|
||||
/** Planet model */
|
||||
public final PlanetModel planetModel;
|
||||
private static class PathSegment extends GeoBaseBounds implements PathComponent {
|
||||
/** Previous path component */
|
||||
public final PathComponent previous;
|
||||
/** Starting point of the segment */
|
||||
|
@ -1319,7 +1257,7 @@ class GeoStandardPath extends GeoBasePath {
|
|||
final GeoPoint end,
|
||||
final Plane normalizedConnectingPlane,
|
||||
final double planeBoundingOffset) {
|
||||
this.planetModel = planetModel;
|
||||
super(planetModel);
|
||||
this.previous = previous;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
|
@ -1409,6 +1347,31 @@ class GeoStandardPath extends GeoBasePath {
|
|||
return dist.doubleValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double distance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
if (!isWithin(x, y, z)) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
final double startingDistance = getStartingDistance(distanceStyle);
|
||||
final double pathDistance = pathDistance(distanceStyle, x, y, z);
|
||||
// System.out.println("In PathSegment distance(), startingDistance = "+startingDistance+"
|
||||
// pathDistance = "+pathDistance);
|
||||
return distanceStyle.fromAggregationForm(
|
||||
distanceStyle.aggregateDistances(startingDistance, pathDistance));
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nearestDistance(
|
||||
final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||
if (!isWithin(x, y, z)) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
return distanceStyle.fromAggregationForm(
|
||||
distanceStyle.aggregateDistances(
|
||||
getStartingDistance(distanceStyle), nearestPathDistance(distanceStyle, x, y, z)));
|
||||
}
|
||||
|
||||
private double computeStartingDistance(final DistanceStyle distanceStyle) {
|
||||
if (previous == null) {
|
||||
return 0.0;
|
||||
|
@ -1472,10 +1435,6 @@ class GeoStandardPath extends GeoBasePath {
|
|||
// Computes the distance along the path to a point on the path where a perpendicular plane
|
||||
// goes through the specified point.
|
||||
|
||||
// First, if this point is outside the endplanes of the segment, return POSITIVE_INFINITY.
|
||||
if (!startCutoffPlane.isWithin(x, y, z) || !endCutoffPlane.isWithin(x, y, z)) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
}
|
||||
// (1) Compute normalizedPerpPlane. If degenerate, then there is no such plane, which means
|
||||
// that the point given is insufficient to distinguish between a family of such planes.
|
||||
// This can happen only if the point is one of the "poles", imagining the normalized plane
|
||||
|
@ -1724,6 +1683,7 @@ class GeoStandardPath extends GeoBasePath {
|
|||
|
||||
@Override
|
||||
public void getBounds(final Bounds bounds) {
|
||||
super.getBounds(bounds);
|
||||
// We need to do all bounding planes as well as corner points
|
||||
bounds
|
||||
.addPoint(start)
|
||||
|
@ -1781,6 +1741,11 @@ class GeoStandardPath extends GeoBasePath {
|
|||
startCutoffPlane,
|
||||
lowerConnectingPlane);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PathSegment (" + ULHC + ", " + URHC + ", " + LRHC + ", " + LLHC + ")";
|
||||
}
|
||||
}
|
||||
|
||||
private static class TreeBuilder {
|
||||
|
@ -1796,7 +1761,7 @@ class GeoStandardPath extends GeoBasePath {
|
|||
componentStack.add(component);
|
||||
depthStack.add(0);
|
||||
while (depthStack.size() >= 2) {
|
||||
if (depthStack.get(depthStack.size() - 1).equals(depthStack.get(depthStack.size() - 2))) {
|
||||
if (depthStack.get(depthStack.size() - 1) == depthStack.get(depthStack.size() - 2)) {
|
||||
mergeTop();
|
||||
} else {
|
||||
break;
|
||||
|
@ -1816,9 +1781,9 @@ class GeoStandardPath extends GeoBasePath {
|
|||
|
||||
private void mergeTop() {
|
||||
depthStack.remove(depthStack.size() - 1);
|
||||
PathComponent firstComponent = componentStack.remove(componentStack.size() - 1);
|
||||
int newDepth = depthStack.remove(depthStack.size() - 1);
|
||||
PathComponent secondComponent = componentStack.remove(componentStack.size() - 1);
|
||||
int newDepth = depthStack.remove(depthStack.size() - 1) + 1;
|
||||
PathComponent firstComponent = componentStack.remove(componentStack.size() - 1);
|
||||
depthStack.add(newDepth);
|
||||
componentStack.add(new PathNode(firstComponent, secondComponent));
|
||||
}
|
||||
|
|
|
@ -126,6 +126,161 @@ public class Plane extends Vector {
|
|||
: Math.nextDown(basePlane.D - MINIMUM_RESOLUTION));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given two points, construct a plane that goes through them and the origin. Then, find a plane
|
||||
* that is perpendicular to that which also goes through the original two points. This is useful
|
||||
* for building path endpoints on worlds that can be ellipsoids.
|
||||
*
|
||||
* @param M is the first vector (point)
|
||||
* @param N is the second vector (point)
|
||||
* @return the plane, or throw an exception if the points given cannot be turned into the plane
|
||||
* desired.
|
||||
*/
|
||||
public static Plane constructPerpendicularCenterPlaneTwoPoints(final Vector M, final Vector N) {
|
||||
final Plane centerPlane = new Plane(M, N);
|
||||
|
||||
// First plane:
|
||||
// A0x + B0y + C0z = 0 (D0 = 0)
|
||||
|
||||
final double A0 = centerPlane.x;
|
||||
final double B0 = centerPlane.y;
|
||||
final double C0 = centerPlane.z;
|
||||
|
||||
// Second plane equations:
|
||||
// A1Mx + B1My + C1Mz + D1 = 0
|
||||
// A1Nx + B1Ny + C1Nz + D1 = 0
|
||||
// simplifying:
|
||||
// A1(Mx-Nx) + B1(My-Ny) + C1(Mz-Nz) = 0
|
||||
// A0*A1 + B0*B1 + C0*C1 = 0
|
||||
// A1^2 + B1^2 + C1^2 = 1
|
||||
|
||||
// Basic strategy: Pick a variable and set it to 1.
|
||||
// e.g. A1 = 1.
|
||||
// Then:
|
||||
// B1 = (-C1(Mz-Nz) - (Mx-Nx)) / (My-Ny)
|
||||
// A0 + B0 * (-C1(Mz-Nz) - (Mx-Nx)) / (My-Ny) + C0 * C1 = 0
|
||||
// C1 * ((-B0 * (Mz-Nz) - (Mx-Nx))/ (My-Ny) + C0) = -A0
|
||||
// C1 = -A0 / ((-B0 * (Mz-Nz) - (Mx-Nx))/ (My-Ny) + C0)
|
||||
// and B1 can be found from above. Then normalized.
|
||||
|
||||
// So the variable we pick has the greatest delta between M and N, but the basic process is
|
||||
// identical.
|
||||
// To find D1 after A1, B1, C1 are found, just plug in one of the points, either M or N.
|
||||
|
||||
final double xDiff = M.x - N.x;
|
||||
final double yDiff = M.y - N.y;
|
||||
final double zDiff = M.z - N.z;
|
||||
|
||||
if (xDiff * xDiff + yDiff * yDiff + zDiff * zDiff < MINIMUM_RESOLUTION_SQUARED) {
|
||||
throw new IllegalArgumentException("Chosen points are numerically identical");
|
||||
}
|
||||
|
||||
final double A1;
|
||||
final double B1;
|
||||
final double C1;
|
||||
// Pick the equations we want to use based on the denominators we will encounter.
|
||||
// There are two levels to this. The first level involves a denominator choice based on
|
||||
// which coefficient we set to 1. The second level involves a denominator choice between
|
||||
// diffs in the other two dimensions.
|
||||
final double A1choice = (C0 * yDiff - B0 * zDiff);
|
||||
final double B1choice = (C0 * xDiff - A0 * zDiff);
|
||||
final double C1choice = (B0 * xDiff - A0 * yDiff);
|
||||
if (Math.abs(A1choice) >= Math.abs(B1choice) && Math.abs(A1choice) >= Math.abs(C1choice)) {
|
||||
// System.out.println("Choosing A1=1");
|
||||
// A1 = 1.0
|
||||
// 1.0 * xDiff + B1 * yDiff + C1 * zDiff = 0
|
||||
// B1 * yDiff = -C1 * zDiff - 1.0 * xDiff
|
||||
// B1 = (-C1 * zDiff - xDiff) / yDiff
|
||||
// A0 + B0 * (-C1 * zDiff - xDiff) / yDiff + C0 * C1 = 0
|
||||
// A0 - B0 * C1 * zDiff / yDiff - B0 * xDiff / yDiff + C0 * C1 = 0
|
||||
// A0 * yDiff - B0 * C1 * zDiff - B0 * xDiff + C0 * C1 * yDiff = 0
|
||||
// C1 * C0 * yDiff - C1 * B0 * zDiff = B0 * xDiff - A0 * yDiff
|
||||
// C1 = (B0 * xDiff - A0 * yDiff) / (C0 * yDiff - B0 * zDiff);
|
||||
A1 = 1.0;
|
||||
if (Math.abs(yDiff) >= Math.abs(zDiff)) {
|
||||
// A1choice is C-B, so numerator is B-A
|
||||
C1 = (B0 * xDiff - A0 * yDiff) / A1choice;
|
||||
B1 = (-C1 * zDiff - xDiff) / yDiff;
|
||||
} else {
|
||||
// A1choice is C-B, so numerator is A-C
|
||||
// 1.0 * xDiff + B1 * yDiff + C1 * zDiff = 0
|
||||
// C1 * zDiff = -B1 * yDiff - 1.0 * xDiff
|
||||
// C1 = (-B1 * yDiff - xDiff) / zDiff
|
||||
// A0 + B0 * B1 - C0 * (B1 * yDiff - xDiff) / zDiff = 0
|
||||
// A0 * zDiff + B0 * B1 * zDiff - C0 * B1 * yDiff - C0 * xDiff = 0
|
||||
// B1 * B0 * zDiff - B1 * C0 * yDiff = C0 * xDiff - A0 * zDiff
|
||||
// B1 = (C0 * xDiff - A0 * zDiff) / (B0 * zDiff - C0 * yDiff);
|
||||
B1 = (A0 * zDiff - C0 * xDiff) / A1choice;
|
||||
C1 = (-B1 * yDiff - xDiff) / zDiff;
|
||||
}
|
||||
} else if (Math.abs(B1choice) >= Math.abs(A1choice)
|
||||
&& Math.abs(B1choice) >= Math.abs(C1choice)) {
|
||||
// System.out.println("Choosing B1=1");
|
||||
// Pick B1 = 1.0
|
||||
// A1 * xDiff + 1.0 * yDiff + C1 * zDiff = 0
|
||||
// A1 * xDiff = -C1 * zDiff - 1.0 * yDiff
|
||||
// A1 = (-C1 * zDiff - yDiff) / xDiff
|
||||
// A0 * (-C1 * zDiff - yDiff) / xDiff + B0 * 1.0 + C0 * C1 = 0
|
||||
// B0 + C0 * C1 - A0 * C1 * zDiff / xDiff - A0 * yDiff / xDiff = 0
|
||||
// B0 * xDiff - A0 * C1 * zDiff - A0 * yDiff + C0 * C1 * xDiff = 0
|
||||
// C1 * C0 * xDiff - C1 * A0 * zDiff = A0 * yDiff - B0 * xDiff
|
||||
// C1 = (A0 * yDiff - B0 * xDiff) / (C0 * xDiff - A0 * zDiff);
|
||||
B1 = 1.0;
|
||||
if (Math.abs(xDiff) >= Math.abs(zDiff)) {
|
||||
// B1choice is C-A, so numerator is A-B
|
||||
C1 = (A0 * yDiff - B0 * xDiff) / B1choice;
|
||||
A1 = (-C1 * zDiff - yDiff) / xDiff;
|
||||
} else {
|
||||
// B1choice is C-A, so numerator is B-C
|
||||
A1 = (B0 * xDiff - C0 * yDiff) / B1choice;
|
||||
C1 = (-A1 * xDiff - yDiff) / zDiff;
|
||||
}
|
||||
} else if (Math.abs(C1choice) >= Math.abs(A1choice)
|
||||
&& Math.abs(C1choice) >= Math.abs(B1choice)) {
|
||||
// System.out.println("Choosing C1=1");
|
||||
// Pick C1 = 1.0
|
||||
// A1 * xDiff + B1 * yDiff + 1.0 * zDiff = 0
|
||||
// A1 * xDiff = -B1 * yDiff - 1.0 * zDiff
|
||||
// A1 = (-B1 * yDiff - zDiff) / xDiff
|
||||
// A0 * (-B1 * yDiff - zDiff) / xDiff + B0 * B1 + C0 * 1.0 = 0
|
||||
// -B1 * A0 * yDiff - A0 * zDiff + B1 * B0 * xDiff + C0 * xDiff = 0
|
||||
// B1 * B0 * xDiff - B1 * A0 * yDiff = A0 * zDiff - C0 * xDiff
|
||||
// B1 = (A0 * zDiff - C0 * xDiff) / (B0 * xDiff - A0 * yDiff)
|
||||
C1 = 1.0;
|
||||
if (Math.abs(xDiff) >= Math.abs(yDiff)) {
|
||||
// C1choice is B - A, so numerator is C-B
|
||||
B1 = (A0 * zDiff - C0 * xDiff) / C1choice;
|
||||
A1 = (-B1 * yDiff - zDiff) / xDiff;
|
||||
} else {
|
||||
// A1 * xDiff + B1 * yDiff + 1.0 * zDiff = 0
|
||||
// A1 * xDiff = -B1 * yDiff - 1.0 * zDiff
|
||||
// B1 = (-A1 * xDiff - zDiff) / yDiff
|
||||
// A0 * A1 + B0 * (-A1 * xDiff - zDiff) / yDiff + C0 * 1.0 = 0
|
||||
// A1 * A0 * yDiff - A1 * B0 * xDiff - B0 * zDiff + C0 * yDiff = 0
|
||||
// A1 * A0 * yDiff - A1 * B0 * xDiff = B0 * zDiff - C0 * yDiff
|
||||
// A1 = (B0 * zDiff - C0 * yDiff) / (A0 * yDiff - A1 * B0 * xDiff)
|
||||
A1 = (C0 * yDiff - B0 * zDiff) / C1choice;
|
||||
B1 = (-A1 * xDiff - zDiff) / yDiff;
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException("Equation appears to be unsolveable");
|
||||
}
|
||||
|
||||
// Normalize the vector
|
||||
final double normFactor = 1.0 / Math.sqrt(A1 * A1 + B1 * B1 + C1 * C1);
|
||||
final Vector v = new Vector(A1 * normFactor, B1 * normFactor, C1 * normFactor);
|
||||
final Plane rval = new Plane(v, -(v.x * M.x + v.y * M.y + v.z * M.z));
|
||||
assert rval.evaluateIsZero(N);
|
||||
// System.out.println(
|
||||
// "M and N both on plane! Dotproduct with centerplane = "
|
||||
// + (rval.x * centerPlane.x + rval.y * centerPlane.y + rval.z * centerPlane.z));
|
||||
|
||||
assert Math.abs(rval.x * centerPlane.x + rval.y * centerPlane.y + rval.z * centerPlane.z)
|
||||
< MINIMUM_RESOLUTION;
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the most accurate normalized plane through an x-y point and including the Z axis. If
|
||||
* none of the points can determine the plane, return null.
|
||||
|
|
|
@ -227,6 +227,16 @@ public class SidedPlane extends Plane implements Membership {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct sided plane from two points. This first constructs a plane that goes through the
|
||||
* center, then finds one that is perpendicular that goes through the same two points.
|
||||
*/
|
||||
public static SidedPlane constructSidedPlaneFromTwoPoints(
|
||||
final Vector insidePoint, final Vector upperPoint, final Vector lowerPoint) {
|
||||
final Plane plane = Plane.constructPerpendicularCenterPlaneTwoPoints(upperPoint, lowerPoint);
|
||||
return new SidedPlane(insidePoint, plane.x, plane.y, plane.z, plane.D);
|
||||
}
|
||||
|
||||
/** Construct a sided plane from three points. */
|
||||
public static SidedPlane constructNormalizedThreePointSidedPlane(
|
||||
final Vector insidePoint, final Vector point1, final Vector point2, final Vector point3) {
|
||||
|
|
|
@ -40,6 +40,7 @@ public class TestGeoPath extends LuceneTestCase {
|
|||
gp = new GeoPoint(PlanetModel.SPHERE, -0.15, 0.05);
|
||||
assertEquals(Double.POSITIVE_INFINITY, p.computeDistance(DistanceStyle.ARC, gp), 0.000001);
|
||||
gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.25);
|
||||
System.out.println("Calling problematic computeDistance...");
|
||||
assertEquals(0.20 + 0.05, p.computeDistance(DistanceStyle.ARC, gp), 0.000001);
|
||||
gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.05);
|
||||
assertEquals(0.0 + 0.05, p.computeDistance(DistanceStyle.ARC, gp), 0.000001);
|
||||
|
|
Loading…
Reference in New Issue