Biquaternions

Jika Anda membuka artikel ini, Anda mungkin sudah pernah mendengar tentang angka empat, dan mungkin bahkan menggunakannya dalam desain Anda. Tapi sekarang saatnya untuk naik ke level yang lebih tinggi - ke biquaternions.

Artikel ini memberikan konsep dasar tentang biquaternions dan operasi dengannya. Untuk pemahaman yang lebih baik tentang bekerja dengan biquaternions, contoh yang jelas dalam Javascript menggunakan Kanvas ditampilkan.

Biquaternion


Biquaternion adalah angka hiperkompleks yang memiliki dimensi 8. Dalam artikel dan literatur berbahasa Inggris, mereka disebut "angka empat ganda," dan dalam literatur berbahasa Rusia ada juga nama "angka empat ganda" atau "angka empat kompleks".

Perbedaan utama dari angka empat adalah bahwa angka empat menggambarkan orientasi objek di ruang, dan biquaternion juga menggambarkan posisi objek di ruang.

Biquaternion dapat direpresentasikan sebagai dua angka empat:

 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 - bagian nyata, menentukan orientasi objek di ruang angkasa;
 textbfq2 - bagian ganda, menentukan posisi objek dalam ruang.

Biquaternion juga disebut sebagai quaternion kompleks, dalam hal ini diwakili sebagai quaternion, masing-masing komponen yang merupakan bilangan ganda (tidak harus bingung dengan kompleks). Nomor ganda A=a1+ epsilona2 dimana a1 dan a2 Apakah bilangan real, dan  epsilon - Simbol Clifford (kompleksitas), memiliki properti  epsilon2=0 . Kami tidak akan mempelajari matematika, karena kami lebih tertarik pada bagian terapan, oleh karena itu, lebih lanjut kami akan mempertimbangkan biquaternion sebagai dua angka empat.

Interpretasi geometris dari biquaternion


Dengan analogi dengan angka empat, dengan mana Anda dapat mengatur orientasi objek, biquaternion juga dapat mengatur posisi. Yaitu Biquaternion menetapkan dua nilai sekaligus - posisi dan orientasi objek dalam ruang. Jika kita mempertimbangkannya dalam dinamika, maka biquaternion mendefinisikan dua kuantitas - kecepatan gerak linier dan kecepatan sudut rotasi objek. Gambar di bawah ini menunjukkan arti geometris dari biquaternion.



Pengembang game tahu bahwa untuk menentukan posisi dan orientasi suatu objek di ruang game, matriks rotasi dan matriks perpindahan digunakan, dan, tergantung pada urutan penerapannya, hasil dari posisi akhir objek berbeda. Bagi mereka yang terbiasa membagi gerakan menjadi operasi terpisah, terima aturan untuk bekerja dengan biquaternions: pertama-tama kita memindahkan objek, lalu kita memutarnya. Faktanya, Anda menggambarkan dua gerakan ini dengan satu angka, meskipun hypercomplex yang kompleks.

Karakteristik skalar


Pertimbangkan karakteristik skalar utama. Di sini perlu untuk memperhatikan fakta bahwa mereka mengembalikan bukan bilangan real biasa, tetapi dua.

1. Norma biquaternion

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



2. Modul Biquaternion

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



Operasi dasar


Pertimbangkan operasi dasar bekerja dengan biquaternions. Seperti yang Anda lihat, mereka sangat mirip dengan operasi serupa dengan angka empat.

1. Pasangan Biquaternion

 widetilde textbfq= beginbmatrix textbfq1 textbfq2 endbmatrix



2. Penambahan dan pengurangan Biquaternion

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



Penambahan dan pengurangan biquaternions bersifat komutatif (istilah dapat dipertukarkan).

3. Perkalian bilangan real dengan biquaternion

a widetilde textbfq= widetilde textbfqa= beginbmatrixa textbfq1a textbfq2 endbmatrix



4. Multiplikasi Biquaternion

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


Perkalian biquaternion adalah non-komutatif (dengan perubahan urutan faktor, hasil dari perkalian biquaternion berbeda).

