Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RaphaelJS shapes Drag&Drop demo

I tried to understand this demo's work: http://raphaeljs.com/graffle.html But I cannot understand this loop in graffle.js source code:

for (var i = 0; i < 4; i++) {
    for (var j = 4; j < 8; j++) {
        var dx = Math.abs(p[i].x - p[j].x),
            dy = Math.abs(p[i].y - p[j].y);
        if ((i == j - 4) || (((i != 3 && j != 6) || p[i].x < p[j].x) && ((i != 2 && j != 7) || p[i].x > p[j].x) && ((i != 0 && j != 5) || p[i].y > p[j].y) && ((i != 1 && j != 4) || p[i].y < p[j].y))) {
            dis.push(dx + dy);
            d[dis[dis.length - 1]] = [i, j];
        }
    }
}

Can anybody explain to me what it does and how it works? Thanks.

like image 202
Guy Fawkes Avatar asked Dec 16 '22 02:12

Guy Fawkes


1 Answers

Before this for loop, a single array is constructed, which contains the 4 positions for each object that the path can join from and to.

var bb1 = obj1.getBBox(),
    bb2 = obj2.getBBox(),
    p = [{x: bb1.x + bb1.width / 2, y: bb1.y - 1},
    {x: bb1.x + bb1.width / 2, y: bb1.y + bb1.height + 1},
    {x: bb1.x - 1, y: bb1.y + bb1.height / 2},
    {x: bb1.x + bb1.width + 1, y: bb1.y + bb1.height / 2},
    {x: bb2.x + bb2.width / 2, y: bb2.y - 1},
    {x: bb2.x + bb2.width / 2, y: bb2.y + bb2.height + 1},
    {x: bb2.x - 1, y: bb2.y + bb2.height / 2},
    {x: bb2.x + bb2.width + 1, y: bb2.y + bb2.height / 2}],

So you have 2 objects obj1 and obj2 that you want to join together with a path. The path has to be drawn to any one of the 4 possible points on the edge of the object:

path positions

Where I have labelled the vertices based on their index in p. Now we are going to loop over the points on obj1 and the points on obj2, using i for the index of the points on obj1 and j for the points on obj2. This way we test each point on obj1 against each point on obj2. What we are aiming to do is to measure (sort of) the distance between only those points which we find suitable for connecting.

// For each pair of adjacent points
for (var i = 0; i < 4; i++) {
    for (var j = 4; j < 8; j++) {

        // Calculate the difference in the X and Y direction (dy and dx)
        var dx = Math.abs(p[i].x - p[j].x),
            dy = Math.abs(p[i].y - p[j].y);

             // If the points are on the same side  OR  
        if ((i == j - 4) || 
            // If the points are **not** opposites (3 and 6) or (2 and 7) or (0 and 5) or (1 and 4) 
            // or, if we have 3 and 6, and the obj1 is to the left side of obj2
            // or, if we have 2 and 7, and the obj1 is to the right side of obj2
            // or, if we have 0 and 5, and the obj1 is higher than obj2
            // or, if we have 1 and 4, and the obj1 is lower than obj2

            (((i != 3 && j != 6) || p[i].x < p[j].x) && ((i != 2 && j != 7) || p[i].x > p[j].x) && ((i != 0 && j != 5) || p[i].y > p[j].y) && ((i != 1 && j != 4) || p[i].y < p[j].y))) {
            // push the sum of dx and dy onto out distance list
            // and push the associated pair of points onto the d list.
            // This is so we can pick the sort-of-closest pair of points.
            dis.push(dx + dy);
            d[dis[dis.length - 1]] = [i, j];
        }
    }
}

So to break down that big if statement

IF

  • we have two points on the same side

    (i == j - 4)

OR

  • we don't have 3 and 6, or if we do point 3 lies to the left of point 6

    ((i != 3 && j != 6) || p[i].x < p[j].x) &&

  • we don't have 2 and 7, or if we do point 2 lies to the right of point 6

    ((i != 2 && j != 7) || p[i].x > p[j].x) &&

  • we don't have 0 and 5, or if we do point 0 lies above point 5

    ((i != 0 && j != 5) || p[i].y > p[j].y) &&

  • we don't have 1 and 4, or if we do point 1 lies below point 4

    ((i != 1 && j != 4) || p[i].y < p[j].y)

THEN

  • this connection is valid, do a cheap measure for how close the points are (dy + dx) and put these valid connections in an array. We will pick the "shortest" distance later.

only if connections

If the points being tested are not one of these opposite pairs, then we measure it still. This is all to stop the path from being inside the 2 objects if we can help it. The first test to see if they are on the same side is a cheap way to skip all of this opposite testing code. After we have measured all of the points which are allowed, we pick the smallest dy+dx and use those points to draw the path.

like image 117
Matt Esch Avatar answered Jan 02 '23 10:01

Matt Esch