Biquaternionen

Wenn Sie diesen Artikel geöffnet haben, haben Sie wahrscheinlich bereits von Quaternionen gehört und sie möglicherweise sogar in Ihren Entwürfen verwendet. Aber es ist Zeit, auf ein höheres Niveau aufzusteigen - zu den Biquaternionen.

Dieser Artikel enthält grundlegende Konzepte zu Biquaternionen und Operationen mit ihnen. Zum besseren Verständnis der Arbeit mit Biquaternionen wird ein klares Beispiel in Javascript unter Verwendung von Canvas gezeigt.

Biquaternion


Das Biquaternion ist eine hyperkomplexe Zahl mit einer Dimension von 8. In englischsprachigen Artikeln und in der Literatur werden sie als "duales Quaternion" bezeichnet, und in der russischsprachigen Literatur gibt es auch die Namen "duales Quaternion" oder "komplexes Quaternion".

Der Hauptunterschied zu Quaternionen besteht darin, dass die Quaternion die Ausrichtung des Objekts im Raum beschreibt und die Biquaternion auch die Position des Objekts im Raum beschreibt.

Biquaternion kann als zwei Quaternionen dargestellt werden:

 w i d e t i l d e t e x t b f q = b e g i n b m a t r i x t e x t b f q 1    t e x t b f q 2 e n d b m a t r i x , 


 t e x t b f q 1 - der Realteil bestimmt die Ausrichtung des Objekts im Raum;
 textbfq2 - Der Doppelteil bestimmt die Position des Objekts im Raum.

Das Biquaternion wird auch als komplexes Quaternion bezeichnet. In diesem Fall wird es als Quaternion dargestellt, wobei jede Komponente eine doppelte Zahl ist (nicht zu verwechseln mit dem Komplex). Doppelte Nummer A=a1+ epsilona2 wo a1 und a2 Sind reelle Zahlen und  epsilon - Clifford-Symbol (Komplexität), das die Eigenschaft besitzt  epsilon2=0 . Wir werden uns nicht mit Mathematik befassen, da wir uns mehr für den angewandten Teil interessieren. Daher werden wir die Biquaternion weiter als zwei Quaternionen betrachten.

Geometrische Interpretation der Biquaternion


In Analogie zur Quaternion, mit der Sie die Ausrichtung des Objekts festlegen können, kann die Biquaternion auch die Position festlegen. Das heißt, Die Biquaternion legt zwei Werte gleichzeitig fest - die Position und Ausrichtung des Objekts im Raum. Wenn wir sie in der Dynamik betrachten, definiert das Biquaternion zwei Größen - die lineare Bewegungsgeschwindigkeit und die Winkeldrehgeschwindigkeit des Objekts. Die folgende Abbildung zeigt die geometrische Bedeutung der Biquaternion.



Spieleentwickler wissen, dass zur Bestimmung der Position und Ausrichtung eines Objekts im Spielraum Rotationsmatrizen und Verschiebungsmatrizen verwendet werden. Abhängig von der Reihenfolge, in der Sie sie anwenden, ist das Ergebnis der endgültigen Position des Objekts unterschiedlich. Für diejenigen, die es gewohnt sind, die Bewegung in separate Operationen zu unterteilen, akzeptieren Sie die Regel für die Arbeit mit Biquaternionen: Zuerst bewegen wir das Objekt, dann drehen wir es. Tatsächlich beschreiben Sie diese beiden Bewegungen mit einer einzigen Zahl, wenn auch mit einem komplexen Hyperkomplex.

Skalareigenschaften


Berücksichtigen Sie die wichtigsten Skalareigenschaften. Hierbei ist darauf zu achten, dass sie keine gewöhnlichen reellen Zahlen zurückgeben, sondern doppelte.

1. Die Norm der Biquaternion

 | widetilde textbfq |= | textbfq1 |+ epsilon(q10q20+ textbfqT1 textbfq2)



2. Biquaternion-Modul

| widetilde textbfq|=| textbfq1|+ epsilon fracq10q20+ textbfqT1 textbfq2| textbfq1|



Grundlegende Operationen


Betrachten Sie die grundlegenden Operationen der Arbeit mit Biquaternionen. Wie Sie sehen können, sind sie ähnlichen Operationen mit Quaternionen sehr ähnlich.

1. Biquaternion-Paarung

 widetilde textbfq= beginbmatrix textbfq1 textbfq2 endbmatrix



