Html 给定一些AABB,找出包含它们的最小总表面积AABB?

Html 给定一些AABB,找出包含它们的最小总表面积AABB?,html,algorithm,math,graphics,html5-canvas,Html,Algorithm,Math,Graphics,Html5 Canvas,我有许多对象需要渲染到HTML5画布上。我的输入是轴对齐边界框的有序列表。这些盒子经常重叠,但也经常在它们之间留下大面积的空白 我希望尽可能减少必须创建的画布表面面积,以便以正确的顺序渲染所有这些项目,同时不必在多个画布上渲染单个对象的部分(从而避免只创建紧密适合所有占用空间的画布的简单解决方案) 因此,基本上,我希望紧密的对象组都在同一画布上渲染,而不重叠的对象应该在单独的画布上渲染。但并非所有重叠对象都应该在单个画布上渲染——例如,一个非常高且非常宽的对象稍微重叠形成一个L,仍然应该在两个单

我有许多对象需要渲染到
HTML5
画布上。我的输入是轴对齐边界框的有序列表。这些盒子经常重叠,但也经常在它们之间留下大面积的空白

我希望尽可能减少必须创建的画布表面面积,以便以正确的顺序渲染所有这些项目,同时不必在多个画布上渲染单个对象的部分(从而避免只创建紧密适合所有占用空间的画布的简单解决方案)

因此,基本上,我希望紧密的对象组都在同一画布上渲染,而不重叠的对象应该在单独的画布上渲染。但并非所有重叠对象都应该在单个画布上渲染——例如,一个非常高且非常宽的对象稍微重叠形成一个L,仍然应该在两个单独的画布上渲染,因为组合它们会在L的开放部分浪费大量画布空间

维持Z顺序也会导致一些困难的情况。例如,下图表示一种可能的排列:

在这种情况下,您可能希望将蓝色和绿色层组合到一个画布中,但如果不同时包含红色层,则无法生成正确的分层,最终会导致大量死角

但您也不能仅将组合层限制为Z顺序上连续的项目。Z顺序可能与上图相同,但红色项目可能与其他项目不重叠,在这种情况下,您确实希望合并蓝色和绿色层


我正在努力想出一个解决这个问题的好算法。有人愿意插话吗?

这里有一个简单的建议,它可能不会解决一些棘手的问题,但至少可以部分解决问题,希望能提出更好的解决方案:

a = <a z-ordered list of the objects> ;
b = [];
bounds = null;
objects = [];
while ( a.length > 0) {
    c = a.pop();
    if( <c overlaps bounds> && <area of combined canvases> < <area of seperate canvases> || bounds === null) {
         objects.push(c);
         bounds = <union of bounds and c, or bounds of c if bounds is null>;
     } else {
          b.push(c);
     }
     if( a.length === 0) {
          a = b;
          b = [];
          <do something with the current bounds and objects list>
          bounds = null;
          objects = [];
     }
}
a=;
b=[];
界限=零;
对象=[];
而(a.length>0){
c=a.pop();
如果(&&<| |边界===null){
物体。推(c);
界限=;
}否则{
b、 推(c);
}
如果(a.length==0){
a=b;
b=[];
界限=零;
对象=[];
}
}
在哪里

 < area of combined canvases> = sum( < area of each canvas> ) - sum( <interesections> )
 < area of seperate conavases> = sum( < area of each canvas> )
=sum()-sum()
<独立画布面积>=总和(<各画布面积>)

这不会捕捉到两个非相交对象都与一个公共对象相交的情况,但可以通过在每次迭代中回顾所有较低z阶对象来改进这种情况。

