[ベクトル]2つのベクトルの成す角

processingで書いてます。

公式

与えられる3つの点:(o), (v1), (v2)において、

$${ dx1 = v1.x - o.x }$$
$${ dy1 = v1.y - o.y }$$
$${ dx2 = v2.x - o.x }$$
$${ dy2 = v2.y - o.y }$$

この2つのベクトル、((dx1, dy1)) と ((dx2, dy2)) に対して、ベクトルの外積と内積は以下のように計算されます:

外積 (cross product):
$${ cross = dx1 \times dy2 - dy1 \times dx2 }$$
内積 (dot product):
$${ dot = dx1 \times dx2 + dy1 \times dy2 }$$

そして、これら2つの量から角度θは以下の式を使用して求められます:

$${ \theta = \text{atan2}(cross, dot) }$$

ここで、(\text{atan2}) は2つの引数を取るアークタンジェント関数です。

float angle(PVector o, PVector v1, PVector v2) {
  float dx1 = v1.x - o.x;
  float dy1 = v1.y - o.y;
  float dx2 = v2.x - o.x;
  float dy2 = v2.y - o.y;
  float cross = dx1 * dy2 - dy1 * dx2;
  float dot = dx1 * dx2 + dy1 * dy2;
  return atan2(cross, dot);
}


関連項目
前回

ベクトル


こんなような感じ
原点O
ベクトルAxisに対してベクトルVが成す角はどんなか

画像1

Sin,Cosのための内積外積

// 内積
public float Dot(PVector v1, PVector v2)
{
   return (v1.x * v2.x + v1.y * v2.y);
}

//外積、疑似外積、疑スカラー、パープ内積
public float Cross(PVector v1, PVector v2)
{
   //+ならv2がv1の右側
   return (v1.x * v2.y - v1.y * v2.x);
}

Sin,Cos

//2つのベクトルのcos
float Cos(PVector v1, PVector v2)
{
   //v1を基準とする時
   //v2の向きが揃ってるなら1
   //v2がv1の左右に直交しているなら0
   //v2がv1の真反対を向いているなら-1 
   float ll = v1.mag() * v2.mag();
   //0除算を回避
   if (ll == 0) { return 0; }
   return Dot(v1, v2) / ll;
}

float Sin(PVector v1, PVector v2)
{
   //0<sinの時v2は右側(ディスプレイ座標(Yが下に+))
   //0=sinの時v2は平行、直線上
   //sin<0の時v2は左側
   float ll = v1.mag() * v2.mag();
   //0除算を回避
   if (ll == 0) { return 0; }
   return Cross(v1, v2) / ll;
}

角度を求める(出力はラジアン)
sin,cosはcross,dotに変更してよい

float CalcuAngle180(PVector v1, PVector v2)
{
 return -(float)Math.atan2(v1.y * v2.x - v2.y * v1.x, v1.x * v2.x + v1.y * v2.y);   
}

float CalcuAngle360(PVector v1, PVector v2)
{
 float s = Sin(v1,v2);
 float c = Cos(v1,v2);  
 if(0<s)
 {
   return -(float)Math.atan2(v1.y * v2.x - v2.y * v1.x, v1.x * v2.x + v1.y * v2.y);
 }
 else if(s<0)
 {
   return 2*PI-(float)Math.atan2(v1.y * v2.x - v2.y * v1.x, v1.x * v2.x + v1.y * v2.y);   
 }
 else
 {    
   if(0<c){return 0;}//前    
   else{return PI;}//後      
 }
}

表示しているパラメータ

 this.V1 = new PVector(400,400);
 this.V2 = new PVector(400,200);
 this.V3 = new PVector(400,600);
 
 PVector lv1 = V2.copy().sub(V1);
 PVector lv2 = V3.copy().sub(V1);  
 
 float sin = Sin(lv1,lv2);
 float sin_radian = asin(sin);
 float sin_degree = sin_radian*180.0/PI;
 float cos = Cos(lv1,lv2);
 float cos_radian = acos(cos);
 float cos_degree = cos_radian*180.0/PI;  
 float angle180 = CalcuAngle180(lv1,lv2);
 float angle180_degree = angle180*180.0/PI;  
 float angle360 = CalcuAngle360(lv1,lv2);
 float angle360_degree = angle360*180.0/PI;  


画像2
画像3
画像4
画像5
画像6
画像7
画像8

以下、ソース


PVector pprev;
PVector prev;
PVector current;
PVector dv = new PVector(0,0);

PVector V1;
PVector V2;
PVector V3;
ArrayList<PVector> Vertices = new ArrayList<PVector>();
float r = 20;

int current_index = -1;

void setup()
{
 size(800,800);
 this.V1 = new PVector(400,400);
 this.V2 = new PVector(400,200);
 this.V3 = new PVector(400,600);
 Vertices.add(V1);
 Vertices.add(V2);
 Vertices.add(V3);
}

