Collision/intersection line-ellipse and line-rectangle javascript

Hola, espero que les guste y sirva esta publicación.

Estaba trabajando para un proyecto en javascript, y necesitaba dibujar una línea desde un punto hacia el punto de colisión con un elipse o un rectángulo. Empecé a googlear, y no tuve suerte. Pude conseguir algunos scripts pero en otros lenguajes de programación, y hechos para otros fines, por lo que tuve que adaptarlos.

Aquí les dejo el script + una demo:



Código


/**
 *  http://programando-soft.blogspot.com
 *
 *  Luciano Rasente
 */

/**
 * =====================================
 *  Requeriments
 * ======================================
 */
 
 function point(_x, _y) {
    return {x: _x, y: _y};
 }
 
 /*
 Common vector2 operations
 Author: Tudor Nita | cgrats.com
 Version: 0.51
 */
function Vec2(x_,y_)
{
    this.x = x_;
    this.y = y_;

    this.isVector = true;

    /* vector * scalar */
    this.mulS = function (value){  return new Vec2(this.x*value, this.y*value);   };
    /* vector * vector */
    this.mulV = function(vec_) {  return new Vec2(this.x * vec_.x, this.y * vec_.y); };
    /* vector / scalar */
    this.divS = function(value) {  return new Vec2(this.x/value,this.y/value);   };
    /* vector + scalar */
    this.addS = function(value) {  return new Vec2(this.x+value,this.y+value);   };
    /* vector + vector */
    this.addV  = function(vec_) {  return new Vec2(this.x+vec_.x,this.y+vec_.y);  };
    /* vector - scalar */
    this.subS = function(value) { return new Vec2(this.x-value, this.y-value);  };
    /* vector - vector */
    this.subV = function(vec_)  { return new Vec2(this.x-vec_.x,this.y-vec_.y);  };
    /* vector absolute */
    this.abs = function()   { return new Vec2(Math.abs(this.x),Math.abs(this.y)); };
    /* dot product */
    this.dot = function(vec_)  { return (this.x*vec_.x+this.y*vec_.y);     };
    /* vector length */
    this.length = function() { return Math.sqrt(this.dot(this));      };
    /* distance between vectors */
    this.dist = function(vec_)  {   return (vec_.subV(this)).length();     };
    /* vector length, squared */
    this.lengthSqr = function() {  return this.dot(this);         };
    /*
     vector linear interpolation
     interpolate between two vectors.
     value should be in 0.0f - 1.0f space
     */
    this.lerp = function(vec_, value) {
        return new Vec2(
            this.x+(vec_.x-this.x)*value,
            this.y+(vec_.y-this.y)*value
        );
    };
    /* normalize THIS vector */
    this.normalize = function() {
        var vlen   = this.length();
        this.x = this.x/ vlen;
        this.y = this.y/ vlen;
    };
}

/**
 * =====================================
 *  Script
 * ======================================
 */

/**
 *  Gets the intersection between a line and a rectangle.
 
 *  @param ex Ellipse x
 *  @param ey Ellipse y
 *  @param ew Ellipse width
 *  @param ew Ellipse height
 *  @param x1 Line begin x
 *  @param y1 Line begin y
 *  @param x2 Line end x
 *  @param y2 Line end y
 *  @returns {x: , y: } or null.
 */
