]> git.tdb.fi Git - libs/math.git/commitdiff
Optimize bounding box bisection with more early culling
authorMikko Rasa <tdb@tdb.fi>
Tue, 18 Apr 2017 11:16:45 +0000 (14:16 +0300)
committerMikko Rasa <tdb@tdb.fi>
Tue, 18 Apr 2017 11:16:45 +0000 (14:16 +0300)
source/geometry/boundingbox.h
source/geometry/compositeshape.h
source/geometry/intersection.h
source/geometry/negation.h
source/geometry/shape.h
source/geometry/transformedshape.h
source/geometry/union.h

index ac384ecd19760a5274f8a09bfa3a38adaeff3c81..4ae29354d2ce2b69b3c48478bf0928bb4ad35129 100644 (file)
@@ -27,6 +27,9 @@ public:
        T get_minimum_coordinate(unsigned i) const { return min_pt[i]; }
        const LinAl::Vector<T, D> &get_maximum_point() const { return max_pt; }
        T get_maximum_coordinate(unsigned i) const { return max_pt[i]; }
+       LinAl::Vector<T, D> get_dimensions() const { return max_pt-min_pt; }
+       T get_dimension(unsigned i) const { return max_pt[i]-min_pt[i]; }
+
        bool is_empty() const { return empty && !negated; }
        bool is_space() const { return empty && negated; }
        bool is_negated() const { return negated; }
index 5d0cd8c59532ed3b782c3cd60908c8ce96778b6c..c472b437e3052f4948cde3a68ac40a9f1107b35a 100644 (file)
@@ -201,10 +201,12 @@ inline Coverage CompositeShape<T, D, O>::get_coverage(const BoundingBox<T, D> &b
        for(typename ShapeArray::const_iterator i=shapes.begin(); i!=shapes.end(); ++i)
        {
                Coverage c = (*i)->get_coverage(bbox);
-               if(i==shapes.begin() || Ops::shortcircuit(c>coverage))
+               if(i==shapes.begin())
                        coverage = c;
+               else
+                       coverage = Ops::combine_coverage(coverage, c);
 
-               if(coverage!=PARTIAL_COVERAGE && Ops::shortcircuit(coverage==FULL_COVERAGE))
+               if((coverage==NO_COVERAGE && Ops::shortcircuit(false)) || (coverage==FULL_COVERAGE && Ops::shortcircuit(true)))
                        break;
        }
 
index 0e555b03f7086185076ca904512605b0ae204e08..24e668dc35a1924a70e1c3815fecbeac8147595e 100644 (file)
@@ -10,6 +10,7 @@ template<typename T, unsigned D>
 struct IntersectionOps
 {
        static BoundingBox<T, D> combine_aabb(const BoundingBox<T, D> &a, const BoundingBox<T, D> &b) { return a&b; }
+       static Coverage combine_coverage(Coverage a, Coverage b) { return ((a==PARTIAL_COVERAGE && b==a) ? UNCERTAIN_COVERAGE : std::min(a, b)); }
        static bool shortcircuit(bool c) { return !c; }
 };
 
index 91802f8bce9906cfdadcae3bc7382c6e193cb423..60b92e33d1d30def4d60f631c8311acbbcd0c115 100644 (file)
@@ -96,7 +96,7 @@ inline Coverage Negation<T, D>::get_coverage(const BoundingBox<T, D> &bbox) cons
        else if(coverage==NO_COVERAGE)
                return FULL_COVERAGE;
        else
-               return PARTIAL_COVERAGE;
+               return coverage;
 }
 
 } // namespace Geometry
