Как определить, находится ли точка между двумя другими? - PullRequest
1 голос
/ 06 октября 2019

Я сделал небольшую функцию, которая работает не совсем правильно ...

function isPointbetweenTwoOthers (pA, pB, pToCheck) {
    var pApB = new THREE.Vector3();
    pApB.subVectors(pA,pB).normalize();

    var pBpA = new THREE.Vector3();
    pBpA.subVectors(pB,pA).normalize();

    var pA_pToCheck = new THREE.Vector3();
    pA_pToCheck.subVectors(pA,pToCheck).normalize();

    var pB_pToCheck = new THREE.Vector3();
    pB_pToCheck.subVectors(pB,pToCheck).normalize();

    if(pA_pToCheck.dot(pApB) <0 || pB_pToCheck.dot(pBpA) <0)
    return false
    else return true
}

enter image description here Что я делаю не так? Согласно выводу моей функции, эта левая верхняя точка находится между двумя справа

@ Пит новая функция:

function isPointBetweenTwoOthers (pA, pB, pToCheck) {
    var pApB = new THREE.Vector3();
    pApB.subVectors(pA,pB)
    var pApB_length = pApB.length()

    var pBpA = new THREE.Vector3();
    pBpA.subVectors(pB,pA)
    var pBpA_length = pBpA.length()
    var pA_pToCheck = new THREE.Vector3();
    pA_pToCheck.subVectors(pA,pToCheck)

    var pB_pToCheck = new THREE.Vector3();
    pB_pToCheck.subVectors(pB,pToCheck)

    var pToCheck_pA = new THREE.Vector3();
    pToCheck_pA.subVectors(pToCheck,pA)
    var pToCheck_pA_length = pToCheck_pA.length()

    if(pToCheck_pA_length > pBpA_length)
    {return false}
        else if(pToCheck_pA.dot(pBpA) > 0.999999 *pToCheck_pA.x*pBpA.x + pToCheck_pA.y*pBpA.y + pToCheck_pA.z*pBpA.z) 
        {return true}
        else {return false}
}

Сейчас (темная сферана рисунке ниже точка C) выводит этот случай так же верно, пока я добавляю 0,999999. Без этого он работает нормально в этом случае ... enter image description here, но в другом случае без 0,9999, он говорит мне, что C не находится между A & B, используя следующие пункты ...

var t1 = new THREE.Vector3(1,1,1); //A
var t2 = new THREE.Vector3(-1,1,1); //B
var t3 = new THREE.Vector3(-0.3999999999999999,1,1);  //C

enter image description here

Ответы [ 2 ]

2 голосов
/ 06 октября 2019

Если вы хотите вычислить вектор от точки pA до точки pB , тогда вы должны вычесть pA форму pB (pB-pA) вместо pB с pA (pA-pB) .
Примечание, .subVectors( a : Vector3, b : Vector3 ) рассчитывает a-b.


Короткий ответ :

Убедитесь, что расстояние между pA и pToCheck и расстояние между pB и pToCheck меньше илиравно расстоянию между pA и pB.
и проверьте, равен ли угол между векторной формой pA до pToCheck соответственно pB до pToCheck и вектором от pA до pB близко к 0:

Это можно сделать с помощью .distanceTo соответственно с .angleTo:

function isPointbetweenTwoOthers (pA, pB, pToCheck) {

    let distAtoB = pA.distanceTo(pB);
    let distAtoC = pA.distanceTo(pToCheck);
    let distBtoC = pB.distanceTo(pToCheck);
    if (distAtoC > distAtoB || distBtoC > distAtoB)
        return false;

    let vAtoB = pB.clone().sub(pA);
    let vBtoA = pA.clone().sub(pB);
    let vAtoC = pToCheck.clone().sub(pA);
    let vBtoC = pToCheck.clone().sub(pB);

    let angleAC = vAtoB.angleTo(vAtoC);
    let angleBC = vBtoA.angleTo(vBtoC);
    let epsilon = 0.0017; // 0.1° in radians 
    if (Math.abs(angleAC) > epsilon || Math.abs(angleBC) > epsilon)
        return false;

    return true;
}

Длинный ответ :

Произведение Dot между вектором A и вектором B равно произведениювеличина (длина) векторов и косинус угла между векторами.

A dot B == |A| * |B| * cos(alpha)

Если 2 вектора Единичные векторы (нормализованные), тогда произведение точек равно косинусу угла между двумя векторами:

uA = normalize(A)
uB = normalize(B)
uA dot uB == cos(alpha)

Косинус угла больше 0, если угол альфа в гвведите значение -90 ° и меньше 90 °. Это значение равно 0 для 90 ° или -90 ° и меньше, чем 0.
Косинус равен 1,0, если угол равен 0 °, и -1,0, если угол равен 180 °.

