これがあるとしましょう:
P1 = (x=2, y=50)
P2 = (x=9, y=40)
P3 = (x=5, y=20)
P1
が円の中心点であると仮定します。常に同じです。 P2
とP3
で構成される角度、つまりP1
の隣の角度が必要です。正確な内角。常に鋭角になるため、-90度未満です。
私は思った:人間、それは単純な幾何学の数学だ。しかし、私は今約6時間の公式を探しており、arccoやベクトルスカラー製品などの複雑なNASAのことについて話している人だけを見つけました。頭が冷蔵庫の中にあるように感じます。
これは簡単な問題だと思う数学の達人がいますか?ここではプログラミング言語は重要ではないと思いますが、それが必要だと思う人にとっては:JavaとObjective-C。両方に必要ですが、これらにはタグを付けていません。
P1が頂点である角度を意味する場合、 Law of Cosines を使用すると動作します。
arccos((P122 + P132 -P232)/(2 * P12 * P13))
p12 は、P1からP2までのセグメントの長さです。
sqrt((P1バツ -P2バツ)2 +(P1y -P2y)2)
ポイントP1からP2までとP1からP3までの2つのベクトルと考えると、非常に簡単になります。
そう:
a =(p1.x-p2.x、p1.y-p2.y)
b =(p1.x-p3.x、p1.y-p3.y)
次に、内積公式を逆にすることができます。
角度を取得するには:
覚えておいて ちょうど意味:a1 * b1 + a2 * b2(ここではちょうど2つの次元...)
角度の計算に対処する最良の方法は、atan2(y, x)
を使用することです。x, y
は、その点からの角度を返し、Originに対するX+
軸を返します。
計算が
double result = atan2(P3.y - P1.y, P3.x - P1.x) -
atan2(P2.y - P1.y, P2.x - P1.x);
つまり、基本的に-P1
で2つのポイントを変換し(つまり、P1
がOriginで終わるようにすべてを変換します)、P3
の絶対角度の違いとP2
の。
atan2
の利点は、完全な円が表されることです(-πとπの間の任意の数を取得できます)。代わりにacos
を使用すると、符号に応じていくつかのケースを処理して正しい結果を計算する必要があります。
atan2
の唯一の特異点は(0, 0)
...です。つまり、P2
とP3
は両方ともP1
とは異なる必要があります。角度について話す感覚。
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-canvasを使用した例
基本的に、2つのベクトルがあります。1つはP1からP2、もう1つはP1からP3です。したがって、必要なのは、2つのベクトル間の角度を計算する式だけです。
良い説明と式については、 こちら をご覧ください。
最近、同様の問題に遭遇しました。正と負の角度を区別する必要があるだけです。これが誰にも役立つ場合、Androidのタッチイベントでの回転の検出について this mailing list から取得したコードスニペットをお勧めします。
@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;
}
数日前、同じ問題に陥り、数学の本に座らなければなりませんでした。いくつかの基本的な式を組み合わせて単純化することで問題を解決しました。
この図を考えてみましょう-
ϴを知りたいので、αとβ最初に。さて、どんな直線に対しても-
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)
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)) )
それだ!
今、次の図を取ります-
この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;
}
Objective-Cでは、これを行うことができます
float xpoint = (((atan2((newPoint.x - oldPoint.x) , (newPoint.y - oldPoint.y)))*180)/M_PI);
または、もっと読む こちら
符号付き角度(-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を超えて拡張することもできますが、これは現在の質問とは関係ありませんが、角度とラジアンが混同しないようにするために、独自の角度クラスを作成することは十分に重要でした。 angle1がangle2より小さいかどうかに関する質問は、角度の定義方法に大きく依存します。行(-1,0)(0,0)(1,0)がMath.PIまたは-Math.PIのどちらで表されるかを決定することも重要です。
最近、私も同じ問題を抱えています... 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;
これは、円上の点に対して水平から反時計回りに角度(0〜360)を返すC#メソッドです。
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;
}
乾杯、ポール
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))))
そこにIS高校の数学を使用してこれに対する簡単な答えがあります。
あなたが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に変更します。