Count the number of intersections of rectangles (cubes)

For simplicity, we will consider the problem of rectangles (for cubes-similarly).

The task is to calculate the total area of intersections of rectangles (or any other additive function of the area of intersections) that are oriented along the axes of the standard coordinate system (SC). It seems that the problem is solved faster than brute force, i.e. faster than in O(n2).

The solution can be constructed as follows:

  1. Let's go over it all ordered pairs of rectangles (their C2n).
  2. For each pair, we calculate the intersection area.

We propose a solution to the problem for the case of two rectangles.

enter a description of the image here

We will denote the projections of points on the axis as Ax, Bx, Cx, Dx, Ay, By... etc.

Then, write down all the projections and sort them according to the ascending value of the coordinates. In order not to duplicate the coordinates, we will take only the points A, B, E for the X-axis, F:

Ax, Ex, Bx, Fx

For the y-axis, respectively:

Hy, Dy, Ey, Ay

Let's start a set and put the dots in it one by one. If there are two points of the same rectangle in the set at the same time, then delete them. If at the same time in the set there are points of different rectangles at each of the coordinates, then we fix the intersection.

Example.

  1. Sx = {} // Set Sx -- on the x-axis
  2. Sy = {} // Set Sy -- on the y axis
  3. Let's add the first ones to the set points for each of the coordinates: Sx = {Ax}, Sy = {Hy}
  4. Add one more point each: Sx = {Ax, Ex}, Sy = {Hy, Dy}. Fixing the intersection of
  5. Sx = {Ax, Ex, Bx}, Sy = {Hy, Dy, Ey}. Calculating the intersection value: (Bx - Ax) * (Hy - Ey)
  6. Removing points from sets: Sx = {Ex}, Sy = {Dy}
  7. Adding points to sets: Sx = {Ex, Fx}, Sy = {Dy, Ay}
  8. Deleting points of identical rectangles: Sx = {}, Sy = {}

Questions:

  • Will it work a similar algorithm for 3 dimensions?
  • Help us generalize it to the case of n rectangles (cubes). I can't think straight.

For the case of double intersections, it requires calculating the total area. Let there be 3 rectangles ABCD, EFGH, IJKL. You need to calculate either the area S (INMO) + S (EOPQ) + S (OSKL), or S(INMO) + 2S(EOPQ) + S(OSKL), whichever is easier to calculate.

enter a description of the image here

Remark

For the case of two the rectangles (cubes) problem is solved easily. Let us have rectangles as the lower left corners and the dimensions of their sides:

x0, y0, z0, dx, dy, dz

Then let's see which of the rectangles lies to the right on each axis. Then we check the intersection conditions and calculate the area. Let's write the code (in golang):

type Box struct {
    dx, dy, dz int
    x, y, z    int
}

func (c *Box) isLineIntersection(p00, p01, p10, p11 int) int {
    if p00 > p10 {
        p00, p01, p10, p11 = p10, p11, p00, p01
    }
    if (p10 >= p01) && (p01 <= p11) {
        return p01 - p10
    } else if (p10 <= p01) && (p11 <= p01) {
        return p11 - p10
    }
    return 0
}


func (c *Box) isIntersection(box1 Box, box2 Box) (intersection int) {
    dx := c.isLineIntersection(box1.x, box1.x+box1.dx, box2.x, box2.x+box2.dx)
    dy := c.isLineIntersection(box1.y, box1.y+box1.dy, box2.y, box2.y+box2.dy)
    dz := c.isLineIntersection(box1.z, box1.z+box1.dz, box2.z, box2.z+box2.dz)
    return dx * dy * dz
}
Author: mr NAE, 2018-08-24

2 answers

Such problems are canonically solved by the classical algorithm Scanning Line. According to your statement, you need to calculate the area of the "territory" covered by more than one rectangle.

In the "simplest" application of this approach:

We represent each of our rectangles by a pair of vertical edges: the left and right edges. We will consider these edges in lexicographic order, sorted by the lower Y, and then by X.

At each step of the algorithm, we will consider a horizontal scanning line and a set of edges sorted from left to right that share common points with this scanning line (i.e., active edges). The scanning line moves from bottom to top. Maintaining such a list of edges when moving from one scan level to another is a simple and efficient operation (remove outgoing edges and insert new ones).

Only those levels should be considered scans at which a vertical edge begins or ends. That is, our scanning line moves from bottom to top by jumping along the end points of our vertical edges.

For each scan level, we analyze the plane coverage directly over the scanning line.

Looking through the list of active edges from left to right, we can easily find out on which vertical edge X1 the multiple coverage of the "territory" with rectangles and at which edge X2 it ends. There may be several such start-end pairs at each scan level. If the next scanning line is located at the level NEXT_Y, then each pair X1-X2 will add the summand (X2 - X1) * (NEXT_Y - Y) to the desired area.

By scanning the entire entrance from bottom to top, we will calculate the desired area.

enter a description of the image here

Such an algorithm is able to work with input from any axially oriented polygons, and not just from rectangles.

Here, for example, is the simplest implementation of this algorithm (C++) with input data taken from your image with three rectangles. We get exactly S (INMO) + S (EOPQ) + S (OSKL), i.e. 22

#include <vector>
#include <set>
#include <algorithm>
#include <iostream>