index 8eb3157a17a19237779838e22efa46c97a67b142..920e4560454799d7828a50a9601e3ad4ba2295b7 100644 (file)
@@ -14,6 +14,7 @@ namespace Geometry {
 enum Coverage
 {
        NO_COVERAGE,
+       UNCERTAIN_COVERAGE,
        PARTIAL_COVERAGE,
        FULL_COVERAGE
 };
@@ -96,6 +97,17 @@ inline BoundingBox<T, D> Shape<T, D>::bisect_axis_aligned_bounding_box(unsigned
 
                const LinAl::Vector<T, D> &min_pt = cell.bounding_box.get_minimum_point();
                const LinAl::Vector<T, D> &max_pt = cell.bounding_box.get_maximum_point();
+
+               // Skip cells that are already fully inside the established bounds.
+               bool internal = true;
+               for(unsigned i=0; (i<D && internal); ++i)
+                       internal = (min_pt[i]>=tight_min_pt[i] && max_pt[i]<=tight_max_pt[i]);
+               if(internal)
+               {
+                       queue.pop_front();
+                       continue;
+               }
+
                LinAl::Vector<T, D> center = (min_pt+max_pt)/T(2);
 
                // Bisect each dimension.
@@ -118,9 +130,8 @@ inline BoundingBox<T, D> Shape<T, D>::bisect_axis_aligned_bounding_box(unsigned
                        child.coverage = get_coverage(child.bounding_box);
                        if(child.coverage==FULL_COVERAGE || (child.level==detail && child.coverage!=NO_COVERAGE))
                        {
-                               /* Adjust the bounds when we are certain that it's covered by at
-                               least part of the shape.  Also adjust for uncertain results if
-                               we're on the last level. */
+                               /* Immediately merge cells with full coverage.  Also merge cells
+                               at the last level. */
                                for(unsigned j=0; j<D; ++j)
                                {
                                        tight_min_pt[j] = std::min(tight_min_pt[j], child_min_pt[j]);
@@ -128,6 +139,17 @@ inline BoundingBox<T, D> Shape<T, D>::bisect_axis_aligned_bounding_box(unsigned
                                }
                        }
                        else if(child.coverage==PARTIAL_COVERAGE)
+                       {
+                               /* Merge cells with confirmed partial coverage so the cell is
+                               left just outside the bounding box. */
+                               for(unsigned j=0; j<D; ++j)
+                               {
+                                       tight_min_pt[j] = std::min(tight_min_pt[j], child_max_pt[j]);
+                                       tight_max_pt[j] = std::max(tight_max_pt[j], child_min_pt[j]);
+                               }
+                       }
+
+                       if(child.level<detail && child.coverage!=NO_COVERAGE && child.coverage!=FULL_COVERAGE)
                                queue.push_back(child);
                }
 
index 002f3c1a1cc859485decd5830f940137f33fda16..9387123508dcf081341c076f8c4e41970a10ead0 100644 (file)
@@ -109,7 +109,30 @@ inline unsigned TransformedShape<T, D>::get_intersections(const Ray<T, D> &ray,
 template<typename T, unsigned D>
 inline Coverage TransformedShape<T, D>::get_coverage(const BoundingBox<T, D> &bbox) const
 {
-       return shape->get_coverage(inverse_trans.transform(bbox));
+       BoundingBox<T, D> local_bbox = inverse_trans.transform(bbox);
+       Coverage coverage = shape->get_coverage(local_bbox);
+       if(coverage==PARTIAL_COVERAGE)
+       {
+               BoundingBox<T, D> outer_bbox = transformation.transform(local_bbox);
+               LinAl::Vector<T, D> min_pt = local_bbox.get_minimum_point();
+               LinAl::Vector<T, D> max_pt = local_bbox.get_maximum_point();
+               for(unsigned i=0; i<D; ++i)
+               {
+                       T scale_ratio = (1-bbox.get_dimension(i)/outer_bbox.get_dimension(i))*local_bbox.get_dimension(i);
+                       T low_gap = bbox.get_minimum_coordinate(i)-outer_bbox.get_minimum_coordinate(i);
+                       T high_gap = outer_bbox.get_maximum_coordinate(i)-bbox.get_maximum_coordinate(i);
+                       min_pt[i] += low_gap*scale_ratio;
+                       max_pt[i] -= high_gap-scale_ratio;
+               }
+
+               local_bbox = BoundingBox<T, D>(min_pt, max_pt);
+               if(shape->get_coverage(local_bbox)>=PARTIAL_COVERAGE)
+                       return PARTIAL_COVERAGE;
+               else
+                       return UNCERTAIN_COVERAGE;
+       }
+       else
+               return coverage;
 }
 
 } // namespace Geometry
index 738f1f240f3de600dac9d63e1e048fe786bc3d75..8c02f2b808dc8a18301653c77e8175e83b2ea555 100644 (file)
@@ -10,6 +10,7 @@ template<typename T, unsigned D>
 struct UnionOps
 {
        static BoundingBox<T, D> combine_aabb(const BoundingBox<T, D> &a, const BoundingBox<T, D> &b) { return a|b; }
+       static Coverage combine_coverage(Coverage a, Coverage b) { return std::max(a, b); }
        static bool shortcircuit(bool c) { return c; }
 };