function getEllipseIntersection(ex, ey, ew, eh, x1, y1, x2, y2) {
    var x0 = ex,
        y0 = ey,
        w0 = ew,
        h0 = eh;
        
    // If the ellipse or line segment are empty, return no intersections.
    if(w0 == 0 || h0 == 0 || (x0 == x1 && y0 == y1)) {
        return null;
    }

    // Translate so the ellipse is centered at the origin.
    var cx = x0 + w0/2;    //  X en el centro del elipse
    var cy = y0 + h0/2;    //  Y en el centro del elipse
    x0 -= cx;
    y0 -= cy;
    x1 -= cx;
    y1 -= cy;
    x2 -= cx;
    y2 -= cy;

    // Get the semimajor and semiminor axes.
    var a = w0 / 2;
    var b = h0 / 2;

    // Calculate the quadratic parameters.
    var A = (x2 - x1) * (x2 - x1) / a / a +
        (y2 - y1) * (y2 - y1) / b / b;
    var B = 2 * x1 * (x2 - x1) / a / a +
        2 * y1 * (y2 - y1) / b / b;
    var C = x1 * x1 / a / a + y1 * y1 / b / b - 1;

    // Make a list of t values.
    var values = [];

    // Calculate the discriminant.
    var discriminant = B * B - 4 * A * C;
    if (discriminant == 0)
    {
        // One real solution.
        values.push(-B / 2 / A);
    }
    else if (discriminant > 0)
    {
        // Two real solutions.
        values.push((-B + Math.sqrt(discriminant)) / 2 / A);
        values.push((-B - Math.sqrt(discriminant)) / 2 / A);
    }

    // Convert the t values into points.
    var points = [];
    for(var i = 0; i < values.length; i++)
    {
        var t = values[i];
        // If the points are on the segment (or we
        // don't care if they are), add them to the list.
        if ((t >= 0) && (t <= 1))
        {
            var x = x1 + (x2 - x1) * t + cx;
            var y = y1 + (y2 - y1) * t + cy;
            points.push({x: x, y: y});
        }
    }

    // Return the points.
    return points[0];
}
 
/**
 *  Gets the intersection between a line and a rectangle.
 *  @param rx Rectangle x
 *  @param ry Rectangle y
 *  @param rw Rectangle width
 *  @param rw Rectangle height
 *  @param x0 Line begin x
 *  @param y0 Line begin y
 *  @param x1 Line end x
 *  @param y1 Line end y
 *  @returns {x: , y: } or null.
 */
function getRectIntersection(rx, ry, rw, rh, x0, y0, x1, y1) {   
    var p1 = point(x0, y0),
        p2 = point(x1, y1);
    
    var tl = point(rx, ry),
        tr = point(rx+rw, ry),
        bl = point(rx, ry+rh), 
        br = point(rx+rw, ry+rh);
    
    var result =
        getLineIntersection(p1, p2, tl, tr) ||
        getLineIntersection(p1, p2, tl, bl) ||
        getLineIntersection(p1, p2, tr, br) ||
        getLineIntersection(p1, p2, bl, br) ||
        null;
    
    return result;
}

/**
 *  Gets the intersection between two lines.
 *  Line point: {x: , y: }
 *
 *  @param a1 Line A begin point
 *  @param a2 Line A end point
 *  @param b1 Line B begin point
 *  @param b2 Line B end point
 */
function getLineIntersection(a1, a2, b1, b2) {
    if(typeof Vec2 === 'undefined')   {
        throw "Vec2 is not defined";
    }
    
    if(typeof a1.isVector === 'undefined') {
        a1 = new Vec2(a1.x, a1.y);
        a2 = new Vec2(a2.x, a2.y);
        b1 = new Vec2(b1.x, b1.y);
        b2 = new Vec2(b2.x, b2.y);
    }
    
    var b = a2.subV(a1);
    var d = b2.subV(b1);
    
    var bDotDPerp = b.x * d.y - b.y * d.x;
    
    // if b dot d == 0, it means the lines are parallel so have infinite intersection points
    if (bDotDPerp == 0) {
        return null;
    }
    
    var c = b1.subV(a1);
    var t = (c.x * d.y - c.y * d.x) / bDotDPerp;
    
    if (t < 0 || t > 1)
    {
        return null;
    }
    
    var u = (c.x * b.y - c.y * b.x) / bDotDPerp;
    
    if (u < 0 || u > 1)
    {
        return null;
    }
    
    return a1.addV(b.mulS(t));
}

Comentarios

Entradas populares de este blog

Ordenar lista en python - Super fácil!