struct Edge
{
  int x, yb, yt;
  bool is_left;
};

struct Rect
{
  int xl, yb, xr, yt;
};

int main()
{
  const std::vector<Rect> rects = 
  {
    { 0, 6, 5, 11 },
    { 3, 4, 7, 10 },
    { 2, 0, 9, 8 }
  };

  using Edges = std::vector<Edge>; 
  Edges edges;
  for (const Rect &r : rects)
  {
    edges.push_back({ r.xl, r.yb, r.yt, true });
    edges.push_back({ r.xr, r.yb, r.yt, false });
  }

  std::sort(edges.begin(), edges.end(), 
    [](const Edge &lhs, const Edge &rhs) { return lhs.yb != rhs.yb ? lhs.yb < rhs.yb : lhs.x < rhs.x; });

  unsigned area = 0;

  auto less_x = [](const Edge &lhs, const Edge &rhs) { return lhs.x < rhs.x; };
  using SweepLine = std::set<Edge, decltype(less_x)>;
  SweepLine sweep_line(less_x);

  Edges::const_iterator it_e = edges.begin();
  for (int sweep_y = it_e->yb, next_sweep_y; sweep_y != std::numeric_limits<int>::max(); sweep_y = next_sweep_y)
  {
    for (; it_e != edges.end() && it_e->yb == sweep_y; ++it_e)  
      sweep_line.insert(*it_e);

    next_sweep_y = it_e != edges.end() ? it_e->yb : std::numeric_limits<int>::max();

    int prev_x;
    unsigned inside_counter = 0;
    unsigned covered_x = 0;

    for (SweepLine::iterator it_swe = sweep_line.begin(); it_swe != sweep_line.end(); )
    {
      if (it_swe->yt == sweep_y)
      {
        it_swe = sweep_line.erase(it_swe);
        continue;
      }

      next_sweep_y = std::min(next_sweep_y, it_swe->yt);

      unsigned prev_inside_counter = inside_counter;
      inside_counter += it_swe->is_left ? +1 : -1;

      if (prev_inside_counter == 1 && inside_counter == 2)
        prev_x = it_swe->x;
      else if (prev_inside_counter == 2 && inside_counter == 1)
        covered_x += it_swe->x - prev_x;

      ++it_swe;
    }

    area += covered_x * (next_sweep_y - sweep_y);
  }

  std::cout << area << std::endl;
}

Http://coliru.stacked-crooked.com/a/bb0023433d9395be

By an elementary modification of the condition in the inner if , you can force this implementation to calculate the area of the union of rectangles

if (prev_inside_counter == 0 && inside_counter == 1)
  prev_x = it_swe->x;
else if (prev_inside_counter == 1 && inside_counter == 0)
  covered_x += it_swe->x - prev_x;

Area at least triple intersection

if (prev_inside_counter == 2 && inside_counter == 3)
  prev_x = it_swe->x;
else if (prev_inside_counter == 3 && inside_counter == 2)
  covered_x += it_swe->x - prev_x;

The area of a strictly double intersection

if (prev_inside_counter != 2 && inside_counter == 2)
  prev_x = it_swe->x;
else if (prev_inside_counter == 2 && inside_counter != 2)
  covered_x += it_swe->x - prev_x;

The area covered by an odd number of rectangles

if ((prev_inside_counter % 2) == 0 && (inside_counter % 2) != 0)
  prev_x = it_swe->x;
else if ((prev_inside_counter % 2) != 0 && (inside_counter % 2) == 0)
  covered_x += it_swe->x - prev_x;

Etc. etc.

I called this algorithm "the simplest" above because it does not know how to perform localized processing of each scan level: at each level, all active edges of this level are viewed from left to right from beginning to end. A competent implementation of such an algorithm should be able to instead, perform localized processing: only in a certain neighborhood of those places in the scan level where new edges appeared or old ones disappeared. But this will be a significantly less trivial algorithm, although the general idea will remain unchanged.

 6
Author: AnT, 2018-08-24 21:14:12

I see it like this. Summing up the total area. One by one, we add the object to the sum. The sum is a list of ordinary rectangles (cubes). For example, the sum is still one rectangle. Another one is added to it. The next sum will be THREE rectangles. (Ⅰ + Ⅱ + Ⅲ). enter a description of the image here

Then we take another (third) one from the list. And intersect with these three. In the complex case, the total will already be many rectangles. The problem is not reduced to linearity in any way. Think, gentlemen of the jury assessors....

  1. Cubic version. The list of X coordinates of all cubes is taken. It is written to the list of X-s (LZ). All Y's in the list of Y's(LY). All Z coordinates are included in the list of Z-s(LZ). These three lists are sorted (X,Y,Z). Creates a cube array with int elements of size Size(LX)xSize(LY)xSize(LZ). Filled in while 0. This will be called an indexed cube. Where the element (i,j,k) is a sign of fullness at the coordinates (LX(i)..LX(i+1),LY(j)..LY(j+1),LZ(k)..LZ(k+1)). Each 3D rectangle should add its own presence to the to the indexed cube by adding a unit to the elements where it left its mark. The total volume is the sum of all the volumes of the indexed cube, where the element is greater than one (i.e., where there were at least two overlaps).
 0
Author: AlexGlebe, 2020-06-12 12:52:24