まず、新しい番号を定義しましょう。心配はいりません。簡単です。
または、簡単に言うと、f =√3× iで、iは虚数単位です。これにより、時計回りに60度回転することは、1/2×(1- f)を掛けることと同じであり、反時計回りに60度回転することは、1/2×(1 + f)を掛けることと同じです。これが奇妙に聞こえる場合は、複素数の乗算は2D平面の回転と同じであることを覚えておいてください。複素数を少し(√3ずつ)虚数方向に「押しつぶす」だけで、平方根...または非整数を処理する必要がなくなります。
点(a、b)をa + b× fと書くこともできます。
これにより、平面内の任意の点を回転させることができます。たとえば、点(2,0)= 2 + 0× fは(1、-1)に回転し、次に(-1、-1)、(- 2,0 )、(-1,1)、( 1,1)そして最後に(2,0)に戻ります。単純にそれを乗算します。
もちろん、これらの点を座標から回転を実行する座標に変換し、その後再び戻す方法が必要です。これには、もう1つの情報が必要です。回転を行う点が垂直線の「左」または「右」である場合。わかりやすくするため、左側にある場合(下の2つの画像の回転の中心[0,0]のように)は「ウォブル」値wは0、右側にある場合は1と宣言します。それの。これにより、元のポイントが3次元に拡張されます。(x、y、w)、正規化後の "w"は0または1です。正規化関数は次のとおりです。
NORM:(x、y、w)->(x + floor(w / 2)、y、w mod 2)。正の値またはゼロのみを返すように「mod」演算が定義されています。
アルゴリズムは次のようになります。
(a - x、b - y、c - w)を計算し、結果を正規化することにより、ポイント(a、b、c)を回転中心(x、y、w)を基準にした位置に変換します。これは明らかに回転中心を(0,0,0)に置きます。
ポイントを「ネイティブ」座標から回転複素座標に変換します:(a、b、c)->(2× a + c、b)= 2× a + c + b × f
必要に応じて、上記の回転数のいずれかを乗算してポイントを回転させます。
上記のように定義された「mod」を使用して、回転座標から「ネイティブ」なものにポイントをra変換します:(r、s)->(floor(r / 2)、s、r mod 2)。
ポイントを回転中心(x、y、z)に追加して正規化することにより、ポイントを元の位置に再変換します。
C ++での「トリプレックス」に基づくfの単純なバージョンは、次のようになります。
class hex {
public:
int x;
int y;
int w; /* "wobble"; for any given map, y+w is either odd or
even for ALL hexes of that map */
hex(int x, int y, int w) : x(x), y(y), w(w) {}
/* rest of the implementation */
};
class triplex {
public:
int r; /* real part */
int s; /* f-imaginary part */
triplex(int new_r, int new_s) : r(new_r), s(new_s) {}
triplex(const hex &hexfield)
{
r = hexfield.x * 2 + hexfield.w;
s = hexfield.y;
}
triplex(const triplex &other)
{
this->r = other.r; this->s = other.s;
}
private:
/* C++ has crazy integer division and mod semantics. */
int _div(int a, unsigned int b)
{
int res = a / b;
if( a < 0 && a % b != 0 ) { res -= 1; }
return res;
}
int _mod(int a, unsigned int b)
{
int res = a % b;
if( res < 0 ) { res += a; }
return res;
}
public:
/*
* Self-assignment operator; simple enough
*/
triplex & operator=(const triplex &rhs)
{
this->r = rhs.r; this->s = rhs.s;
return *this;
}
/*
* Multiplication operators - our main workhorse
* Watch out for overflows
*/
triplex & operator*=(const triplex &rhs)
{
/*
* (this->r + this->s * f) * (rhs.r + rhs.s * f)
* = this->r * rhs.r + (this->r * rhs.s + this->s * rhs.r ) * f
* + this->s * rhs.s * f * f
*
* ... remembering that f * f = -3 ...
*
* = (this->r * rhs.r - 3 * this->s * rhs.s)
* + (this->r * rhs.s + this->s * rhs.r) * f
*/
int new_r = this->r * rhs.r - 3 * this->s * rhs.s;
int new_s = this->r * rhs.s + this->s * rhs.r;
this->r = new_r; this->s = new_s;
return *this;
}
const triplex operator*(const triplex &other)
{
return triplex(*this) *= other;
}
/*
* Now for the rotations ...
*/
triplex rotate60CW() /* rotate this by 60 degrees clockwise */
{
/*
* The rotation is the same as multiplikation with (1,-1)
* followed by halving all values (multiplication by (1/2, 0).
* If the values come from transformation from a hex field,
* they will always land back on the hex field; else
* we might lose some information due to the last step.
*/
(*this) *= triplex(1, -1);
this->r /= 2;
this->s /= 2;
}
triplex rotate60CCW() /* Same, counter-clockwise */
{
(*this) *= triplex(1, 1);
this->r /= 2;
this->s /= 2;
}
/*
* Finally, we'd like to get a hex back (actually, I'd
* typically create this as a constructor of the hex class)
*/
operator hex()
{
return hex(_div(this->r, 2), this->s, _mod(this->r, 2));
}
};