Isis 3 Programmer Reference
BulletShapeModel.cpp
Go to the documentation of this file.
1 
25 #include "BulletShapeModel.h"
26 
27 #include <numeric>
28 
29 #include <QtGlobal>
30 #include <QVector>
31 
32 #include "IException.h"
33 #include "Intercept.h"
34 #include "IString.h"
35 #include "Latitude.h"
36 #include "Longitude.h"
37 #include "NaifStatus.h"
38 #include "Pvl.h"
39 #include "ShapeModel.h"
40 #include "SpecialPixel.h"
41 #include "Statistics.h"
42 #include "SurfacePoint.h"
43 #include "Target.h"
44 
45 
46 using namespace std;
47 
48 namespace Isis {
49 
53  BulletShapeModel::BulletShapeModel() : ShapeModel(), m_model(), m_tolerance(DBL_MAX),
54  m_intercept(btVector3(0,0,0), btVector3(0,0,0)) {
55  // defaults for ShapeModel parent class include:
56  // name = empty string
57  // surfacePoint = null sp
58  // hasIntersection = false
59  // hasNormal = false
60  // normal = (0,0,0)
61  // hasEllipsoidIntersection = false
62  setName("Bullet");
63  }
64 
79  ShapeModel(target), m_model(), m_tolerance(DBL_MAX),
80  m_intercept(btVector3(0,0,0), btVector3(0,0,0)) {
81 
82  // defaults for ShapeModel parent class include:
83  // name = empty string
84  // surfacePoint = null sp
85  // hasIntersection = false
86  // hasNormal = false
87  // normal = (0,0,0)
88  // hasEllipsoidIntersection = false
89 
90  setName("Bullet"); // Really is used as type in the system at present!
91 
92  PvlGroup &kernels = pvl.findGroup("Kernels", Pvl::Traverse);
93 
94  QString shapefile;
95  if (kernels.hasKeyword("ElevationModel")) {
96  shapefile = (QString) kernels["ElevationModel"];
97  }
98  else { // if (kernels.hasKeyword("ShapeModel")) {
99  shapefile = (QString) kernels["ShapeModel"];
100  }
101 
102  QScopedPointer<BulletTargetShape> v_shape( BulletTargetShape::load(shapefile) );
103  if (v_shape.isNull() ) {
104  QString mess = "Cannot create a BulletShape from " + shapefile;
105  throw IException(IException::User, mess, _FILEINFO_);
106  }
107 
108  // Attempt to initialize the DSK file - exception ensues if errors occur
109  // error thrown if ShapeModel=Null (i.e. Ellipsoid)
110  m_model.reset(new BulletWorldManager(shapefile));
111  m_model->addTarget( v_shape.take() );
112  }
113 
114 
125  ShapeModel(target), m_model(), m_tolerance(DBL_MAX),
126  m_intercept(btVector3(0,0,0), btVector3(0,0,0)) {
127  // Attempt to initialize the DSK file - exception ensues if errors occur
128  // error thrown if ShapeModel=Null (i.e. Ellipsoid)
129  btAssert ( shape != 0 );
130  m_model.reset( new BulletWorldManager( shape->name() ) );
131  m_model->addTarget( shape );
132  setName("Bullet"); // Really is used as type in the system at present!
133  }
134 
146  ShapeModel(target), m_model(model), m_tolerance(DBL_MAX),
147  m_intercept(btVector3(0,0,0), btVector3(0,0,0)) {
148 
149  // TODO create valid Target
150  // Using this constructor, ellipsoidNormal(),
151  // calculateSurfaceNormal(), and setLocalNormalFromIntercept()
152  // methods can not be called
153  }
154 
155 
160 
161 
168  return ( m_tolerance );
169  }
170 
171 
177  void BulletShapeModel::setTolerance(const double &tolerance) {
178  m_tolerance = tolerance;
179  }
180 
181 
193  bool BulletShapeModel::intersectSurface(std::vector<double> observerPos,
194  std::vector<double> lookDirection) {
195 
197  btVector3 observer(observerPos[0], observerPos[1], observerPos[2]);
198  btVector3 lookdir(lookDirection[0], lookDirection[1], lookDirection[2]);
199  btVector3 rayEnd = castLookDir(observer, lookdir);
200  BulletClosestRayCallback result(observer, rayEnd);
201  bool success = m_model->raycast(observer, rayEnd, result);
202  updateShapeModel(result);
203  return ( success );
204  }
205 
225  const std::vector<double> &observerPos,
226  const bool &checkOcclusion) {
227 
228  // Set up for finding all rays along origin vector through lat/lon surface point
230  btVector3 origin(0.0, 0.0, 0.0);
231  btVector3 lookdir = latlonToVector(lat, lon);
232  btVector3 rayEnd = castLookDir(origin, lookdir);
233  BulletAllHitsRayCallback results( origin, rayEnd, false);
234 
235  // If no intersections (unlikely for this case), we are done!
236  if ( !m_model->raycast(origin, rayEnd, results) ) {
237  return ( false );
238  }
239 
240  // Sort the intersections based on distance to the observer
241  btVector3 observer(observerPos[0], observerPos[1], observerPos[2]);
242  QVector<BulletClosestRayCallback> points = sortHits(results, observer);
243 
244  // If occlusion is being checked
245  if ( checkOcclusion ) {
246  for (int i = 0 ; i < points.size() ; i++) {
247 
248  // Check for occlusion
249  // If the hit is occluded then move on.
250  // Otherwise, it is the closest non-occluded point so take it.
251  BulletClosestRayCallback &hit = points[i];
252  if ( !isOccluded(hit, observer) ) {
253  updateShapeModel( hit );
254  break;
255  }
256  }
257  }
258 
259  // If occlusion is not being checked, take the intersection closest to the observer
260  else {
261  updateShapeModel( points[0] );
262  }
263 
264  // Is set by the update routine
265  return ( hasIntersection() );
266  }
267 
268 
288  const std::vector<double> &observerPos,
289  const bool &checkOcclusion) {
290 
291  // Set up for finding all rays along origin vector through lat/lon surface point
293  btVector3 origin(0.0, 0.0, 0.0);
294  btVector3 surfPointVec = pointToVector(surfpt);
295  btVector3 rayEnd = castLookDir(origin, surfPointVec);
296  BulletAllHitsRayCallback results(origin, rayEnd, false);
297 
298  // Cast the ray to find all intersections
299  // If no intersections (unlikely for this case), we are done!
300  if ( !m_model->raycast(origin, rayEnd, results) ) {
301  return ( false );
302  }
303 
304  // Sort the intersections based on distance to the surface point
305  QVector<BulletClosestRayCallback> points = sortHits(results, surfPointVec);
306 
307  // If occlusion is being checked
308  if ( checkOcclusion ) {
309  btVector3 observer(observerPos[0], observerPos[1], observerPos[2]);
310  for (int i = 0 ; i < points.size() ; i++) {
311 
312  // Check for occlusion
313  // If the hit is occluded then move on.
314  // Otherwise, it is the closest non-occluded point so take it.
315  BulletClosestRayCallback &hit = points[i];
316  if ( !isOccluded(hit, observer) ) {
317  updateShapeModel( hit );
318  break;
319  }
320  }
321  }
322 
323  // If occlusion is not being checked, take the intersection closest to the observer
324  else {
325  updateShapeModel( points[0] );
326  }
327 
328  // Is set by the update routine
329  return ( hasIntersection() );
330 
331  }
332 
333 
343  const btVector3 &observer) const {
344  // If the callback does not have an intersection return true
345  if ( !hit.isValid() ) {
346  return true;
347  }
348 
349  // Check if the emission angle is greater than 90 degrees
350  // If true, then it is occluded, if false then unknown
351  btVector3 psB = (observer - hit.point()).normalized();
352  btScalar angle = std::acos( hit.normal().dot( psB ) ) * RAD2DEG;
353  if ( std::fabs( angle ) > 90.0 - DBL_MIN) {
354  return true;
355  }
356 
357  // Ray cast from the observer to the intersection
358  // If we have an intersection, test for occlusion
359  BulletClosestRayCallback results( observer, hit.point() );
360  if ( !m_model->raycast(observer, hit.point(), results) ) {
361  return false;
362  }
363 
364  // Is this intersection the same as the previous intersection?
365  if ( results.isVisible( hit, getTolerance() ) ) {
366  return false;
367  }
368 
369  return true;
370  }
371 
372 
380  std::vector<double> fakepos(3, 0.0);
381  (void) intersectSurface(surfacePoint, fakepos, false);
382  }
383 
384 
390  }
391 
392 
411  const Longitude &lon) {
412 
413  // Cast a ray from the origin through the surface point at the input lat/lon
414  btVector3 origin(0.0, 0.0, 0.0);
415  btVector3 lookdir = latlonToVector(lat, lon);
416  btVector3 rayEnd = castLookDir(origin, lookdir);
417 
418  BulletAllHitsRayCallback result(origin, rayEnd, false);
419  if ( m_model->raycast(origin, rayEnd, result) ) {
420  BulletClosestRayCallback hit = result.hit();
421  return ( Distance(hit.point().length(), Distance::Kilometers) );
422  }
423  return ( Distance() );
424  }
425 
426 
437 
438  // Sanity check
439  if ( !hasIntersection() ) { // hasIntersection() <==> !m_intercept.isNull()
440  QString mess = "Intercept point does not exist - cannot provide normal vector";
442  }
443 
444  btVector3 normal = m_intercept.normal();
445  setNormal(normal[0], normal[1], normal[2]);
446  }
447 
448 
456  bool BulletShapeModel::isDEM() const {
457  return false;
458  }
459 
460 
470  bool BulletShapeModel::isVisibleFrom(const std::vector<double> observerPos,
471  const std::vector<double> lookDirection) {
472 
473  if ( !m_intercept.isValid() ) return (false);
474 
475  // Check if the emission angle is greater than 90 degrees
476  // If true, then it is occluded, if false then unknown
477  btVector3 observer(observerPos[0], observerPos[1], observerPos[2]);
478  btVector3 psB = (observer - m_intercept.point()).normalized();
479  btScalar angle = std::acos( m_intercept.normal().dot( psB ) ) * RAD2DEG;
480  if ( std::fabs( angle ) > 90.0 - DBL_MIN) {
481  return false;
482  }
483 
484  btVector3 rayEnd = castLookDir( observer, btVector3(lookDirection[0], lookDirection[1], lookDirection[2]) );
485  BulletClosestRayCallback results(observer, rayEnd);
486  (void) m_model->raycast(observer, rayEnd, results);
487  return ( m_intercept.isVisible( results, getTolerance() ) );
488  }
489 
490 
517 
518  // Sanity check
519  if ( !hasIntersection() ) { // hasIntersection() <==> !m_intercept.isNull()
520  QString mess = "Intercept point does not exist - cannot provide normal vector";
522  }
523 
525  }
526 
527 
532  // ShapeModel (parent class) throws error if no intersection
534  }
535 
540  // ShapeModel (parent class) throws error if no intersection
541  setNormal(ellipsoidNormal().toStdVector());// this takes care of setHasNormal(true);
542  }
543 
544 
560 
561  // Sanity check on state
562  if ( !hasIntersection() ) {
563  QString msg = "An intersection must be defined before computing the surface normal.";
565  }
566 
567  if ( !surfaceIntersection()->Valid() ) {
568  QString msg = "The surface point intersection must be valid to compute the surface normal.";
570  }
571 
572  if (!hasValidTarget()) {
573  QString msg = "A valid target must be defined before computing the surface normal.";
575  }
576 
577  // Get the coordinates of the current surface point
578  SpiceDouble pB[3];
580 
581  // Get the body radii and compute the true normal of the ellipsoid
582  QVector<double> norm(3);
583  // need a case for target == NULL
586  surfnm_c(radii[0].kilometers(), radii[1].kilometers(), radii[2].kilometers(),
587  pB, &norm[0]);
589 
590  return (norm);
591  }
592 
593 
599  return (*m_model);
600  }
601 
602 
610  btScalar BulletShapeModel::maxDistance() const {
611  return m_model->getTarget()->maximumDistance();
612  }
613 
614 
626  btVector3 BulletShapeModel::castLookDir(const btVector3 &observer,
627  const btVector3 &lookdir) const {
628  btScalar lookScale = observer.length() + maxDistance();
629  return ( observer + lookdir.normalized() * lookScale );
630  }
631 
632 
644  btVector3 BulletShapeModel::latlonToVector(const Latitude &lat, const Longitude &lon) const {
645  double latAngle = lat.radians();
646  double lonAngle = lon.radians();
647  btVector3 lookDir( cos(latAngle) * cos(lonAngle),
648  cos(latAngle) * sin(lonAngle),
649  sin(latAngle) );
650  return ( lookDir );
651  }
652 
653 
661  btVector3 BulletShapeModel::pointToVector(const SurfacePoint &surfpt) const {
662  btVector3 point;
663  surfpt.ToNaifArray( &point[0] );
664  return ( point );
665  }
666 
667 
675  SurfacePoint BulletShapeModel::makeSurfacePoint(const btVector3 &point) const {
676  SurfacePoint surfpt;
677  surfpt.FromNaifArray( &point[0] );
678  return ( surfpt );
679  }
680 
681 
693  const btVector3 &sortPoint) const {
695  for (int i = 0 ; i < hits.size() ; i++) {
696  BulletClosestRayCallback hit = hits.hit(i);
697  sortMap.insert( hit.distance(sortPoint), hit );
698  }
699  return ( QVector<BulletClosestRayCallback>::fromList( sortMap.values() ) );
700  }
701 
702 
717  m_intercept = result;
718  if ( m_intercept.isValid() ) {
719  ShapeModel::setSurfacePoint( makeSurfacePoint(m_intercept.point()) ); // sets ShapeModel::m_hasIntersection=t, ShapeModel::m_hasNormal=f
720 
721  btVector3 normal = m_intercept.normal();
722  setNormal(normal[0], normal[1], normal[2]);
723  }
724  else {
726  setHasNormal(false);
727  }
728 
729  return;
730  }
731 }; // namespace Isis
btScalar distance() const
Returns the distance from the intersection point to the beginning of the ray.
This class defines a body-fixed surface point.
Definition: SurfacePoint.h:148
std::vector< double > normal()
Returns the local surface normal at the current intersection point.
Definition: ShapeModel.cpp:395
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
btVector3 castLookDir(const btVector3 &observer, const btVector3 &lookdir) const
Compute the end point of a ray based on an observer and look direction.
PvlGroupIterator findGroup(const QString &name, PvlGroupIterator beg, PvlGroupIterator end)
Find a group with the specified name, within these indexes.
Definition: PvlObject.h:141
QVector< double > ellipsoidNormal()
Compute the true surface normal vector of an ellipsoid.
bool hasValidTarget() const
Returns the status of the target.
Definition: ShapeModel.cpp:443
void calculateDefaultNormal()
Calculate the surface normal of the ellipsoid as the default.
Bullet ray tracing callback to return all intersections along a ray&#39;s path.
void setNormal(const std::vector< double >)
Sets the normal for the currect intersection point.
Definition: ShapeModel.cpp:481
double radians() const
Convert an angle to a double.
Definition: Angle.h:243
Bullet World manager maintains a proper state for target bodies.
bool hasIntersection()
Returns intersection status.
Definition: ShapeModel.cpp:362
Distance localRadius(const Latitude &lat, const Longitude &lon)
Compute the radius of the body at a lat/lon point.
bool isDEM() const
Indicates that this shape model is not from a DEM.
static BulletTargetShape * load(const QString &dem, const Pvl *conf=0)
Load a DEM file into the target shape.
Namespace for the standard library.
void calculateLocalNormal(QVector< double *> cornerNeighborPoints)
Compute the normal for a local region of surface points.
This class is designed to encapsulate the concept of a Latitude.
Definition: Latitude.h:63
The distance is being specified in kilometers.
Definition: Distance.h:58
Search child objects.
Definition: PvlObject.h:170
double m_tolerance
! Bullet collision world that contains the representation of the body.
SurfacePoint * surfaceIntersection() const
Returns the surface intersection for this ShapeModel.
Definition: ShapeModel.cpp:352
bool intersectSurface(std::vector< double > observerPos, std::vector< double > lookDirection)
This method computes a DEM intercept point given an observer location and look direction using the Bu...
btScalar maxDistance() const
! The results of the last ray cast.
SurfacePoint makeSurfacePoint(const btVector3 &point) const
Convert a vector into a surface point.
This error is for when a programmer made an API call that was illegal.
Definition: IException.h:162
Distance measurement, usually in meters.
Definition: Distance.h:47
double getTolerance() const
Returns the occlusion tolerance in kilometers.
btVector3 point() const
Return the intersection point, if one exists.
std::vector< Distance > targetRadii() const
Returns the radii of the body in km.
Definition: ShapeModel.cpp:459
This class is designed to encapsulate the concept of a Longitude.
Definition: Longitude.h:52
QVector< BulletClosestRayCallback > sortHits(const BulletAllHitsRayCallback &hits, const btVector3 &sortPoint) const
Sort the hits in an AllHitsRayCallback based on distance to a point.
btVector3 normal() const
Return the local surface normal at the intersection, if an intersection exists.
virtual void setSurfacePoint(const SurfacePoint &surfacePoint)
Set the internal surface point.
void calculateSurfaceNormal()
compute the ellipsoid surface normal of the target
Contains multiple PvlContainers.
Definition: PvlGroup.h:57
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
Bullet ray tracing callback for closest hit on target surface.
A type of error that could only have occurred due to a mistake on the user&#39;s part (e...
Definition: IException.h:142
bool isOccluded(const BulletClosestRayCallback &hit, const btVector3 &observer) const
Check if an intersection is occluded from an observer.
BulletShapeModel()
Default constructor that creates a shape model without any internal model.
Container for cube-like labels.
Definition: Pvl.h:135
virtual void clearSurfacePoint()
Clears or resets the current surface point.
Definition: ShapeModel.cpp:380
This class is used to create and store valid Isis3 targets.
Definition: Target.h:76
BulletClosestRayCallback m_intercept
! Tolerance of occlusion check in kilometers.
virtual void clearSurfacePoint()
Clear the saved surface point and reset the saved intersection.
Define shapes and provide utilities for Isis3 targets.
Definition: ShapeModel.h:78
void updateShapeModel(const BulletClosestRayCallback &result)
Update shape model - carefully!!
void setLocalNormalFromIntercept()
Set the normal vector to the intercept point normal.
btVector3 pointToVector(const SurfacePoint &point) const
Convert a surface point into a vector.
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
const BulletClosestRayCallback & hit(const int &index=0) const
Return a callback for the intersection at a given index.
void ToNaifArray(double naifOutput[3]) const
A naif array is a c-style array of size 3.
QString name() const
Return name of the target shape.
void FromNaifArray(const double naifValues[3])
A naif array is a c-style array of size 3.
virtual void setSurfacePoint(const SurfacePoint &surfacePoint)
Set surface intersection point.
Definition: ShapeModel.cpp:559
static void CheckErrors(bool resetNaif=true)
This method looks for any naif errors that might have occurred.
Definition: NaifStatus.cpp:43
Isis exception class.
Definition: IException.h:107
int size() const
Returns the number of intersections found.
bool isVisible(const BulletClosestRayCallback &other, const btScalar tolerance=DBL_MAX) const
Check if the intersection in this is visible based on another callback.
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
void setName(QString name)
Sets the shape name.
Definition: ShapeModel.cpp:526
bool isValid() const
Checks if the callback is valid/has a valid intersection.
void setHasNormal(bool status)
Sets the flag to indicate whether this ShapeModel has a surface normal.
Definition: ShapeModel.cpp:575
const double RAD2DEG
Multiplier for converting from radians to degrees.
Definition: Constants.h:60
btVector3 latlonToVector(const Latitude &lat, const Longitude &lon) const
Convert a pair of latitude and longitude values into a unit vector pointing from the origin of the bo...
void setTolerance(const double &tolerance)
Sets the occlusion tolerance.
Bullet Target Shape for planetary bodies.
const BulletWorldManager & model() const
Returns a direct reference to the Bullet world that contains the target shape and can perform ray cas...
Unless noted otherwise, the portions of Isis written by the USGS are public domain.
virtual bool isVisibleFrom(const std::vector< double > observerPos, const std::vector< double > lookDirection)
Check if the saved intercept is visible from a observer with a given look direction.