Search code examples
javascriptarrayssvgoverlappingrect

Overlapping rectangles in a array


I have an array with rectangles. I have another array with those rectangles that overlaps. I have spent hours on trying to figure how to loop through the array and find a new y-postion to the overlapping rec, I have a big bug in my mind....

I can only move on y-axis since the x is dependent on a date scale. I would realy appreciate some code example. The rectangles are not equal in size.

Data example:

"people": [
{ "firstName":"John" , "startDate":"2012-01-01", "endDate":"2014-01-01", "basketValue":"10"}, 
{ "firstName":"Anna" ,  "startDate":"2011-01-01", "endDate":"2013-04-01", "basketValue":"20" }, 
{ "firstName":"Victor" ,  "startDate":"2011-01-01", "endDate":"2013-04-01", "basketValue":"13" },
{ "firstName":"Tom" ,  "startDate":"2011-01-01", "endDate":"2012-07-01", "basketValue":"20" },  
{ "firstName":"Santa" ,  "startDate":"2011-01-01", "endDate":"2012-12-24", "basketValue":"20" }, 
{ "firstName":"Peter" , "startDate":"2012-01-01", "endDate":"2012-02-21", "basketValue":"4" }
{ "firstName":"Carol" , "startDate":"2013-01-01", "endDate":"2013-07-05", "basketValue":"14" }
{ "firstName":"Sophie" , "startDate":"2012-09-01", "endDate":"2012-12-24", "basketValue":"8" }
]

while(loop){    
//overlappingRects array with those that overlaps
newY= overlappingRects[0].y+overlappingRects[0].barHeight + newY;
log(newY);
//this my logic error arrRec holds all of the recs
for(j=0;j<arrRec.length;j++){
    if(arrRec[j].Name!==overlappingRects[0].Name){
    log(overlappingRects[0].Name + ' ' + arrRec[j].Name);

        //How do I solve this that it not overlap with the other surounding rects
        overlap = rectOverlap(overlappingRects[0],arrRec[j]);
        if(overlap==false){
            //check for date...
                overlappingRects[0].y = arrRec[j].y;
                overlappingRects[0].endY = overlappingRects[0].barHeight + overlappingRects[0].y;
                arrRec[overlappingRects[0].key].y =overlappingRects[0].y;
                arrRec[overlappingRects[0].key].endY=overlappingRects[0].endY;
                overlappingRects.splice(0,1);
                break;
            }

        }

    }

}
if(overlappingRects.length==0 ){loop=false;}

}

I have provided a file on my dropbox on how it looks :-( (Not allowed to share here)

https://www.dropbox.com/s/3gmp16ymf0j2kvm/canvas.png


Solution

  • Assuming that the rectangles are represented as this one:

    { "position": {"x": <x coord>, "y": <y coord>},
      "width": <pixel value>,
      "height": <pixel value>
    }
    

    The algorithm should be quite easy:

    • sort the rectangles based on the top left corner.
    • start with the second rectangle and check that it does not overlap the previous one
    • if it overlap change the y value of the rectangle coordinate to the smallest value needed to remove the overlapping
    • repeat until the last rectangle position is fixed.

    The test for overlapping is simple:
    previous.position.y + previous.height > current.position.y

    You can change the position in a similar way:
    current.position.y = previous.position.y + previous.height + gap where gap is the pixel size of the gap between rows.

    To me, you can optimize the Gantt chart (it seems a Gantt chart to me) in this way:

    bar.sort(function(a,b) {
        var y = (a.position.y - b.position.y);
        return y?y:a.position.x - b.position.x;
    });
    
    for (i=1; i<bar.length; i++) {
        bar[i].position.y = bar[i-1].position.y + bar[i-1].height + gap;
    }
    

    This is a first try, the issue with this algorithm is that there is only a rectangle per line. To obtain a compact view we need to consider the x position of the rectangles.

    We can complicate the things a little:

    var gap = {"x": 2, "y": 5};
    
    bar.sort(function(a,b) {
        var y = (a.position.y - b.position.y);
        return y?y:a.position.x - b.position.x;
    });
    
    for (i=1; i<bar.length; i++) {
        for (j = i-1; j>=0; j--) {
            safe = {
                "y": bar[j].position.y + bar[j].height + gap.y,
                "x": bar[j].position.x + bar[j].width + gap.x
            };
            if (bar[i].position.y <= safe.y) { // the rects can overlap
                if (bar[i].position.x <= safe.x) { // the rects do overlap
                    bar[i].position.y = safe.y;
                }
            }
        }
    }
    

    disclaimer: I've not tested the code but it should work removing eventual syntax errors and glitches.