Files
autopilot/page.html
2026-01-05 11:37:25 +05:00

317 lines
11 KiB
HTML

<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Симулятор проекции дрона</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
}
.container {
display: flex;
gap: 30px;
align-items: flex-start;
}
.canvas-wrapper {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
canvas {
border: 2px solid #333;
display: block;
}
.controls {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
min-width: 300px;
}
.control-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #333;
}
input[type="range"] {
width: 100%;
margin-bottom: 5px;
}
.value-display {
text-align: right;
color: #666;
font-size: 14px;
}
h1 {
text-align: center;
color: #333;
}
.info {
background: #e3f2fd;
padding: 10px;
border-radius: 4px;
margin-top: 20px;
font-size: 14px;
}
</style>
</head>
<body>
<h1>Симулятор проекции камеры дрона</h1>
<div class="container">
<div class="canvas-wrapper">
<canvas id="canvas" width="720" height="720"></canvas>
</div>
<div class="controls">
<div class="control-group">
<label for="yaw">Поворот (Yaw):</label>
<input type="range" id="yaw" min="-180" max="180" value="0" step="1">
<div class="value-display"><span id="yaw-value">0</span>°</div>
</div>
<div class="control-group">
<label for="pitch">Тангаж (Pitch):</label>
<input type="range" id="pitch" min="-90" max="90" value="0" step="0.1">
<div class="value-display"><span id="pitch-value">0</span>°</div>
</div>
<div class="control-group">
<label for="roll">Крен (Roll):</label>
<input type="range" id="roll" min="-90" max="90" value="0" step="0.1">
<div class="value-display"><span id="roll-value">0</span>°</div>
</div>
<div class="control-group">
<label for="height">Высота (масштаб):</label>
<input type="range" id="height" min="0.5" max="1.5" value="1" step="0.01">
<div class="value-display"><span id="height-value">1.00</span></div>
</div>
<div class="info">
<strong>Координаты углов:</strong>
<div id="coordinates" style="margin-top: 10px; font-family: monospace; font-size: 12px;"></div>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/14.8.1/math.js"></script>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Элементы управления
const yawSlider = document.getElementById('yaw');
const pitchSlider = document.getElementById('pitch');
const rollSlider = document.getElementById('roll');
const heightSlider = document.getElementById('height');
const yawValue = document.getElementById('yaw-value');
const pitchValue = document.getElementById('pitch-value');
const rollValue = document.getElementById('roll-value');
const heightValue = document.getElementById('height-value');
const coordinatesDiv = document.getElementById('coordinates');
// Исходные углы изображения 1000x1000
const sourceCorners = [
[-500, -500, 0], // Верхний левый
[500, -500, 0], // Верхний правый
[500, 500, 0], // Нижний правый
[-500, 500, 0] // Нижний левый
];
// Функция умножения матриц
function multiplyMatrixVector(matrix, vector) {
return [
matrix[0][0] * vector[0] + matrix[0][1] * vector[1] + matrix[0][2] * vector[2],
matrix[1][0] * vector[0] + matrix[1][1] * vector[1] + matrix[1][2] * vector[2],
matrix[2][0] * vector[0] + matrix[2][1] * vector[1] + matrix[2][2] * vector[2]
];
}
// Создание матрицы вращения вокруг X (крен)
function rotationX(angle) {
const rad = angle * Math.PI / 180;
const c = Math.cos(rad);
const s = Math.sin(rad);
return [
[1, 0, 0],
[0, c, -s],
[0, s, c]
];
}
// Создание матрицы вращения вокруг Y (тангаж)
function rotationY(angle) {
const rad = angle * Math.PI / 180;
const c = Math.cos(rad);
const s = Math.sin(rad);
return [
[c, 0, s],
[0, 1, 0],
[-s, 0, c]
];
}
// Создание матрицы вращения вокруг Z (поворот)
function rotationZ(angle) {
const rad = angle * Math.PI / 180;
const c = Math.cos(rad);
const s = Math.sin(rad);
return [
[c, -s, 0],
[s, c, 0],
[0, 0, 1]
];
}
// Умножение двух матриц 3x3
function multiplyMatrices(a, b) {
const result = [[0,0,0], [0,0,0], [0,0,0]];
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
for (let k = 0; k < 3; k++) {
result[i][j] += a[i][k] * b[k][j];
}
}
}
return result;
}
function inv(a) {
return math.inv(math.matrix(a))._data;
}
// Проекция 3D точки на 2D плоскость
function project3Dto2D(point, height) {
const focalLength = 360;
const distance = 1000 * height;
const z = point[2] + distance;
// if (z <= 0) z = 0.01; // Избегаем деления на ноль
const x = 360 + (focalLength * point[0]) / z;
const y = 360 + (focalLength * point[1]) / z;
return [x, y];
}
// Главная функция отрисовки
function render() {
const yaw = parseFloat(yawSlider.value);
const pitch = parseFloat(pitchSlider.value);
const roll = parseFloat(rollSlider.value);
const height = parseFloat(heightSlider.value);
// Обновление отображаемых значений
yawValue.textContent = yaw;
pitchValue.textContent = pitch.toFixed(1);
rollValue.textContent = roll.toFixed(1);
heightValue.textContent = height.toFixed(2);
// Создание комбинированной матрицы вращения
const Rz = rotationZ(yaw);
const Ry = rotationY(pitch);
const Rx = rotationX(roll);
// R = Rz * Ry * Rx
const R_temp = multiplyMatrices(Rz, Ry);
const _R = multiplyMatrices(R_temp, Rx);
_R[0][2] = _R[1][2] = 0;
_R[2][2] = 1;
const R = inv(_R);
// Трансформация углов
const transformedCorners = sourceCorners.map(corner => {
const rotated = multiplyMatrixVector(R, corner);
const scaled = [
rotated[0],
rotated[1],
rotated[2]
];
return project3Dto2D(scaled, height);
});
// Очистка canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Рисование сетки для справки
ctx.strokeStyle = '#e0e0e0';
ctx.lineWidth = 1;
for (let i = 0; i <= 720; i += 90) {
ctx.beginPath();
ctx.moveTo(i, 0);
ctx.lineTo(i, 720);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(0, i);
ctx.lineTo(720, i);
ctx.stroke();
}
// Рисование центральной точки
ctx.fillStyle = '#ff0000';
ctx.beginPath();
ctx.arc(360, 360, 5, 0, Math.PI * 2);
ctx.fill();
// Рисование четырёхугольника
ctx.strokeStyle = '#0066cc';
ctx.fillStyle = 'rgba(0, 102, 204, 0.2)';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(transformedCorners[0][0], transformedCorners[0][1]);
for (let i = 1; i < transformedCorners.length; i++) {
ctx.lineTo(transformedCorners[i][0], transformedCorners[i][1]);
}
ctx.closePath();
ctx.fill();
ctx.stroke();
// Рисование углов
ctx.fillStyle = '#ff6600';
transformedCorners.forEach((corner, i) => {
ctx.beginPath();
ctx.arc(corner[0], corner[1], 6, 0, Math.PI * 2);
ctx.fill();
// Подписи углов
ctx.fillStyle = '#333';
ctx.font = '12px Arial';
ctx.fillText(`${i + 1}`, corner[0] + 10, corner[1] - 10);
ctx.fillStyle = '#ff6600';
});
// Вывод координат
let coordText = '';
transformedCorners.forEach((corner, i) => {
coordText += `Угол ${i + 1}: (${corner[0].toFixed(1)}, ${corner[1].toFixed(1)})<br>`;
});
coordinatesDiv.innerHTML = coordText;
}
// Обработчики событий
yawSlider.addEventListener('input', render);
pitchSlider.addEventListener('input', render);
rollSlider.addEventListener('input', render);
heightSlider.addEventListener('input', render);
// Начальная отрисовка
render();
</script>
</body>
</html>