Three.js - 如何确定一个点是否在一条线上?

时间:2016-02-07 23:01:13

标签: javascript vector 3d three.js

如何判断点(x,y,z)是否在pointA和pointB之间的一条线上?

我想要的是一个布尔函数,它可以做到这一点:

pointA        // random THREE.Vector3
pointB        // random THREE.Vector3
pointToCheck  // random THREE.Vector3
var isOnLine = THREE.pointOnLine(pointA, pointB, pointToCheck)

if (isOnLine) {
  console.log('point is on the line');
}

以下是可视化图像:

enter image description here

3 个答案:

答案 0 :(得分:2)

Cross product两个向量可以帮助我们解决这个问题。

function isPointOnLine (pointA, pointB, pointToCheck) {
    var c = new THREE.Vector3();   
    c.crossVectors(pointA.clone().sub(pointToCheck), pointB.clone().sub(pointToCheck));
    return !c.length();
}

THREE.isPointOnLineAndBetweenPoints = function (pointA, pointB, pointToCheck) {
    if (!isPointOnLine(pointA, pointB, pointToCheck)) {
        return false;
    }

    var dx = pointB.x - pointA.x;
    var dy = pointB.y - pointA.y;

    // if a line is a more horizontal than vertical:
    if (Math.abs(dx) >= Math.abs(dy)) {
        if (dx > 0) {
            return pointA.x <= pointToCheck.x && pointToCheck.x <= pointB.x;
        } else {
            return pointB.x <= pointToCheck.x && pointToCheck.x <= pointA.x;
        }
    } else {
        if (dy > 0 ) {
            return pointA.y <= pointToCheck.y && pointToCheck.y <= pointB.y;
        } else {
            return pointB.y <= pointToCheck.y && pointToCheck.y <= pointA.y;
        }
    }
}

电话:

THREE.isPointOnLineAndBetweenPoints(new THREE.Vector3(1, 0, 0), new THREE.Vector3(2, 0, 0), new THREE.Vector3(2, 0, 0));

如果您想知道这一点是否在一条线上,请使用以下功能:

isPointOnLine(new THREE.Vector3(1, 0, 0), new THREE.Vector3(2, 0, 0), new THREE.Vector3(2, 0, 0));

答案 1 :(得分:0)

这种简单得多的方法。

derived_with_keys

答案 2 :(得分:-1)

您可以为三维线生成方程的对称形式,插入pointToCheck上的点,并确定它是否在线上。这是代码:

// Pick two arbitrary points to be on the line
var pointA =  new THREE.Vector3( -70,  -4, -100 );
var pointB =  new THREE.Vector3( 65,  22, 14 );

// Equation that takes in three points, pointA and pointB
// on a three-dimensional line and pointToCheck unknown, and
// returns true if pointToCheck is on the line and false if not
// optional param betweenCheck will additionally check if point
// is between pointA and pointB
var isOnLine = function(pointA, pointB, pointToCheck, betweenCheck) {
  xVector = pointB.x - pointA.x;
  yVector = pointB.y - pointA.y;
  zVector = pointB.z - pointA.z;
  vector = [xVector, yVector, zVector];

  if (!!betweenCheck) {

      // test if point is between pointA and pointB
      if (pointToCheck.x < Math.min[pointA.x, pointB.x] ||
        pointToCheck.x > Math.max[pointA.x, pointB.x]) {
          return false;
      }
      if (pointToCheck.y < Math.min[pointA.y, pointB.y] ||
        pointToCheck.y > Math.max[pointA.y, pointB.y]) {
          return false;
      } 
      if (pointToCheck.z < Math.min[pointA.z, pointB.z] ||
        pointToCheck.z > Math.max[pointA.z, pointB.z]) {
          return false;
      } 
  }


  // equation for the vector function generating this line is:
  // [pointA.x, pointA.y, pointA.z] + t[vector], or
  // [pointA.x + t * xVector, pointA.y + t * yVector, 
  // pointA.z + t * zVector], or
  // parametric form:
  // x = pointA.x + (t * xVector)
  // y = pointA.y + (t * yVector)
  // z = pointA.z + (t * zVector), or
  // symmetric form:
  // x - pointA.x    y - pointA.y     z - pointA.z
  // ------------ = -------------- = --------------
  // xVector         yVector            zVector
  //
  // So to test for whether pointToCheck is on line, we plug in 
  // its coordinates to x, y and z in the symmetric form 
  // and see if the equations balance
  var x = (pointToCheck.x - pointA.x) / xVector;
  var y = (pointToCheck.y - pointA.y) / yVector;
  var z = (pointToCheck.z - pointA.z) / zVector;
  var results = [x, y, z];

  // Handle any axis where no change occurred by removing the
  // point to check, as it's irrelevent to determining whether
  // point to check is on the line.  
  for (var i = 0; i < 2; i++) {
    if (isNaN(results[i])) {
      results.splice(i, 1);
    }
  }

  var first = results[0];
  // Cycle through remaining results and make sure they are all
  // the same
  for (var i = 0; i < results.length; i++) {

    // If any point is different, return false, as the point to
    // check is not on the line
    if (results[i] !== first) {
      return false
    }
  }

  // All the symmetric equations were equal (or irrelevant) and 
  // the pointToCheck is on the line
  return true;
}

