如何用p5.js计算圆上直线的交点

How to calculate intersection point of a line on a circle using p5.js(如何用p5.js计算圆上直线的交点)
本文介绍了如何用p5.js计算圆上直线的交点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一条直线(se),我知道它从圆内开始,我知道在圆外结束。我正在尝试找到直线与圆相交的点l

我正在使用p5.js库,并且可以访问其所有Vector函数。

我的想法是,如果我能在直线上与半径成直角,我就可以开始解决一些问题。

// Get the vector between s and c
let scVector = p5.Vector.sub(start, circleCenter);
// Get the angle between se and sc
let escAngle = this.v.angleBetween(scVector);
// Get the distance between t (where the right angle hits the center of the circle) and c
let tcDistance = Math.sin(escAngle) * scVector.mag();
// Get the distance between t and where the line intersects the circle
let tlDistance = Math.sqrt(Math.pow(hole.r, 2) - Math.pow(tcDistance, 2));
// Get the distance between the start point and t
let stDistance = Math.sqrt(Math.pow(scVector.mag(), 2) - Math.pow(tcDistance, 2));
// Add the two distances together, giving me the distance between s and l
let totalDistance = tcDistance + stDistance;
// Create a new Vector at this angle, at the totalDistance Magnitude, then add it to the current position
let point = p5.Vector.fromAngle(this.v.heading(), totalDistance).add(start);
// Mark a point (hopefully l) on the edge of the circle.
points.push({
  x: point.x,
  y: point.y,
  fill: '#ffffff'
})

不幸的是,当我的对象穿过圆圈时,它们没有在边缘留下点,而是离圆圈的边缘更远。

小圆点是标记位置,彩色圆点是物体(有起点和终点)

我这里有一个演示,有问题的部分在第42行之后: https://codepen.io/EightArmsHQ/pen/be0461014f9868e3462868776d9c8f1a

如有任何帮助,我们将不胜感激。

推荐答案

若要查找点和直线的交点,我建议使用现有算法,如WolframMathWorld - Circle-Line Intersection。

算法简短,可以用一个简短的函数来表示,很好地解释了这一点。输入参数p1p2cpt类型为p5.Vectorr为标量类型。该参数定义了一条从p1p2的环状线条和一个圆心cpt和半径r的圆。此函数返回感兴趣的截面点列表,其中可能为空:

intersectLineCircle = function(p1, p2, cpt, r) {

    let sign = function(x) { return x < 0.0 ? -1 : 1; };

    let x1 = p1.copy().sub(cpt);
    let x2 = p2.copy().sub(cpt);

    let dv = x2.copy().sub(x1)
    let dr = dv.mag();
    let D = x1.x*x2.y - x2.x*x1.y;

    // evaluate if there is an intersection
    let di = r*r*dr*dr - D*D;
    if (di < 0.0)
        return [];
   
    let t = sqrt(di);

    ip = [];
    ip.push( new p5.Vector(D*dv.y + sign(dv.y)*dv.x * t, -D*dv.x + p.abs(dv.y) * t).div(dr*dr).add(cpt) );
    if (di > 0.0) {
        ip.push( new p5.Vector(D*dv.y - sign(dv.y)*dv.x * t, -D*dv.x - p.abs(dv.y) * t).div(dr*dr).add(cpt) ); 
    }
    return ip;
}

如果要验证某个点位于";与其他点之间的";,则可以使用Dot product。如果您知道一条线上的3个点,那么计算点之间的距离就足够了,以确定1个点是否在其他2个点之间。
p1p2px属于p5.Vector类型。这些点在同一条线上。如果px介于p1p2false之间,则函数返回true,否则:

inBetween = function(p1, p2, px) {

    let v = p2.copy().sub(p1);
    let d = v.mag();
    v = v.normalize();

    let vx = px.copy().sub(p1);
    let dx = v.dot(vx);
    
    return dx >= 0 && dx <= d;
}

请参阅我为测试该函数而实现的示例。该圆由鼠标跟踪,并由一条随机移动的线相交:

