web-dev-qa-db-fra.com

Teste si deux lignes se croisent - Fonction JavaScript

J'ai essayé de rechercher une fonction javascript qui détecte si deux lignes se coupent.

La fonction prendra les valeurs x, y des deux points de début pour chaque ligne (nous les appellerons ligne A et ligne B).

Est de retourner vrai s'ils se croisent, sinon faux. 

Exemple de la fonction. Je suis content si la réponse utilise un objet vectoriel à la place. 

Function isIntersect (lineAp1x, lineAp1y, lineAp2x, lineAp2y, lineBp1x, lineBp1y, lineBp2x, lineBp2y) 
{

    // JavaScript line intersecting test here. 

}

Quelques informations d’arrière-plan: ce code est destiné à un jeu que j’essaie de créer dans le document html5 et fait partie de ma détection des collisions.

26
Jarrod
// returns true iff the line from (a,b)->(c,d) intersects with (p,q)->(r,s)
function intersects(a,b,c,d,p,q,r,s) {
  var det, gamma, lambda;
  det = (c - a) * (s - q) - (r - p) * (d - b);
  if (det === 0) {
    return false;
  } else {
    lambda = ((s - q) * (r - a) + (p - r) * (s - b)) / det;
    gamma = ((b - d) * (r - a) + (c - a) * (s - b)) / det;
    return (0 < lambda && lambda < 1) && (0 < gamma && gamma < 1);
  }
};

Explication: (vecteurs, une matrice et un déterminant effronté)  

Les lignes peuvent être décrites par un vecteur initial, v, et un vecteur de direction, d:

r = v + lambda*d 

Nous utilisons un point (a,b) comme vecteur initial et la différence entre eux (c-a,d-b) comme vecteur de direction. De même pour notre deuxième ligne.

Si nos deux lignes se croisent, il doit y avoir un point, X, qui est accessible en parcourant une certaine distance, lambda, le long de notre première ligne et également accessible en unités gamma en déplacement le long de notre seconde ligne. Cela nous donne deux équations simultanées pour les coordonnées de X:

X = v1 + lambda*d1 
X = v2 + gamma *d2

Ces équations peuvent être représentées sous forme de matrice. Nous vérifions que le déterminant est non nul pour voir si l'intersection X existe même.

S'il y a une intersection, nous devons alors vérifier que cette intersection se situe réellement entre les deux ensembles de points. Si lambda est supérieur à 1, l'intersection est au-delà du deuxième point. Si lambda est inférieur à 0, l'intersection est antérieure au premier point. 

Par conséquent, 0<lambda<1 && 0<gamma<1 indique que les deux lignes se croisent!

30
Dan Fox
function lineIntersect(x1,y1,x2,y2, x3,y3,x4,y4) {
    var x=((x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4))/((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4));
    var y=((x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4))/((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4));
    if (isNaN(x)||isNaN(y)) {
        return false;
    } else {
        if (x1>=x2) {
            if (!(x2<=x&&x<=x1)) {return false;}
        } else {
            if (!(x1<=x&&x<=x2)) {return false;}
        }
        if (y1>=y2) {
            if (!(y2<=y&&y<=y1)) {return false;}
        } else {
            if (!(y1<=y&&y<=y2)) {return false;}
        }
        if (x3>=x4) {
            if (!(x4<=x&&x<=x3)) {return false;}
        } else {
            if (!(x3<=x&&x<=x4)) {return false;}
        }
        if (y3>=y4) {
            if (!(y4<=y&&y<=y3)) {return false;}
        } else {
            if (!(y3<=y&&y<=y4)) {return false;}
        }
    }
    return true;
}

La wiki page dont j'ai trouvé la réponse.

31
AlexM

J'ai réécrit la réponse de Peter Wone à une seule fonction en utilisant x/y au lieu de lat ()/long ()

function isIntersecting(p1, p2, p3, p4) {
    function CCW(p1, p2, p3) {
        return (p3.y - p1.y) * (p2.x - p1.x) > (p2.y - p1.y) * (p3.x - p1.x);
    }
    return (CCW(p1, p3, p4) != CCW(p2, p3, p4)) && (CCW(p1, p2, p3) != CCW(p1, p2, p4));
}
7
Jonathan

Voici une version basée sur this Gist avec des noms de variables plus concis et du café.

Version javascript

var lineSegmentsIntersect = (x1, y1, x2, y2, x3, y3, x4, y4)=> {
    var a_dx = x2 - x1;
    var a_dy = y2 - y1;
    var b_dx = x4 - x3;
    var b_dy = y4 - y3;
    var s = (-a_dy * (x1 - x3) + a_dx * (y1 - y3)) / (-b_dx * a_dy + a_dx * b_dy);
    var t = (+b_dx * (y1 - y3) - b_dy * (x1 - x3)) / (-b_dx * a_dy + a_dx * b_dy);
    return (s >= 0 && s <= 1 && t >= 0 && t <= 1);
}

Version CoffeeScript