Если вы просто хотите проверить, находится ли pC на линии между pA и pB , то вам нужно проверить,угол между векторами равен 0 ° (или 180 °), а косинус угла равен 1,0 (или -1,0).
Из-за ограниченной точности операций вы не можете напрямую сравнить с 1,0. Вы должны убедиться, что косинус больше, чем значение, близкое к 1,0.
Векторы должны быть нормализованы и использовать эпсилон из ~ 0,0016, что соответствуеткосинус (0,1 °):

     pA      pC      pB
       x-------x-------x

vAtoB  |-------------->|
vAtoC  |------>|  
vBtoC          |<------|
nvAtoB = normalize(pB-pA)
nvAtoC = normalize(pC-pA)
nvBtoC = normalize(pC-pB)

isOn = dot(nvAtoB, nvAtoC) > (1-epsilon) AND dot(nvAtoB, nvBtoC) < -(1-epsilon)

Функция должна быть:

function isPointbetweenTwoOthers (pA, pB, pToCheck) {
    var nvAtoB = new THREE.Vector3();
    nvAtoB.subVectors(pB, pA).normalize();

    var nvAtoC = new THREE.Vector3();
    nvAtoC.subVectors(pToCheck, pA).normalize();

    var nvBtoC = new THREE.Vector3();
    nvBtoC.subVectors(pToCheck, pB).normalize();

    let epsilon = 0.0016;
    let cos90epsi = 1.0 - epsilon;
    return nvAtoB.dot(nvAtoC) > cos90epsi && nvAtoB.dot(nvBtoC) < -cos90epsi;
}

Другой подход - найти ближайшую точку pX до pC на линии от pA до pB . Если pX находится между pA и pB , а расстояние от pX до pC ниже определенного порога,тогда рС составляет от пА до пБ . Поскольку pX находится на линии, определяемой от pA до pB , достаточно сравнить косинус угла с 0,0.

Еслиу вас есть линия, определенная точкой O на единичном векторе направления D, тогда точку пересечения X можно рассчитать, сдвинув точку O вдоль линии (D) на расстояниеd:

X = O + D * d

Таким образом, формула для точки пересечения:

O ... any point on the line
D ... unit vector which points in the direction of the line
P ... the "Point"

X = O + D * dot(P-O, D); 

intersection line point

Расчет по точкамна линии A, B и точка P выглядит так:

D = normalize(B-A);
X = A + D * dot(P-A, D);

К счастью THREE.js обеспечивает .projectOnVector(), что делаетрасчет D * dot(P-A, D). См. Также Векторная проекция :

function isPointbetweenTwoOthers (pA, pB, pToCheck) {

    let vAtoB = pB.clone().sub(pA);
    let vAtoC = pToCheck.clone().sub(pA);
    let vBtoC = pToCheck.clone().sub(pB);

    // fast exit, C can't be between A and B
    if (nvAtoB.dot(nvAtoC) < 0.0 || nvAtoB.dot(nvBtoC) > 0.0)
       return false;

    // nearest point X to C on the line A, B
    let X = pA.clone().add( vAtoC.clone().projectOnVector( vAtoB ) );

    // distance between X and C
    let distXtoC = X.distanceTo( C );

    let threshold = 1.0; // ???
    return distXtoC < threshold;
}

Это может быть дополнительно упрощено с помощью Cross product . Величина перекрестного произведения двух векторов равна площади параллелограмма, на которую распространяются векторы.
Это означает, что если перекрестное произведение близко к 0,0, то векторы параллельны и pC равнона линии от пА до пБ :

function isPointbetweenTwoOthers (pA, pB, pToCheck) {

    let vAtoB = pB.clone().sub(pA);
    let vAtoC = pToCheck.clone().sub(pA);
    let vBtoC = pToCheck.clone().sub(pB);

    // fast exit, C can't be between A and B
    if (nvAtoB.dot(nvAtoC) < 0.0 || nvAtoB.dot(nvBtoC) > 0.0)
       return false;

    // cross product and area spawned by the normalized vectors B-A and C-A
    let nv = vAtoB.normalize().cross(vAtoC.normalize());
    let area = nv.length();

    let epsilon = 0.0016; // ???
    return Math.abs(area) < epsilon;
}
2 голосов
/ 06 октября 2019

Чтобы проверить, находится ли C между A и B, вычислите, например, разности векторов u=subVectors(pB,pA) и v=subVectors(pC,pA). Вычислите длины обоих векторов lu и lv соответственно. Если lv>lu, вернуть false, поскольку C находится дальше от A, чем B. Если это не так, проверьте, равняется ли произведение точек u и v на lu*lv (или, может быть, если оно превышает 0.9999*lu*lv, чтобы иметь некоторую числовую погрешность), и в этом случае верните значение true, в противном случае - значение false. ,Пояснение: если C не дальше от A, чем B, а векторы от A до B и от A до C параллельны (но не антипараллельны), произведение точек равно lu*lv (cos (0 градусов) = 1в то время как cos (180 градусов, антипараллельный случай) = - 1) и C лежат между A и B.

...