var sketch = function( p ) {

p.setup = function() {
    let sketchCanvas = p.createCanvas(p.windowWidth, p.windowHeight);
    sketchCanvas.parent('p5js_canvas')
}

let points = [];
let move = []

// Circle-Line Intersection
// http://mathworld.wolfram.com/Circle-LineIntersection.html
p.intersectLineCircle = function(p1, p2, cpt, r) {

    let sign = function(x) { return x < 0.0 ? -1 : 1; };

    let x1 = p1.copy().sub(cpt);
    let x2 = p2.copy().sub(cpt);

    let dv = x2.copy().sub(x1)
    let dr = dv.mag();
    let D = x1.x*x2.y - x2.x*x1.y;

    // evaluate if there is an intersection
    let di = r*r*dr*dr - D*D;
    if (di < 0.0)
        return [];
   
    let t = p.sqrt(di);

    ip = [];
    ip.push( new p5.Vector(D*dv.y + sign(dv.y)*dv.x * t, -D*dv.x + p.abs(dv.y) * t).div(dr*dr).add(cpt) );
    if (di > 0.0) {
        ip.push( new p5.Vector(D*dv.y - sign(dv.y)*dv.x * t, -D*dv.x - p.abs(dv.y) * t).div(dr*dr).add(cpt) ); 
    }
    return ip;
}

p.inBetween = function(p1, p2, px) {

    let v = p2.copy().sub(p1);
    let d = v.mag();
    v = v.normalize();

    let vx = px.copy().sub(p1);
    let dx = v.dot(vx);
    
    return dx >= 0 && dx <= d;
}

p.endlessLine = function(x1, y1, x2, y2) {

    p1 = new p5.Vector(x1, y1);
    p2 = new p5.Vector(x2, y2);

    let dia_len = new p5.Vector(p.windowWidth, p.windowHeight).mag();
    let dir_v = p5.Vector.sub(p2, p1).setMag(dia_len);
    let lp1 = p5.Vector.add(p1, dir_v);
    let lp2 = p5.Vector.sub(p1, dir_v);

    p.line(lp1.x, lp1.y, lp2.x, lp2.y);
}

p.draw = function() {
        
    if (points.length == 0) {

        points = [];
        move = [];
        for (let i=0; i < 2; ++i ) {
            points.push( new p5.Vector(p.random(p.windowWidth-20)+10, p.random(p.windowHeight-20)+10));
            move.push( new p5.Vector(p.random(2)-1, p.random(2)-1) );
        }
        points.push( new p5.Vector(p.mouseX, p.mouseY));
    }
    else
    {
        for (let i=0; i < 2; ++i ) {
            points[i] = points[i].add(move[i]);
            if (points[i].x < 10 || points[i].x > p.windowWidth-10)
                move[i].x *= -1; 
            if (points[i].y < 10 || points[i].y > p.windowHeight-10)
                move[i].y *= -1;    
            move[i].x = Math.max(-1, Math.min(1, move[i].x+p.random(0.2)-0.1))
            move[i].y = Math.max(-1, Math.min(1, move[i].y+p.random(0.2)-0.1))
        }
        points[2].x = p.mouseX;
        points[2].y = p.mouseY;
    }
    let circle_diameter = p.min(p.windowWidth, p.windowHeight) / 2.0;

    let isectP = p.intersectLineCircle(...points, circle_diameter/2.0);

    // draw the scene

    p.background(192);
    
    p.stroke(0, 0, 255);
    p.fill(128, 128, 255);
    for (let i=0; i < points.length; ++i ) {
        p.ellipse(points[i].x, points[i].y, 10, 10);
    }

    for (let i=0; i < isectP.length; ++i ) {
        if (p.inBetween(points[0], points[1], isectP[i])) {
            p.stroke(255, 0, 0);
            p.fill(255, 128, 0);
        } else {
            p.stroke(255, 128, 0);
            p.fill(255, 255, 0);
        }

        p.ellipse(isectP[i].x, isectP[i].y, 10, 10);
    }

    p.stroke(0, 255, 0);
    p.noFill();
    p.endlessLine(points[0].x, points[0].y, points[1].x, points[1].y)
    p.ellipse(points[2].x, points[2].y, circle_diameter, circle_diameter);
}

p.windowResized = function() {
    p.resizeCanvas(p.windowWidth, p.windowHeight);
    points = [];
}

p.mouseClicked = function() {
    points = [];
}

p.keyPressed = function() {
    points = []
}

};

var circle_line = new p5(sketch);
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
<div id="p5js_canvas"></div>

这篇关于如何用p5.js计算圆上直线的交点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

Update another component when Formik form changes(当Formik表单更改时更新另一个组件)
Formik validation isSubmitting / isValidating not getting set to true(Formik验证正在提交/isValiating未设置为True)
React Validation Max Range Using Formik(使用Formik的Reaction验证最大范围)
Validation using Yup to check string or number length(使用YUP检查字符串或数字长度的验证)
Updating initialValues prop on Formik Form does not update input value(更新Formik表单上的初始值属性不会更新输入值)
password validation with yup and formik(使用YUP和Formick进行密码验证)