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

6

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

我想要一个布尔函数来完成这个任务:

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');
}

以下是可视化图片:

在此输入图像描述


1
一个简单的方法是计算由这三个点形成的三角平面的面积。如果面积为0,则它们在同一条直线上。 - mwcz
请注意浮点数限制,检查一个点(x,y)是否在一条直线上的两个点之间:https://dev59.com/B2Mm5IYBdhLWcg3wVt0s - gaitat
3个回答

2
两个向量的叉积可以帮助我们解决这个问题。

两个向量的叉积

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));

函数 isPointOnLineAndBetweenPoints 对于 3D 向量是否正确? - taseenb
检查点P是否在线段A-B内的函数不起作用。虽然这种方法并非适用于所有情况,但它简单易行,在大多数情况下都能正常工作:`/**
  • Check if p is between a and b, or outside
  • @param a Start of the segment
  • @param b End of the segment
  • @param p Point to check */ isPointOnSegment (a, b, p) { const tolerance = 0.0001 const ab = a.distanceTo(b) const pa = p.distanceTo(a) const pb = p.distanceTo(b) const difference = Math.abs(ab - (pa + pb))
return difference < tolerance && difference > -tolerance }`
- taseenb

0

这是更简单的方法。

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;
    }

    let d = pointA.distanceTo(pointB);

    return pointA.distanceTo(pointToCheck) < d && pointB.distanceTo(pointToCheck) < d;
}

-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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接