15

Line1とLine2の2つの行があります。各線は2つの点(P1L1(x1, y1), P2L1(x2, y2)とで定義されP1L1(x1, y1), P2L3(x2, y3))ます。この2本の線で定義される内角を知りたいです。

そのために、横軸を使用して各線の角度を計算します。

double theta1 = atan(m1) * (180.0 / PI);
double theta2 = atan(m2) * (180.0 / PI);

角度を知った後、私は以下を計算します:

double angle = abs(theta2 - theta1);

私が抱えている問題や疑問は、正しい角度を取得することもあれば、補完的な角度を取得することもあります(私にとっては外側)。180º内角を知るために減算するとき、どうすればわかりますか?それを行うためのより良いアルゴリズムはありますか?私はいくつかの方法を試したので:ドット積、次の式:

result = (m1 - m2) / (1.0 + (m1 * m2));

しかし、いつも同じ問題があります。外角と内角のどちらがあるのか​​わかりませんでした!

4

8 に答える 8

23

あなたが探しているのは、2つの角度の内積(ドット積のエントリも確認することをお勧めします)思います。あなたの場合、それは次のように与えられます:

float dx21 = x2-x1;
float dx31 = x3-x1;
float dy21 = y2-y1;
float dy31 = y3-y1;
float m12 = sqrt( dx21*dx21 + dy21*dy21 );
float m13 = sqrt( dx31*dx31 + dy31*dy31 );
float theta = acos( (dx21*dx31 + dy21*dy31) / (m12 * m13) );

答えはラジアンです。

編集: これが完全な実装です。問題のある値をp1、p2、およびp3に置き換えて、何が得られるかを知らせてください。点p1は、2本の線の定義に従って、2本の線が交差する頂点です。

#include <math.h>
#include <iostream>

template <typename T> class Vector2D
{
private:
    T x;
    T y;

public:
    explicit Vector2D(const T& x=0, const T& y=0) : x(x), y(y) {}
    Vector2D(const Vector2D<T>& src) : x(src.x), y(src.y) {}
    virtual ~Vector2D() {}

    // Accessors
    inline T X() const { return x; }
    inline T Y() const { return y; }
    inline T X(const T& x) { this->x = x; }
    inline T Y(const T& y) { this->y = y; }

    // Vector arithmetic
    inline Vector2D<T> operator-() const
        { return Vector2D<T>(-x, -y); }

    inline Vector2D<T> operator+() const
        { return Vector2D<T>(+x, +y); }

    inline Vector2D<T> operator+(const Vector2D<T>& v) const
        { return Vector2D<T>(x+v.x, y+v.y); }

    inline Vector2D<T> operator-(const Vector2D<T>& v) const
        { return Vector2D<T>(x-v.x, y-v.y); }

    inline Vector2D<T> operator*(const T& s) const
        { return Vector2D<T>(x*s, y*s); }

    // Dot product
    inline T operator*(const Vector2D<T>& v) const
        { return x*v.x + y*v.y; }

    // l-2 norm
    inline T norm() const { return sqrt(x*x + y*y); }

    // inner angle (radians)
    static T angle(const Vector2D<T>& v1, const Vector2D<T>& v2)
    {
        return acos( (v1 * v2) / (v1.norm() * v2.norm()) );
    }
};

int main()
{
    Vector2D<double> p1(215, 294);
    Vector2D<double> p2(174, 228);
    Vector2D<double> p3(303, 294);

    double rad = Vector2D<double>::angle(p2-p1, p3-p1);
    double deg = rad * 180.0 / M_PI;

    std::cout << "rad = " << rad << "\tdeg = " << deg << std::endl;

    p1 = Vector2D<double>(153, 457);
    p2 = Vector2D<double>(19, 457);
    p3 = Vector2D<double>(15, 470);

    rad = Vector2D<double>::angle(p2-p1, p3-p1);
    deg = rad * 180.0 / M_PI;

    std::cout << "rad = " << rad << "\tdeg = " << deg << std::endl;

    return 0;
}

上記のコードは次のようになります。

rad = 2.12667   deg = 121.849
rad = 0.0939257 deg = 5.38155

于 2010-05-31T23:51:03.703 に答える
5
if (result > 180)
{
     result = 360 - result;
}

そうすれば、それは常に内角になります。結果が出たら追加するだけです。

于 2010-05-31T23:10:42.443 に答える
4

0度から360度の角度の間が必要な場合は、次のコードを使用します。その完全にテストされ、機能します:

static inline CGFloat angleBetweenLinesInRadians(CGPoint line1Start, CGPoint line1End, CGPoint line2Start, CGPoint line2End) {
double angle1 = atan2(line1Start.y-line1End.y, line1Start.x-line1End.x);
double angle2 = atan2(line2Start.y-line2End.y, line2Start.x-line2End.x);
double result = (angle2-angle1) * 180 / 3.14;
if (result<0) {
    result+=360;
}
return result;

}

注:回転は時計回りになります。

于 2015-04-07T13:49:11.370 に答える
2

全体のポイントは、与えられた答えよりもはるかに簡単です:

atan(slope)を使用すると、(文字通り)1ビットの情報が失われます。つまり、(0..2 * PI)の範囲に正確に2つの角度(theta)と(theta + PI)があり、同じ値になります。関数tan()の場合。

atan2(deltax、deltay)を使用するだけで、直角になります。例えば

atan2(1,1) == PI/4
atan2(-1,-1) == 5*PI/4

次に、減算して絶対値を取り、PIより大きい場合は2*PIから減算します。

于 2010-07-07T15:15:46.000 に答える
1

2つのベクトル間の内角(v1、v2)=アークコサイン(内角(v1、v2)/(module(v1)* module(v2)))。

ここで、内積(v1、v2)= xv1 * xv2 + yv1 * yv2

