Definieren wir zunächst eine neue Nummer. Keine Sorge, es ist einfach.
Oder einfach ausgedrückt: f = √3 × i , wobei i die imaginäre Einheit ist . Damit entspricht eine Drehung um 60 Grad im Uhrzeigersinn der Multiplikation mit 1/2 × (1 - f ) und eine Drehung um 60 Grad gegen den Uhrzeigersinn der Multiplikation mit 1/2 × (1 + f ) . Wenn dies seltsam klingt, denken Sie daran, dass die Multiplikation mit einer komplexen Zahl der Drehung in der 2D-Ebene entspricht. Wir "quetschen" nur die komplexen Zahlen in der imaginären Richtung ein wenig (um √3), um uns nicht mit Quadratwurzeln oder Nicht-Ganzzahlen befassen zu müssen.
Wir können den Punkt (a, b) auch als a + b × f schreiben .
Dadurch können wir jeden Punkt in der Ebene drehen. Zum Beispiel dreht sich der Punkt (2,0) = 2 + 0 × f zu (1, -1), dann zu (-1, -1), (-2,0), (-1,1), ( 1,1) und schließlich zurück zu (2,0), einfach durch Multiplikation.
Natürlich brauchen wir eine Möglichkeit, diese Punkte von unseren Koordinaten in diejenigen zu übersetzen, in denen wir die Rotationen durchführen, und dann wieder zurück. Dazu wird eine weitere Information benötigt: Wenn der Punkt, um den wir die Drehung durchführen, links oder rechts von der vertikalen Linie liegt. Der Einfachheit halber erklären wir, dass es einen "Wobble" -Wert w von 0 hat, wenn es links davon ist (wie der Mittelpunkt der Drehung [0,0] in Ihren unteren beiden Bildern), und von 1, wenn es rechts davon ist davon. Dies erweitert unsere ursprünglichen Punkte auf dreidimensional; ( x , y , w ), wobei "w" nach der Normalisierung entweder 0 oder 1 ist. Die Normalisierungsfunktion ist:
NORM: ( x , y , w ) -> ( x + Etage ( w / 2), y , w mod 2), wobei die "mod" -Operation so definiert ist, dass nur positive Werte oder Null zurückgegeben werden.
Unser Algorithmus sieht nun wie folgt aus:
Transformieren Sie unsere Punkte ( a , b , c ) in ihre Positionen relativ zum Rotationszentrum ( x , y , w ), indem Sie ( a - x , b - y , c - w ) berechnen und dann das Ergebnis normalisieren. Dies setzt das Rotationszentrum offensichtlich auf (0,0,0).
Transformieren Sie unsere Punkte von ihren "nativen" Koordinaten in die komplexen Rotationskoordinaten: ( a , b , c ) -> (2 × a + c , b ) = 2 × a + c + b × f
Drehen Sie unsere Punkte, indem Sie sie nach Bedarf mit einer der oben genannten Rotationszahlen multiplizieren.
Ra-transformiere die Punkte von den Rotationskoordinaten zurück zu ihren "nativen": ( r , s ) -> (Boden ( r / 2), s , r mod 2), wobei "mod" wie oben definiert ist.
Transformieren Sie die Punkte wieder in ihre ursprüngliche Position, indem Sie sie zum Rotationszentrum ( x , y , z ) hinzufügen und normalisieren.
Eine einfache Version unserer "Triplex" -Nummern, die auf f in C ++ basieren, würde folgendermaßen aussehen:
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));
}
};