javascript 如何求一个点和一条直线的交点?

3lxsmp7m  于 2023-01-01  发布在  Java
关注(0)|答案(1)|浏览(215)

我想找到一个点+方向和一条直线的交点。

  • 点位于多边形的中心,线是多边形的边。
  • 方向是多边形最长线段的弧度旋转。

为了说明我的问题,我做了一个截图:
image of the current state
你可以看到中心点,右边多边形的最长线段+最长线段的起点,都是黑色的。

  • 紫色和红色的点是我的算法找到的交点。
  • 底部和右侧的交叉点是正确的,但在左侧它找到了2个交叉点,其中一个是正确的。
  • 顶部交点未到达多边形的边。
  • 紫色的点应该是顶部和底部的交点
  • 较小的红点应位于多边形的左侧和右侧,因为您可以看到它们部分混合在一起。

我的代码:
第一个循环计算最长线段和该线段的旋转。第二个循环根据最长线段的旋转检查垂直和水平方向上的相交

let sqLongestSide = 0;
let longestSideXDiff = 0;
let longestSideYDiff = 0;

for (let i = 1; i < coordinates.length; i++) {
    let pointFrom = coordinates[i - 1];
    let pointTo = coordinates[i];

    const xDiff = pointFrom[0] - pointTo[0];
    const yDiff = pointFrom[1] - pointTo[1];

    const sqDistance = Math.sqrt(xDiff * xDiff + yDiff * yDiff);

    const roundedDistance = Math.round(sqDistance * 100) / 100;
    const roundedLongestSide = Math.round(sqLongestSide * 100) / 100;

    if (roundedDistance > roundedLongestSide) {
        sqLongestSide = sqDistance;
        longestSideXDiff = xDiff;
        longestSideYDiff = yDiff;
    }
}

const rotation = Math.atan(longestSideYDiff / longestSideXDiff);