2. Addition und Subtraktion von Biquaternionen

 widetilde textbfq pm widetilde textbfp= beginbmatrix textbfq1 pm textbfp1 textbfq2 pm textbfp2 endbmatrix



Das Addieren und Subtrahieren von Biquaternionen ist kommutativ (Begriffe können ausgetauscht werden).

3. Multiplikation der reellen Zahl durch Biquaternion

a widetilde textbfq= widetilde textbfqa= beginbmatrixa textbfq1a textbfq2 endbmatrix



4. Biquaternion Multiplikation

 widetilde textbfq otimes widetilde textbfp= beginbmatrix textbfq1 otimes textbfp1 textbfq1 otimes textbfp2+ textbfq2 otimes textbfp1 endbmatrix


Die Biquaternion-Multiplikation ist nicht kommutativ (bei einer Änderung der Reihenfolge der Faktoren ist das Ergebnis der Biquaternion-Multiplikation unterschiedlich).

Diese Operation ist eine der Hauptoperationen bei der Arbeit mit Biquaternionen und hat eine physikalische Bedeutung, nämlich das Ergebnis der Biquaternionenmultiplikation ist die Operation des Addierens der Rotationen und linearen Bewegungen von zwei Biquaternionen.

5. Biquaternion umkehren

 widetilde textbfq1= frac widetilde textbfq | widetilde textbfq |



Bestimmung der Biquaternion durch Orientierungswinkel und Positionsvektor


Zunächst definieren wir die Koordinatensysteme, in denen wir die Ausrichtung und Position des Objekts im Raum berücksichtigen. Dies muss durchgeführt werden, um den Realteil des Biquaternions (Orientierungsquaternion) anzugeben, dessen Rotationssequenz das resultierende Quaternion aus den Orientierungswinkeln beeinflusst. Hier werden wir von Flugzeugwinkeln geführt - Gieren  psi Tonhöhe  vartheta und rollen  gamma .

Definieren Sie das Basiskoordinatensystem. Stellen Sie sich vor, Sie stehen auf der Erdoberfläche und schauen in Richtung Norden.

Punkt O o - der Ursprung des Koordinatensystems am Ursprungspunkt des Objekts.
Die Achse O o Y g - ist vertikal nach oben gerichtet und der Richtung des Schwerkraftvektors entgegengesetzt.
Die Achse O o X g - ist entlang der Tangente des lokalen Meridians nach Norden gerichtet.
Achse O o Z g - ergänzt das System nach rechts und ist nach rechts in Richtung Osten gerichtet.

Das zweite Koordinatensystem ist verbunden. Stellen Sie sich zum Beispiel ein Flugzeug oder ein anderes Objekt vor.
Punkt O - Der Ursprung des Koordinatensystems liegt in der Regel am Punkt des Massenschwerpunkts des Objekts.
OY- Achse - vertikal nach oben gerichtet und senkrecht zur horizontalen Ebene des Objekts.
Achse OX - nach vorne zum vorderen Punkt des Objekts gerichtet.
OZ- Achse - ergänzt das System auf der rechten Seite.

Die Position des Objekts im Raum wird durch den Radiusvektor des Ursprungs (Punkt O ) des zugehörigen Koordinatensystems relativ zum festen Basiskoordinatensystem bestimmt. Die Ausrichtung des zugehörigen Koordinatensystems relativ zur Basis wird durch drei aufeinanderfolgende Einschaltungen bestimmt:

Gierwinkel  psi - Drehung um die Achse OY ,
Nickwinkel  vartheta - Drehung um die Achse OZ ,
Rollwinkel  gamma - Drehung um die Achse OX .

Für die anfängliche Bestimmung der Biquaternion müssen Sie den Real- und den Doppelteil der Biquaternion angeben. Die Ausrichtung und Position des Objekts wird unter Verwendung von Ausrichtungswinkeln relativ zu einem bestimmten Basiskoordinatensystem festgelegt  psi, vartheta, gamma und Positionsvektor des Massenschwerpunkts r=(rx,ry,rz)T .

Der eigentliche Teil  textbfq1 kann mit der Formel eingestellt werden:

