Artikel " Indikator cakrawala buatan pada kanvas HTML5 " menampilkan kode indikator dengan tata letak volumetrik dari objek yang dikelola berdasarkan penemuan A.P. Plentsov dan N.A. Zakonovoy. ...
Salah satu kelebihan gagasan indikator dengan tata letak volumetrik adalah keefektifannya. Kali ini, format visualisasi horizon buatan yang tidak biasa akan diadaptasi untuk sistem realitas tertambah .
HUD vs HDD
Fitur desain HUD
Penambahan informasi instrumental ke realitas yang diamati sangat berbeda dari indikasi nilai parameter yang biasa. Kekhususan tugas tercermin dalam desain visual HUD . Dalam sistem yang paling kompleks dan kritis ( misalnya , di tempat kerja pilot pesawat), biasanya, indikasi hijau monokrom digunakan dalam desain "garis besar".
Desain minimalis HUD adalah jawaban untuk serangkaian persyaratan sistem yang saling bertentangan. Misalnya, elemen kontur dapat memiliki dimensi sudut yang cukup bagi operator untuk membaca tanpa menghalangi pandangan ruang luar.
Persyaratan solusi
Mari kita tentukan ketentuan utama dari tugas untuk pengembangan kelas indikator horizon buatan:
1. Konstruktor kelas harus memiliki argumen berikut:
- ukuran muka indikator;
- batas nilai gulungan yang ditampilkan;
- batas nilai nada yang ditampilkan.
2. Batas tampilan setiap sudut ditentukan oleh satu nilai, yang tidak boleh dilampaui oleh nilai absolut dari nilai yang ditampilkan. Nilai batas tidak boleh melebihi 90 derajat.
3. Skala nada harus memiliki tujuh tanda numerik untuk sudut dalam derajat. Skala skala harus dioptimalkan saat membuat instance objek, interval nilai yang ditampilkan harus minimal jika kondisi berikut terpenuhi:
- tanda atas dan bawah adalah kelipatan 30;
- nilai maksimum sudut pitch yang diteruskan ke konstruktor tidak melampaui skala, termasuk jika dikalikan -1.
4. Timbangan putar harus memiliki tanda dalam kelipatan 30 derajat di sekeliling seluruh lingkar pelat jam, terlepas dari sudut putar maksimum yang dilaporkan ke perancang. Tanda skala roll harus ditampilkan dengan mempertimbangkan posisi pitch dari tata letak, yaitu, dial harus berputar dalam bidang simetri tempat kerja dengan sudut pitch di sekitar sumbu yang melewati pusat dial.
5. Model kendaraan sebaiknya dibuat dalam bentuk figur datar berbentuk panah. Rasio panjang tata letak dengan lebarnya harus memastikan penggunaan area layar yang rasional. Misalnya, jika skala pitch dibatasi hingga 90 derajat, maka layout harus sekitar setengah lebarnya. Jika skala dibatasi hingga 30 derajat, proporsi tinggi layar yang signifikan tidak lagi digunakan, seperti yang ditunjukkan di sisi kanan diagram.
Untuk menskalakan skala dengan benar dengan jarak yang lebih kecil, Anda perlu mengubah proporsi tata letak.
6. Kelas harus memiliki fungsi update yang menerima nilai sudut roll dan pitch saat ini.
7. Indikator harus berwarna hijau dan garis besar. Jumlah elemen indikator harus sekecil mungkin, hal ini diperlukan untuk memastikan bahwa ikhtisar animasi latar belakang dipertahankan.
Hasil
Anda dapat mengevaluasi indikator yang dihasilkan secara interaktif di halaman github .
Objek dalam contoh ini selalu bergerak dengan ketat searah sumbu longitudinalnya. Dimungkinkan untuk mengatur nilai kecepatan gerakan, sudut roll dan pitch. Gerakan dilakukan hanya pada bidang vertikal, karena nilai sudut heading konstan.
Kode indikator
Kode indikasi horizon buatan ditampilkan di bawah ini. Kelas Attitude menggunakan pustaka three.js .
Kode kelas sikap
class Attitude {
constructor(camera, scene, radius, maxPitch, maxRoll) {
//:
// 30 :
if (maxPitch > 90) maxPitch = 90;
this.maxPitch = maxPitch;
maxPitch /= 30;
maxPitch = Math.ceil(maxPitch) * 30;
//:
if (maxRoll > 90) maxRoll = 90;
this.maxRoll = maxRoll;
// :
let skeletonLength = radius / Math.sin(maxPitch * Math.PI / 180);
// :
let geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(0, 0, -skeletonLength / 4));
geometry.vertices.push(new THREE.Vector3(-radius, 0, 0));
geometry.vertices.push(new THREE.Vector3(0, 0, -skeletonLength));
geometry.vertices.push(new THREE.Vector3(radius, 0, 0));
geometry.vertices.push(new THREE.Vector3(0, 0, -skeletonLength / 4)); //
// :
let material = new THREE.LineBasicMaterial({ color: 0x00ff00, linewidth: 1 });
// :
this.skeleton = new THREE.Line(geometry, material);
scene.add(this.skeleton);
// :
let pitchScaleStep = maxPitch / 3;
let textLabelsPos = [];//
for (let i = 0; i < 7; i++) {
let lineGeometry = new THREE.Geometry();
// :
let leftPoint = new THREE.Vector3(-radius / 10,
skeletonLength * Math.sin((maxPitch - pitchScaleStep * i) * Math.PI / 180),
-skeletonLength * Math.cos((maxPitch - pitchScaleStep * i) * Math.PI / 180));
let rightPoint = new THREE.Vector3();
rightPoint.copy(leftPoint);
rightPoint.x += (radius / 5);
// :
lineGeometry.vertices.push(leftPoint);
lineGeometry.vertices.push(rightPoint);
let line = new THREE.Line(lineGeometry, material);
scene.add(line);
//
let textPos = new THREE.Vector3();
textPos.copy(leftPoint);
textLabelsPos.push(textPos);
}
// :
let rollScaleStep = 30;
this.rollLines = [];
for (let i = 0; i < 12; i++) {
if (i != 3 && i != 9) {//
let lineGeometry = new THREE.Geometry();
// :
lineGeometry.vertices.push(new THREE.Vector3(-Math.cos(
i * rollScaleStep * Math.PI / 180) * radius * 1.1,
Math.sin(i * rollScaleStep * Math.PI / 180) * radius * 1.1,
0));
lineGeometry.vertices.push(new THREE.Vector3(-Math.cos(
i * rollScaleStep * Math.PI / 180) * radius * 0.9,
Math.sin(i * rollScaleStep * Math.PI / 180) * radius * 0.9,
0));
this.rollLines.push(new THREE.Line(lineGeometry, material));
scene.add(this.rollLines[this.rollLines.length - 1]);
}
}
// :
for (let i = 0; i < 7; i++) {
let labelText = document.createElement('div');
labelText.style.position = 'absolute';
labelText.style.width = 100;
labelText.style.height = 100;
labelText.style.color = "Lime";
labelText.style.fontSize = window.innerHeight / 35 + "px";
labelText.innerHTML = Math.abs(maxPitch - pitchScaleStep * i);
let position3D = textLabelsPos[i];
let position2D = to2D(position3D);
labelText.style.top = (position2D.y) * 100 / window.innerHeight - 2 + '%';
labelText.style.left = (position2D.x) * 100 / window.innerWidth - 4 + '%';
document.body.appendChild(labelText);
}
function to2D(pos) {
let vector = pos.project(camera);
vector.x = window.innerWidth * (vector.x + 1) / 2;
vector.y = -window.innerHeight * (vector.y - 1) / 2;
return vector;
}
}
update(roll, pitch) {
// :
if (pitch > this.maxPitch) pitch = this.maxPitch;
if (pitch < -this.maxPitch) pitch = -this.maxPitch;
if (roll > this.maxRoll) roll = this.maxRoll;
if (roll < -this.maxRoll) roll = -this.maxRoll;
// ,
this.skeleton.rotation.z = -roll * Math.PI / 180;
this.skeleton.rotation.x = pitch * Math.PI / 180;
// :
let marksNum = this.rollLines.length;
for (let i = 0; i < marksNum; i++)
this.rollLines[i].rotation.x = pitch * Math.PI / 180;
}
}
Mengurai kode
, .
XOZ,
OZ,
z.
YOZ. z.
Attitude . . , , .
( to2D()), โ add().
. . 3 .
30, 60 90 . - .
radius , skeletonLength maxPitch: , . , maxPitch.
, . , .
, .
, . .
three.js . :
1. , update(), , , . . , โ .
2. , ( ), .
update() :
html. 3D .
YOZ. z.
Attitude . . , , .
constructor(camera, scene, radius, maxPitch, maxRoll){
( to2D()), โ add().
. . 3 .
if (maxPitch > 90) maxPitch = 90;
this.maxPitch = maxPitch;
maxPitch /= 30;
maxPitch = Math.ceil(maxPitch) * 30;
30, 60 90 . - .
let skeletonLength = radius / Math.sin(maxPitch * Math.PI / 180);
radius , skeletonLength maxPitch: , . , maxPitch.
, . , .
, .
let geometry = new THREE.Geometry();
geometry.vertices.push(new THREE.Vector3(0, 0, -skeletonLength / 4));
geometry.vertices.push(new THREE.Vector3(-radius, 0, 0));
geometry.vertices.push(new THREE.Vector3(0, 0, -skeletonLength));
geometry.vertices.push(new THREE.Vector3(radius, 0, 0));
geometry.vertices.push(new THREE.Vector3(0, 0, -skeletonLength / 4));
let material = new THREE.LineBasicMaterial({ color: 0x00ff00, linewidth: 1 });
this.skeleton = new THREE.Line(geometry, material);
scene.add(this.skeleton);
, . .
three.js . :
1. , update(), , , . . , โ .
2. , ( ), .
update() :
- ;
- .
html. 3D .
Kekurangan indikator
Kenalan cepat dengan peragaan interaktif sudah cukup untuk memperhatikan kesulitan dalam membaca bacaan pada sudut absolut yang besar:
- awal penurunan kualitas indikasi gulungan sesuai dengan sudut pitch 75-80 derajat, di mana skala gulungan menjadi sangat dikompresi;
- awal penurunan kualitas indikasi nilai kecil sudut pitch sesuai dengan nilai sudut roll 70-75 derajat, di mana siluet model kehilangan sapuannya;
- indikasi posisi terbalik dari objek dalam solusi yang disajikan tidak termasuk dalam prinsip.
Perlu dicatat bahwa tidak ada indikasi horizon buatan yang berfungsi sempurna di posisi spasial kendaraan mana pun. Solusi yang disajikan dapat dianggap cocok untuk digunakan pada manuver dengan intensitas sedang.