This commit is contained in:
2026-10-06 10:28:24 +03:00
parent 11b6ac9433
commit 9afbc04a7d
6 changed files with 1218 additions and 103 deletions
+189 -39
View File
@@ -2,13 +2,15 @@ const canvas = document.getElementById('graphCanvas');
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
const center = width / 2; // середина — точка (0,0) (150px)
const R_PIXELS = 100; // Длина R в пикселях на канвасе для отрисовки
const center = width / 2;
// Элементы формы
const form = document.getElementById('point-form');
const yInput = document.getElementById('y-input');
const yError = document.getElementById('y-error');
const funcInput = document.getElementById('func-input');
const funcError = document.getElementById('func-error');
const previewFuncBtn = document.getElementById('preview-func-btn');
const rHidden = document.getElementById('r-hidden');
const rError = document.getElementById('r-error');
const rButtons = document.querySelectorAll('.r-button');
@@ -22,6 +24,8 @@ const clearHistoryBtn = document.getElementById('clear-history-btn');
const STORAGE_KEY = 'web_lab1_history';
let history = [];
let currentR = null;
let formSubmitted = false;
let customFunction = null;
try {
const rawData = JSON.parse(localStorage.getItem(STORAGE_KEY));
@@ -61,22 +65,71 @@ rButtons.forEach(btn => {
btn.classList.add('active');
rHidden.value = btn.getAttribute('data-val');
currentR = parseFloat(rHidden.value);
rError.textContent = '';
if (formSubmitted) {
rError.textContent = '';
}
validateForm();
drawGraph();
});
});
checkboxes.forEach(cb => {
cb.addEventListener('change', validateForm);
cb.addEventListener('change', () => {
if (formSubmitted) {
globalError.textContent = '';
}
validateForm();
});
});
yInput.addEventListener('input', () => {
validateY();
if (formSubmitted) {
yError.textContent = '';
}
validateForm();
});
funcInput.addEventListener('input', () => {
if (formSubmitted) {
funcError.textContent = '';
}
validateForm();
});
previewFuncBtn.addEventListener('click', () => {
const funcStr = funcInput.value.trim();
if (funcStr === '') {
customFunction = null;
funcError.textContent = '';
drawGraph();
return;
}
if (!currentR) {
funcError.textContent = 'Сначала выберите значение R';
return;
}
try {
customFunction = parseFunction(funcStr);
funcError.textContent = '';
drawGraph();
} catch (e) {
funcError.textContent = e.message;
customFunction = null;
}
});
function checkHit(x, y, r) {
if (customFunction) {
try {
return customFunction(x, y, r);
} catch (e) {
return false;
}
}
// Первая четверть (X >= 0, Y >= 0): Четверть круга (R)
if (x >= 0 && y >= 0) {
return (x * x + y * y) <= (r * r);
@@ -97,42 +150,95 @@ function checkHit(x, y, r) {
return false;
}
function validateY() {
function parseFunction(funcStr) {
if (!funcStr || funcStr.trim() === '') {
return null;
}
const normalized = funcStr.trim()
.replace(/abs\(/g, 'Math.abs(')
.replace(/sin\(/g, 'Math.sin(')
.replace(/cos\(/g, 'Math.cos(')
.replace(/tan\(/g, 'Math.tan(')
.replace(/sqrt\(/g, 'Math.sqrt(')
.replace(/\^/g, '**');
const allowedChars = /^[xyrRMathabsincoqrt.()+\-*/<>=\s\d.]+$/;
if (!allowedChars.test(normalized)) {
throw new Error('Недопустимые символы в функции');
}
try {
const func = new Function('x', 'y', 'r', 'R', `return ${normalized};`);
func(0, 0, 1, 1);
return (x, y, r) => func(x, y, r, r);
} catch (e) {
throw new Error('Синтаксическая ошибка в функции');
}
}
function validateFunction(showErrors = false) {
const funcStr = funcInput.value.trim();
if (funcStr === '') {
return true;
}
try {
parseFunction(funcStr);
return true;
} catch (e) {
if (showErrors) {
funcError.textContent = e.message;
}
return false;
}
}
function validateY(showErrors = false) {
const valStr = yInput.value.trim().replace(',', '.');
if (valStr === '') {
yError.textContent = 'Поле Y обязательно к заполнению.';
if (showErrors) {
yError.textContent = 'Поле Y обязательно к заполнению.';
}
return false;
}
const y = Number(valStr);
if (isNaN(y)) {
yError.textContent = 'Y должно быть числом (буквы недопустимы).';
if (showErrors) {
yError.textContent = 'Y должно быть числом (буквы недопустимы).';
}
return false;
}
if ((y < -3 || y > 5) && y !== 81) {
yError.textContent = 'Значение Y должно принадлежать диапазону от -3 до 5 включительно.';
if (showErrors) {
yError.textContent = 'Значение Y должно принадлежать диапазону от -3 до 5 включительно.';
}
return false;
}
yError.textContent = '';
return true;
}
function validateForm() {
function validateForm(showErrors = false) {
let isValid = true;
if (!validateY()) isValid = false;
if (!validateY(showErrors)) isValid = false;
if (!validateFunction(showErrors)) isValid = false;
const validRValues = [1, 2, 3, 4, 5];
if (!rHidden.value) {
rError.textContent = 'Необходимо выбрать значение R.';
if (showErrors) {
rError.textContent = 'Необходимо выбрать значение R.';
}
isValid = false;
} else if (!validRValues.includes(parseFloat(rHidden.value))) {
rError.textContent = 'Обнаружена подмена значения R!';
if (showErrors) {
rError.textContent = 'Обнаружена подмена значения R!';
}
isValid = false;
} else {
rError.textContent = '';
}
const validXValues = [-2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2];
@@ -149,13 +255,15 @@ function validateForm() {
});
if (!xChecked) {
globalError.textContent = 'Выберите хотя бы одно значение X.';
if (showErrors) {
globalError.textContent = 'Выберите хотя бы одно значение X.';
}
isValid = false;
} else if (xSpoofed) {
globalError.textContent = 'Ошибка: передано недопустимое значение X!';
if (showErrors) {
globalError.textContent = 'Ошибка: передано недопустимое значение X!';
}
isValid = false;
} else {
globalError.textContent = '';
}
submitBtn.disabled = !isValid;
@@ -165,7 +273,9 @@ function validateForm() {
form.addEventListener('submit', (e) => {
e.preventDefault();
if (!validateForm()) return;
formSubmitted = true;
if (!validateForm(true)) return;
const y = parseFloat(yInput.value.trim().replace(',', '.'));
// пасхалка
@@ -181,6 +291,14 @@ form.addEventListener('submit', (e) => {
}
const r = parseFloat(rHidden.value);
const funcStr = funcInput.value.trim();
try {
customFunction = parseFunction(funcStr);
} catch (e) {
funcError.textContent = e.message;
return;
}
// Дата, время и тд
const dateString = new Date().toLocaleString("ru-RU", {
@@ -233,25 +351,57 @@ function renderHistory() {
function drawGraph() {
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = '#3399FF';
const R_PIXELS = currentR ? (100 * currentR / 3) : 100;
if (customFunction && currentR) {
const imageData = ctx.createImageData(width, height);
const data = imageData.data;
for (let py = 0; py < height; py++) {
for (let px = 0; px < width; px++) {
const x = ((px - center) / R_PIXELS) * currentR;
const y = ((center - py) / R_PIXELS) * currentR;
const hit = checkHit(x, y, currentR);
const index = (py * width + px) * 4;
if (hit) {
data[index] = 51;
data[index + 1] = 153;
data[index + 2] = 255;
data[index + 3] = 255;
} else {
data[index] = 255;
data[index + 1] = 255;
data[index + 2] = 255;
data[index + 3] = 255;
}
}
}
ctx.putImageData(imageData, 0, 0);
} else {
ctx.fillStyle = '#3399FF';
// Прямоугольник
ctx.fillRect(center - R_PIXELS, center - R_PIXELS / 2, R_PIXELS, R_PIXELS / 2);
// Прямоугольник
ctx.fillRect(center - R_PIXELS, center - R_PIXELS / 2, R_PIXELS, R_PIXELS / 2);
// Четверть круга
ctx.beginPath();
ctx.moveTo(center, center);
ctx.arc(center, center, R_PIXELS, -Math.PI / 2, 0, false);
ctx.closePath();
ctx.fill();
// Четверть круга
ctx.beginPath();
ctx.moveTo(center, center);
ctx.arc(center, center, R_PIXELS, -Math.PI / 2, 0, false);
ctx.closePath();
ctx.fill();
// Треугольник
ctx.beginPath();
ctx.moveTo(center, center);
ctx.lineTo(center + R_PIXELS, center);
ctx.lineTo(center, center + R_PIXELS / 2);
ctx.closePath();
ctx.fill();
// Треугольник
ctx.beginPath();
ctx.moveTo(center, center);
ctx.lineTo(center + R_PIXELS, center);
ctx.lineTo(center, center + R_PIXELS / 2);
ctx.closePath();
ctx.fill();
}
// Оси
ctx.strokeStyle = '#000';
@@ -306,8 +456,8 @@ function drawGraph() {
if (currentR && currentR > 0) {
history.forEach(rec => {
let drawX = center + (rec.x / currentR) * R_PIXELS;
let drawY = center - (rec.y / currentR) * R_PIXELS;
let drawX = center + (rec.x / rec.r) * R_PIXELS;
let drawY = center - (rec.y / rec.r) * R_PIXELS;
ctx.beginPath();
ctx.arc(drawX, drawY, 4, 0, 2 * Math.PI, false);