1 /*
2  * MOAB, a Mesh-Oriented datABase, is a software component for creating,
3  * storing and accessing finite element mesh data.
4  *
5  * Copyright 2004 Sandia Corporation.  Under the terms of Contract
6  * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
7  * retains certain rights in this software.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  */
15 
16 /*
17  * The algorithms for the calculation of the oriented box from a
18  * set of points or a set of cells was copied from the implementation
19  " in the "Visualization Toolkit".  J.K. - 2006-07-19
20  *
21  * Program:   Visualization Toolkit
22  * Module:    $RCSfile$
23  *
24  * Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
25  * All rights reserved.
26  * See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
27  */
28 
29 /**\file OrientedBox.cpp
30  *\author Jason Kraftcheck (kraftche@cae.wisc.edu)
31  *\date 2006-07-18
32  */
33 
34 #include "moab/Interface.hpp"
35 #include "moab/CN.hpp"
36 #include "moab/OrientedBox.hpp"
37 #include "moab/Range.hpp"
38 #include <ostream>
39 #include <assert.h>
40 #include <limits>
41 
42 namespace moab {
43 
operator <<(std::ostream & s,const OrientedBox & b)44 std::ostream& operator<<( std::ostream& s, const OrientedBox& b )
45 {
46   return s << b.center
47            << " + "
48            << b.axes.col(0)
49 #if MB_ORIENTED_BOX_UNIT_VECTORS
50            << ":" << b.length[0]
51 #endif
52            << " x "
53            << b.axes.col(1)
54 #if MB_ORIENTED_BOX_UNIT_VECTORS
55            << ":" << b.length[1]
56 #endif
57            << " x "
58            << b.axes.col(2)
59 #if MB_ORIENTED_BOX_UNIT_VECTORS
60            << ":" << b.length[2]
61 #endif
62             ;
63 }
64 
65 /**\brief Find closest point on line
66  *
67  * Find the point on the line for which a line trough the
68  * input point \a p and the result position is orthogonal to
69  * the input line.
70  * \param p  The point for which to find the perpendicular
71  * \param b  A point on the line
72  * \param m  The direction of the line
73  * \return   The location on the line specified as 't' in the
74  *           formula t * m + b
75  */
point_perp(const CartVect & p,const CartVect & b,const CartVect & m)76 static double point_perp( const CartVect& p,   // closest to this point
77                           const CartVect& b,   // point on line
78                           const CartVect& m )  // line direction
79 {
80 #if MB_ORIENTED_BOX_UNIT_VECTORS
81   double t = (m % (p - b));
82 #else
83   double t = (m % (p - b)) / (m % m);
84 #endif
85   return Util::is_finite(t) ? t : 0.0;
86 }
87 
order_axes_by_length(double ax1_len,double ax2_len,double ax3_len)88 void OrientedBox::order_axes_by_length(double ax1_len, double ax2_len, double ax3_len)
89 {
90 
91   CartVect len( ax1_len, ax2_len, ax3_len );
92 
93   if (len[2] < len[1])
94     {
95       if (len[2] < len[0]) {
96 	std::swap( len[0], len[2] );
97 	axes.swapcol( 0, 2 );
98       }
99     }
100   else if (len[1] < len[0]) {
101     std::swap( len[0], len[1] );
102     axes.swapcol( 0, 1 );
103   }
104   if (len[1] > len[2]) {
105     std::swap( len[1], len[2] );
106     axes.swapcol( 1, 2 );
107   }
108 
109 #if MB_ORIENTED_BOX_UNIT_VECTORS
110     length = len;
111     if (len[0] > 0.0)
112       axes.colscale(0, 1.0/len[0]);
113     if (len[1] > 0.0)
114       axes.colscale(1, 1.0/len[1]);
115     if (len[2] > 0.0)
116       axes.colscale(2 ,1.0/len[2]);
117 #endif
118 
119 #if MB_ORIENTED_BOX_OUTER_RADIUS
120     radius = len.length();
121 #endif
122 }
123 
OrientedBox(const CartVect axes_in[3],const CartVect & mid)124 OrientedBox::OrientedBox( const CartVect axes_in[3], const CartVect& mid )
125   : center(mid)
126 {
127 
128   axes = Matrix3( axes_in[0], axes_in[1], axes_in[2], false );
129 
130   order_axes_by_length( axes_in[0].length(),axes_in[1].length(),axes_in[2].length() );
131 
132 }
133 
OrientedBox(const Matrix3 & axes_mat,const CartVect & mid)134 OrientedBox::OrientedBox( const Matrix3& axes_mat, const CartVect& mid )
135   : center(mid), axes(axes_mat)
136 {
137   order_axes_by_length( axes.col(0).length(), axes.col(1).length(), axes.col(2).length() );
138 }
139 
tag_handle(Tag & handle_out,Interface * instance,const char * name)140 ErrorCode OrientedBox::tag_handle( Tag& handle_out,
141                                        Interface* instance,
142                                        const char* name)
143 {
144     // We're going to assume this when mapping the OrientedBox
145     // to tag data, so assert it.
146 #if MB_ORIENTED_BOX_OUTER_RADIUS
147   const int rad_size = 1;
148 #else
149   const int rad_size = 0;
150 #endif
151 #if MB_ORIENTED_BOX_UNIT_VECTORS
152   const int SIZE = rad_size + 15;
153 #else
154   const int SIZE = rad_size + 12;
155 #endif
156   assert( sizeof(OrientedBox) == SIZE*sizeof(double) );
157 
158   return instance->tag_get_handle( name, SIZE, MB_TYPE_DOUBLE,
159                                    handle_out, MB_TAG_DENSE|MB_TAG_CREAT );
160 }
161 
162 /**\brief Common code for box calculation
163  *
164  * Given the orientation of the box and an approximate center,
165  * calculate the exact center and extents of the box.
166  *
167  *\param result.center  As input, the approximate center of the box.
168  *                      As output, the exact center of the box.
169  *\param result.axes    As input, directions of principal axes corresponding
170  *                      to the orientation of the box.  Axes are assumed to
171  *                      be unit-length on input.  Output will include extents
172  *                      of box.
173  *\param points  The set of points the box should contain.
174  */
box_from_axes(OrientedBox & result,Interface * instance,const Range & points)175 static ErrorCode box_from_axes( OrientedBox& result,
176                                   Interface* instance,
177                                   const Range& points )
178 {
179   ErrorCode rval;
180 
181     // project points onto axes to get box extents
182   CartVect min(std::numeric_limits<double>::max()),
183              max(-std::numeric_limits<double>::max());
184   for (Range::iterator i = points.begin(); i != points.end(); ++i)
185   {
186     CartVect coords;
187     rval = instance->get_coords( &*i, 1, coords.array() );MB_CHK_ERR(rval);
188 
189     for (int d = 0; d < 3; ++d)
190     {
191       const double t = point_perp( coords, result.center, result.axes.col(d) );
192       if (t < min[d])
193         min[d] = t;
194       if (t > max[d])
195         max[d] = t;
196     }
197   }
198 
199     // We now have a box defined by three orthogonal line segments
200     // that intersect at the center of the box.  Each line segment
201     // is defined as result.center + t * result.axes[i], where the
202     // range of t is [min[i], max[i]].
203 
204     // Calculate new center
205   const CartVect mid = 0.5 * (min + max);
206   result.center += mid[0] * result.axes.col(0) +
207                    mid[1] * result.axes.col(1) +
208                    mid[2] * result.axes.col(2);
209 
210     // reorder axes by length
211   CartVect range = 0.5 * (max - min);
212   if (range[2] < range[1])
213   {
214     if (range[2] < range[0]) {
215       std::swap( range[0], range[2] );
216       result.axes.swapcol( 0, 2 );
217     }
218   }
219   else if (range[1] < range[0]) {
220     std::swap( range[0], range[1] );
221     result.axes.swapcol( 0, 1 );
222   }
223   if (range[1] > range[2]) {
224     std::swap( range[1], range[2] );
225     result.axes.swapcol( 1, 2 );
226   }
227 
228     // scale axis to encompass all points, divide in half
229 #if MB_ORIENTED_BOX_UNIT_VECTORS
230   result.length = range;
231 #else
232   result.axes.colscale(0, range[0]);
233   result.axes.colscale(1, range[1]);
234   result.axes.colscale(2, range[2]);
235 #endif
236 
237 #if MB_ORIENTED_BOX_OUTER_RADIUS
238   result.radius = range.length();
239 #endif
240 
241   return MB_SUCCESS;
242 }
243 
244 
compute_from_vertices(OrientedBox & result,Interface * instance,const Range & vertices)245 ErrorCode OrientedBox::compute_from_vertices( OrientedBox& result,
246                                                   Interface* instance,
247                                                   const Range& vertices )
248 {
249   const Range::iterator begin = vertices.lower_bound( MBVERTEX );
250   const Range::iterator end = vertices.upper_bound( MBVERTEX );
251   size_t count = 0;
252 
253     // compute mean
254   CartVect v;
255   result.center = CartVect( 0, 0, 0 );
256   for (Range::iterator i = begin; i != end; ++i)
257   {
258     ErrorCode rval = instance->get_coords( &*i, 1, v.array() );
259     if (MB_SUCCESS != rval)
260       return rval;
261     result.center += v;
262     ++count;
263   }
264   result.center /= count;
265 
266     // compute covariance matrix
267   Matrix3 a( 0.0 );
268   for (Range::iterator i = begin; i != end; ++i)
269   {
270     ErrorCode rval = instance->get_coords( &*i, 1, v.array() );
271     if (MB_SUCCESS != rval)
272       return rval;
273 
274     v -= result.center;
275     a += outer_product( v, v );
276   }
277   a /= count;
278 
279     // Get axes (Eigenvectors) from covariance matrix
280   CartVect lambda;
281   a.eigen_decomposition(lambda, result.axes);
282 
283     // Calculate center and extents of box given orientation defined by axes
284   return box_from_axes( result, instance, vertices );
285 }
286 
covariance_data_from_tris(CovarienceData & result,Interface * instance,const Range & elements)287 ErrorCode OrientedBox::covariance_data_from_tris( CovarienceData& result,
288                                                  Interface* instance,
289                                                  const Range& elements )
290 {
291   ErrorCode rval;
292   const Range::iterator begin = elements.lower_bound( CN::TypeDimensionMap[2].first );
293   const Range::iterator end = elements.lower_bound( CN::TypeDimensionMap[3].first );
294 
295     // compute mean and moments
296   result.matrix = Matrix3(0.0);
297   result.center = CartVect(0.0);
298   result.area = 0.0;
299   for (Range::iterator i = begin; i != end; ++i)
300   {
301     const EntityHandle* conn = NULL;
302     int conn_len = 0;
303     rval = instance->get_connectivity( *i, conn, conn_len );
304     if (MB_SUCCESS != rval)
305       return rval;
306 
307       // for each triangle in the 2-D cell
308     for (int j = 2; j < conn_len; ++j)
309     {
310       EntityHandle vertices[3] = { conn[0], conn[j-1], conn[j] };
311       CartVect coords[3];
312       rval = instance->get_coords( vertices, 3, coords[0].array() );
313       if (MB_SUCCESS != rval)
314         return rval;
315 
316         // edge vectors
317       const CartVect edge0 = coords[1] - coords[0];
318       const CartVect edge1 = coords[2] - coords[0];
319       const CartVect centroid = (coords[0] + coords[1] + coords[2]) / 3;
320       const double tri_area2 = (edge0 * edge1).length();
321       result.area += tri_area2;
322       result.center += tri_area2 * centroid;
323 
324       result.matrix += tri_area2 * (9 * outer_product( centroid,  centroid  ) +
325                                     outer_product( coords[0], coords[0] ) +
326                                     outer_product( coords[1], coords[1] ) +
327                                     outer_product( coords[2], coords[2] ));
328     } // for each triangle
329   } // for each element
330 
331   return MB_SUCCESS;
332 }
333 
334 
compute_from_2d_cells(OrientedBox & result,Interface * instance,const Range & elements)335 ErrorCode OrientedBox::compute_from_2d_cells( OrientedBox& result,
336                                                   Interface* instance,
337                                                   const Range& elements )
338 {
339     // Get orientation data from elements
340   CovarienceData data;
341   ErrorCode rval = covariance_data_from_tris( data, instance, elements );
342   if (MB_SUCCESS != rval)
343     return rval;
344 
345     // get vertices from elements
346   Range points;
347   rval = instance->get_adjacencies( elements, 0, false, points, Interface::UNION );
348   if (MB_SUCCESS != rval)
349     return rval;
350 
351     // Calculate box given points and orientation data
352   return compute_from_covariance_data( result, instance, data, points );
353 }
354 
compute_from_covariance_data(OrientedBox & result,Interface * instance,CovarienceData & data,const Range & vertices)355 ErrorCode OrientedBox::compute_from_covariance_data(
356                                                 OrientedBox& result,
357                                                 Interface* instance,
358                                                 CovarienceData& data,
359                                                 const Range& vertices )
360 {
361   if (data.area <= 0.0) {
362     Matrix3 empty_axes (0.0);
363     result = OrientedBox( empty_axes, CartVect(0.) );
364     return MB_SUCCESS;
365   }
366 
367     // get center from sum
368   result.center = data.center / data.area;
369 
370     // get covariance matrix from moments
371   data.matrix /= 12 * data.area;
372   data.matrix -= outer_product( result.center, result.center );
373 
374     // get axes (Eigenvectors) from covariance matrix
375   CartVect lambda;
376   data.matrix.eigen_decomposition(lambda, result.axes);
377 
378     // We now have only the axes.  Calculate proper center
379     // and extents for enclosed points.
380   return box_from_axes( result, instance, vertices );
381 }
382 
contained(const CartVect & point,double tol) const383 bool OrientedBox::contained( const CartVect& point, double tol ) const
384 {
385   CartVect from_center = point - center;
386 #if MB_ORIENTED_BOX_UNIT_VECTORS
387   return fabs(from_center % axes.col(0)) - length[0] <= tol &&
388          fabs(from_center % axes.col(1)) - length[1] <= tol &&
389          fabs(from_center % axes.col(2)) - length[2] <= tol ;
390 #else
391   for (int i = 0; i < 3; ++i) {
392     double length = axes.col(i).length();
393     if (fabs(from_center % axes.col(i)) - length*length > length*tol)
394       return false;
395   }
396   return true;
397 #endif
398 }
399 
compute_from_covariance_data(OrientedBox & result,Interface * moab_instance,const CovarienceData * data,unsigned data_length,const Range & vertices)400 ErrorCode OrientedBox::compute_from_covariance_data( OrientedBox& result,
401                                                 Interface* moab_instance,
402                                                 const CovarienceData* data,
403                                                 unsigned data_length,
404                                                 const Range& vertices )
405 {
406     // Sum input CovarienceData structures
407   CovarienceData data_sum( Matrix3(0.0), CartVect(0.0), 0.0 );
408   for (const CovarienceData* const end = data+data_length; data != end; ++data) {
409     data_sum.matrix += data->matrix;
410     data_sum.center += data->center;
411     data_sum.area += data->area;
412   }
413     // Compute box from sum of structs
414   return compute_from_covariance_data( result, moab_instance, data_sum, vertices );
415 }
416 
417 
418 
419 //bool OrientedBox::contained( const OrientedBox& box, double tol ) const
420 //{
421 //  for (int i = -1; i < 2; i += 2)
422 //  {
423 //    for (int j = -1; j < 2; j += 2)
424 //    {
425 //      for (int k = -1; k < 2; k += 2)
426 //      {
427 //        CartVect corner( center );
428 //#ifdef MB_ORIENTED_BOX_UNIT_VECTORS
429 //        corner += i * box.length[0] * box.axis.col(0);
430 //        corner += j * box.length[1] * box.axis.col(1);
431 //        corner += k * box.length[2] * box.axis.col(2);
432 //#else
433 //        corner += i * box.axis.col(0);
434 //        corner += j * box.axis.col(1);
435 //        corner += k * box.axis.col(2);
436 //#endif
437 //        if (!contained( corner, tol ))
438 //          return false;
439 //      }
440 //    }
441 //  }
442 //  return true;
443 //}
444 
445 
446 /* This is a helper function to check limits on ray length, turning the box-ray
447  * intersection test into a box-segment intersection test. Use this to test the
448  * limits against one side (plane) of the box. The side of the box (plane) is
449  * normal to an axis.
450  *
451  *   normal_par_pos  Coordinate of particle's position along axis normal to side of box
452  *   normal_par_dir  Coordinate of particle's direction along axis normal to side of box
453  *   half_extent     Distance between center of box and side of box
454  *   nonneg_ray_len  Maximum ray length in positive direction (in front of origin)
455  *   neg_ray_len     Maximum ray length in negative direction (behind origin)
456  *   return          true if intersection with plane occurs within distance limits
457  *
458  * ray equation:   intersection = origin + dist*direction
459  * plane equation: intersection.plane_normal = half_extent
460  *
461  * Assume plane_normal and direction are unit vectors. Combine equations.
462  *
463  *     (origin + dist*direction).plane_normal = half_extent
464  *     origin.plane_normal + dist*direction.plane_normal = half_extent
465  *     dist = (half_extent - origin.plane_normal)/(direction.plane_normal)
466  *
467  * Although this solves for distance, avoid floating point division.
468  *
469  *     dist*direction.plane_normal = half_extent - origin.plane_normal
470  *
471  * Use inequalities to test dist against ray length limits. Be aware that
472  * inequalities change due to sign of direction.plane_normal.
473  */
check_ray_limits(const double normal_par_pos,const double normal_par_dir,const double half_extent,const double * nonneg_ray_len,const double * neg_ray_len)474 inline bool check_ray_limits(const double  normal_par_pos,
475                              const double  normal_par_dir,
476                              const double  half_extent,
477                              const double* nonneg_ray_len,
478                              const double* neg_ray_len ) {
479 
480   const double extent_pos_diff = half_extent - normal_par_pos;
481 
482   // limit in positive direction
483   if(nonneg_ray_len) { // should be 0 <= t <= nonneg_ray_len
484     assert(0 <= *nonneg_ray_len);
485     if       (normal_par_dir>0) { // if/else if needed for pos/neg divisor
486       if(*nonneg_ray_len*normal_par_dir>=extent_pos_diff && extent_pos_diff>=0) return true;
487     } else if(normal_par_dir<0) {
488       if(*nonneg_ray_len*normal_par_dir<=extent_pos_diff && extent_pos_diff<=0) return true;
489     }
490   } else {            // should be 0 <= t
491     if       (normal_par_dir>0) { // if/else if needed for pos/neg divisor
492       if(extent_pos_diff>=0) return true;
493     } else if(normal_par_dir<0) {
494       if(extent_pos_diff<=0) return true;
495     }
496   }
497 
498   // limit in negative direction
499   if(neg_ray_len) {   // should be neg_ray_len <= t < 0
500     assert(0 >= *neg_ray_len);
501     if       (normal_par_dir>0) { // if/else if needed for pos/neg divisor
502       if(*neg_ray_len*normal_par_dir<=extent_pos_diff && extent_pos_diff<0) return true;
503     } else if(normal_par_dir<0) {
504       if(*neg_ray_len*normal_par_dir>=extent_pos_diff && extent_pos_diff>0) return true;
505     }
506   }
507 
508   return false;
509 }
510 
511 /* This implementation copied from cgmMC (overlap.C).
512  * Original author:  Tim Tautges?
513  */
intersect_ray(const CartVect & ray_origin,const CartVect & ray_direction,const double reps,const double * nonneg_ray_len,const double * neg_ray_len) const514 bool OrientedBox::intersect_ray( const CartVect& ray_origin,
515                                  const CartVect& ray_direction,
516 				 const double    reps,
517 				 const double*   nonneg_ray_len,
518                                  const double*   neg_ray_len ) const
519 {
520   // test distance from box center to line
521   const CartVect cx       = center - ray_origin;
522   const double dist_s     = cx % ray_direction;
523   const double dist_sq    = cx % cx - (dist_s*dist_s);
524   const double max_diagsq = outer_radius_squared(reps);
525 
526   // For the largest sphere, no intersections exist if discriminant is negative.
527   // Geometrically, if distance from box center to line is greater than the
528   // longest diagonal, there is no intersection.
529   // manipulate the discriminant: 0 > dist_s*dist_s - cx%cx + max_diagsq
530   if(dist_sq > max_diagsq) return false;
531 
532   // If the closest possible intersection must be closer than nonneg_ray_len. Be
533   // careful with absolute value, squaring distances, and subtracting squared
534   // distances.
535   if (nonneg_ray_len) {
536     assert(0<=*nonneg_ray_len);
537     double max_len;
538     if(neg_ray_len) {
539       assert(0>=*neg_ray_len);
540       max_len = std::max(*nonneg_ray_len,-(*neg_ray_len));
541     } else {
542       max_len = *nonneg_ray_len;
543     }
544     const double temp = fabs(dist_s) - max_len;
545     if(0.0<temp && temp*temp>max_diagsq) return false;
546   }
547 
548   // if smaller than shortest diagonal, we do hit
549   if (dist_sq < inner_radius_squared(reps)) {
550     // nonnegative direction
551     if(dist_s>=0.0 ) {
552       if(nonneg_ray_len) {
553         if(*nonneg_ray_len>dist_s) return true;
554       } else {
555         return true;
556       }
557     // negative direction
558     } else {
559       if(neg_ray_len && *neg_ray_len<dist_s) return true;
560     }
561   }
562 
563   // get transpose of axes
564   Matrix3 B = axes.transpose();
565 
566   // transform ray to box coordintae system
567   CartVect par_pos = B * (ray_origin - center);
568   CartVect par_dir = B * ray_direction;
569 
570   // Fast Rejection Test: Ray will not intersect if it is going away from the box.
571   // This will not work for rays with neg_ray_len. length[0] is half of box width
572   // along axes.col(0).
573   const double half_x = length[0] + reps;
574   const double half_y = length[1] + reps;
575   const double half_z = length[2] + reps;
576   if(!neg_ray_len) {
577     if ((par_pos[0] >  half_x && par_dir[0] >= 0) ||
578 	(par_pos[0] < -half_x && par_dir[0] <= 0))
579       return false;
580 
581     if ((par_pos[1] >  half_y && par_dir[1] >= 0) ||
582 	(par_pos[1] < -half_y && par_dir[1] <= 0))
583       return false;
584 
585     if ((par_pos[2] >  half_z && par_dir[2] >= 0) ||
586 	(par_pos[2] < -half_z && par_dir[2] <= 0))
587       return false;
588   }
589 
590   // test if ray_origin is inside box
591   if (par_pos[0] <= half_x && par_pos[0] >= -half_x &&
592       par_pos[1] <= half_y && par_pos[1] >= -half_y &&
593       par_pos[2] <= half_z && par_pos[2] >= -half_z)
594     return true;
595 
596     //test two xy plane
597   if (fabs(par_dir[0] * (half_z - par_pos[2]) + par_dir[2] * par_pos[0])
598         <= fabs(par_dir[2] * half_x) &&  // test against x extents using z
599       fabs(par_dir[1] * (half_z - par_pos[2]) + par_dir[2] * par_pos[1])
600         <= fabs(par_dir[2] * half_y) &&  // test against y extents using z
601       check_ray_limits( par_pos[2], par_dir[2], half_z, nonneg_ray_len, neg_ray_len ) )
602     return true;
603   if (fabs(par_dir[0] * (-half_z - par_pos[2]) + par_dir[2] * par_pos[0])
604         <= fabs(par_dir[2] * half_x) &&
605       fabs(par_dir[1] * (-half_z - par_pos[2]) + par_dir[2] * par_pos[1])
606         <= fabs(par_dir[2] * half_y) &&
607       check_ray_limits( par_pos[2], par_dir[2], -half_z, nonneg_ray_len, neg_ray_len ) )
608     return true;
609 
610     //test two xz plane
611   if (fabs(par_dir[0] * (half_y - par_pos[1]) + par_dir[1] * par_pos[0])
612         <= fabs(par_dir[1] * half_x) &&
613       fabs(par_dir[2] * (half_y - par_pos[1]) + par_dir[1] * par_pos[2])
614         <= fabs(par_dir[1] * half_z) &&
615       check_ray_limits( par_pos[1], par_dir[1], half_y, nonneg_ray_len, neg_ray_len ) )
616     return true;
617   if (fabs(par_dir[0] * (-half_y - par_pos[1]) + par_dir[1] * par_pos[0])
618         <= fabs(par_dir[1] * half_x) &&
619       fabs(par_dir[2] * (-half_y - par_pos[1]) + par_dir[1] * par_pos[2])
620         <= fabs(par_dir[1] * half_z) &&
621       check_ray_limits( par_pos[1], par_dir[1], -half_y, nonneg_ray_len, neg_ray_len ) )
622     return true;
623 
624     //test two yz plane
625   if (fabs(par_dir[1] * (half_x - par_pos[0]) + par_dir[0] * par_pos[1])
626         <= fabs(par_dir[0] * half_y) &&
627       fabs(par_dir[2] * (half_x - par_pos[0]) + par_dir[0] * par_pos[2])
628         <= fabs(par_dir[0] * half_z) &&
629       check_ray_limits( par_pos[0], par_dir[0], half_x, nonneg_ray_len, neg_ray_len ) )
630     return true;
631   if (fabs(par_dir[1] * (-half_x - par_pos[0]) + par_dir[0] * par_pos[1])
632         <= fabs(par_dir[0] * half_y) &&
633       fabs(par_dir[2] * (-half_x - par_pos[0]) + par_dir[0] * par_pos[2])
634         <= fabs(par_dir[0] * half_z) &&
635       check_ray_limits( par_pos[0], par_dir[0], -half_x, nonneg_ray_len, neg_ray_len ) )
636     return true;
637 
638   return false;
639 }
640 
make_hex(EntityHandle & hex,Interface * instance)641 ErrorCode OrientedBox::make_hex( EntityHandle& hex, Interface* instance )
642 {
643   ErrorCode rval;
644   int signs[8][3] = { { -1, -1, -1 },
645                       {  1, -1, -1 },
646                       {  1,  1, -1 },
647                       { -1,  1, -1 },
648                       { -1, -1,  1 },
649                       {  1, -1,  1 },
650                       {  1,  1,  1 },
651                       { -1,  1,  1 } };
652 
653   std::vector<EntityHandle> vertices;
654   for (int i = 0; i < 8; ++i)
655   {
656     CartVect coords(center);
657     for (int j = 0; j < 3; ++j){
658 #if MB_ORIENTED_BOX_UNIT_VECTORS
659       coords += signs[i][j] * (axes.col(j)*length[j]);
660 #else
661       coords += signs[i][j] * axes.col(j);
662 #endif
663     }
664     EntityHandle handle;
665     rval = instance->create_vertex( coords.array(), handle );
666     if (MB_SUCCESS != rval) {
667       instance->delete_entities( &vertices[0], vertices.size() );
668       return rval;
669     }
670     vertices.push_back( handle );
671   }
672 
673   rval = instance->create_element( MBHEX, &vertices[0], vertices.size(), hex );
674   if (MB_SUCCESS != rval) {
675     instance->delete_entities( &vertices[0], vertices.size() );
676     return rval;
677   }
678 
679   return MB_SUCCESS;
680 }
681 
closest_location_in_box(const CartVect & input_position,CartVect & output_position) const682 void OrientedBox::closest_location_in_box(
683                                     const CartVect& input_position,
684                                     CartVect& output_position ) const
685 {
686     // get coordinates on box axes
687   const CartVect from_center = input_position - center;
688 
689 #if MB_ORIENTED_BOX_UNIT_VECTORS
690   CartVect local( from_center % axes.col(0),
691                     from_center % axes.col(1),
692                     from_center % axes.col(2) );
693 
694   for (int i = 0; i < 3; ++i) {
695     if (local[i] < -length[i])
696       local[i] = -length[i];
697     else if (local[i] > length[i])
698       local[i] =  length[i];
699   }
700 #else
701   CartVect local( (from_center % axes.col(0)) / (axes.col(0) % axes.col(0)),
702                     (from_center % axes.col(1)) / (axes.col(1) % axes.col(1)),
703                     (from_center % axes.col(2)) / (axes.col(2) % axes.col(2)) );
704 
705   for (int i = 0; i < 3; ++i) {
706     if (local[i] < -1.0)
707       local[i] = -1.0;
708     else if (local[i] > 1.0)
709       local[i] = 1.0;
710   }
711 #endif
712 
713   output_position = center
714                   + local[0] * axes.col(0)
715                   + local[1] * axes.col(1)
716                   + local[2] * axes.col(2);
717 }
718 
719 } // namespace moab
720