Как рассчитать угол из трех точек?
Предположим, у вас есть это:
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. Мне нужно это для обоих, но не отметили это для них.
Ответы
Ответ 1
Если вы имеете в виду угол, в котором P1 является вершиной, то с помощью Закона Козинеса должно работать:
агссоз ((Р <суб > 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 - Р2 <суб > усуб > ) 2)
Ответ 2
Это становится очень простым, если вы считаете, что это два вектора: один от точки 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]()
Помните, что
просто означает:
a1 * b1 + a2 * b2 (здесь всего 2 измерения)
Ответ 3
Если у вас есть 3 балла, у вас есть треугольник, длина кромки которого известна. Поэтому используйте правило косинуса:
http://en.wikipedia.org/wiki/Law_of_cosines
Ответ 4
Позвольте мне привести пример в 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
В принципе, у вас есть два вектора, один вектор от P1 до P2 и другой от P1 до P3. Итак, все, что вам нужно, это формула для вычисления угла между двумя векторами.
Посмотрите здесь для хорошего объяснения и формулы.
![alt text]()
Ответ 6
Если вы думаете о 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)
Ответ 7
Лучший способ справиться с вычислением углов - использовать 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)
... что означает, что как P2
, так и P3
должны отличаться от P1
, поскольку в этом случае не имеет смысла говорить о угле.
Ответ 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;
}
Ответ 9
В Objective-C вы можете сделать это с помощью
float xpoint = (((atan2((newPoint.x - oldPoint.x) , (newPoint.y - oldPoint.y)))*180)/M_PI);
Или читайте здесь
Ответ 10
Очень простое геометрическое решение с пояснением
Несколько дней назад эта проблема попала в эту же проблему, и ей пришлось сидеть с математической книгой. Я решил проблему, объединив и упростив некоторые основные формулы.
Давайте рассмотрим эту цифру -
![angle]()
Мы хотим знать Θ, поэтому нам нужно сначала выяснить α и β. Теперь для любой прямой -
y = m * x + c
Let-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 α
(из уравнения-2) и tan b
(из уравнения (3)) в уравнении (4) и применения упрощения получим -
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]()
Этот метод С# или 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;
}
Ответ 11
Вы упомянули подписанный угол (-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 (также чтобы убедиться, что градусы и радианы не смешиваются). Вопросы относительно того, является ли угол1 меньше угла2, критически зависят от того, как определяются углы. Также может быть важно решить, представлена ли линия (-1,0) (0,0) (1,0) как Math.PI или -Math.PI
Ответ 12
![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;
Ответ 13
Здесь используется метод С# для возврата угла (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;
}
Cheers,
Пол
Ответ 14
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))))
Ответ 15
Ну, другие ответы, похоже, охватывают все, что требуется, поэтому я хотел бы просто добавить это, если вы используете JMonkeyEngine:
Vector3f.angleBetween(otherVector)
так вот что я здесь искал:)
Ответ 16
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;
}
}