Search code examples
javascripthtml5-canvaskineticjs

Possible to detect collisions of Paths or Kinetic Blobs/Layers?


I have multiple layers on my stage. Each layer contains images surrounded with a blub ( see this question). Each blub is draggable.

Is it possible to detect collisions between the blubs while moving them? I don't want to have overlapping bubbles, but rather if they collide, they should melt.


Solution

  • You can determine if blobs are colliding.

    There are at least 2 methods:

    • calculate the bounding box for all blobs and test if the bounding boxes collide.
    • draw each blob on a separate offscreen canvas and use pixel testing to see if they collide.

    The bounding box method is faster.

    The pixel-testing method is more precise, but slower are requires many more resources.

    An example:

    Here's how to calculate and test if 2 blob bounding boxes are colliding.

    A Demo: http://jsfiddle.net/m1erickson/9tB7d/

    Start with a Kinetic Blob

    var blueBlob = new Kinetic.Line({
       points: [73,140,340,23,500,109,300,170],
       stroke: 'blue',
       strokeWidth: 10,
       fill: '#aaf',
       tension: 0.8,
       closed: true
    });
    

    That blob is made up of a set of Bezier curves.

    Get the Bezier curves that make up the blob:

    function kineticBlob2Beziers(blob){
        var beziers=[];
        var start=blob.getPoints();
        var pts=blob.getTensionPoints();
        var n=0;
        var lastN=pts.length-2;
        var sx=start[0];
        var sy=start[1];
        while(n<lastN){
            bez={
                s: {x:sx,y:sy},
                c1:{x:pts[n++],y:pts[n++]},
                c2:{x:pts[n++],y:pts[n++]},
                e: {x:pts[n++],y:pts[n++]}
            };
            beziers.push(bez);
            sx=pts[n-2];
            sy=pts[n-1];
        }
        return(beziers);
    }
    

    Calculate the blobs bounding box using its Bezier curves:

    function getBlobBB(beziers){
        var minX=1000000;
        var minY=1000000;
        var maxX=-1000000;
        var maxY=-1000000;
        for(var i=0;i<beziers.length;i++){
            var bez=beziers[i];
            for(var t=0.00;t<=1.00;t+=.01){
                var pt=getCubicBezierXYatT(bez.s,bez.c1,bez.c2,bez.e,t);
                if(pt.x<minX){minX=pt.x;}
                if(pt.x>maxX){maxX=pt.x;}
                if(pt.y<minY){minY=pt.y;}
                if(pt.y>maxY){maxY=pt.y;}
            }
        }
        return({x:minX,y:minY,width:maxX-minX,height:maxY-minY});
    }
    
    function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
        var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
        var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
        return({x:x,y:y});
    }
    
    // cubic helper formula at T distance
    function CubicN(T, a,b,c,d) {
        var t2 = T * T;
        var t3 = t2 * T;
        return a + (-a * 3 + T * (3 * a - a * T)) * T
        + (3 * b + T * (-6 * b + b * 3 * T)) * T
        + (c * 3 - c * 3 * T) * t2
        + d * t3;
    }
    

    Determines if 2 bounding boxes (rectangles) are colliding:

    function Colliding(left1,top1,right1,bottom1,left2,top2,right2,bottom2){
    
        return(!(
            left1   > right2 ||
            right1  < left2  ||
            bottom1 < top2   ||
            top1    >bottom2
        ));
    
    }