\ textbf {q} _1 = \ begin {bmatrix} \ cos \ frac {\ psi} {2} \ cos \ frac {\ vartheta} {2} \ cos \ frac {\ gamma} {2} & - & \ sin \ frac {\ psi} {2} \ sin \ frac {\ vartheta} {2} \ sin \ frac {\ gamma} {2} \\ \ cos \ frac {\ psi} {2} \ cos \ frac { \ vartheta} {2} \ sin \ frac {\ gamma} {2} & + & \ sin \ frac {\ psi} {2} \ sin \ frac {\ vartheta} {2} \ cos \ frac {\ gamma} {2} \\ \ cos \ frac {\ psi} {2} \ sin \ frac {\ vartheta} {2} \ sin \ frac {\ gamma} {2} & + & \ sin \ frac {\ psi} { 2} \ cos \ frac {\ vartheta} {2} \ cos \ frac {\ gamma} {2} \\ \ cos \ frac {\ psi} {2} \ sin \ frac {\ vartheta} {2} \ cos \ frac {\ gamma} {2} & - & \ sin \ frac {\ psi} {2} \ cos \ frac {\ vartheta} {2} \ sin \ frac {\ gamma} {2} \ end {bmatrix}


Bitte beachten Sie, dass bei einer anderen Rotationssequenz auch die Ausdrücke unterschiedlich sind.

Doppelteil  textbfq2 definiert durch den Ausdruck:

 textbfq2= frac12 textbfr otimes textbfq1



Berechnung der Orientierungswinkel und des Positionsvektors aus der Biquaternion. Inverse Transformation


Die Orientierungswinkel können aus dem Realteil der Biquaternion berechnet werden  textbfq1 ::

 psi= arctan frac2(q0q2q1q3)q20+q21q22q23


 vartheta= arcsin(2(q1q2+q0q3))


 gamma= arctan frac2(q0q1q2q3)q20q21+q22q23



Die Position des Objekts wird durch den Ausdruck bestimmt:

 textbfr=2 textbfq2 otimes textbfq11


Das Ergebnis ist ein Vektor in Quaternionsform  textbfr=(0,rx,ry,rz)T

Drehen und bewegen Sie die Vektorbiquaternion


Eine der großen Eigenschaften von Biquaternionen ist die Drehung und Bewegung eines Vektors von einem Koordinatensystem zum anderen. Sei O o X g Y g Z g ein festes Basiskoordinatensystem und OXYZ das verbundene Koordinatensystem des Objekts. Dann kann die Ausrichtung und Position des Objekts relativ zum Basiskoordinatensystem durch die Biquaternion festgelegt werden  widetilde textbfq . Wenn ein Vektor angegeben ist  textbfr In einem verbundenen Koordinatensystem können Sie dann einen Vektor erhalten  textbfr0 im Basiskoordinatensystem mit der Formel:

 textbfr0= widetilde textbfq otimes textbfr otimes widetilde textbfq1


und zurück:

 textbfr= widetilde textbfq1 otimes textbfr0 otimes widetilde textbfq


wo  textbfr Ist ein Vektor in Biquaternionsform,  textbfr=(1,0,0,0,0,rx,ry,rz)

Biquaternion JavaScript Library


Alle oben genannten Operationen mit Biquaternionen sind in der Javascript-Bibliothek implementiert, abhängig von Ihren Aufgaben, die in anderen Programmiersprachen implementiert werden können. Die Hauptfunktionen der Arbeit mit Biquaternionen:
FunktionBeschreibung
DualQuaternion.dqBiquaternion Körper als Array von 8 Zahlen
DualQuaternion(dq0, dq1, dq2, dq3, dq4, dq5, dq6, dq7)Ein Konstruktor, der eine Biquaternion definiert, indem er alle acht Zahlen angibt
DualQuaternion.fromEulerVector(psi, theta, gamma, v)Holen Sie sich die Biquaternion, indem Sie die Ausrichtung des Objekts mit Euler-Winkeln und dem Positionsvektor des Objekts einstellen
DualQuaternion.getEulerVector()Holen Sie sich Euler-Winkel und Positionsvektor von Biquaternion
DualQuaternion.getVector()Holen Sie sich den Positionsvektor von Biquaternion
DualQuaternion.getReal()Holen Sie sich den Realteil der Biquaternion (bestimmt die Ausrichtung des Objekts im Raum)
DualQuaternion.getDual()Holen Sie sich den doppelten Teil der Biquaternion (bestimmt die Position des Objekts im Raum)
DualQuaternion.norm()Holen Sie sich die Biquaternion-Norm als doppelte Zahl
DualQuaternion.mod()Holen Sie sich das Biquaternion-Modul als doppelte Nummer
DualQuaternion.conjugate()Holen Sie sich die konjugierte Biquaternion
DualQuaternion.inverse()Holen Sie sich Reverse Biquaternion
DualQuaternion.mul(DQ2)Biquaternion Multiplikation
DualQuaternion.toString()Konvertieren Sie die Biquaternion in eine Zeichenfolge, um sie beispielsweise an die Debug-Konsole auszugeben