Operasi ini adalah salah satu yang utama ketika bekerja dengan biquaternions dan membawa makna fisik, yaitu, hasil dari perkalian biquaternion adalah operasi penambahan rotasi dan gerakan linear dari dua biquaternions.

5. Membalik biquaternion

 widetilde textbfq1= frac widetilde textbfq | widetilde textbfq |



Penentuan biquaternion melalui sudut orientasi dan vektor posisi


Pertama, kita mendefinisikan sistem koordinat di mana kita akan mempertimbangkan orientasi dan posisi objek di ruang angkasa. Ini harus dilakukan untuk menentukan bagian nyata dari biquaternion (orientasi angka empat), urutan rotasi yang mempengaruhi angka empat yang dihasilkan dari sudut orientasi. Di sini kita akan dipandu oleh sudut-sudut pesawat - yaw  psi melenggang  vartheta dan berguling  gamma .

Tentukan sistem koordinat dasar. Bayangkan Anda berdiri di permukaan Bumi dan melihat ke arah Utara.

Titik O o - asal dari sistem koordinat, yang terletak di titik asal objek.
Sumbu O o Yg - diarahkan secara vertikal ke atas, dan berlawanan dengan arah vektor gravitasi.
Sumbu O oXg - diarahkan ke Utara, di sepanjang garis singgung meridian lokal.
Axis O o Zg - melengkapi sistem ke kanan dan diarahkan ke kanan, ke arah Timur.

Sistem koordinat kedua terhubung. Bayangkan, misalnya, pesawat terbang atau benda lain.
Titik O - asal sistem koordinat, sebagai aturan, terletak di titik pusat massa objek.
Sumbu OY - diarahkan secara vertikal ke atas, dan tegak lurus terhadap bidang horizontal objek.
Axis OX - diarahkan maju ke titik depan objek.
Sumbu OZ - melengkapi sistem ke kanan.

Posisi objek dalam ruang ditentukan oleh radius vektor asal (titik O ) dari sistem koordinat terkait relatif terhadap sistem koordinat basis tetap. Orientasi sistem koordinat terkait relatif terhadap basis ditentukan oleh tiga putaran berturut-turut:

sudut menganga  psi - Rotasi di sekitar sumbu OY ,
sudut pitch  vartheta - Rotasi di sekitar sumbu OZ ,
sudut gulungan  gamma - Rotasi di sekitar sumbu OX .

Untuk penentuan awal biquaternion, Anda harus menentukan bagian nyata dan ganda biquaternion. Orientasi dan posisi objek diatur relatif ke sistem koordinat basis tertentu menggunakan sudut orientasi  psi, vartheta, gamma dan vektor posisi pusat massa r=(rx,ry,rz)T .

Bagian yang sebenarnya  textbfq1 dapat diatur menggunakan rumus:

\ 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}


Harap perhatikan bahwa jika Anda memiliki urutan rotasi yang berbeda, maka ekspresi juga akan berbeda.

Bagian ganda  textbfq2 didefinisikan oleh ungkapan:

 textbfq2= frac12 textbfr otimes textbfq1



Perhitungan sudut orientasi dan vektor posisi dari biquaternion. Transformasi terbalik


Sudut orientasi dapat dihitung dari bagian sebenarnya dari biquaternion  textbfq1 :

 psi= arctan frac2(q0q2q1q3)q20+q21q22q23


 vartheta= arcsin(2(q1q2+q0q3))


 gamma= arctan frac2(q0q1q2q3)q20q21+q22q23



Posisi objek ditentukan oleh ekspresi:

 textbfr=2 textbfq2 otimes textbfq11


hasilnya adalah vektor dalam bentuk angka empat  textbfr=(0,rx,ry,rz)T

Putar dan pindahkan biquaternion vektor


