feat: Добавлена функция для вычисления координат точки на окружности

This commit is contained in:
Ilia Mashkov
2025-11-20 15:25:25 +03:00
parent 5ef223d8d4
commit b84acfc3e7
2 changed files with 220 additions and 0 deletions

View File

@@ -0,0 +1,163 @@
import { CIRCLE_RADIUS } from '@/widgets/TimeFrameSlider/model'
import { calculateCoordinates } from './calculateCoordinates'
describe('calculateCoordinates', () => {
// Тесты на валидацию dotsAmount
describe('Валидация dotsAmount', () => {
it('должен выбросить ошибку, если dotsAmount меньше 2', () => {
expect(() => calculateCoordinates(1, 0)).toThrow(
'Количество точек должно быть от 2 до 6'
)
})
it('должен выбросить ошибку, если dotsAmount равен 0', () => {
expect(() => calculateCoordinates(0, 0)).toThrow(
'Количество точек должно быть от 2 до 6'
)
})
it('должен выбросить ошибку, если dotsAmount отрицательный', () => {
expect(() => calculateCoordinates(-1, 0)).toThrow(
'Количество точек должно быть от 2 до 6'
)
})
it('должен выбросить ошибку, если dotsAmount больше 6', () => {
expect(() => calculateCoordinates(7, 0)).toThrow(
'Количество точек должно быть от 2 до 6'
)
})
})
// Тесты на валидацию dotIndex
describe('Валидация dotIndex', () => {
it('должен выбросить ошибку, если dotIndex отрицательный', () => {
expect(() => calculateCoordinates(4, -1)).toThrow(
'Индекс точки должен быть от 0 до 5'
)
})
it('должен выбросить ошибку, если dotIndex больше 5', () => {
expect(() => calculateCoordinates(6, 6)).toThrow(
'Индекс точки должен быть от 0 до 5'
)
})
it('должен выбросить ошибку, если dotIndex больше или равен dotsAmount', () => {
expect(() => calculateCoordinates(3, 3)).toThrow(
'Индекс точки должен быть меньше количества точек'
)
})
it('должен выбросить ошибку, если dotIndex больше dotsAmount', () => {
expect(() => calculateCoordinates(2, 4)).toThrow(
'Индекс точки должен быть меньше количества точек'
)
})
})
// Тесты на корректные вычисления координат
describe('Корректные вычисления координат', () => {
it('должен вернуть корректные координаты для 2 точек, индекс 0', () => {
const result = calculateCoordinates(2, 0)
expect(result.x).toBeCloseTo(CIRCLE_RADIUS, 5)
expect(result.y).toBeCloseTo(0, 5)
})
it('должен вернуть корректные координаты для 2 точек, индекс 1', () => {
const result = calculateCoordinates(2, 1)
expect(result.x).toBeCloseTo(-CIRCLE_RADIUS, 5)
expect(result.y).toBeCloseTo(0, 5)
})
it('должен вернуть корректные координаты для 4 точек, индекс 0', () => {
const result = calculateCoordinates(4, 0)
expect(result.x).toBeCloseTo(CIRCLE_RADIUS, 5)
expect(result.y).toBeCloseTo(0, 5)
})
it('должен вернуть корректные координаты для 4 точек, индекс 1', () => {
const result = calculateCoordinates(4, 1)
expect(result.x).toBeCloseTo(0, 5)
expect(result.y).toBeCloseTo(CIRCLE_RADIUS, 5)
})
it('должен вернуть корректные координаты для 4 точек, индекс 2', () => {
const result = calculateCoordinates(4, 2)
expect(result.x).toBeCloseTo(-CIRCLE_RADIUS, 5)
expect(result.y).toBeCloseTo(0, 5)
})
it('должен вернуть корректные координаты для 4 точек, индекс 3', () => {
const result = calculateCoordinates(4, 3)
expect(result.x).toBeCloseTo(0, 5)
expect(result.y).toBeCloseTo(-CIRCLE_RADIUS, 5)
})
it('должен вернуть корректные координаты для 6 точек, индекс 0', () => {
const result = calculateCoordinates(6, 0)
expect(result.x).toBeCloseTo(CIRCLE_RADIUS, 5)
expect(result.y).toBeCloseTo(0, 5)
})
it('должен вернуть корректные координаты для 6 точек, индекс 3', () => {
const result = calculateCoordinates(6, 3)
expect(result.x).toBeCloseTo(-CIRCLE_RADIUS, 5)
expect(result.y).toBeCloseTo(0, 5)
})
it('должен вернуть координаты с правильной структурой объекта', () => {
const result = calculateCoordinates(3, 0)
expect(result).toHaveProperty('x')
expect(result).toHaveProperty('y')
expect(typeof result.x).toBe('number')
expect(typeof result.y).toBe('number')
})
})
// Граничные случаи
describe('Граничные случаи', () => {
it('должен работать с минимальным количеством точек (2)', () => {
expect(() => calculateCoordinates(2, 0)).not.toThrow()
expect(() => calculateCoordinates(2, 1)).not.toThrow()
})
it('должен работать с максимальным количеством точек (6)', () => {
expect(() => calculateCoordinates(6, 0)).not.toThrow()
expect(() => calculateCoordinates(6, 5)).not.toThrow()
})
it('должен работать с последним допустимым индексом для каждого количества точек', () => {
expect(() => calculateCoordinates(2, 1)).not.toThrow()
expect(() => calculateCoordinates(3, 2)).not.toThrow()
expect(() => calculateCoordinates(4, 3)).not.toThrow()
expect(() => calculateCoordinates(5, 4)).not.toThrow()
expect(() => calculateCoordinates(6, 5)).not.toThrow()
})
})
// Тесты на математическую корректность
describe('Математическая корректность', () => {
it('должен расположить точки на окружности заданного радиуса', () => {
const result = calculateCoordinates(4, 1)
const distanceFromCenter = Math.sqrt(result.x ** 2 + result.y ** 2)
expect(distanceFromCenter).toBeCloseTo(CIRCLE_RADIUS, 5)
})
it('должен равномерно распределять точки по окружности', () => {
const dotsAmount = 4
const results = []
for (let i = 0; i < dotsAmount; i++) {
results.push(calculateCoordinates(dotsAmount, i))
}
// Проверяем, что все точки на одинаковом расстоянии от центра
const distances = results.map((r) => Math.sqrt(r.x ** 2 + r.y ** 2))
const firstDistance = distances[0]
distances.forEach((distance) => {
expect(distance).toBeCloseTo(firstDistance, 5)
})
})
})
})