Datei dual_quaternion.js
 /** * * Author 2017, Akhramovich A. Sergey (akhramovichsa@gmail.com) * see https://github.com/infusion/Quaternion.js */ // 'use strict'; /** * Dual Quaternion constructor * * @constructor * @returns {DualQuaternion} */ function DualQuaternion(dq0, dq1, dq2, dq3, dq4, dq5, dq6, dq7) { if (dq0 === undefined) { this.dq = [1, 0, 0, 0, 0, 0, 0, 0]; } else { this.dq = [dq0, dq1, dq2, dq3, dq4, dq5, dq6, dq7]; } return this; }; //         DualQuaternion['fromEulerVector'] = function(psi, theta, gamma, v) { var q_real = new Quaternion.fromEuler(psi, theta, gamma); var q_v = new Quaternion(0, v[0], v[1], v[2]); var q_dual = q_v.mul(q_real); return new DualQuaternion( q_real.q[0], q_real.q[1], q_real.q[2], q_real.q[3], q_dual.q[0]*0.5, q_dual.q[1]*0.5, q_dual.q[2]*0.5, q_dual.q[3]*0.5); }; DualQuaternion.prototype = { 'dq': [1, 0, 0, 0, 0, 0, 0, 0], /** *    (psi, theta, gamma)      */ 'getEulerVector': function() { var euler_angles = this.getReal().getEuler(); var q_dual = this.getDual(); var q_dual_2 = new Quaternion(2.0*q_dual.q[0], 2.0*q_dual.q[1], 2.0*q_dual.q[2], 2.0*q_dual.q[3]); var q_vector = q_dual_2.mul(this.getReal().conjugate()); return [euler_angles[0], euler_angles[1], euler_angles[2], q_vector.q[1], q_vector.q[2], q_vector.q[3]]; }, /** *       */ 'getVector': function() { var euler_vector = this.getEulerVector(); return [euler_vector[3], euler_vector[4], euler_vector[5]]; }, /** *     * @returns {Quaternion} */ 'getReal': function() { return new Quaternion(this.dq[0], this.dq[1], this.dq[2], this.dq[3]); }, /** *     * @returns {Quaternion} */ 'getDual': function() { return new Quaternion(this.dq[4], this.dq[5], this.dq[6], this.dq[7]); }, /** *   * !   ! */ 'norm': function() { return [Math.pow(this.dq[0], 2) + Math.pow(this.dq[1], 2) + Math.pow(this.dq[2], 2) + Math.pow(this.dq[3], 2), this.dq[0]*this.dq[4] + this.dq[1]*this.dq[5] + this.dq[2]*this.dq[6] + this.dq[3]*this.dq[7]]; }, /** *   * !   ! */ 'mod': function() { var q_real_mod = Math.sqrt(Math.pow(this.dq[0], 2) + Math.pow(this.dq[1], 2) + Math.pow(this.dq[2], 2) + Math.pow(this.dq[3], 2)); return [q_real_mod, (this.dq[0]*this.dq[4] + this.dq[1]*this.dq[5] + this.dq[2]*this.dq[6] + this.dq[3]*this.dq[7])/q_real_mod]; }, /** *   * DQ' := (dq0, -dq1, -dq2, -dq3, dq4, -dq5, -dq6, -dq7) */ 'conjugate': function() { return new DualQuaternion(this.dq[0], -this.dq[1], -this.dq[2], -this.dq[3], this.dq[4], -this.dq[5], -this.dq[6], -this.dq[7]); }, //    'inverse': function() { var q_real_norm = new Quaternion(this.dq[0], this.dq[1], this.dq[2], this.dq[3]).norm(); var dq_norm_inv = [q_real_norm, - (this.dq[0]*this.dq[4] + this.dq[1]*this.dq[5] + this.dq[2]*this.dq[6] + this.dq[3]*this.dq[7])/q_real_norm]; var dq_conj = this.conjugate(); //      return new DualQuaternion( dq_norm_inv[0] * dq_conj.dq[0], dq_norm_inv[0] * dq_conj.dq[1], dq_norm_inv[0] * dq_conj.dq[2], dq_norm_inv[0] * dq_conj.dq[3], dq_norm_inv[0] * dq_conj.dq[4] + dq_norm_inv[1] * dq_conj.dq[0], dq_norm_inv[0] * dq_conj.dq[5] + dq_norm_inv[1] * dq_conj.dq[1], dq_norm_inv[0] * dq_conj.dq[6] + dq_norm_inv[1] * dq_conj.dq[2], dq_norm_inv[0] * dq_conj.dq[7] + dq_norm_inv[1] * dq_conj.dq[3]); }, /** *   * q1_real*q2_real, q1_real*q2_dual + q1_dual*q2_real */ 'mul': function(DQ2) { var q1_real = this.getReal(); var q1_dual = this.getDual(); var q2_real = DQ2.getReal(); var q2_dual = DQ2.getDual(); var q_res_real = q1_real.mul(q2_real); var q_res_dual_1 = q1_real.mul(q2_dual); var q_res_dual_2 = q1_dual.mul(q2_real); return new DualQuaternion( q_res_real.q[0], q_res_real.q[1], q_res_real.q[2], q_res_real.q[3], q_res_dual_1.q[0] + q_res_dual_2.q[0], q_res_dual_1.q[1] + q_res_dual_2.q[1], q_res_dual_1.q[2] + q_res_dual_2.q[2], q_res_dual_1.q[3] + q_res_dual_2.q[3]); }, /** *    */ 'transformVector': function (v) { var dq_res = this.mul(new DualQuaternion(1, 0, 0, 0, 0, v[0], v[1], v[2])).mul(this.conjugate()); return [dq_res.dq[5], dq_res.dq[6], dq_res.dq[7]]; }, /** *   ,   */ 'toString': function() { return '[' + this.dq[0].toString() + ', ' + this.dq[1].toString() + ', ' + this.dq[2].toString() + ', ' + this.dq[3].toString() + ', ' + this.dq[4].toString() + ', ' + this.dq[5].toString() + ', ' + this.dq[6].toString() + ', ' + this.dq[7].toString() + ']'; } } /* // TEST: var dq1 = new DualQuaternion.fromEulerVector(0 * Math.PI/180.0, 0 * Math.PI/180, 0 * Math.PI/180, [10, 20, 30]); console.log(dq1.toString()); console.log('getEulerVector = ', dq1.getEulerVector()); console.log('norm = ', dq1.norm()); console.log('mod = ', dq1.mod()); console.log('conjugate = ', dq1.conjugate().dq); console.log('inverse = ', dq1.inverse().dq); var dq2 = new DualQuaternion.fromEulerVector(0 * Math.PI/180.0, 0 * Math.PI/180, 0 * Math.PI/180, [10, 0, 0]); console.log('mul = ', dq1.mul(dq2).dq); console.log('transformVector ??? = ', dq1.transformVector([0, 0, 0])); */ 