for (let i = 1; i < coordinates.length; i++) {

    let pointFrom = coordinates[i - 1];
    let pointTo = coordinates[i];

    const intersectionTopAndBottom = intersectionPoint(anchor, rotation, [pointFrom, pointTo]);

    if (intersectionTopAndBottom) {
        setPointStyle(drawContext, "#FF00FF", 20);
        drawContext.drawPoint(new Point(intersectionTopAndBottom));
        drawContext.drawLineString(new LineString([anchor, intersectionTopAndBottom]));
    }

    const intersectionLeftAndRight = intersectionPoint(anchor, (rotation + Math.PI / 2), [pointFrom, pointTo]);

    if (intersectionLeftAndRight) {
        setPointStyle(drawContext, "#FF0000", 10);
        drawContext.drawPoint(new Point(intersectionLeftAndRight));
        setLineStyle(drawContext, "#FF0000", 5);
        drawContext.drawLineString(new LineString([anchor, intersectionLeftAndRight]));
    }

求交集的函数如下所示:

function intersectionPoint(
  point: Coordinate,
  theta: number,
  line: Coordinate[]
): Coordinate {
  const x0 = Math.round(point[0] * 100) / 100;
  const y0 = Math.round(point[1] * 100) / 100;

  const x1 = Math.round(line[0][0] * 100) / 100;
  const y1 = Math.round(line[0][1] * 100) / 100;
  const x2 = Math.round(line[1][0] * 100) / 100;
  const y2 = Math.round(line[1][1] * 100) / 100;

  // Check if the line is vertical or horizontal
  if (x1 === x2) {
    // The line is vertical, so the intersection point is simply the point with the same x-coordinate as the line
    return [x1, y0];
  } else if (y1 === y2) {
    // The line is horizontal, so the intersection point is simply the point with the same y-coordinate as the line
    return [x0, y1];
  }

  // Convert the line to slope-intercept form
  const slope = (y2 - y1) / (x2 - x1);
  const intercept = y1 - slope * x1;

  // Convert the point and direction to slope-intercept form
  const slope2 = Math.tan(theta);
  const intercept2 = y0 - slope2 * x0;

  // Find the intersection point of the two lines
  const x = (intercept2 - intercept) / (slope - slope2);
  const y = slope * x + intercept;

  return [x, y];
}

我只需要4个交叉点,但得到更多的这个特定的多边形,但简单的矩形,我得到正确的结果。
编辑:
只是为了弄清楚。我需要对射线应用一个旋转。这是在第一个循环中计算的最长线段的旋转。
@Blindman67你的解决方案给了我正确的结果为第一个多边形,我只需要交叉"十字"旋转。如下图:
correct
该算法给出以下结果:
not correct

vql8enpb

vql8enpb1#

澄清

澄清我对你问题的理解。
如果给定一组点P1 - 6和一个点A,则沿x和y轴查找与投影点A相交的点,即点B1 - 7

您的函数给出了点B1B2B3B4B5。但点B4B5不正确
你说你只想要4分,B1B2B3B6
溶液
看起来感兴趣点只是由点P1 - 6创建的线段上的点
我们可以使用一个函数来找到一条直线(无限长)和一条线段(有限长有一个起点和终点)的截距。

示例代码

函数interceptLineSeg将找到线段上的点(包括起点,不包括终点)。如果同时包括起点和终点,则会(可能由于浮点错误)找到两次相同的点。

    • 注意**浮点错误可能导致一个点被找到两次。您可能需要检查找到的两个点是否太接近。
    • 注意**如果点A位于与x或y轴对齐的线(由多边形边线段定义)上,则截距将取决于构成多边形的点的方向(顺时针或逆时针)
const TAU = Math.PI * 2;
const ctx = canvas.getContext("2d");
const P2 = (x, y) => ({x, y});      // 2d point
const L2 = (p1, p2) => ({p1, p2});  // 2d line
const A = P2(100, 115);
const points =  [P2(140,20), P2(140, 140), P2(40, 140), P2(40, 115), P2(80, 80), P2(80, 50)];

function addCircle(p, r) {ctx.moveTo(p.x + r, p.y); ctx.arc(p.x, p.y, r, 0, TAU) }
function addLine(l) {  ctx.moveTo(l.p1.x, l.p1.y);  ctx.lineTo(l.p2.x, l.p2.y) }
function strokePath(points, close = false) {
  var i = 0;
  ctx.beginPath();
  while (i < points.length - 1) { addLine(L2(points[i ++], points[i])); }
  close && addLine(L2(points[0], points[points.length - 1]));
  ctx.stroke();
}
function markPoints(points, r) {
  var i = 0;
  ctx.beginPath();
  while (i < points.length) { addCircle(points[i++], r); }
  ctx.fill();
}

ctx.lineWidth = 3;
ctx.strokeStyle = "#0088ff";
ctx.fillStyle = "#0088ff";
strokePath(points, true);
markPoints(points, 5);

ctx.lineWidth = 1.5;
ctx.strokeStyle = "#ff0000";
ctx.fillStyle = "#ff0000";
strokePath([P2(A.x - 100, A.y), P2(A.x + 100, A.y)]);
strokePath([P2(A.x, A.y - 100), P2(A.x, A.y + 100)]);

markPoints([A], 5);

const iPoints = intercepts(A, points);
ctx.fillStyle = "#000";
markPoints(iPoints, 5);
console.log("Points found: " + iPoints.length);

function interceptLineSeg(l, s){  // l is line, s is seg
    const v1x = l.p2.x - l.p1.x;
    const v1y = l.p2.y - l.p1.y;
    const v2x = s.p2.x - s.p1.x;
    const v2y = s.p2.y - s.p1.y;
    const c = v1x * v2y - v1y * v2x; // cross product of line vectors
    if (c !== 0) {                   // only if not parallel 
    
        // get unit pos of intercept on line segment
        const u = (v1x * (l.p1.y - s.p1.y) - v1y * (l.p1.x - s.p1.x)) / c;
        if (u >= 0 && u < 1) { // is intercept on segment (include only start)
            return P2(s.p1.x + v2x * u, s.p1.y + v2y * u);
        }
    }
    return; // returns undefined if no intercept
}

// a is point 
// p is array of points
function intercepts(a, p) {
    var i = 0;
    const hLine = L2(a, P2(a.x + 100, a.y));
    const vLine = L2(a, P2(a.x, a.y + 100));
    const wLine = L2();
    const intercepts = [];
    ahy = a.y;
    avx = a.x;
    avy = a.y + 100;
    while (i < p.length) {
        wLine.p1 = p[i];
        wLine.p2 = p[(i + 1) % p.length];
        const hl = interceptLineSeg(hLine, wLine);
        const vl = interceptLineSeg(vLine, wLine);
        if (hl) {
            intercepts.push(hl);
        } else if (vl) {
            intercepts.push(vl);
        }
        i ++;
    }
    return intercepts;
}
<canvas id="canvas" width="300" height="300"></canvas>

相关问题