void draw()
{
 background(255);
 
 PVector lv1 = V2.copy().sub(V1);
 PVector lv2 = V3.copy().sub(V1);  
 
 float sin = Sin(lv1,lv2);
 float sin_radian = asin(sin);
 float sin_degree = sin_radian*180.0/PI;
 float cos = Cos(lv1,lv2);
 float cos_radian = acos(cos);
 float cos_degree = cos_radian*180.0/PI;  
 float angle180 = CalcuAngle180(lv1,lv2);
 float angle180_degree = angle180*180.0/PI;  
 float angle360 = CalcuAngle360(lv1,lv2);
 float angle360_degree = angle360*180.0/PI;  

 textSize(20);
 fill(0);
 text("sin ="+str(sin),0,0,400,100);
 text("sin_radian ="+str(sin_radian),0,20,400,100);
 text("sin_degree ="+str(sin_degree),0,40,400,100);
 text("cos ="+str(cos),0,60,400,100);
 text("cos_radian ="+str(cos_radian),0,80,400,100);
 text("cos_degree ="+str(cos_degree),0,100,400,100);
 text("angle180 ="+str(angle180),0,120,400,100);
 text("angle180_degree ="+str(angle180_degree),0,140,400,100);
 text("angle360 ="+str(angle360),0,160,400,100);
 text("angle360_degree ="+str(angle360_degree),0,180,400,100);
 
 fill(255);
 line(V1.x,V1.y,V2.x,V2.y);
 line(V1.x,V1.y,V3.x,V3.y);  
 for(int i = 0; i<this.Vertices.size(); i++)
 {
   PVector v = this.Vertices.get(i);
   circle(v.x,v.y,this.r);    
 }   
 circle(V1.x,V1.y,10);
 
 fill(0);
 text("O",V1.x,V1.y,400,100);
 text("Axis",V2.x,V2.y,400,100);  
 text("V",V3.x,V3.y,400,100);  
 fill(255);
}

void mousePressed()
{
 pprev = new PVector(mouseX,mouseY);
 prev = new PVector(mouseX,mouseY);
 current = new PVector(mouseX,mouseY);
 for(int i = 0; i<this.Vertices.size(); i++)
 {
   if(PointInCircle(this.Vertices.get(i),this.r,new PVector(mouseX,mouseY)))
   {
      current_index = i;               
   }
 }  
}

void mouseDragged()
{
 pprev = prev.copy();
 prev = current.copy();
 current.x = mouseX;
 current.y = mouseY;  
 dv = PVector.sub(current,prev);   
 if(0<=current_index)
 {
   for(int i = 0; i<this.Vertices.size(); i++)
   {
     this.Vertices.get(current_index).x = mouseX;
     this.Vertices.get(current_index).y = mouseY;    
   }    
 }
}

void mouseReleased()
{
 pprev = null;
 prev = null;
 current = null;  
 current_index=-1;
}

// 内積
public float Dot(PVector v1, PVector v2)
{
   return (v1.x * v2.x + v1.y * v2.y);
}

//外積、疑似外積、疑スカラー、パープ内積
public float Cross(PVector v1, PVector v2)
{
   //+ならv2がv1の右側
   return (v1.x * v2.y - v1.y * v2.x);
}

//2つのベクトルのcos
float Cos(PVector v1, PVector v2)
{
   //v1を基準とする時
   //v2の向きが揃ってるなら1
   //v2がv1の左右に直交しているなら0
   //v2がv1の真反対を向いているなら-1 
   float ll = v1.mag() * v2.mag();
   //0除算を回避
   if (ll == 0) { return 0; }
   return Dot(v1, v2) / ll;
}

float Sin(PVector v1, PVector v2)
{
   //0<sinの時v2は右側(ディスプレイ座標(Yが下に+))
   //0=sinの時v2は平行、直線上
   //sin<0の時v2は左側
   float ll = v1.mag() * v2.mag();
   //0除算を回避
   if (ll == 0) { return 0; }
   return Cross(v1, v2) / ll;
}

float CalcuAngle180(PVector v1, PVector v2)
{
 return -(float)Math.atan2(v1.y * v2.x - v2.y * v1.x, v1.x * v2.x + v1.y * v2.y);   
}

float CalcuAngle360(PVector v1, PVector v2)
{
 float s = Sin(v1,v2);
 float c = Cos(v1,v2);  
 if(0<s)
 {
   return -(float)Math.atan2(v1.y * v2.x - v2.y * v1.x, v1.x * v2.x + v1.y * v2.y);
 }
 else if(s<0)
 {
   return 2*PI-(float)Math.atan2(v1.y * v2.x - v2.y * v1.x, v1.x * v2.x + v1.y * v2.y);   
 }
 else
 {    
   if(0<c){return 0;}//前    
   else{return PI;}//後      
 }
}

//円周上を含まないPointIn
boolean PointInCircle(PVector center, float radius, PVector v)
{
 float dx = v.x-center.x;
 float dy = v.y-center.y;
 if(dx*dx+dy*dy<radius*radius){return true;}
 return false;
}

この記事が気に入ったらサポートをしてみませんか?