Ein Beispiel für die Arbeit mit Biquaternionen


Betrachten Sie ein kleines Spiel, um die Grundlagen der Verwendung von Biquaternionen als Beispiel besser zu verstehen. Der rechteckige Bereich wird festgelegt - die Karte. Ein Schiff, das auf einer Karte mit einer auf der Karte montierten Rotationskanone schwimmt. Hierbei ist zu berücksichtigen, dass für das Schiff das Grundkoordinatensystem das Koordinatensystem der Karte und für die Waffe das Grundkoordinatensystem das Schiff ist. Alle Objekte werden im Kartenkoordinatensystem gezeichnet. Hier ist es interessant zu sehen, wie Sie mithilfe der Biquaternion-Multiplikationseigenschaft vom Kanonenkoordinatensystem zum Kartenkoordinatensystem wechseln können. Die Bewegung des Schiffes wird mit den Tasten W, A, S, D gesteuert. Die Richtung der Waffe wird mit dem Mauszeiger eingestellt.



Das Schiff und die Waffe werden in zwei Klassen beschrieben: Ship und Gun . Im Konstruktor der Schiffsklasse werden seine Form in Form von Biquaternionspunkten, die anfängliche Ausrichtung und Position auf der Karte in Form this.dq_pos Biquaternion.dq_pos this.dq_pos .

