kmVec2でのベクトル計算
cocos2dにも標準で入っているライブラリKazmathの勉強をする。
今回は特にkmVec2に関するメソッドを色々と試す。
以前にも一度ベクトル計算を試している(※)が、kmVec2Dotとか何をしているか分かっていない。
2Dでベクトル計算ができると色々と捗りそうだったので、この機会にメソッドの効果をメモしておく。
※ベクトル計算使ってる記事
CCNodeのカテゴリで、他のCCNodeをゆったり追うSoftFollowというのを作ってみた その2 - 追記2/26 - taoru's memo
kmVec2メソッド | |
---|---|
length(kmVec2) | x,yの二乗を足した値の平方根を返す(大きさ) |
lengthSq(kmVec2) | x,yの二乗を足した値を返す |
normalize(kmVec2&) | 単位ベクトルを返す |
add(kmVec2, kmVec2) | 2ベクトルの加算結果を返す |
sub(kmVec2, kmVec2) | 2ベクトルの減算結果を返す |
dot(kmVec2, kmVec2) | 2ベクトルの内積を返す |
scale(kmVec2&, kmScalar) | 指定ベクトルをkmScalar倍する |
equal(kmVec2, kmVec2) | 2ベクトルが等しければtrueを返す |
transform(kmVec2, kmMat3) | 指定ベクトルを行列変換する |
計算後のベクトルを表示したかったので、適当に表示用のメソッドをまず作った
- (void)displaySegmentWithVec:(kmVec2)vec color:(ccColor3B)color { CCDrawNode *drawNode = (CCDrawNode *)[self getChildByTag:0]; if (!drawNode) { drawNode = [CCDrawNode node]; drawNode.position = ccp(self.contentSize.width*0.5f, self.contentSize.height*0.5f); [self addChild:drawNode z:0 tag:0]; } // fixedVecにvecを50倍したベクトルを代入 kmVec2 fixedVec; kmVec2Scale(&fixedVec, &vec, 50.0f); CGPoint vecPoint = ccp(fixedVec.x, fixedVec.y); // (0,0)から(vec.x,vec.y)までの線分を描画 [drawNode drawSegmentFrom:CGPointZero to:vecPoint radius:1.5f color:ccc4FFromccc3B(color)]; // 元のVectorを表示 CCLabelTTF *pointLabel = [CCLabelTTF labelWithString:@"" fontName:@"Helvetica" fontSize:16]; pointLabel.position = ccpAdd(drawNode.position, ccp(vecPoint.x, vecPoint.y + 10)); pointLabel.string = NSStringFromCGPoint(ccp(vec.x, vec.y)); pointLabel.color = color; [self addChild:pointLabel z:1]; }
では補完が出る順(名前順)に試していく。
kmVec2Add
kmVec2* kmVec2Add(kmVec2* pOut, const kmVec2* pV1, const kmVec2* pV2) { pOut->x = pV1->x + pV2->x; pOut->y = pV1->y + pV2->y; return pOut; }2ベクトルの加算
kmVec2 vec1 = (kmVec2){1, 1}; kmVec2 vec2 = (kmVec2){2, 0}; kmVec2 resultVec; kmVec2Add(&resultVec, &vec1, &vec2); [self displaySegmentWithVec:vec1 color:ccGREEN]; [self displaySegmentWithVec:vec2 color:ccRED]; [self displaySegmentWithVec:resultVec color:ccWHITE];
kmVec2AreEqual
int kmVec2AreEqual(const kmVec2* p1, const kmVec2* p2) { return ( (p1->x < p2->x + kmEpsilon && p1->x > p2->x - kmEpsilon) && (p1->y < p2->y + kmEpsilon && p1->y > p2->y - kmEpsilon) ); }ベクトルの比較( are なのは複数形ってこと?)
kmVec2 vec1 = (kmVec2){1, 1}; kmVec2 vec2 = (kmVec2){1, 1}; BOOL isEqualVec = kmVec2AreEqual(&vec1, &vec2); CCLOG(@"isEqualVec = %d",isEqualVec); // 1 (YES) が出力される
kmVec2Dot
kmScalar kmVec2Dot(const kmVec2* pV1, const kmVec2* pV2) { return pV1->x * pV2->x + pV1->y * pV2->y; }ベクトルの内積値を計算して返す。
"ドット"ってなんだよって思ってたけど、ドット積(点乗積、内積)のことらしい。
(wikipedia)ドット積
内積を表す式が[A・B]だからか…
内積は計算にcosθが含まれているので、2ベクトルのなす角を得たり直行判定に使える。kmVec2 vec1 = (kmVec2){1, 1}; kmVec2 vec2 = (kmVec2){0, 2}; float vecDot = kmVec2Dot(&vec1, &vec2); CCLOG(@"vec1とvec2の内積 = %f",vecDot); // 2.0が出力される
kmVec2Fill
kmVec2* kmVec2Fill(kmVec2* pOut, kmScalar x, kmScalar y) { pOut->x = x; pOut->y = y; return pOut; }
ベクトルの代入. ただの代入なのでコード割愛.
kmVec2Length
kmScalar kmVec2Length(const kmVec2* pIn) { return sqrtf(kmSQR(pIn->x) + kmSQR(pIn->y)); }ベクトルの大きさを返す.
kmVec2LengthSqというメソッドもあるが、そちらは sqrtfする前の値を返す
kmVec2Normalize
kmVec2* kmVec2Normalize(kmVec2* pOut, const kmVec2* pIn) { kmScalar l = 1.0f / kmVec2Length(pIn); kmVec2 v; v.x = pIn->x * l; v.y = pIn->y * l; pOut->x = v.x; pOut->y = v.y; return pOut; }大きさ1の単位ベクトルを計算して返す。
もっと早く知っておくべきだった…orzkmVec2 vec1 = (kmVec2){3, 2}; kmVec2 resultVec; kmVec2Normalize(&resultVec, &vec1); CCLOG(@"resultVecの大きさ = %f", kmVec2Length(&resultVec)); // 1.0 が出力される [self displaySegmentWithVec:vec1 color:ccGREEN]; [self displaySegmentWithVec:resultVec color:ccWHITE];
kmVec2Scale
kmVec2* kmVec2Scale(kmVec2* pOut, const kmVec2* pIn, const kmScalar s) { pOut->x = pIn->x * s; pOut->y = pIn->y * s; return pOut; }ベクトルの大きさを指定倍する.
kmVec2 vec1 = (kmVec2){1, 1}; kmVec2 resultVec; kmVec2Scale(&resultVec, &vec1, 2.0f); CCLOG(@"resultVecの大きさ = %f", kmVec2Length(&resultVec)); // 2.828427 が出力される [self displaySegmentWithVec:vec1 color:ccGREEN]; [self displaySegmentWithVec:resultVec color:ccWHITE];
kmVec2Subtract
kmVec2* kmVec2Subtract(kmVec2* pOut, const kmVec2* pV1, const kmVec2* pV2) { pOut->x = pV1->x - pV2->x; pOut->y = pV1->y - pV2->y; return pOut; }2ベクトルの減算結果ベクトルを返す
kmVec2 vec1 = (kmVec2){1, 2}; kmVec2 vec2 = (kmVec2){3, 1}; kmVec2 resultVec; kmVec2Subtract(&resultVec, &vec1, &vec2); [self displaySegmentWithVec:vec1 color:ccGREEN]; [self displaySegmentWithVec:vec2 color:ccRED]; [self displaySegmentWithVec:resultVec color:ccWHITE];
kmVec2Transform
kmVec2* kmVec2Transform(kmVec2* pOut, const kmVec2* pV, const kmMat3* pM) { kmVec2 v; v.x = pV->x * pM->mat[0] + pV->y * pM->mat[3] + pM->mat[6]; v.y = pV->x * pM->mat[1] + pV->y * pM->mat[4] + pM->mat[7]; pOut->x = v.x; pOut->y = v.y; return pOut; }ベクトルを行列で一次変換する.
数Bを真面目に勉強していなかったので辛い。
1時変換の参考にしたページ:■行列と1次変換kmMat3は三次行列だけど、kmVec2は二次元なので2行3列しか使われていない(mat[2],mat[5],mat[8]を使ってない)
kmVec2 vec1 = (kmVec2){1, 2}; kmVec2 resultVec; // Y軸に関する対称移動 kmMat3 mat3 = (kmMat3){ -1, 0, 0, 0, 1, 0, 0, 0, 0 }; kmVec2Transform(&resultVec, &vec1, &mat3); [self displaySegmentWithVec:vec1 color:ccGREEN]; [self displaySegmentWithVec:resultVec color:ccWHITE];
kmVec2TransformCoord
kmVec2* kmVec2TransformCoord(kmVec2* pOut, const kmVec2* pV, const kmMat3* pM) { assert(0); return NULL; }使うとAsserionを吐いて落ちる。
訳がわからないよ。kmVec3TransformCoordは実装されていたのでそっちを覗いてみたところ、
どうやらkmVec4Transform(1次元上の行列変換)にかけて、結果の4次元目で結果のxyzを割っている(?)仮にkmVec3TransformCoordと同じようなロジックでkmVec2TransformCoordを実装するとしたら、
まずkazmath/vec3.h をincludeしてからこんな感じになるのだが…kmVec2* kmVec2TransformCoord(kmVec2* pOut, const kmVec2* pV, const kmMat3* pM) { kmVec3 v; kmVec3 inV; kmVec3Fill(&inV, pV->x, pV->y, 1.0); kmVec3Transform(&v, &inV,pM); pOut->x = v.x / v.z; pOut->y = v.y / v.z; return pOut; }kmVec3Transformの3番目の引数はkmMat4のため、このコードは動かない。
kmVec4Transformの3番目の引数はkmMat4のため、kmVec3TransformCoordは動く。行列変換は難しい(小並感)
以上で kmVec2 を関するメソッドを全て試してみたことになる。
結構使えるかも?
参考ページ
平面幾何におけるベクトル演算 3.内積と外積
行列と一次変換
2ベクトルのなす角を求める
なす角を求めるには、arcCos( 内積 / (vec1の大きさ * vec2の大きさ) ) を計算すればよい
kmVec2 vec1 = (kmVec2){1, 1}; kmVec2 vec2 = (kmVec2){2, 0}; // 内積 float vecDot = kmVec2Dot(&vec1, &vec2); float vec1Length = kmVec2Length(&vec1); float vec2Length = kmVec2Length(&vec2); float radian = acosf(vecDot / (vec1Length * vec2Length)); CCLOG(@"vec1 と vec2 のなす角 = %f",kmRadiansToDegrees(radian)); [self displaySegmentWithVec:vec1 color:ccGREEN]; [self displaySegmentWithVec:vec2 color:ccRED];