/*
Copyright (C) 2001, 2007 United States Government as represented by
the Administrator of the National Aeronautics and Space Administration.
All Rights Reserved.
*/
package gov.nasa.worldwind.view;
import gov.nasa.worldwind.geom.Angle;
import gov.nasa.worldwind.geom.Matrix;
import gov.nasa.worldwind.geom.Position;
import gov.nasa.worldwind.geom.Vec4;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.render.DrawContext;
import gov.nasa.worldwind.util.Logging;
/**
* @author dcollins
* @version $Id: OrbitViewCollisionSupport.java 5165 2008-04-24 21:00:05Z dcollins $
*/
public class OrbitViewCollisionSupport
{
private double collisionThreshold;
private int numIterations;
public OrbitViewCollisionSupport()
{
setNumIterations(1);
}
public double getCollisionThreshold()
{
return this.collisionThreshold;
}
public void setCollisionThreshold(double collisionThreshold)
{
if (collisionThreshold < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", collisionThreshold);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.collisionThreshold = collisionThreshold;
}
public int getNumIterations()
{
return this.numIterations;
}
public void setNumIterations(int numIterations)
{
if (numIterations < 1)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", numIterations);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.numIterations = numIterations;
}
public boolean isColliding(OrbitView orbitView, double nearDistance, DrawContext dc)
{
if (orbitView == null)
{
String message = Logging.getMessage("nullValue.OrbitViewIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (nearDistance < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", nearDistance);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
Globe globe = dc.getGlobe();
if (globe == null)
{
String message = Logging.getMessage("nullValue.DrawingContextGlobeIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
Matrix modelviewInv = getModelviewInverse(orbitView.getOrbitViewModel(), globe,
orbitView.getCenterPosition(), orbitView.getHeading(), orbitView.getPitch(), orbitView.getZoom());
if (modelviewInv != null)
{
// OrbitView is colliding when its eye point is below the collision threshold.
double heightAboveSurface = computeViewHeightAboveSurface(dc, modelviewInv,
orbitView.getFieldOfView(), orbitView.getViewport(), nearDistance);
return heightAboveSurface < this.collisionThreshold;
}
return false;
}
public Position computeCenterPositionToResolveCollision(OrbitView orbitView, double nearDistance, DrawContext dc)
{
if (orbitView == null)
{
String message = Logging.getMessage("nullValue.OrbitViewIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (nearDistance < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", nearDistance);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
Globe globe = dc.getGlobe();
if (globe == null)
{
String message = Logging.getMessage("nullValue.DrawingContextGlobeIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
Position newCenter = null;
for (int i = 0; i < this.numIterations; i++)
{
Matrix modelviewInv = getModelviewInverse(orbitView.getOrbitViewModel(), globe,
newCenter != null ? newCenter : orbitView.getCenterPosition(),
orbitView.getHeading(), orbitView.getPitch(), orbitView.getZoom());
if (modelviewInv != null)
{
double heightAboveSurface = computeViewHeightAboveSurface(dc, modelviewInv,
orbitView.getFieldOfView(), orbitView.getViewport(), nearDistance);
double adjustedHeight = heightAboveSurface - this.collisionThreshold;
if (adjustedHeight < 0)
{
newCenter = new Position(
newCenter != null ? newCenter.getLatLon() : orbitView.getCenterPosition().getLatLon(),
(newCenter != null ? newCenter.getElevation() : orbitView.getCenterPosition().getElevation()) - adjustedHeight);
}
}
}
return newCenter;
}
public Angle computePitchToResolveCollision(OrbitView orbitView, double nearDistance, DrawContext dc)
{
if (orbitView == null)
{
String message = Logging.getMessage("nullValue.OrbitViewIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (nearDistance < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", nearDistance);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
Globe globe = dc.getGlobe();
if (globe == null)
{
String message = Logging.getMessage("nullValue.DrawingContextGlobeIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
Angle newPitch = null;
for (int i = 0; i < this.numIterations; i++)
{
Matrix modelviewInv = getModelviewInverse(orbitView.getOrbitViewModel(), globe,
orbitView.getCenterPosition(), orbitView.getHeading(),
newPitch != null ? newPitch : orbitView.getPitch(),
orbitView.getZoom());
if (modelviewInv != null)
{
double heightAboveSurface = computeViewHeightAboveSurface(dc, modelviewInv,
orbitView.getFieldOfView(), orbitView.getViewport(), nearDistance);
double adjustedHeight = heightAboveSurface - this.collisionThreshold;
if (adjustedHeight < 0)
{
Vec4 eyePoint = getEyePoint(modelviewInv);
Vec4 centerPoint = globe.computePointFromPosition(orbitView.getCenterPosition());
if (eyePoint != null && centerPoint != null)
{
Position eyePos = globe.computePositionFromPoint(eyePoint);
// Compute the eye point required to resolve the collision.
Vec4 newEyePoint = globe.computePointFromPosition(eyePos.getLatitude(), eyePos.getLongitude(),
eyePos.getElevation() - adjustedHeight);
// Compute the pitch that corresponds with the elevation of the eye point
// (but not necessarily the latitude and longitude).
Vec4 normalAtCenter = globe.computeSurfaceNormalAtPoint(centerPoint);
Vec4 newEye_sub_center = newEyePoint.subtract3(centerPoint).normalize3();
double dot = normalAtCenter.dot3(newEye_sub_center);
if (dot >= -1 || dot <= 1)
{
double angle = Math.acos(dot);
newPitch = Angle.fromRadians(angle);
}
}
}
}
}
return newPitch;
}
private double computeViewHeightAboveSurface(DrawContext dc, Matrix modelviewInv,
Angle fieldOfView, java.awt.Rectangle viewport, double nearDistance)
{
double height = Double.POSITIVE_INFINITY;
if (dc != null && modelviewInv != null && fieldOfView != null && viewport != null && nearDistance >= 0)
{
Vec4 eyePoint = getEyePoint(modelviewInv);
if (eyePoint != null)
{
double eyeHeight = computePointHeightAboveSurface(dc, eyePoint);
if (eyeHeight < height)
height = eyeHeight;
}
Vec4 nearPoint = getPointOnNearPlane(modelviewInv, fieldOfView, viewport, nearDistance);
if (nearPoint != null)
{
double nearHeight = computePointHeightAboveSurface(dc, nearPoint);
if (nearHeight < height)
height = nearHeight;
}
}
return height;
}
private double computePointHeightAboveSurface(DrawContext dc, Vec4 point)
{
double height = Double.POSITIVE_INFINITY;
if (dc != null && dc.getGlobe() != null && point != null)
{
Globe globe = dc.getGlobe();
Position position = globe.computePositionFromPoint(point);
Position surfacePosition = null;
// Look for the surface geometry point at 'position'.
Vec4 pointOnGlobe = dc.getPointOnGlobe(position.getLatitude(), position.getLongitude());
if (pointOnGlobe != null)
surfacePosition = globe.computePositionFromPoint(pointOnGlobe);
// Fallback to using globe elevation values.
if (surfacePosition == null)
surfacePosition = new Position(position.getLatLon(),
globe.getElevation(position.getLatitude(), position.getLongitude()) * dc.getVerticalExaggeration());
height = position.getElevation() - surfacePosition.getElevation();
}
return height;
}
private Matrix getModelviewInverse(OrbitViewModel orbitViewModel, Globe globe,
Position centerPosition, Angle heading, Angle pitch, double zoom)
{
if (orbitViewModel != null && globe != null && centerPosition != null && heading != null && pitch != null)
{
// Use the OrbitViewModel to compute the current modelview matrix.
Matrix modelview = orbitViewModel.computeTransformMatrix(globe,
centerPosition, heading, pitch, zoom);
if (modelview != null)
return modelview.getInverse();
}
return null;
}
private Vec4 getEyePoint(Matrix modelviewInv)
{
return modelviewInv != null ? Vec4.UNIT_W.transformBy4(modelviewInv) : null;
}
private Vec4 getPointOnNearPlane(Matrix modelviewInv, Angle fieldOfView, java.awt.Rectangle viewport,
double nearDistance)
{
if (modelviewInv != null && fieldOfView != null && viewport != null && nearDistance >= 0)
{
// Computes the point on the bottom center of the near clip plane.
double aspect = viewport.getHeight() / viewport.getWidth();
double nearClipHeight = 2 * aspect * nearDistance * fieldOfView.tanHalfAngle();
Vec4 nearClipVec = new Vec4(0, -nearClipHeight / 2.0, -nearDistance, 1);
return nearClipVec.transformBy4(modelviewInv);
}
return null;
}
}