Salah satu sifat besar biquaternions adalah rotasi dan pergerakan vektor dari satu sistem koordinat ke yang lain. Biarkan O oX g Yg Zg menjadi sistem koordinat basis tetap, dan OXYZ menjadi sistem koordinat objek yang terhubung. Kemudian orientasi dan posisi objek relatif terhadap sistem koordinat dasar dapat ditentukan oleh biquaternion  widetilde textbfq . Jika suatu vektor ditentukan  textbfr dalam sistem koordinat yang terhubung, maka Anda bisa mendapatkan vektor  textbfr0 dalam sistem koordinat dasar menggunakan rumus:

 textbfr0= widetilde textbfq otimes textbfr otimes widetilde textbfq1


dan kembali:

 textbfr= widetilde textbfq1 otimes textbfr0 otimes widetilde textbfq


dimana  textbfr Apakah vektor dalam bentuk biquaternion,  textbfr=(1,0,0,0,0,0,rx,ry,rz)

Perpustakaan JavaScript Biquaternion


Semua operasi di atas dengan biquaternions diimplementasikan di perpustakaan-javascript, tergantung pada tugas Anda, itu dapat diimplementasikan dalam bahasa pemrograman lain. Fungsi utama bekerja dengan biquaternions:
FungsiDeskripsi
DualQuaternion.dqBadan Biquaternion sebagai array dari 8 angka
DualQuaternion(dq0, dq1, dq2, dq3, dq4, dq5, dq6, dq7)Konstruktor yang mendefinisikan biquaternion dengan menentukan semua delapan angka
DualQuaternion.fromEulerVector(psi, theta, gamma, v)Dapatkan biquaternion dengan mengatur orientasi objek dengan sudut Euler dan vektor posisi objek
DualQuaternion.getEulerVector()Dapatkan sudut Euler dan vektor posisi dari biquaternion
DualQuaternion.getVector()Dapatkan vektor posisi dari biquaternion
DualQuaternion.getReal()Dapatkan bagian nyata dari biquaternion (menentukan orientasi objek dalam ruang)
DualQuaternion.getDual()Dapatkan bagian ganda dari biquaternion (menentukan posisi objek dalam ruang)
DualQuaternion.norm()Dapatkan norma biquaternion sebagai angka ganda
DualQuaternion.mod()Dapatkan modul biquaternion sebagai nomor ganda
DualQuaternion.conjugate()Dapatkan biquaternion terkonjugasi
DualQuaternion.inverse()Dapatkan Biquaternion Terbalik
DualQuaternion.mul(DQ2)Multiplikasi Biquaternion
DualQuaternion.toString()Konversikan biquaternion menjadi string, misalnya, untuk output ke konsol debug

File 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])); */ 


Contoh bekerja dengan biquaternions


Untuk pemahaman yang lebih baik tentang dasar-dasar penggunaan biquaternions sebagai contoh, pertimbangkan permainan kecil. Area persegi panjang diatur - peta. Sebuah kapal melayang di peta dengan pistol putar yang terpasang di peta. Di sini perlu untuk memperhitungkan bahwa untuk kapal, sistem koordinat dasar adalah sistem koordinat peta, dan untuk pistol, sistem koordinat dasar adalah kapal. Semua objek digambar dalam sistem koordinat peta dan di sini akan menarik untuk melihat bagaimana Anda dapat beralih dari sistem koordinat pistol ke sistem koordinat peta menggunakan properti multiplikasi biquaternion. Pergerakan kapal dikendalikan oleh tombol W, A, S, D. Arah pistol diatur oleh kursor mouse.



Kapal dan senapan dijelaskan oleh dua kelas: Ship dan Gun . Dalam konstruktor kelas kapal, bentuknya dalam bentuk titik biquaternion, orientasi awal dan posisi pada peta dalam bentuk this.dq_pos .

Peningkatan Biquaternion juga diberikan untuk kontrol kapal. Saat bergerak bolak-balik (tombol W, S), hanya bagian ganda dari biquaternion yang akan berubah, dan ketika mengontrol kanan-kiri (tombol A, D), bagian nyata dan ganda dari biquaternion akan berubah, yang menentukan sudut rotasi.

 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; }; 

Di kelas itu sendiri, hanya ada satu fungsi untuk merender kapal Ship.draw() . Perhatikan penerapan operasi biquaternion untuk mengalikan setiap titik kapal dengan biquaternion dari posisi saat ini dan orientasi kapal.

 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(); } }; 

