JavaScript合并相交矩形

时间:2011-03-02 07:02:42

标签: javascript algorithm merge intersection

我需要一种方法来合并一个矩形对象数组(具有x,y,w,h属性的对象),只有它们相交。例如:

merge([{x:0, y:0, w:5, h:5}, {x:1, y:1, w:5, h:5}])

会返回:[{x:0, y:0, w:6, h:6}]


merge([{x:0, y:0, w:1, h:1}, {x:5, y:5, w:1, h:1}])

会返回:[{x:0, y:0, w:1, h:1}, {x:5, y:5, w:1, h:1}]


merge([{x:0, y:0, w:5, h:5}, {x:1, y:1, w:5, h:5}, {x:15, y:15, w:1, h:1}])

会返回:[{x:0, y:0, w:6, h:6}, {x:15, y:15, w:1, h:1}]


如果两个矩形相交,则应将最小边界矩形替换为两个矩形。如果新MBR导致与其他矩形交叉,则需要在合并后再次检查该列表。对于我的生活,我无法理解。

3 个答案:

答案 0 :(得分:7)

我不确定这是否会起作用,但我不知道......

function mergeAll(array) {
  do {
     var newArr = [], didMerge = false, i = 0;

     while (i < array.length) {
        if (intersects(array[i], array[i+1]) {
          newArr.push(merge(array[i], array[i+1]));
          i++;
          didMerge = true;
        }
        i++;
     }
     array = newArr;
  } while (didMerge);
  return array;
}

function intersects(r1, r2) {
    return !( r2.x > r1.x+r1.w
           || r2.x+r2.w < r1.x
           || r2.y > r1.y+r1.h
           || r2.y+r2.h < r1.y
           );
}

function merge(r1, r2) {
   return { x: Math.min(r1.x, r2.x),
            y: Math.min(r1.y, r2.y),
            w: Math.max(r1.w, r2.w),
            h: Math.max(r1.h, r2.h)
          }
}

答案 1 :(得分:2)

这可以通过将问题建模为图表来解决。节点是矩形,每当它们中的任何两个之间存在交叉时,我们认为这两个节点通过边连接。

我们的目标是将矩形集分成直接或间接相互连接的组。这基本上是图的connected component。这可以使用breadth first searchdepth first search找到。

找到所有组件后,我们只需找到每个组件中最高的左上角和最右下角,找到它们的边界框。

可以使用@Marcus'答案中提供的功能来检查交叉点。

此过程的总体复杂度为O(n ^ 2),其中n是矩形的数量。

答案 2 :(得分:0)

如果有人需要完全正常工作的例子,请在这里重新播放https://repl.it/@anjmao/merge-rectangles和代码:

function mergeAll(array) {
    let newArr, didMerge, i;
    do {
        newArr = [];
        didMerge = false;
        i = 0;
        while (i < array.length) {
            const curr = array[i];
            const next = array[i + 1];
            if (intersects(curr, next)) {
                newArr.push(merge(curr, next));
                i++;
                didMerge = true;
            } else {
                newArr.push(curr);
            }
            i++;
        }
        if (newArr.length > 0) {
          array = newArr; 
        }
    } while (didMerge);
    return array;
}

function sort(array) {
    array.sort((r1, r2) => {
        if (r1.x === r2.x) {
            return r1.y - r2.y;
        }
        return r1.x - r2.x;
    });
}

function intersects(r1, r2) {
    if (!r2) {
        return false;
    }
    return !(r2.x > r1.x + r1.w
        || r2.x + r2.w < r1.x
        || r2.y > r1.y + r1.h
        || r2.y + r2.h < r1.y
    );
}

function merge(r1, r2) {
    const w = r1.w > r2.w ? r1.w : r2.w + (r2.x - r1.x);
    const h = r1.h > r2.h ? r1.h : r2.h + (r2.y - r1.y);
    return {
        x: Math.min(r1.x, r2.x),
        y: Math.min(r1.y, r2.y),
        w: w,
        h: h
    }
}

mergeAll([{x:0, y:0, w:5, h:5}, {x:1, y:1, w:5, h:5}, {x:15, y:15, w:1, h:1}])