以下是一些测试:

// Some quick testing on example lines (you can change the
// coords of pointA and pointB above and they will still pass)
pointsOnLine = [];
pointsOffLine = [];
pointsOnLineBetween = [];
pointsOffLineBetween = [];

var generatePoints = function() {
  xVector = pointB.x - pointA.x;
  yVector = pointB.y - pointA.y;
  zVector = pointB.z - pointA.z;
  vector = [xVector, yVector, zVector];
  for (var i = 0; i < 100; i++) {
    var t = parseInt(Math.random() * 100);
    var direction = Math.random() < .5 ? true : false
    if (!direction) {
      t = -t;
    }
    var newPointCoords = new THREE.Vector3( 
      pointA.x + (xVector * t),
      pointA.y + (yVector * t),
      pointA.z + (zVector * t)
    );
    pointsOnLine.push(newPointCoords);
    var newPointCoords = new THREE.Vector3( 
      pointA.x + (xVector * t) + parseInt(Math.random() * 100),
      pointA.y + (yVector * t) - parseInt(Math.random() * 100),
      pointA.z + (zVector * t) + parseInt(Math.random() * 100)
    );
    pointsOffLine.push(newPointCoords);

    var x = ((Math.max(pointA.x, pointB.x) - Math.min(pointA.x, pointB.x)) / 
        2) + Math.min(pointA.x, pointB.x);
    var y = ((Math.max(pointA.y, pointB.y) - Math.min(pointA.y, pointB.y)) / 
        2) + Math.min(pointA.y, pointB.y)
    var z = ((Math.max(pointA.z, pointB.z) - Math.min(pointA.z, pointB.z)) / 
        2) + Math.min(pointA.z, pointB.z)
    var newPointCoords = new THREE.Vector3(x, y, z);
    pointsOnLineBetween.push(newPointCoords);

    var x = ((Math.max(pointA.x, pointB.x) - Math.min(pointA.x, pointB.x)) / 
        Math.abs(t)) + Math.min(pointA.x, pointB.x);
    var y = ((Math.max(pointA.y, pointB.y) - Math.min(pointA.y, pointB.y)) / 
        Math.abs(t) * 2) + Math.min(pointA.y, pointB.y)
    var z = ((Math.max(pointA.z, pointB.z) - Math.min(pointA.z, pointB.z)) / 
        Math.abs(t) * 3) + Math.min(pointA.z, pointB.z)
    var newPointCoords = new THREE.Vector3(x, y, z);
    pointsOffLineBetween.push(newPointCoords);

  }
}

generatePoints();

for (var i=0; i < pointsOnLine.length; i++) {
  if (!isOnLine(pointA, pointB, pointsOnLine[i])) {
    console.log('error', pointsOnLine[i]);
  } else {
    console.log('test passed -- point on line')
  }
}
for (var i=0; i < pointsOffLine.length; i++) {
  if (isOnLine(pointA, pointB, pointsOffLine[i])) {
    console.log('error', pointsOffLine[i]);
  } else {
    console.log('test passed -- point off line')
  }
}
for (var i=0; i < pointsOnLineBetween.length; i++) {
  if (!isOnLine(pointA, pointB, pointsOnLineBetween[i], true)) {
    console.log('error', pointsOnLineBetween[i]);
  } else {
    console.log('test passed -- point on line between')
  }
}
for (var i=0; i < pointsOffLineBetween.length; i++) {
  if (isOnLine(pointA, pointB, pointsOffLineBetween[i], true)) {
    console.log('error', pointsOffLineBetween[i]);
  } else {
    console.log('test passed -- point off line between')
  }
}

Plunkr