250 lines
8.7 KiB
HTML
250 lines
8.7 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: 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>
|