Biquaternion-Inkremente werden auch für die Schiffskontrolle angegeben. Beim Hin- und Herbewegen (Tasten W, S) ändert sich nur der doppelte Teil des Biquaternions, und beim Steuern von rechts nach links (Tasten A, D) ändern sich der reale und der doppelte Teil des Biquaternions, wodurch der Drehwinkel festgelegt wird.

 function Ship(ctx, v) { this.ctx = ctx; this.dq_pos = new DualQuaternion.fromEulerVector(0*Math.PI/180, 0, 0, v); //   this.dq_forward_left = new DualQuaternion.fromEulerVector(0, 0, 0, [ 15, 0, -10]); this.dq_forward_right = new DualQuaternion.fromEulerVector(0, 0, 0, [ 15, 0, 10]); this.dq_backward_left = new DualQuaternion.fromEulerVector(0, 0, 0, [-15, 0, -10]); this.dq_backward_right = new DualQuaternion.fromEulerVector(0, 0, 0, [-15, 0, 10]); this.dq_forward_forward = new DualQuaternion.fromEulerVector(0, 0, 0, [ 30, 0, 0]); //      this.dq_dx_left = new DualQuaternion.fromEulerVector( 1*Math.PI/180, 0, 0, [0, 0, 0]); this.dq_dx_right = new DualQuaternion.fromEulerVector(-1*Math.PI/180, 0, 0, [0, 0, 0]); this.dq_dx_forward = new DualQuaternion.fromEulerVector(0, 0, 0, [ 1, 0, 0]); this.dq_dx_backward = new DualQuaternion.fromEulerVector(0, 0, 0, [-1, 0, 0]); return this; }; 

In der Klasse selbst gibt es nur eine Funktion zum Rendern des Ship.draw() . Achten Sie auf die Anwendung der Biquaternion-Operation, bei der jeder Punkt des Schiffes mit der Biquaternion der aktuellen Position und Ausrichtung des Schiffes multipliziert wird.

 Ship.prototype = { 'ctx': 0, 'dq_pos': new DualQuaternion.fromEulerVector(0, 0, 0, 0, 0, 0), /** *   */ 'draw': function() { //         v_pos = this.dq_pos.getVector(); v_forward_left = this.dq_pos.mul(this.dq_forward_left).getVector(); v_forward_right = this.dq_pos.mul(this.dq_forward_right).getVector(); v_backward_left = this.dq_pos.mul(this.dq_backward_left).getVector(); v_backward_right = this.dq_pos.mul(this.dq_backward_right).getVector(); v_forward_forward = this.dq_pos.mul(this.dq_forward_forward).getVector(); //   ctx.beginPath(); ctx.moveTo(v_backward_left[0], v_backward_left[2]); ctx.lineTo(v_forward_left[0], v_forward_left[2]); ctx.lineTo(v_forward_left[0], v_forward_left[2]); ctx.lineTo(v_forward_forward[0], v_forward_forward[2]); ctx.lineTo(v_forward_right[0], v_forward_right[2]); ctx.lineTo(v_backward_right[0], v_backward_right[2]); ctx.lineTo(v_backward_left[0], v_backward_left[2]); ctx.stroke(); ctx.closePath(); } }; 

Im Konstruktor der Waffenklasse wird seine Form in Form von Biquaternionspunkten angegeben. Die Waffe wird als Linie angezeigt. Die anfängliche Ausrichtung und Position auf dem Schiff wird durch die Biquaternion this.dq_pos . Außerdem wird die Bindung an das Schiff festgelegt, auf dem es installiert ist. Die Waffe auf dem Schiff kann sich nur drehen, so dass Biquaternion-Inkremente bei der Steuerung der Waffe nur den Realteil der Biquaternion ändern, der den Drehwinkel festlegt. In diesem Beispiel wird das Gerät vom Mauszeiger geführt, sodass die Drehung der Pistole sofort erfolgt.

 function Gun(ctx, ship, v) { this.ctx = ctx; this.ship = ship; //     this.dq_pos = new DualQuaternion.fromEulerVector(0, 0, 0, v); //   this.dq_forward = new DualQuaternion.fromEulerVector(0, 0, 0, [20, 0, 0]); this.dq_backward = new DualQuaternion.fromEulerVector(0, 0, 0, [ 0, 0, 0]); //     this.dq_dx_left = new DualQuaternion.fromEulerVector( 1*Math.PI/180, 0, 0, [0, 0, 0]); this.dq_dx_right = new DualQuaternion.fromEulerVector(-1*Math.PI/180, 0, 0, [0, 0, 0]); return this; }; 

In der Ship.draw() auch nur eine Funktion des Renderings Ship.draw() implementiert. Die Waffe wird als Linie angezeigt, die durch zwei Punkte this.dq_backward und this.dq_forward . Um die Koordinaten der Punkte der Waffe zu bestimmen, wird die Operation der Biquaternion-Multiplikation verwendet.

 Gun.prototype = { 'ctx': 0, 'ship': 0, 'dq_pos': new DualQuaternion.fromEulerVector(0, 0, 0, [0, 0, 0]), /** *   */ 'draw': function() { //     v_pos = this.ship.dq_pos.getVector(); v_forward = this.ship.dq_pos.mul(this.dq_backward).mul(this.dq_forward).getVector(); v_backward = this.ship.dq_pos.mul(this.dq_backward).getVector(); //   ctx.beginPath(); ctx.moveTo(v_backward[0], v_backward[2]); ctx.lineTo(v_forward[0], v_forward[2]); ctx.stroke(); ctx.closePath(); } }; 

Die Verarbeitung der Schiffs- und Waffensteuerung wird durch Ereignisse implementiert. Vier Variablen leftPressed, upPressed, rightPressed, downPressed , die in der Hauptprogrammschleife verarbeitet werden, sind für das Drücken und Loslassen der Schiffssteuertasten verantwortlich.

 leftPressed = false; rightPressed = false; upPressed = false; downPressed = false; dq_mouse_pos = new DualQuaternion.fromEulerVector(0, 0, 0, [0, 0, 0]); document.addEventListener("keydown", keyDownHandler, false); document.addEventListener("keyup", keyUpHandler, false); document.addEventListener("mousemove", mouseMoveHandler, false); //     function keyDownHandler(e) { if (e.keyCode == 37 || e.keyCode == 65 || e.keyCode == 97) { leftPressed = true; } //  A else if (e.keyCode == 38 || e.keyCode == 87 || e.keyCode == 119) { upPressed = true; } //  W else if (e.keyCode == 39 || e.keyCode == 68 || e.keyCode == 100) { rightPressed = true; } //  D else if (e.keyCode == 40 || e.keyCode == 83 || e.keyCode == 115) { downPressed = true; } //  S } //     function keyUpHandler(e) { if (e.keyCode == 37 || e.keyCode == 65 || e.keyCode == 97) { leftPressed = false; } //  A else if (e.keyCode == 38 || e.keyCode == 87 || e.keyCode == 119) { upPressed = false; } //  W else if (e.keyCode == 39 || e.keyCode == 68 || e.keyCode == 100) { rightPressed = false; } //  D else if (e.keyCode == 40 || e.keyCode == 83 || e.keyCode == 115) { downPressed = false; } //  S } 

Eine der interessantesten Funktionen im Hinblick auf die Verwendung von Biquaternion-Operationen besteht darin, die Schiffskanone in Richtung des Mauszeigers zu steuern. Zunächst werden die Koordinaten des Mauszeigers in der Biquaternion dq_mouse_pos . Dann wird die Biquaternion der Position der Maus relativ zum Schiff unter Verwendung der Biquaternion-Multiplikation berechnet. Die Biquaternion des Schiffes wird der Maus entnommen. Biquaternion dq_mouse_pos_about_ship = ship_1.dq_pos.inverse().mul(dq_mouse_pos);
(Hinweis: Sequentielle Biquaternion-Multiplikationsoperationen werden von rechts nach links gelesen). Und schließlich wird der Winkel zwischen den Vektoren des Werkzeugs und der Maus bestimmt. Dem Startpunkt der Waffe gun_1.dq_backward der empfangene Wert zugewiesen.

 function mouseMoveHandler(e) { var relativeX = e.clientX - canvas.offsetLeft; var relativeY = e.clientY - canvas.offsetTop; //           if (relativeX > 0 && relativeX < canvas.width && relativeY > 0 && relativeY < canvas.height) { //    dq_mouse_pos = new DualQuaternion.fromEulerVector(0, 0, 0, [relativeX, 0, relativeY]); //      //  .       //     // DQ_ship^(-1) * DQ_mouse dq_mouse_pos_about_ship = ship_1.dq_pos.inverse().mul(dq_mouse_pos); //       q_gun_mouse = new Quaternion.fromBetweenVectors(gun_1.dq_forward.getVector(), dq_mouse_pos_about_ship.getVector()); dq_gun_mouse = new DualQuaternion(q_gun_mouse.q[0], q_gun_mouse.q[1], q_gun_mouse.q[2], q_gun_mouse.q[3], 0, 0, 0, 0); gun_1.dq_backward = dq_gun_mouse; // console.log(dq_gun_mouse.getEulerVector()); // console.log(relativeX + ' ' + relativeY + ' ' + gun_1.dq_forward.toString()); } } 

Im Hauptteil des Programms werden Objekte des Schiffs und der Kanonen ship_1 und gun_1 , Debugging-Informationen angezeigt und die Schiffssteuerungsverarbeitung durchgeführt.

 var canvas = document.getElementById("myCanvas"); var ctx = canvas.getContext("2d"); ship_1 = new Ship(ctx, [100, 0, 100]); gun_1 = new Gun(ctx, ship_1, [0, 0, 0]); function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); ship_1.draw(); gun_1.draw(); // Debug info ship_euler_vector = ship_1.dq_pos.getEulerVector(); ship_euler_vector[0] = ship_euler_vector[0]*180/Math.PI; ship_euler_vector[1] = ship_euler_vector[1]*180/Math.PI; ship_euler_vector[2] = ship_euler_vector[2]*180/Math.PI; ship_euler_vector = ship_euler_vector.map(function(each_element){ return each_element.toFixed(2); }); ship_dq = ship_1.dq_pos.dq.map(function(each_element){ return each_element.toFixed(2); }); gun_dq = ship_1.dq_pos.mul(gun_1.dq_backward).dq.map(function(each_element){ return each_element.toFixed(2); }); ctx.font = "8pt Courier"; ctx.fillText("Ship: " + ship_dq + " | psi, theta, gamma, vector:" + ship_euler_vector, 10, 20); ctx.fillText("Gun: " + gun_dq, 10, 40); //   if (leftPressed) { ship_1.dq_pos = ship_1.dq_pos.mul(ship_1.dq_dx_left); } if (rightPressed) { ship_1.dq_pos = ship_1.dq_pos.mul(ship_1.dq_dx_right); } if (upPressed) { ship_1.dq_pos = ship_1.dq_pos.mul(ship_1.dq_dx_forward); } if (downPressed) { ship_1.dq_pos = ship_1.dq_pos.mul(ship_1.dq_dx_backward); } requestAnimationFrame(draw); } draw(); 

Der Link zum Archiv enthält den vollständigen Code der Bibliotheken für die Arbeit mit Quaternionen und Biquaternionen, das Programmskript selbst und die Datei index.html, die lokal im Browser geöffnet werden kann, um das obige Beispiel auszuführen.

Ein Beispiel für die Arbeit mit Biquaternionen

Fazit


Möglicherweise haben Sie eine Frage: Warum sollten Sie einen so komplexen mathematischen Apparat verwenden, wenn Sie mit Standardwerkzeugen zum Bewegen und Drehen von Objekten auskommen können? Einer der Hauptvorteile besteht darin, dass die Biquaternionsform des Schreibens rechnerisch effizienter ist, da alle Operationen mit Biquaternionen nach dem Erweitern der Ausdrücke linear sind. Dieses Video, Geometrisches Skinning mit ungefährer Mischung aus zwei Quaternionen, zeigt, wie viel effizienter Biquaternionsberechnungen sind als andere Methoden.

Ich habe hauptsächlich Informationen über die Verwendung von Biquaternionen aus englischen Quellen entnommen.
Aus der heimischen Literatur kann ich zwei Bücher empfehlen:

  1. Chelnokov Yuri Nikolaevich. Quaternion- und Biquaternion-Modelle und Methoden der Festkörpermechanik und ihre Anwendungen. Geometrie und Bewegungskinematik. - monumentale theoretische Arbeit.
  2. Gordeev Vadim Nikolaevich. Quaternionen und Biquaternionen mit Anwendungen in Geometrie und Mechanik. - Es ist in einer verständlicheren Sprache verfasst und zeigt Anwendungen bei der Gestaltung gekrümmter räumlicher Strukturen.

Source: https://habr.com/ru/post/de436210/


All Articles