feat: add simulations
This commit is contained in:
316
page.html
Normal file
316
page.html
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
<!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>
|
||||||
249
page2.html
Normal file
249
page2.html
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
<!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: 800px;
|
||||||
|
margin: 50px auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
display: block;
|
||||||
|
margin: 20px auto;
|
||||||
|
background: #f9f9f9;
|
||||||
|
}
|
||||||
|
.controls {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 150px 1fr 80px;
|
||||||
|
gap: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
input[type="range"] {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.value {
|
||||||
|
text-align: center;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Система координат с матрицей трансформации 3×3</h1>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
<label for="scale">Масштаб:</label>
|
||||||
|
<input type="range" id="scale" min="0.5" max="3" step="0.1" value="1">
|
||||||
|
<span class="value" id="scaleValue">1.0</span>
|
||||||
|
|
||||||
|
<label for="projX">Проекция X (px):</label>
|
||||||
|
<input type="range" id="projX" min="-1.0" max="1.0" step="0.01" value="0">
|
||||||
|
<span class="value" id="projXValue">0.00</span>
|
||||||
|
|
||||||
|
<label for="projY">Проекция Y (py):</label>
|
||||||
|
<input type="range" id="projY" min="-1.0" max="1.0" step="0.01" value="0">
|
||||||
|
<span class="value" id="projYValue">0.00</span>
|
||||||
|
|
||||||
|
<label for="rotation">Поворот:</label>
|
||||||
|
<input type="range" id="rotation" min="0" max="360" step="1" value="0">
|
||||||
|
<span class="value" id="rotationValue">0°</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<canvas id="canvas" width="600" height="600"></canvas>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const canvas = document.getElementById('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
// Исходные точки квадрата
|
||||||
|
const originalPoints = [
|
||||||
|
[-50, -50, 1],
|
||||||
|
[-25, -50, 1],
|
||||||
|
[0, -50, 1],
|
||||||
|
[25, -50, 1],
|
||||||
|
[50, -50, 1],
|
||||||
|
[50, 50, 1],
|
||||||
|
[-50, 50, 1]
|
||||||
|
];
|
||||||
|
|
||||||
|
// Элементы управления
|
||||||
|
const scaleInput = document.getElementById('scale');
|
||||||
|
const projXInput = document.getElementById('projX');
|
||||||
|
const projYInput = document.getElementById('projY');
|
||||||
|
const rotationInput = document.getElementById('rotation');
|
||||||
|
|
||||||
|
// Отображение значений
|
||||||
|
const scaleValue = document.getElementById('scaleValue');
|
||||||
|
const projXValue = document.getElementById('projXValue');
|
||||||
|
const projYValue = document.getElementById('projYValue');
|
||||||
|
const rotationValue = document.getElementById('rotationValue');
|
||||||
|
|
||||||
|
// Умножение матриц 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 applyMatrix(matrix, point) {
|
||||||
|
const x = matrix[0][0] * point[0] + matrix[0][1] * point[1] + matrix[0][2] * point[2];
|
||||||
|
const y = matrix[1][0] * point[0] + matrix[1][1] * point[1] + matrix[1][2] * point[2];
|
||||||
|
const w = matrix[2][0] * point[0] + matrix[2][1] * point[1] + matrix[2][2] * point[2];
|
||||||
|
|
||||||
|
return [x / w, y / w, 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создание матрицы масштабирования
|
||||||
|
function getScaleMatrix(s) {
|
||||||
|
return [
|
||||||
|
[s, 0, 0],
|
||||||
|
[0, s, 0],
|
||||||
|
[0, 0, 1]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создание матрицы поворота
|
||||||
|
function getRotationMatrix(angle) {
|
||||||
|
const rad = angle * Math.PI / 180;
|
||||||
|
const cos = Math.cos(rad);
|
||||||
|
const sin = Math.sin(rad);
|
||||||
|
return [
|
||||||
|
[cos, -sin, 0],
|
||||||
|
[sin, cos, 0],
|
||||||
|
[0, 0, 1]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создание матрицы проекции
|
||||||
|
function getProjectionMatrix(px, py) {
|
||||||
|
return [
|
||||||
|
[1, 0, 0],
|
||||||
|
[0, 1, 0],
|
||||||
|
[px, py, 1]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Рисование сетки координат
|
||||||
|
function drawGrid() {
|
||||||
|
ctx.strokeStyle = '#ddd';
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
|
||||||
|
// Вертикальные линии
|
||||||
|
for (let x = 0; x <= canvas.width; x += 50) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, 0);
|
||||||
|
ctx.lineTo(x, canvas.height);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Горизонтальные линии
|
||||||
|
for (let y = 0; y <= canvas.height; y += 50) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, y);
|
||||||
|
ctx.lineTo(canvas.width, y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Оси координат
|
||||||
|
ctx.strokeStyle = '#999';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(canvas.width / 2, 0);
|
||||||
|
ctx.lineTo(canvas.width / 2, canvas.height);
|
||||||
|
ctx.moveTo(0, canvas.height / 2);
|
||||||
|
ctx.lineTo(canvas.width, canvas.height / 2);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Рисование четырехугольника
|
||||||
|
function drawQuadrilateral(points) {
|
||||||
|
const centerX = canvas.width / 2;
|
||||||
|
const centerY = canvas.height / 2;
|
||||||
|
|
||||||
|
// Рисуем четырехугольник
|
||||||
|
ctx.fillStyle = 'rgba(66, 153, 225, 0.3)';
|
||||||
|
ctx.strokeStyle = '#2c5aa0';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(centerX + points[0][0], centerY - points[0][1]);
|
||||||
|
for (let i = 1; i < points.length; i++) {
|
||||||
|
ctx.lineTo(centerX + points[i][0], centerY - points[i][1]);
|
||||||
|
}
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Рисуем точки
|
||||||
|
ctx.fillStyle = '#e53e3e';
|
||||||
|
points.forEach(point => {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(centerX + point[0], centerY - point[1], 5, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновление сцены
|
||||||
|
function update() {
|
||||||
|
const scale = parseFloat(scaleInput.value);
|
||||||
|
const projX = parseFloat(projXInput.value) / 50;
|
||||||
|
const projY = parseFloat(projYInput.value) / 50;
|
||||||
|
const rotation = parseFloat(rotationInput.value);
|
||||||
|
|
||||||
|
// Обновление отображаемых значений
|
||||||
|
scaleValue.textContent = scale.toFixed(1);
|
||||||
|
projXValue.textContent = projX.toFixed(2);
|
||||||
|
projYValue.textContent = projY.toFixed(2);
|
||||||
|
rotationValue.textContent = rotation + '°';
|
||||||
|
|
||||||
|
// Создание матриц трансформации
|
||||||
|
const scaleMatrix = getScaleMatrix(scale);
|
||||||
|
const rotationMatrix = getRotationMatrix(rotation);
|
||||||
|
const projectionMatrix = getProjectionMatrix(projX, projY);
|
||||||
|
|
||||||
|
// Комбинированная матрица: проекция * поворот * масштаб
|
||||||
|
let transformMatrix = multiplyMatrices(scaleMatrix, rotationMatrix);
|
||||||
|
transformMatrix = multiplyMatrices(transformMatrix, projectionMatrix);
|
||||||
|
|
||||||
|
// Применение трансформации к точкам
|
||||||
|
const transformedPoints = originalPoints.map(point =>
|
||||||
|
applyMatrix(transformMatrix, point)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Отрисовка
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
drawGrid();
|
||||||
|
drawQuadrilateral(transformedPoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обработчики событий
|
||||||
|
scaleInput.addEventListener('input', update);
|
||||||
|
projXInput.addEventListener('input', update);
|
||||||
|
projYInput.addEventListener('input', update);
|
||||||
|
rotationInput.addEventListener('input', update);
|
||||||
|
|
||||||
|
// Начальная отрисовка
|
||||||
|
update();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user