View File

@@ -0,0 +1,57 @@
import {
CIRCLE_RADIUS,
FULL_CIRCLE_DEGREES,
HALF_CIRCLE_DEGREES,
} from '@/widgets/TimeFrameSlider/model'
/**
* Интерфейс для координат точки.
*/
export interface DotCoordinates {
/**
* X координата точки.
*/
x: number
/**
* Y координата точки.
*/
y: number
}
/**
* Функция для вычисления координат точки на круге исходя из общего количества точек и индекса текущей точки.
* @param {number} dotsAmount - Количество точек (от 2 до 6).
* @param {number} dotIndex - Индекс текущей точки.
* @returns {DotCoordinates} Координаты точки.
*/
export function calculateCoordinates(
dotsAmount: number,
dotIndex: number
): DotCoordinates {
// Валидация dotsAmount
if (dotsAmount < 2 || dotsAmount > 6) {
throw new Error('Количество точек должно быть от 2 до 6')
}
// Валидация dotIndex
if (dotIndex < 0 || dotIndex > 5) {
throw new Error('Индекс точки должен быть от 0 до 5')
}
// Дополнительная проверка: dotIndex не должен превышать dotsAmount - 1
if (dotIndex >= dotsAmount) {
throw new Error('Индекс точки должен быть меньше количества точек')
}
// Угол для текущей точки (в градусах)
const angle = (FULL_CIRCLE_DEGREES / dotsAmount) * dotIndex
// Конвертация в радианы для тригонометрических функций
const radian = (angle * Math.PI) / HALF_CIRCLE_DEGREES
// Вычисление координат на круге
const x = CIRCLE_RADIUS * Math.cos(radian)
const y = CIRCLE_RADIUS * Math.sin(radian)
return { x, y }
}