如何从三点计算角度?

时间:2009-07-31 07:55:05

标签: algorithm math geometry

让我们说你有这个:

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

假设P1是圆的中心点。它总是一样的。 我希望由P2P3构成的角度,或者换句话说P1旁边的角度。内角是精确的。它总是一个锐角,所以小于-90度。

我想:伙计,这是简单的几何数学。但我现在已经找了大约6个小时的公式,而且只找到人们谈论复杂的美国宇航局的东西,如arccos和矢量标量产品。我的头感觉像在冰箱里。

这里的一些数学专家认为这是一个简单的问题?我不认为编程语言在这里很重要,但对于那些认为它的人来说:java和objective-c。我需要它,但没有标记它们。

16 个答案:

答案 0 :(得分:87)

如果你的意思是P1是顶点的角度那么使用Law of Cosines应该有效:

  

ARCCOS ((P <子> 12 2   + P 13 2 - P 23 2 )/(2 *   P 12 * P 13 ))

其中P 12 是从P1到P2的段的长度,由

计算
  

sqrt((P1 x -   P2 x 2 +   (P1 y -   P2 <子>ý 2

答案 1 :(得分:47)

如果你认为它是两个向量,一个从点P1到P2,一个从P1到P3,它变得非常简单

所以:
a =(p1.x - p2.x,p1.y - p2.y)
b =(p1.x - p3.x,p1.y - p3.y)

然后您可以反转点积公式:
dot product
获得角度:
angle between two vectors

请记住,dot product仅表示: a1 * b1 + a2 * b2(这里只有2个尺寸......)

答案 2 :(得分: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最终在原点),然后你考虑P3的绝对角度和P2

atan2的优点是表示整圆(你可以在-π和π之间得到任何数字)而不是acos你需要根据要计算的符号来处理几个案例正确的结果。

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

答案 3 :(得分: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));
}

奖金:Example with HTML5-canvas

答案 4 :(得分:15)

基本上你有两个向量,一个从P1到P2,另一个从P1到P3。所以你需要的是一个计算两个向量之间角度的公式。

请查看here以获得更好的解释和公式。

alt text

答案 5 :(得分:12)

如果您认为P1是圆圈的中心,那么您的想法太复杂了。 你有一个简单的三角形,所以law of cosines可以解决你的问题。不需要任何极坐标变换或某些。假设距离为P1-P2 = A,P2-P3 = B且P3-P1 = C:

  

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

您需要做的就是计算距离A,B和C的长度。 这些都可以从您的点的x坐标和y坐标轻松获得 Pythagoras' theorem

  

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

答案 6 :(得分:8)

我最近遇到了类似的问题,只是我需要区分正角度和负角度。如果这对任何人都有用,我建议我从this mailing list抓取的关于检测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 :(得分:7)

非常简单的几何解法及解释

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

让我们考虑这个数字 -

angle

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

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)

在eqn-4中替换tan α(来自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)) )

就是这样!

现在,请看下面的图 -

angle

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

答案 8 :(得分:6)

答案 9 :(得分: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是否小于角度2的问题关键取决于如何定义角度。确定一条线(-1,0)(0,0)(1,0)是否表示为Math.PI或-Math.PI

也很重要。

答案 10 :(得分:4)

my angle demo program

最近,我也有同样的问题......在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;

答案 11 :(得分: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;
    }

干杯, 保罗

答案 12 :(得分:2)

&#13;
&#13;
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))))
&#13;
&#13;
&#13;

答案 13 :(得分: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 ..你也可以删除&#34; 180 +&#34;如果你想要

答案 14 :(得分:-1)

好吧,其他答案似乎涵盖了所需的一切,所以如果您使用的是JMonkeyEngine,我想添加它:

Vector3f.angleBetween(otherVector)

因为我来这里寻找:)

答案 15 :(得分:-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;
}

}