1

So I'm looking to represent non-overlapping ranges in an N dimensional space.

I think CGAL has this functionality, and facilitates fast querying of points as the example shows below.

What I'm not sure of is how to extend this kind of query to find open windows.

So in this case I make 2 rectangles and it would be nice if there was a way find an opening of a certain size.

#include <CGAL/Cartesian.h>
#include <CGAL/Segment_tree_k.h>
#include <CGAL/Range_segment_tree_traits.h>
typedef CGAL::Cartesian<double> K;
typedef CGAL::Segment_tree_map_traits_2<K, char> Traits;
typedef CGAL::Segment_tree_2<Traits > Segment_tree_2_type;
int main()
{
  typedef Traits::Interval Interval;
  typedef Traits::Pure_interval Pure_interval;
  typedef Traits::Key Key;
  std::list<Interval> InputList, OutputList1, OutputList2;
  InputList.push_back(Interval(Pure_interval(Key(1,2), Key(1,2)),'a'));
  InputList.push_back(Interval(Pure_interval(Key(2,3), Key(2,3)),'b'));
  Segment_tree_2_type Segment_tree_2(InputList.begin(),InputList.end());
  // ??? probably has multiple solutions?
  Interval find_me=Interval(Pure_interval(Key(0,3), Key(0,1)),'');
  Interval opening = Segment_tree_2.find_opening(find_me);
  return 0;
}
Mikhail
  • 7,749
  • 11
  • 62
  • 136
  • The "open window" means a rectangle without intersections with any predefined rectangles, stored in the segment tree, right? – HEKTO Mar 16 '21 at 03:49
  • @HEKTO Yep. Also the rectangle can't be rotated in the sense the 1x2 isn't the same as 2x1. – Mikhail Mar 16 '21 at 06:41
  • The hypothetical function `find_opening` should return *any* open window with given size, right? I think you need somehow exclude cases, when the open window is found outside the convex hull of given ranges. – HEKTO Mar 16 '21 at 14:36
  • Yeah. A window outside the space isn't useful. – Mikhail Mar 17 '21 at 18:33

1 Answers1

1

I don't think the Segment Tree from the CGAL library can help you to solve this problem, because this tree was designed to perform only two types of queries (window_query and enclosing_query). In both cases the search process returns a subset of the original set of D-dimensional intervals, which was used to build the tree - however you are interested in the open space "between" these intervals, which is not represented explicitly by this data structure.

Problems, similar to the problem you're asking about, were studied in Computational Geometry for a long time - the simplest case is finding a largest (by area or by perimeter) empty rectangle among a set of points on the plane. Generalizations of this problem have been studied as well - for more general obstacles (segments, rectangles, polygons) and higher dimensions. Please see this Wikipage for more information (including references).

However, finding the largest rectangle might be overabundant for you - any rectangle with size more or equal than the given size will suffice, and it will save you some time compared to the largest rectangle search. If your set of rectangles is static, but the size of the empty rectangle you wish to find varies, then it makes sense to preprocess this set into some data structure (as you mentioned above). There are some publications where they present algorithms to find all the maximal empty rectangles and save them in a list. Maximal empty rectangle is defined as a rectangle, which can’t be extended in any direction without intersecting with obstacles. Sorry to say, I couldn’t find any such publications, which can be accessed for free.

I’m suggesting a simple recursive algorithm, which can find an empty rectangle with width and height more or equal than the requested size. The idea is to start from the bounding box of the rectangle set and process rectangles from this set one by one. Each such rectangle is subtracted from the current empty rectangle, however result of this subtraction is represented as a set of maximal rectangles, which may overlap. For example, result of subtraction of the rectangle [0,2)x[0,2) from the rectangle [1,3)x[1,3) is a set of two rectangles [2,3)x[1,3) and [1,3)x[2,3). The algorithm returns an empty rectangle and a Boolean flag, indicating success or fail.

using RVec = std::vector<Rectangle>;
using Result = std::pair<Rectangle, bool>;

Result find(const RVec& V, double W, double H, const Rectangle& R, unsigned J)
{
  if (R.sizeIsLess(W, H))
  {
    // ------ the empty rectangle R is too small
    return {R, false};
  }
  else if (J < V.size())
  {
    // ------ process the obstacle rectangle with number J
    for (const auto& r: subtract(R, V[J]))
    {
      const auto res = find(V, W, H, r, J + 1);
      if (res.second) return {res.first, true};
    }
    return {R, false};
  }
  else
  {
    // ------ the empty rectangle R is big enough, and all the obstacles are processed
    return {R, true};
  }
}

auto find(const RVec& V, double W, double H)
{
  return find(V, W, H, bbox(V), 0);
}

I can’t prove that this algorithm works correctly for any possible set of rectangular obstacles and for any requested width and height, however it worked well in all my tests. The algorithm is recursive, so the limited stack size might be a problem for really large rectangle sets.

The algorithm can be randomized by shuffling the rectangle set and/or the result of rectangles subtraction – then you’ll possibly get multiple solutions for the given rectangle set and given width and height. The algorithm can be extended to higher dimensions as well – then the function subtract will need to be modified. If you are interested I’ll add this function (for 2D case) into this answer.

HEKTO
  • 3,876
  • 2
  • 24
  • 45