lineSegmentsIntersect = (x1, y1, x2, y2, x3, y3, x4, y4)->
    a_dx = x2 - x1
    a_dy = y2 - y1
    b_dx = x4 - x3
    b_dy = y4 - y3
    s = (-a_dy * (x1 - x3) + a_dx * (y1 - y3)) / (-b_dx * a_dy + a_dx * b_dy)
    t = (+b_dx * (y1 - y3) - b_dy * (x1 - x3)) / (-b_dx * a_dy + a_dx * b_dy)
    (0 <= s <= 1 and 0 <= t <= 1)
3
1j01

Tout d’abord, trouvez les coordonnées de l’intersection - voici la description détaillée: http://www.mathopenref.com/coordintersection.html

Ensuite, vérifiez si la coordonnée x de l'intersection se situe dans les plages x de l'une des lignes (ou faites de même avec la coordonnée y, si vous préférez), C'est-à-dire. si xIntersection est entre lineAp1x et lineAp2x, ils se coupent.

2
egres

Pour tous ceux qui souhaitent une solution prête pour coldfusion, voici ce que j’ai adapté de http://grepcode.com/file/repository.grepcode.com/Java/root/jdk/openjdk/7-b147/ Java/awt/geom/Line2D.Java # Line2D.linesIntersect% 28double% 2Cdouble% 2Cdouble% 2Cdouble% 2Cdouble% 2Cdouble% 2Cdouble% 2Cdouble% 2Cdouble% 29

les fonctions importantes sont ccw et linesIntersect de Java.awt.geom.Line2D et je les ai écrites dans coldfusion, alors allons-y:

<cffunction name="relativeCCW" description="schnittpunkt der vier punkte (2 geraden) berechnen">
<!---
Returns an indicator of where the specified point (px,py) lies with respect to this line segment. See the method comments of relativeCCW(double,double,double,double,double,double) to interpret the return value.
Parameters:
px the X coordinate of the specified point to be compared with this Line2D
py the Y coordinate of the specified point to be compared with this Line2D
Returns:
an integer that indicates the position of the specified coordinates with respect to this Line2D
--->
<cfargument name="x1" type="numeric" required="yes" >
<cfargument name="y1" type="numeric" required="yes">
<cfargument name="x2" type="numeric" required="yes" >
<cfargument name="y2" type="numeric" required="yes">
<cfargument name="px" type="numeric" required="yes" >
<cfargument name="py" type="numeric" required="yes">
    <cfscript>
    x2 = x2 - x1;
    y2 = y2 - y1;
    px = px - x1;
    py = py - y1;
    ccw = (px * y2) - (py * x2);
    if (ccw EQ 0) {
        // The point is colinear, classify based on which side of
        // the segment the point falls on.  We can calculate a
        // relative value using the projection of px,py onto the
        // segment - a negative value indicates the point projects
        // outside of the segment in the direction of the particular
        // endpoint used as the Origin for the projection.
        ccw = (px * x2) + (py * y2);
        if (ccw GT 0) {
            // Reverse the projection to be relative to the original x2,y2
            // x2 and y2 are simply negated.
            // px and py need to have (x2 - x1) or (y2 - y1) subtracted
            //    from them (based on the original values)
            // Since we really want to get a positive answer when the
             //    point is "beyond (x2,y2)", then we want to calculate
            //    the inverse anyway - thus we leave x2 & y2 negated.       
            px = px - x2;
            py = py - y2;
            ccw = (px * x2) + (py * y2);
            if (ccw LT 0) {
                ccw = 0;
                }
        }
    }
    if (ccw LT 0) {
        ret = -1;
    }
    else if (ccw GT 0) {
        ret = 1;
    }
    else {
        ret = 0;
    }   
    </cfscript> 
    <cfreturn ret>
</cffunction>


<cffunction name="linesIntersect" description="schnittpunkt der vier punkte (2 geraden) berechnen">
<cfargument name="x1" type="numeric" required="yes" >
<cfargument name="y1" type="numeric" required="yes">
<cfargument name="x2" type="numeric" required="yes" >
<cfargument name="y2" type="numeric" required="yes">
<cfargument name="x3" type="numeric" required="yes" >
<cfargument name="y3" type="numeric" required="yes">
<cfargument name="x4" type="numeric" required="yes" >
<cfargument name="y4" type="numeric" required="yes">
    <cfscript>
    a1 = relativeCCW(x1, y1, x2, y2, x3, y3);
    a2 = relativeCCW(x1, y1, x2, y2, x4, y4);
    a3 = relativeCCW(x3, y3, x4, y4, x1, y1);
    a4 = relativeCCW(x3, y3, x4, y4, x2, y2);
    aa = ((relativeCCW(x1, y1, x2, y2, x3, y3) * relativeCCW(x1, y1, x2, y2, x4, y4) LTE 0)
            && (relativeCCW(x3, y3, x4, y4, x1, y1) * relativeCCW(x3, y3, x4, y4, x2, y2) LTE 0));
    </cfscript>
 <cfreturn aa>
</cffunction>

J'espère que cela peut aider à s'adapter à d'autres langues?

0
Raffael Meier