Boost logo

Geometry :

Subject: [ggl] Point/Box in Box Test (Border Cases - 3D)
From: Frank Glinka (glinkaf)
Date: 2011-07-07 09:47:03


Hi Barend!

Am 03.07.2011 16:36, schrieb Barend Gehrels:
> Hi Frank,
>
> I updated the trunk, and moved the within_code from within_util to
> within.hpp.

Thank you Barend! I used it and it works as described/expected. A thing
I should notice is that the performance of the original 'within'
implementation is negatively impacted by this change.

I attached my basic ad-hoc micro-benchmark if you are interested in
doing your own shot. Results with VS2010 in x86 Release and /O2 (run on
Core 2 Duo 2.4 GHz):

Run with old within: 5.72521s
Run with updated within: 23.2128s
Run with within_code: 25.2096s
Run with intersects: 6.86401s
Run with custom coveredBy: 4.29001s (based on outside (>) check solely)

As the difference is quite significant, it might be a thought to
consider providing specialized implementations of 'within' and
'coveredBy' instead of using the 'outside, onBorder, inside' predicate
as an abstraction in both versions.

> It stays in namespace detail until there is complete agreement on the
> interface (parameter, strategy, function name), so maybe you will have
> to rename something in the future, but the functionality is there. I now
> believe that it is the best to adapt to PostGIS and Oracle here, they
> call it "coveredBy". See
> http://postgis.refractions.net/documentation/manual-1.5/ST_CoveredBy.html,
> defining it as "Returns 1 (TRUE) if no point in Geometry A is outside
> GeometryB", which is what I described. So covered_by would be a
> convenient and (I think) intuitive name.

I like that name and staying compliant to established terms is always
good. I guess you noticed that this specification would allow for a
simpler implementation than currently provided by within_code!? (Which
is able to distinguish between 'inside' and 'on border' while the
specification only requires 'on border' - which probably causes the
previously mentioned performance overhead.)

Best regards,
Frank
-------------- next part --------------
// GeometryTest.cpp : Micro-benchmark for certain boost.geometry functions.
// Author: Frank Glinka, University of Muenster, Germany

#include <vector>
#include <iostream>

#include <boost\thread\xtime.hpp>
#include <boost/geometry/algorithms/within.hpp>
#include <boost/geometry/strategies/strategies.hpp> // It seems this header must be included, otherwise the register
                                                     // macros fail to do their job.
#include <boost/geometry/algorithms/intersects.hpp>
#include <boost/geometry/geometries/register/point.hpp>
#include <boost/geometry/geometries/register/box.hpp>

// Create custom vector type.
struct Point3D {
  float x, y, z;

  Point3D() : x(0.0f), y(0.0f), z(0.0f) {}

  Point3D(float a, float b, float c) : x(a), y(b), z(c) {}
};

// Create custom space type.
class Box3D {
public:
  Box3D() : origin(Point3D(0.0f, 0.0f, 0.0f)), size(Point3D(0.0f, 0.0f, 0.0f)) {
  }

  Box3D(Point3D a, Point3D b) : origin(a), size(b) {
  }

  Point3D origin, size;
};

// Register custom types.
BOOST_GEOMETRY_REGISTER_POINT_3D(Point3D, float, boost::geometry::cs::cartesian, x, y, z)
BOOST_GEOMETRY_REGISTER_BOX(Box3D, Point3D, origin, size);

// Helper class for time measurements.
class Timer {
private:
  boost::xtime begin, end;

public:
  Timer() {
    start();
  }

  inline void start() {
    boost::xtime_get(&begin, boost::TIME_UTC);
    end = begin;
  }

  inline double stop() {
    boost::xtime_get(&end, boost::TIME_UTC);
    end.sec -= begin.sec;
    end.nsec -= begin.nsec;
    if(end.nsec < 0) {
      --end.sec;
      end.nsec += 1000000000;
    }

    return duration();
  }

  inline double duration() {
    return ((double)end.sec) + ((double) end.nsec / 1000000000);
  }
};

int main(int argc, char *argv[], char *envp[]) {
  // Experiment settings.
  const size_t batch_size = 1000000;
  const size_t iterations = 1000;
  
  // Create data structures (set of points).
  const Box3D box(Point3D(0,0,0), Point3D(batch_size/2, batch_size/2, batch_size/2));
  const Box3D boxTwo(Point3D(1,1,1), Point3D(2, 2, 2));
  std::vector<Point3D> points;
  points.reserve(batch_size);
  for(size_t i = 0; i < batch_size; ++i) {
    points.push_back(Point3D((float)i, (float)i, (float)i));
  }

  for(int i = 0; i < 10; i++) {
    // Point within box performance test...
    int containsCounter = 0;
    Timer timer = Timer();
    for(size_t j = 0; j < iterations; j++) {
      for(size_t i = 0; i < batch_size; i++) {
        if(boost::geometry::within(points[i], box)) {
          ++containsCounter;
        }
      }
    }
    std::cout << "Point within box testrun took " << timer.stop() << " seconds. Contains: " << containsCounter << std::endl;
  }

  {
    // Point within_code box performance test...
    int containsCounter = 0;
    Timer timer = Timer();
    for(size_t j = 0; j < iterations; j++) {
      for(size_t i = 0; i < batch_size; i++) {
        if(boost::geometry::detail::within_code(points[i], box) != -1) {
          ++containsCounter;
        }
      }
    }
    std::cout << "Point within_code box testrun took " << timer.stop() << " seconds. Contains: " << containsCounter << std::endl;
  }

  {
    // Point intersection box performance test...
    int containsCounter = 0;
    Timer timer = Timer();
    for(size_t j = 0; j < iterations; j++) {
      for(size_t i = 0; i < batch_size; i++) {
        if(boost::geometry::intersects(points[i], box)) {
          ++containsCounter;
        }
      }
    }
    std::cout << "Point intersects box testrun took " << timer.stop() << " seconds. Contains: " << containsCounter << std::endl;

  }

  {
    // Manual point coveredBy box performance test (<)...
    int containsCounter = 0;
    Timer timer = Timer();
    for(size_t j = 0; j < iterations; j++) {
      for(size_t i = 0; i < batch_size; i++) {
        if(points[i].x < box.origin.x || points[i].x > box.size.x || points[i].y < box.origin.y || points[i].y > box.size.y || points[i].z < box.origin.z || points[i].z > box.size.z) {
          continue;
        } else {
          ++containsCounter;
        }
      }
    }
    std::cout << "Manual point coveredBy box testrun (<) took " << timer.stop() << " seconds. Contains: " << containsCounter << std::endl;
  }
   
  //{
  // // Manual point coveredBy box performance test (>=)...
  // int containsCounter = 0;
  // Timer timer = Timer();
  // for(size_t j = 0; j < iterations; j++) {
  // for(size_t i = 0; i < batch_size; i++) {
  // if(points[i].x >= box.origin.x && points[i].x <= box.size.x && points[i].y >= box.origin.y && points[i].y <= box.size.y && points[i].z >= box.origin.z && points[i].z <= box.size.z) {
  // ++containsCounter;
  // }
  // }
  // }
  // std::cout << "Manual point coveredBy box performance testrun (>=) took " << timer.stop() << " seconds. Contains: " << containsCounter << std::endl;
  //}

  // Clean up data structures (set of points).
  points.clear();
  std::vector<Point3D>(points).swap(points);

  return 0;
}


Geometry list run by mateusz at loskot.net