Dalam konstruktor kelas senjata, bentuknya dalam bentuk titik biquaternion ditentukan. Pistol akan ditampilkan sebagai garis. Orientasi awal dan posisi pada kapal diatur oleh this.dq_pos . Juga, pengikatan ke kapal tempat ia diinstal juga diatur. Pistol di kapal hanya bisa berputar, jadi penambahan biquaternion saat mengendalikan pistol hanya akan mengubah bagian sebenarnya dari biquaternion, yang menetapkan sudut rotasi. Dalam contoh ini, implement dipandu oleh kursor mouse, sehingga rotasi pistol akan terjadi secara instan.

 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; }; 

Di kelas senjata, hanya satu fungsi renderingnya Ship.draw() juga diimplementasikan. Pistol ditampilkan sebagai garis, yang ditetapkan oleh dua titik this.dq_backward dan this.dq_forward . Untuk menentukan koordinat titik-titik pistol, operasi multiplikasi biquaternion digunakan.

 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(); } }; 

Pemrosesan kontrol kapal dan senjata diterapkan melalui berbagai peristiwa. Empat variabel leftPressed, upPressed, rightPressed, downPressed , yang diproses dalam loop program utama, bertanggung jawab untuk menekan dan melepaskan kunci kontrol kapal.

 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 } 

Salah satu fungsi yang paling menarik, dari sudut pandang menggunakan operasi biquaternion, adalah untuk mengontrol pistol kapal ke arah penunjuk mouse. Pertama, koordinat penunjuk tetikus ditentukan dalam dq_mouse_pos . Kemudian, biquaternion dari posisi mouse relatif terhadap kapal dihitung menggunakan perkalian biquaternion. Biquaternion kapal diambil dari dq_mouse_pos_about_ship = ship_1.dq_pos.inverse().mul(dq_mouse_pos); mouse dq_mouse_pos_about_ship = ship_1.dq_pos.inverse().mul(dq_mouse_pos);
(Catatan: operasi multiplikasi biquaternion berurutan dibaca dari kanan ke kiri). Dan akhirnya, sudut antara vektor alat dan mouse ditentukan. Gun titik awal gun_1.dq_backward diberikan nilai yang diterima.

 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()); } } 

Di bagian utama program, objek kapal dan senjata ship_1 dan gun_1 , informasi debugging ditampilkan, dan pemrosesan kontrol kapal dilakukan.

 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(); 

Tautan ke arsip berisi kode lengkap perpustakaan untuk bekerja dengan angka empat dan dua kutip, skrip program itu sendiri, dan file index.html, yang dapat dibuka secara lokal di browser untuk menjalankan contoh di atas.

Contoh bekerja dengan biquaternions

Kesimpulan


Anda mungkin memiliki pertanyaan: mengapa menggunakan alat matematika yang begitu rumit ketika Anda bisa bertahan dengan alat standar untuk memindahkan dan memutar objek? Salah satu keuntungan utama adalah bahwa bentuk penulisan biquaternion lebih efisien secara komputasi, karena semua operasi dengan biquaternions setelah ekspresi diperluas adalah linier. Video ini, Geometric Skinning dengan Approximate Dual Quaternion Blending, menunjukkan betapa jauh lebih efisien perhitungan biquaternion daripada metode lain.

Saya terutama mengambil informasi tentang penggunaan biquaternion dari sumber-sumber bahasa Inggris.
Dari literatur domestik, saya dapat menyarankan dua buku:

  1. Chelnokov Yuri Nikolaevich. Quaternion dan model biquaternion dan metode mekanika padat dan aplikasinya. Geometri dan kinematika gerak. - karya teoritis monumental.
  2. Gordeev Vadim Nikolaevich. Kuarter dan biquaternion dengan aplikasi dalam geometri dan mekanika. - Ini ditulis dalam bahasa yang lebih dimengerti dan menunjukkan aplikasi dalam tugas membentuk struktur spasial melengkung.

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


All Articles