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
46using namespace std;
47
48namespace 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
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";
441 throw IException(IException::Programmer, mess, _FILEINFO_);
442 }
443
444 btVector3 normal = m_intercept.normal();
446 }
447
448
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
516 void BulletShapeModel::calculateLocalNormal(QVector<double *> neighborPoints) {
517
518 // Sanity check
519 if ( !hasIntersection() ) { // hasIntersection() <==> !m_intercept.isNull()
520 QString mess = "Intercept point does not exist - cannot provide normal vector";
521 throw IException(IException::Programmer, mess, _FILEINFO_);
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.";
564 throw IException(IException::Programmer, msg, _FILEINFO_);
565 }
566
567 if ( !surfaceIntersection()->Valid() ) {
568 QString msg = "The surface point intersection must be valid to compute the surface normal.";
569 throw IException(IException::Programmer, msg, _FILEINFO_);
570 }
571
572 if (!hasValidTarget()) {
573 QString msg = "A valid target must be defined before computing the surface normal.";
574 throw IException(IException::Programmer, msg, _FILEINFO_);
575 }
576
577 // Get the coordinates of the current surface point
578 SpiceDouble pB[3];
579 surfaceIntersection()->ToNaifArray(pB);
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
584 QVector<Distance> radii = QVector<Distance>::fromStdVector(targetRadii());
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
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
692 QVector<BulletClosestRayCallback> BulletShapeModel::sortHits(const BulletAllHitsRayCallback &hits,
693 const btVector3 &sortPoint) const {
694 QMap<btScalar, BulletClosestRayCallback> sortMap;
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();
723 }
724 else {
726 setHasLocalNormal(false);
727 }
728
729 return;
730 }
731}; // namespace Isis
Bullet ray tracing callback to return all intersections along a ray's path.
const BulletClosestRayCallback & hit(const int &index=0) const
Return a callback for the intersection at a given index.
Bullet ray tracing callback for closest hit on target surface.
btScalar distance() const
Returns the distance from the intersection point to the beginning of the ray.
bool isValid() const
Checks if the callback is valid/has a valid intersection.
btVector3 point() const
Return the intersection point, if one exists.
bool isVisible(const BulletClosestRayCallback &other, const btScalar tolerance=DBL_MAX) const
Check if the intersection in this is visible based on another callback.
btVector3 normal() const
Return the local surface normal at the intersection, if an intersection exists.
const BulletWorldManager & model() const
Returns a direct reference to the Bullet world that contains the target shape and can perform ray cas...
BulletClosestRayCallback m_intercept
! Tolerance of occlusion check in kilometers.
void calculateSurfaceNormal()
compute the ellipsoid surface normal of the target
void updateShapeModel(const BulletClosestRayCallback &result)
Update shape model - carefully!!
void calculateLocalNormal(QVector< double * > cornerNeighborPoints)
Compute the normal for a local region of surface points.
Distance localRadius(const Latitude &lat, const Longitude &lon)
Compute the radius of the body at a lat/lon point.
SurfacePoint makeSurfacePoint(const btVector3 &point) const
Convert a vector into a surface point.
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...
QVector< double > ellipsoidNormal()
Compute the true surface normal vector of an ellipsoid.
double getTolerance() const
Returns the occlusion tolerance in kilometers.
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.
BulletShapeModel()
Default constructor that creates a shape model without any internal model.
void calculateDefaultNormal()
Calculate the surface normal of the ellipsoid as the default.
btVector3 castLookDir(const btVector3 &observer, const btVector3 &lookdir) const
Compute the end point of a ray based on an observer and look direction.
QVector< BulletClosestRayCallback > sortHits(const BulletAllHitsRayCallback &hits, const btVector3 &sortPoint) const
Sort the hits in an AllHitsRayCallback based on distance to a point.
virtual void clearSurfacePoint()
Clear the saved surface point and reset the saved intersection.
double m_tolerance
! Bullet collision world that contains the representation of the body.
void setLocalNormalFromIntercept()
Set the normal vector to the intercept point normal.
btScalar maxDistance() const
! The results of the last ray cast.
bool isDEM() const
Indicates that this shape model is not from a DEM.
bool isOccluded(const BulletClosestRayCallback &hit, const btVector3 &observer) const
Check if an intersection is occluded from an observer.
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...
virtual void setSurfacePoint(const SurfacePoint &surfacePoint)
Set the internal surface point.
btVector3 pointToVector(const SurfacePoint &point) const
Convert a surface point into a vector.
void setTolerance(const double &tolerance)
Sets the occlusion tolerance.
Bullet Target Shape for planetary bodies.
static BulletTargetShape * load(const QString &dem, const Pvl *conf=0)
Load a DEM file into the target shape.
QString name() const
Return name of the target shape.
Bullet World manager maintains a proper state for target bodies.
Distance measurement, usually in meters.
Definition Distance.h:34
@ Kilometers
The distance is being specified in kilometers.
Definition Distance.h:45
Isis exception class.
Definition IException.h:91
@ User
A type of error that could only have occurred due to a mistake on the user's part (e....
Definition IException.h:126
@ Programmer
This error is for when a programmer made an API call that was illegal.
Definition IException.h:146
This class is designed to encapsulate the concept of a Latitude.
Definition Latitude.h:51
This class is designed to encapsulate the concept of a Longitude.
Definition Longitude.h:40
static void CheckErrors(bool resetNaif=true)
This method looks for any naif errors that might have occurred.
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
Contains multiple PvlContainers.
Definition PvlGroup.h:41
Container for cube-like labels.
Definition Pvl.h:119
@ Traverse
Search child objects.
Definition PvlObject.h:158
Define shapes and provide utilities for Isis targets.
Definition ShapeModel.h:66
virtual void clearSurfacePoint()
Clears or resets the current surface point.
bool hasIntersection()
Returns intersection status.
void setNormal(const std::vector< double >)
Sets the surface normal for the currect intersection point.
void setLocalNormal(const std::vector< double >)
Sets the local normal for the currect intersection point.
virtual SurfacePoint * surfaceIntersection() const
Returns the surface intersection for this ShapeModel.
bool hasValidTarget() const
Returns the status of the target.
virtual void setSurfacePoint(const SurfacePoint &surfacePoint)
Set surface intersection point.
virtual std::vector< double > normal()
Returns the surface normal at the current intersection point.
std::vector< Distance > targetRadii() const
Returns the radii of the body in km.
void setHasLocalNormal(bool status)
Sets the flag to indicate whether this ShapeModel has a local normal.
void setName(QString name)
Sets the shape name.
This class defines a body-fixed surface point.
void FromNaifArray(const double naifValues[3])
A naif array is a c-style array of size 3.
This class is used to create and store valid Isis targets.
Definition Target.h:63
This is free and unencumbered software released into the public domain.
Definition Calculator.h:18
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
const double RAD2DEG
Multiplier for converting from radians to degrees.
Definition Constants.h:44
Namespace for the standard library.