如何从三个点计算角度?[关闭]


120

可以说你有这个:

P1 = (x=2, y=50)
P2 = (x=9, y=40)
P3 = (x=5, y=20)

假设这P1是一个圆的中心。总是一样的。我想要由P2和构成P3的角度,换句话说就是与相邻的角度P1。内角要精确。它始终是锐角,因此小于-90度。

我以为:那是简单的几何数学。但是我现在已经寻找了大约6个小时的公式,却只发现人们谈论复杂的NASA东西,例如arccos和矢量标量产品。我的头好像在冰箱里。

这里的一些数学大师认为这是一个简单的问题?我认为这里的编程语言并不重要,但是对于那些认为确实重要的人来说:java和Objective-c。我都需要它,但是还没有为它们添加标签。

Answers:


87

如果您的意思是P1是顶点的角度,那么使用余弦定律应该可以:

arccos((P 12 2 + P 13 2 -P 23 2)/(2 * P 12 * P 13))

其中P 12是从P1到P2的线段的长度,计算公式为

sqrt((P1 x -P2 x2 +(P1 y -P2 y2



@Rafa Firenze cos ^ -1是acos的常用符号,但是acos不太明确。en.wikipedia.org/wiki/Inverse_trigonometric_functions
geon 2014年

我将保留编辑内容,因为它不会造成任何损害,但是具有Math / CS / EE学位的cos ^ -1当然是最常见的表示法。
Lance Roberts 2014年

1
仅有少数几种语言使用脱字符号来表示“的力量”,因此,如果您不想将其称为arcos,请输入cos⁻¹。(如果您使用的是难以输入指数的商业操作系统,我希望可以购买一些键帽应用程序,或者可以安装浏览器插件。或者可以进行网络搜索以及复制和粘贴。)
Michael Scheper'3

1
@MichaelScheper,我只在HTML受限的注释中使用插入符号。我肯定会在任何实际答案中使用子/上标表示法。
Lance Roberts

47

如果您将其视为两个向量,一个从点P1到P2,另一个从P1到P3,则它变得非常简单。

因此:
a =(p1.x-p2.x,p1.y-p2.y)
b =(p1.x-p3.x,p1.y-p3.y)

然后,您可以将点积公式反转:
点积
得到角度:
两个向量之间的角度

请记住,这点积只是意味着:a1 * b1 + a2 * b2(此处只有2个维度...)


1
矢量的幅度
丹尼尔·利特尔

检查atan2解决方案。
Luc Boissaye

25

处理角度计算的最佳方法是使用atan2(y, x)给定的点x, y返回该点与X+轴相对于原点的角度。

鉴于计算是

double result = atan2(P3.y - P1.y, P3.x - P1.x) -
                atan2(P2.y - P1.y, P2.x - P1.x);

也就是说,你基本上是由两点翻译-P1(换句话说,让你翻译的一切P1,然后你考虑的绝对角度的差异两端向上的原点)P3P2

的优点atan2是可以表示出完整的圆圈(您可以在-π到π之间得到任何数字),而acos您需要根据符号来处理几种情况以计算出正确的结果。

唯一的奇异点atan2(0, 0)...,这意味着两者P2P3必须与之不同,P1因为在这种情况下谈论角度是没有意义的。


感谢您的回答。那正是我想要的。简单的解决方案,如果我在值是负数时加2pi,则可以轻松获得逆时针角度。
马里奥

@marcpt:atan2正是这个问题所需要的,但是看起来大多数解决此问题的人只是看不懂或无法理解acos基于解决方案的原因。对我来说幸运的是,很多年前,我离开了“互联网上有人错”(xkcd.com/386)的阶段,我不会为捍卫显而易见的斗争而奋斗:-)
6502

感谢您指出这一点,但是您可以这样处理3D吗?
nicoco

1
@nicoco:在三个维度上如何定义角度?更具体地说,角度可以是负数还是大于pi(180度)?3d中的两个非平行向量定义了一个平面,但是可以从两侧“看到”该平面:从一侧看A会出现在B的“左侧”,而从另一侧会出现在“右侧”。 。
6502

@ 6505感谢您的回答,我在认真思考之前就发布了。我现在知道了。
nicoco

19

让我举一个JavaScript的例子,我为此付出了很多努力:

/**
 * Calculates the angle (in radians) between two vectors pointing outward from one center
 *
 * @param p0 first point
 * @param p1 second point
 * @param c center point
 */
function find_angle(p0,p1,c) {
    var p0c = Math.sqrt(Math.pow(c.x-p0.x,2)+
                        Math.pow(c.y-p0.y,2)); // p0->c (b)   
    var p1c = Math.sqrt(Math.pow(c.x-p1.x,2)+
                        Math.pow(c.y-p1.y,2)); // p1->c (a)
    var p0p1 = Math.sqrt(Math.pow(p1.x-p0.x,2)+
                         Math.pow(p1.y-p0.y,2)); // p0->p1 (c)
    return Math.acos((p1c*p1c+p0c*p0c-p0p1*p0p1)/(2*p1c*p0c));
}

奖励:HTML5画布示例


5
您可以通过减少sqrt和平方来提高效率。在这里(用Ruby编写)或在此更新的演示版(JavaScript)中查看我的答案
Phrogz 2014年

您可以使用atan2作为更简单的解决方案。
Luc Boissaye

15

基本上,您有两个向量,一个向量从P1到P2,另一个向量从P1到P3。因此,您只需要一个公式即可计算两个向量之间的角度。

看看这里的一个很好的解释和公式。

替代文字


12

如果您认为P1是圆心,则您认为过于复杂。您有一个简单的三角形,因此您的问题可以用余弦定律解决。无需任何极坐标转换或类似方法。假设距离为P1-P2 = A,P2-P3 = B和P3-P1 = C:

角度= arccos((B ^ 2-A ^ 2-C ^ 2)/ 2AC)

您所需要做的就是计算距离A,B和C的长度。这些点可以从您的点的x和y坐标以及毕达哥拉斯定理轻松获得

长度= sqrt((X2-X1)^ 2 +(Y2-Y1)^ 2)


我有点困惑如何实际实现此功能,因为您将P1等视为单个值而不是(x,y)
Dominic

@Dominic Tobias:符号P1-P2 = A不应理解为“要计算A,从P1中减去P2”,而应理解为“我将A定义为从P1到P2的距离”,然后可以使用第二个公式来计算。我只是想定义距离的简写,以使方程式更易读。
2017年

8

最近我遇到了类似的问题,只需要区分正角和负角即可。如果这对任何人都有用,我建议我从此邮件列表中获取的代码片段,用于检测Android触摸事件的旋转:

 @Override
 public boolean onTouchEvent(MotionEvent e) {
    float x = e.getX();
    float y = e.getY();
    switch (e.getAction()) {
    case MotionEvent.ACTION_MOVE:
       //find an approximate angle between them.

       float dx = x-cx;
       float dy = y-cy;
       double a=Math.atan2(dy,dx);

       float dpx= mPreviousX-cx;
       float dpy= mPreviousY-cy;
       double b=Math.atan2(dpy, dpx);

       double diff  = a-b;
       this.bearing -= Math.toDegrees(diff);
       this.invalidate();
    }
    mPreviousX = x;
    mPreviousY = y;
    return true;
 }

7

带有解释的非常简单的几何解决方案

几天前,一个人陷入了同样的问题,不得不坐在那本数学书上。我通过组合和简化一些基本公式解决了这个问题。


让我们考虑这个数字-

角度

我们想知道ϴ,所以我们需要先找出αβ。现在,对于任何直线,

y = m * x + c

A =(ax,ay)B =(bx,by)O =(ox,oy)。因此,对于OA行-

oy = m1 * ox + c   ⇒ c = oy - m1 * ox   ...(eqn-1)

ay = m1 * ax + c   ⇒ ay = m1 * ax + oy - m1 * ox   [from eqn-1]
                   ⇒ ay = m1 * ax + oy - m1 * ox
                   ⇒ m1 = (ay - oy) / (ax - ox)
                   ⇒ tan α = (ay - oy) / (ax - ox)   [m = slope = tan ϴ]   ...(eqn-2)

同样,对于OB行-

tan β = (by - oy) / (bx - ox)   ...(eqn-3)

现在,我们需要ϴ = β - α。在三角函数中,我们有一个公式

tan (β-α) = (tan β + tan α) / (1 - tan β * tan α)   ...(eqn-4)

在替换tan αeqn-4中的(来自eqn-2)和tan b(来自eqn-3)的值,并进行简化后,我们得到-

tan (β-α) = ( (ax-ox)*(by-oy)+(ay-oy)*(bx-ox) ) / ( (ax-ox)*(bx-ox)-(ay-oy)*(by-oy) )

所以,

ϴ = β-α = tan^(-1) ( ((ax-ox)*(by-oy)+(ay-oy)*(bx-ox)) / ((ax-ox)*(bx-ox)-(ay-oy)*(by-oy)) )

这就对了!


现在,拿下图-

角度

这个C#或,Java方法计算角度(Θ) -

    private double calculateAngle(double P1X, double P1Y, double P2X, double P2Y,
            double P3X, double P3Y){

        double numerator = P2Y*(P1X-P3X) + P1Y*(P3X-P2X) + P3Y*(P2X-P1X);
        double denominator = (P2X-P1X)*(P1X-P3X) + (P2Y-P1Y)*(P1Y-P3Y);
        double ratio = numerator/denominator;

        double angleRad = Math.Atan(ratio);
        double angleDeg = (angleRad*180)/Math.PI;

        if(angleDeg<0){
            angleDeg = 180+angleDeg;
        }

        return angleDeg;
    }

该方法如何用于等边三角形?
Vikrant

1
好吧,您的答案现在工作正常。这是我上周的代码中的一些逻辑问题。
Vikrant

6

在Objective-C中,您可以通过

float xpoint = (((atan2((newPoint.x - oldPoint.x) , (newPoint.y - oldPoint.y)))*180)/M_PI);

在这里阅读更多


7
嗯不 有三个点,中心不在(0,0)处,这给出了直角的角度,而不是顶点的角度。角度的“ xpoint”是什么名字?
Jim Balter

4

您提到了一个符号角(-90)。在许多应用中,角度可能带有符号(正号和负号,请参见http://en.wikipedia.org/wiki/Angle)。如果这些点是(例如)P2(1,0),P1(0,0),P3(0,1),则角度P3-P1-P2通常为正(PI / 2),而角度P2-P1- P3为负。使用边的长度不会在+和-之间进行区分,因此,如果这很重要,则需要使用向量或Math.atan2(a,b)之类的函数。

角度也可以扩展到2 * PI以上,尽管这与当前问题无关,但我编写自己的Angle类非常重要(还要确保度和弧度不会混淆)。关于angle1是否小于angle2的问题主要取决于如何定义角度。决定将(-1,0)(0,0)(1,0)表示为Math.PI还是-Math.PI也可能很重要


4

我的角度演示程序

最近,我也遇到了同样的问题……在Delphi中,它与Objective-C非常相似。

procedure TForm1.FormPaint(Sender: TObject);
var ARect: TRect;
    AWidth, AHeight: Integer;
    ABasePoint: TPoint;
    AAngle: Extended;
begin
  FCenter := Point(Width div 2, Height div 2);
  AWidth := Width div 4;
  AHeight := Height div 4;
  ABasePoint := Point(FCenter.X+AWidth, FCenter.Y);
  ARect := Rect(Point(FCenter.X - AWidth, FCenter.Y - AHeight),
    Point(FCenter.X + AWidth, FCenter.Y + AHeight));
  AAngle := ArcTan2(ClickPoint.Y-Center.Y, ClickPoint.X-Center.X) * 180 / pi;
  AngleLabel.Caption := Format('Angle is %5.2f', [AAngle]);
  Canvas.Ellipse(ARect);
  Canvas.MoveTo(FCenter.X, FCenter.Y);
  Canvas.LineTo(FClickPoint.X, FClickPoint.Y);
  Canvas.MoveTo(FCenter.X, FCenter.Y);
  Canvas.LineTo(ABasePoint.X, ABasePoint.Y);
end;

2

这是一种C#方法,用于从圆的逆时针方向将角度(0-360)返回到水平方向。

    public static double GetAngle(Point centre, Point point1)
    {
        // Thanks to Dave Hill
        // Turn into a vector (from the origin)
        double x = point1.X - centre.X;
        double y = point1.Y - centre.Y;
        // Dot product u dot v = mag u * mag v * cos theta
        // Therefore theta = cos -1 ((u dot v) / (mag u * mag v))
        // Horizontal v = (1, 0)
        // therefore theta = cos -1 (u.x / mag u)
        // nb, there are 2 possible angles and if u.y is positive then angle is in first quadrant, negative then second quadrant
        double magnitude = Math.Sqrt(x * x + y * y);
        double angle = 0;
        if(magnitude > 0)
            angle = Math.Acos(x / magnitude);

        angle = angle * 180 / Math.PI;
        if (y < 0)
            angle = 360 - angle;

        return angle;
    }

保罗,干杯


2

function p(x, y) {return {x,y}}

function normaliseToInteriorAngle(angle) {
	if (angle < 0) {
		angle += (2*Math.PI)
	}
	if (angle > Math.PI) {
		angle = 2*Math.PI - angle
	}
	return angle
}

function angle(p1, center, p2) {
	const transformedP1 = p(p1.x - center.x, p1.y - center.y)
	const transformedP2 = p(p2.x - center.x, p2.y - center.y)

	const angleToP1 = Math.atan2(transformedP1.y, transformedP1.x)
	const angleToP2 = Math.atan2(transformedP2.y, transformedP2.x)

	return normaliseToInteriorAngle(angleToP2 - angleToP1)
}

function toDegrees(radians) {
	return 360 * radians / (2 * Math.PI)
}

console.log(toDegrees(angle(p(-10, 0), p(0, 0), p(0, -10))))


0

使用高中数学有一个简单的答案。

假设您有3分

获取从A点到B点的角度

angle = atan2(A.x - B.x, B.y - A.y)

获取从B点到C的角度

angle2 = atan2(B.x - C.x, C.y - B.y)

Answer = 180 + angle2 - angle
If (answer < 0){
    return answer + 360
}else{
    return answer
}

我只是在最近制作的项目中使用了此代码,将B更改为P1。如果您愿意,也可以删除“ 180 +”


-1

好吧,其他答案似乎涵盖了所有必需的内容,因此,如果您使用的是JMonkeyEngine,我想仅添加以下内容:

Vector3f.angleBetween(otherVector)

因为那是我来这里寻找的:)


-2
      Atan2        output in degrees
       PI/2              +90
         |                | 
         |                |    
   PI ---.--- 0   +180 ---.--- 0       
         |                |
         |                |
       -PI/2             +270

public static double CalculateAngleFromHorizontal(double startX, double startY, double endX, double endY)
{
    var atan = Math.Atan2(endY - startY, endX - startX); // Angle in radians
    var angleDegrees = atan * (180 / Math.PI);  // Angle in degrees (can be +/-)
    if (angleDegrees < 0.0)
    {
        angleDegrees = 360.0 + angleDegrees;
    }
    return angleDegrees;
}

// Angle from point2 to point 3 counter clockwise
public static double CalculateAngle0To360(double centerX, double centerY, double x2, double y2, double x3, double y3)
{
    var angle2 = CalculateAngleFromHorizontal(centerX, centerY, x2, y2);
    var angle3 = CalculateAngleFromHorizontal(centerX, centerY, x3, y3);
    return (360.0 + angle3 - angle2)%360;
}

// Smaller angle from point2 to point 3
public static double CalculateAngle0To180(double centerX, double centerY, double x2, double y2, double x3, double y3)
{
    var angle = CalculateAngle0To360(centerX, centerY, x2, y2, x3, y3);
    if (angle > 180.0)
    {
        angle = 360 - angle;
    }
    return angle;
}

}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.