[ベクトル]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が成す角はどんなか
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;
以下、ソース
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;
}
この記事が気に入ったらサポートをしてみませんか?