module(v)= sqrt(pow(xv、2)+ pow(yv、2))

したがって、あなたの質問の答えは次の例に実装されています。

#define PI   3.14159258

int main()
{
    double x1,y1,x2,y2,y3;
    double m1, m2;
    double mod1, mod2, innerp, angle;

    cout << "x1 :";
    cin >> x1;
    cout << "y1 :";
    cin >> y1;
    cout << "x2 :";
    cin >> x2;
    cout << "y2 :";
    cin >> y2;
    cout << "y3 :";
    cin >> y3;

    m1 = atan((y2-y1)/(x2-x1)) * 180 / PI;
    m2 = atan((y3-y1)/(x2-x1)) * 180 / PI;

    mod1   = sqrt(pow(y2-y1,2)+pow(x2-x1,2));
    mod2   = sqrt(pow(y3-y1,2)+pow(x2-x1,2));
    innerp = (x2-x1)*(x2-x1) + (y2-y1)*(y3-y1);
    angle  = acos(innerp / (mod1 * mod2)) * 180 / PI;

    cout << "m1 : " << m1 << endl;
    cout << "m2 : " << m2 << endl;
    cout << "angle : " << angle << endl;
}
于 2010-06-01T00:33:01.010 に答える
1

絶対値を使用すると、常に鋭角が得られます。つまり、タンジェントシータ=(1 + m1 * m2)上のm1-m2の絶対値です。逆正接を取る場合、答えはラジアンまたは度になりますが、計算機は設定されています。申し訳ありませんが、これはプログラミング用語ではありません。私は数学の教師であり、プログラマーではありません...

于 2011-02-03T21:13:23.583 に答える
0

2本の線の交点の鈍角ではなく鋭角が欲しいというあなたの質問を正しく理解していただければ幸いです。私は正しいですか?

交差点の鋭角と鈍角は、互いに180度の補数です。すなわち

 acute + obtuse = PI.

http://www.mathworks.com/access/helpdesk/help/techdoc/ref/atan.html は、atanが+/- pi/2で漸近的であることを示しています。

したがって、勾配の+/-表記法または正の表記法のどちらを使用する場合でも、atanの2つの結果の最大差は円周率または180度です。0 to pi

次の擬似コードを検討してください。

acuteAngle(m1, m2){
  a = atan(m1) - atan(m2);

  // if obtuse get the complementary acute angle:
  if (a>PI/2) 
    a = PI - a;
  return a;
} 

この関数acuteAngleは、数学的に何をする必要があるかを示しています。

ただし、PI / 2の近傍の角度の値には使用できません。これは、角度とその近傍の結果との2進比較では、鈍角と鋭角のどちらが表されているかが疑わしいためです。

したがって、2本の線の点の座標を比較する必要があります。[(x2,y2)(x3,y3)]から形成される3番目の線が、仮想の斜辺よりも短いか、等しいか、長いかを調べます。

ピタゴラスの定理により、角度が正確にPI / 2または90度の場合、斜辺が形成されます。彼の架空の斜辺線をL3Hypoと呼びましょう。

あなたの心の中で幾何学的な視覚化によって、

  • 3行目がL3Hypoより長い場合、角度は鈍角になります。
  • 短い場合、角度は鋭角です。
  • そうでなければ、完璧な90。

したがって、

L1.lengthSquared = sq(x2-x1) + sq(y2-y1)
L2.lengthSquared = sq(x3-x1) + sq(y3-y1)
L3Hypo.lengthSquared = L1.lengthSquared + L2.lengthSquared
L3.lengthSquared = sq(x3-x2) + sq(y3-y2)

したがって、次の擬似コード、

struct Point{
  double x, y;
}

// no need to struct, for clarity only
struct Line{
  double lengthSquared;
}

#define sq(n) (n*n)
int isObtuse(Point P1, P2, P3){
  Line L1, L2, L3, L3Hypo;

  L1.lengthSquared = sq(P2.x-P1.x) + sq(P2.y-P1.y);
  L2.lengthSquared = sq(P3.x-P1.x) + sq(P3.y-P1.y);
  L3Hypo.lengthSquared = L1.lengthSquared + L2.lengthSquared;
  L3.lengthSquared = sq(P3.x-P2.x) + sq(P3.y-P2.y);

  if (L3>L3Hypo) return 1; //obtuse
  else if (L3<L3Hypo) return -1; //acute
  else return 0;
}

関数getGradient(Point P、Q)がすでにあると仮定します。

double m1m2 = getGradient(P1,P2);
double m1m3 = getGradient(P1,P3);
double a = Abs(atan(m1m2) - atan(m1m3));
if (isObtuse(P1, P2, P3)>0)
  a = PI - a;

私は擬似コードでいくつかのタイプミスを犯したかもしれませんが(うまくいけばそうではありません)、概念の要点を示しました。もしそうなら、誰かがタイプミスを編集するのにとても親切かもしれません。

さらに 、しかし、それを熟考した後、私は、精度のための闘争が、指令のためにその最も弱いリンクでピボットすることを発見しました

#define PI 3.14159blah..blah..blah.

したがって、すべてのトラブルを保存して、これを実行する方がよいでしょう。

double m1m2 = getGradient(P1,P2);
double m1m3 = getGradient(P1,P3);
double a = Abs(atan(m1m2) - atan(m1m3));
double b = PI - a;
return min(a, b);//the smaller of the two is the acute
于 2010-06-01T09:52:37.110 に答える
0

外角と内角の関係は、減算の順序によって完全に決まります(考えてみてください)。常に内角を確実に取得するには、大きいシータから小さいシータを引く必要があります。また、期待するデータの種類のために、この関数を使用することもatan2できます。

于 2010-05-31T23:15:59.903 に答える