从Aabs的z阶数组开始

  • 将每个AABB添加到一个结果数组(也是z顺序的)

    a。将要添加的每个AABB与结果数组中已存在的所有其他AABB进行比较

    • 查看要添加的AABB和任何其他AABB的哪种组合将产生最小的附加表面积。(可能不会,且待添加的AABB不应与任何其他AABB组合。)

    • 当组合将导致较小的表面积时,检查交叉点问题(即,另一个AABB与另一个AABB重叠,并与待添加的AABB重叠)

    • 如果不存在此类交叉问题,请记住另一个AABB,并继续寻找更好的可能组合

    b。当最终找到最佳组合(或无组合)时,将要添加的AABB添加到结果数组中

    • 根据交叉点问题的存在,将要添加的AABB可以与结果数组中的其他AABB组合并插入其中

    • 否则,组合或新AABB本身将添加到结果数组的顶部

  • 对下一个AABB重复此操作

  • 这不是一个漂亮的算法,也不是每件事都做得很完美。一方面,当发现AABB的组合时,它不会试图找出是否可以将第三个或第四个(或第五个)AABB添加到组合中以改善区域保护

    下面是此算法在Javascript中的实现:

    algorithm = function(allAABBsInSortedOrder) {
        var smallestCanvasSurfaceArea = [];
    
        goog.array.forEach(allAABBsInSortedOrder, function(aabb) {
            smallestCanvasSurfaceArea = findSmallestSurfaceArea(aabb, smallestCanvasSurfaceArea);
        })
    };
    
    findSmallestSurfaceArea = function(nextAABB, combinedAABBsInSortedOrder) {
        var nextAABBarea = areaOf(nextAABB);
    
        if (!nextAABB) {
            return combinedAABBsInSortedOrder;
        }
    
        var aabbToCombineWith = {'index': -1, 'area': nextAABBarea, 'upOrDown': 0};
    
        goog.array.forEach(combinedAABBsInSortedOrder, function(aabb, idx) {
            // Improvement - exhaustive combinations (three AABBs? Four?)
            if (areaOf(combine(aabb, nextAABB) - nextAABBarea <= aabbToCombineWith['area']) {
                var overlapLower = false;
                var overlapNext = false;
    
                goog.array.forEach(combinedAABBsInSortedOrder, function(intersectAABB, intersectIdx) {
                    if (intersectIdx > idx) {
                        if (checkForIntersect(aabb, intersectAABB)) {
                            overlapLower = true;
                        }
                        if (checkForIntersect(nextAABB, intersectAABB)) {
                            overlapNext = true;
                        }
                    }
                });
    
                if (overlapLower && !overlapNext) {
                    aabbsToCombineWith['index'] = idx;
                    aabbsToCombineWith['area'] = areaOf(aabb);
                    aabbsToCombineWith['upOrDown'] = -1;
                }
                else if (!overlapLower && overlapNext) {
                    aabbsToCombineWith['index'] = idx;
                    aabbsToCombineWith['area'] = areaOf(aabb);
                    aabbsToCombineWith['upOrDown'] = 1;
                }
                else if (!overlapLower && !overlapNext) {
                    aabbsToCombineWith['index'] = idx;
                    aabbsToCombineWith['area'] = areaOf(aabb);
                    aabbsToCombineWith['upOrDown'] = 0;
                }
            }
        });
    
        if (aabbToCombineWith['index'] != -1) {
            var combinedAABB = combine(combinedAABBsInSortedOrder[aabbToCombineWith['index']], nextAABB);
            if (aabbToCombineWith['upOrDown'] == -1) {
                combinedAABBsInSortedOrder[aabbToCombineWith['index']] = combinedAABB;
            }
            else {
                combinedAABBsInSortedOrder.push(combinedAABB);
                combinedAABBsInSortedOrder.splice(aabbToConbineWith['index'], 1);
            }
        }
        else {
            combinedAABBsInSortedOrder.push(nextAABB);
        }
    
        return combinedAABBsInSortedOrder;
    };
    
    算法=函数(AllaabsinssortedOrder){
    var smallestcanvassurfaceearea=[];
    goog.array.forEach(allabbsinssortedorder,函数(aabb){
    smallestCanvasSurfaceArea=FindSalestSurfaceArea(aabb,smallestCanvasSurfaceArea);
    })
    };
    FindSalestSurfaceArea=函数(nextAABB,组合aAbsSinsortedOrder){
    var NextaAb面积=面积(nextAABB);
    如果(!nextabb){
    返回组合式Abbsin分拣机;
    }
    var aabbToCombineWith={'index':-1,'area':nextAABBarea,'upOrDown':0};
    goog.array.forEach(组合AbbsinsSortedOrder,函数(aabb,idx){
    //改进-详尽的组合(三个AABB?四个?)
    if(联合收割机区域(aabb,nextAABB)-nextAABB区域idx){
    if(检查相交(aabb,相交aabb)){
    重叠下限=真;
    }
    if(检查交叉点(下一个TAABB,交叉点AABB)){
    重叠下一个=真;
    }
    }
    });
    if(overlapplower&!overlappnext){
    aabbsToCombineWith['index']=idx;
    AABBS与['area']=面积(aabb)组合;
    aabbsToCombineWith['upOrDown']=-1;
    }
    else if(!OverlappLower&&OverlappNext){
    aabbsToCombineWith['index']=idx;
    AABBS与['area']=面积(aabb)组合;
    aabbsToCombineWith['upOrDown']=1;
    }
    如果(!OverlappLower&&!OverlappNext),则为else{
    aabbsToCombineWith['index']=idx;
    AABBS与['area']=面积(aabb)组合;
    aabbsToCombineWith['upOrDown']=0
    
    function minBoxes(boxes) {
        var result = [];
    
        boxes.forEach(function(box1) {
            var bestCombinedBox,
                bestIndex;
            result.reduce(function(bestSavings, box2, i) {
                var x = Math.min(box1.x, box2.x),
                    y = Math.min(box1.y, box2.y),
                    w = Math.max(box1.x + box1.w, box2.x + box2.w) - x,
                    h = Math.max(box1.y + box1.h, box2.y + box2.h) - y;
                var savings = box1.w * box1.h + box2.w * box2.h - w * h;
                if(savings > bestSavings) {
                    bestCombinedBox = {x:x, y:y, w:w, h:h};
                    bestIndex = i;
                    return savings;
                }
                return bestSavings;
            }, 0);
            if(bestCombinedBox) {
                result[bestIndex] = bestCombinedBox; //faster than splicing
            } else {
                result.push(box1);
            }
        });
    
        return result;
    }
    
    minBoxes([{x:0, y:0, w:5, h:5}, {x:1, y:1, w:5, y:5}]);
    
    function minBoxes(boxes) {
        boxes = boxes.sort(function(box1, box2) {
            return box1.z - box2.z;
        }).map(function(box1) {
            var overlapping = {};
            boxes.forEach(function(box2) {
                if(box1 != box2
                    && box1.x + box1.w > box2.x
                    && box2.x + box2.w > box1.x
                    && box1.y + box1.h > box2.y
                    && box2.y + box2.h > box1.y
                ) {
                    overlapping[box2.z] = true;
                }
            });
            return {
                x: box1.x,
                y: box1.y,
                w: box1.w,
                h: box1.h,
                minZ: box1.z,
                maxZ: box1.z,
                overlapping: overlapping
            };
        });
    
        var result = [];
    
        boxes.forEach(function(box1) {
            var bestBox,
                bestIndex;
    
            function combinedBox(box2) {
                var x = Math.min(box1.x, box2.x),
                    y = Math.min(box1.y, box2.y);
                return {
                    x: x,
                    y: y,
                    w: Math.max(box1.x + box1.w, box2.x + box2.w) - x,
                    h: Math.max(box1.y + box1.h, box2.y + box2.h) - y
                };
            }
            result.reduce(function(bestSavings, box2, i) {
                //check z-order
                var min = Math.max(Math.min(box1.minZ, box2.maxZ), Math.min(box1.maxZ, box2.minZ)),
                    max = Math.min(Math.max(box1.minZ, box2.maxZ), Math.max(box1.maxZ, box2.minZ));
                for(var z in box1.overlapping) {
                    if(min < z && z < max && z in box2.overlapping) {
                        return bestSavings;
                    }
                }
                for(var z in box2.overlapping) {
                    if(min < z && z < max && z in box1.overlapping) {
                        return bestSavings;
                    }
                }
                //check area savings
                var combined = combinedBox(box2);
                var savings = box1.w * box1.h + box2.w * box2.h - combined.w * combined.h;
                if(savings > bestSavings) {
                    bestBox = box2;
                    bestIndex = i;
                    return savings;
                }
                return bestSavings;
            }, 0);
            if(bestBox) {
                var combined = combinedBox(bestBox);
                combined.minZ = Math.min(box1.minZ, bestBox.minZ);
                combined.maxZ = Math.max(box1.maxZ, bestBox.maxZ);
                combined.overlapping = box1.overlapping;
                for(var z in bestBox.overlapping) {
                    combined.overlapping[z] = true;
                }
                result[bestIndex] = combined; //faster than splicing
            } else {
                result.push(box1);
            }
        });
    
        return result.map(function(box) {
            return {
                x: box.x,
                y: box.y,
                w: box.w,
                h: box.h,
                z: (box.minZ + box.maxZ) / 2 //really, any value in this range will work
            };
        });
    }
    
    minBoxes([{x:0, y:0, w:5, h:5, z:0}, {x:1, y:1, w:5, y:5, z:1}]);