Source: circle.js

/**
 * @module Circle
 * @description 1 dimensional [hypersphere]{@link https://en.wikipedia.org/wiki/N-sphere}.
 * All the vertices form a circle.
 *
 * @author [Andrej Hristoliubov]{@link https://github.com/anhr}
 *
 * @copyright 2011 Data Arts Team, Google Creative Lab
 *
 * @license under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
*/


import HyperSphere from './hyperSphere.js';
import three from '../three.js'
import RandomVertice from './RandomVertice/randomVerticeCircle.js';
//import RandomCloud from './RandomVertice/randomCloudCircle.js';
//import Vertice from './VerticeCircle.js'
import * as utils from './utilsCircle.js'

const sCircle = 'Circle',
	π = Math.PI;

/**
 * 1 dimensional [hypersphere]{@link https://en.wikipedia.org/wiki/N-sphere}.
 * All the vertices form a circle.
 * @class
 * @extends HyperSphere
 */
class Circle extends HyperSphere {

	/**
	 * @param {Options} options See <a href="../../../master/jsdoc/Options/Options.html" target="_blank">Options</a>.
	 * @param {object} [classSettings] <b>Circle</b> class settings. See <a href="./module-HyperSphere-HyperSphere.html" target="_blank">HyperSphere classSettings</a>.
	 **/
	constructor(options, classSettings) {

		super(options, classSettings);
		this.logHyperSphere();

	}

	//base methods

	planesGeometry(){}
	get axes() { return {

			//порядок размещения осей в декартовой системе координат
			//нужно что бы широта двигалась по оси y а долгота вращалась вокруг y
			indices: [1, 0],

		}

	}
	newHyperSphere(options, classSettings) { return new Circle(options, classSettings); }
	get cookieName(){ return 'Circle' + (this.classSettings.cookieName ? '_' + this.classSettings.cookieName : ''); }
	get probabilityDensity(){
		
		return {

			sectorValueName: 'sectorLength',
			sectorValue: (probabilityDensity, i) => {
				
				const sector = probabilityDensity[i], r = this.classSettings.r, hb = sector.hb, ht = sector.ht,
					angle = (hb) => {

						const M = Math.sqrt(r * r - hb * hb);//Прилежащий катет прямоугольного треугольника
							return Math.atan(hb / M);//угол прямоугольного треугольника https://poschitat.online/ugly-pryamougolnogo-treugolnika						
						
					}
				sector[this.probabilityDensity.sectorValueName] = Math.abs(r * (angle(hb) - angle(ht)) * 2);//длинна дуги сектора https://mnogoformul.ru/dlina-dugi#:~:text=%D0%94%D0%BB%D0%B8%D0%BD%D0%B0%20%D0%B4%D1%83%D0%B3%D0%B8%20%D0%BF%D0%BE%D0%BB%D0%BD%D0%BE%D0%B9%20%D0%BE%D0%BA%D1%80%D1%83%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D0%B8%20%D1%80%D0%B0%D0%B2%D0%BD%D0%B0,%2C%20%D0%B3%D0%B4%D0%B5%20r%20%2D%20%D1%80%D0%B0%D0%B4%D0%B8%D1%83%D1%81%20%D0%BE%D0%BA%D1%80%D1%83%D0%B6%D0%BD%D0%BE%D1%81%D1%82%D0%B8.
				return sector[this.probabilityDensity.sectorValueName];
				
			},
			
		}
		
	}
	defaultAngles() { return { count: 3, } }//random triangle
	pushRandomAngle(verticeAngles) { this.pushRandomLongitude(verticeAngles); }
	name(getLanguageCode) {

		//Localization
		
		const lang = {

			name: "Circle",

		};

		const _languageCode = getLanguageCode();

		switch (_languageCode) {

			case 'ru'://Russian language

				lang.name = 'Окружность';

				break;

		}
		return lang.name;
		
	}

	intersection(color) {

		const THREE = three.THREE,
			classSettings = this.classSettings,
			settings = classSettings.settings,
			options = settings.options,
			r = classSettings.r,
			ip = classSettings.intersection.position,//координата сечения
			mesh = new THREE.Line( new THREE.BufferGeometry().setFromPoints( [
				new THREE.Vector3( options.scales.x.min * r, 0, 0 ), new THREE.Vector3( options.scales.x.max * r, 0, 0 )
			] ), new THREE.LineBasicMaterial( { color: color } ) ),
			vectors = settings.object.geometry.position;
		mesh.position.copy(new THREE.Vector3(0, ip * r, 0));

		//длинна дуги
		const angle = (leg) => Math.asin(leg / r),
			ai = angle(ip);//угол наклона точки пересечения окружности с линией сечения
		vectors.forEach(vector => {

			const a = angle(vector.y) - ai;//угол между векторами текущей вершины и точки пересечения
			console.log(a);
		})
		return mesh;
		
	}

	randomVertices(middleVerticeAngles, scene, boCloud = false, boCreateHypersphere = true) {
		
		const classSettings = this.classSettings;
		if (!classSettings.randomArc) return;

		if (!this.params) this.params = {
				
//				oppositeVertice: middleVerticeAngles,
//				arc: this.arc,
				debug: classSettings.debug ? { notRandomVertices: true,} : false,
				classSettings: classSettings,//используется для вычисления случайной точки в RandomVerticeHSphere HyperSphereNavigator.calculateNewPoint
				
			}
		this.params.oppositeVertice = middleVerticeAngles;
		this.params.arc = this.arc;
		
		if (this.randomVertice && (boCloud === false) && (boCreateHypersphere === false)) this.randomVertice.params = this.params;//Делается очередной шаг проигрывателя и это уже не первая точка
		else {
			
//			this.randomVertice = new this.RandomCloud(params);
			if (this.randomVertice) this.randomVertice.paramsVerticeOnChange();//middleVerticeAngles);
			else this.randomVertice = new this.RandomVertice(this.params, 200);

		}
		if (boCreateHypersphere) {
			
			if (!this.hsRandomVertice) this.hsRandomVertice = this.randomVertice.getHyperSphere(classSettings, scene, this.middleVerticeColor);

		}
		
	}

	//Overridden methods from base class

	middlePosition(points, boCloud, boCreateHypersphere) {
	
		const _this = this;
		//https://chat.deepseek.com/a/chat/s/348ed591-765f-4ab2-8924-a3546b62ef24
		/*
		Заданы несколько точек на поверхности окружности в декартовой системе координат. Начало координат в центре окружности.
		Найти точку на поверхности окружности, равноудаленную от заданных точек. Написать код на javascript.
		*/
		/**
		 * Находит точку на окружности, равноудаленную от заданных точек
		 * @param {Array} points - Массив точек вида [{x, y}, {x, y}, ...]
		 * @returns {Object} Точка на окружности {x, y} или null, если решение не найдено
		 */
		function findEquidistantPointOnCircle(points) {

			if (!points || points.length === 0) {
				console.error(sCircle + ': findEquidistantPointOnCircle. No points');
				return null;
			}

/*			
			const settings = _this.classSettings.settings;
			const radius = _this.classSettings.overriddenProperties.r(settings.guiPoints ? settings.guiPoints.timeId : settings.options.player.getTimeId());
*/				
			const radius = _this.r;
			
			/*
			// 1. Проверяем, что все точки лежат на окружности
			const radius = Math.sqrt(points[0].x * points[0].x + points[0].y * points[0].y);
	
			for (let i = 1; i < points.length; i++) {
				const r = Math.sqrt(points[i].x * points[i].x + points[i].y * points[i].y);
				if (Math.abs(r - radius) > 1e-10) {
					console.warn('Не все точки лежат на окружности одного радиуса!');
					return null;
				}
			}
			*/
	
			// 2. Если только одна точка, возвращаем диаметрально противоположную
			if (points.length === 1) {
				console.error(sCircle + ': findEquidistantPointOnCircle. Если только одна точка, возвращаем диаметрально противоположную');
				return {
					x: -points[0].x,
					y: -points[0].y
				};
			}
	
			// 3. Вычисляем среднее направление (центр масс точек)
			let sumX = 0;
			let sumY = 0;
	
			for (const point of points) {
				sumX += point.x;
				sumY += point.y;
			}
	
			const avgX = sumX / points.length;
			const avgY = sumY / points.length;
	
			// 4. Нормализуем вектор среднего направления к длине радиуса
			const length = Math.sqrt(avgX * avgX + avgY * avgY);
			_this.setArc(radius, radius - length);
//			_this.arc = π * (radius - length);

			let middleVertice, middleVerticeAngles;
			
			if (length < 1e-10) {

				//Противоположные вершины расположены на противоположных краях окружности. В этом случае с равной вероятностью средняя вершина может распологаться с одной или с другой половины окружности.
				middleVerticeAngles = utils.angles([((_this.vertice2angles(points[0])[0] + _this.vertice2angles(points[1])[0]) / 2) + (Math.random() > 0.5 ? 0 : π)])
				middleVertice = _this.a2v(middleVerticeAngles, radius);
//				return _this.a2v(_this.getRandomMiddleAngles(points), radius);
				
			} else {
			
				const scale = radius / length;
				const result = {
					x: avgX * scale,
					y: avgY * scale
				};
		
				// 5. Также проверяем противоположную точку (она тоже может быть решением)
				const opposite = {
					x: -result.x,
					y: -result.y
				};
		
				// 6. Выбираем точку с минимальной дисперсией расстояний
				middleVertice = selectBetterPoint(result, opposite, points);
				middleVerticeAngles = _this.vertice2angles(middleVertice);

			}

//			_this.randomVertices(middleVerticeAngles);
			_this.randomVertices(middleVerticeAngles, _this.object3D.parent, boCloud, boCreateHypersphere);
/*			
			const classSettings = _this.classSettings;
			if (classSettings.randomArc) {

				const params = {
						
						//vertice: angles,
						oppositeVertice: middleVerticeAngles,
						arc: _this.arc,
						debug: classSettings.debug ? { notRandomVertices: true,} : false,
						
					}
				if (_this.randomVertice) _this.randomVertice.params = params;
				else {
					
					_this.randomVertice = new _this.RandomCloud(params);
					_this.hsRandomVertice = _this.randomVertice.getHyperSphere(classSettings.settings.options, classSettings, _this.middleVerticeColor);

				}
				
			}
*/			
			return middleVertice;
			
		}

		/**
		 * Выбирает точку с меньшей дисперсией расстояний до заданных точек
		 */
		function selectBetterPoint(point1, point2, points) {
			const variance1 = calculateDistanceVariance(point1, points);
			const variance2 = calculateDistanceVariance(point2, points);
	
			return variance1 <= variance2 ? point1 : point2;
		}

		/**
		 * Вычисляет дисперсию расстояний от точки до всех заданных точек
		 */
		function calculateDistanceVariance(point, points) {
			const distances = points.map(p => {
				const dx = p.x - point.x;
				const dy = p.y - point.y;
				return Math.sqrt(dx * dx + dy * dy);
			});
	
			const mean = distances.reduce((sum, d) => sum + d, 0) / distances.length;
			const variance = distances.reduce((sum, d) => sum + Math.pow(d - mean, 2), 0) / distances.length;
	
			return variance;
		}
		
		/**
		 * Альтернативный метод: решает задачу минимизации методом градиентного спуска
		 */
		/*
		function findEquidistantPointGradient(points, learningRate = 0.01, iterations = 1000) {
			if (!points || points.length === 0) {
				return null;
			}
	
			const radius = Math.sqrt(points[0].x * points[0].x + points[0].y * points[0].y);
	
			// Начинаем со случайной точки на окружности
			let angle = Math.random() * 2 * Math.PI;
			let point = {
				x: radius * Math.cos(angle),
				y: radius * Math.sin(angle)
			};
	
			// Градиентный спуск
			for (let i = 0; i < iterations; i++) {
				// Вычисляем градиент функции стоимости
				let gradX = 0;
				let gradY = 0;
		
				// Вычисляем среднее расстояние
				const distances = points.map(p => {
					const dx = p.x - point.x;
					const dy = p.y - point.y;
					return Math.sqrt(dx * dx + dy * dy);
				});
		
				const meanDistance = distances.reduce((sum, d) => sum + d, 0) / distances.length;
		
				// Вычисляем градиент
				for (let j = 0; j < points.length; j++) {
					const dx = point.x - points[j].x;
					const dy = point.y - points[j].y;
					const distance = distances[j];
			
					if (distance > 1e-10) {
						const diff = distance - meanDistance;
						gradX += diff * (dx / distance);
						gradY += diff * (dy / distance);
					}
				}
		
				// Обновляем точку (двигаемся против градиента)
				point.x -= learningRate * gradX;
				point.y -= learningRate * gradY;
		
				// Проецируем обратно на окружность
				const length = Math.sqrt(point.x * point.x + point.y * point.y);
				point.x = point.x * radius / length;
				point.y = point.y * radius / length;
		
				// Уменьшаем learning rate
				learningRate *= 0.995;
			}
	
			return point;
		}
		*/
		/*
		// Пример использования
		function example() {
			// Создаем тестовые точки на окружности радиуса 5
			const radius = 5;
			const points = [
				{ x: 5, y: 0 },
				{ x: 3, y: 4 },
				{ x: -4, y: 3 },
				{ x: -3, y: -4 }
			];
	
			console.log('Исходные точки:', points);
	
			// Метод 1: через среднее направление
			const result1 = findEquidistantPointOnCircle(points);
			console.log('Метод среднего направления:', result1);
	
			// Метод 2: градиентный спуск
			const result2 = findEquidistantPointGradient(points);
			console.log('Градиентный спуск:', result2);
	
			// Проверяем расстояния
			if (result1) {
				console.log('\nПроверка для метода 1:');
				points.forEach((p, i) => {
					const dx = p.x - result1.x;
					const dy = p.y - result1.y;
					const distance = Math.sqrt(dx * dx + dy * dy);
					console.log(`Расстояние до точки ${i}: ${distance.toFixed(4)}`);
				});
			}
		}

		// Запуск примера
		example();
		*/
		return findEquidistantPointOnCircle(points);

	}
	ZeroArray() { return [0]; }
/*	
	Euler(params) {

		return;//в одномерной гиперсфере углы Эйлена не используются потому что поворот вершины делается на угол params.oppositeVertice.longitude
		
	}
*/	
	Vertice(angles) { return utils.angles(angles); }
	/**
	 * Converts a vertice position to vertice angles.
	 * @param {array} vertice array of the vertice axes
	 * @returns Vertice angles.
	 */
	vertice2angles(vertice) {

/*		
		const x = [],//для разных размерностей гиперсферы координаты вершины расположены в разном порядке в соответствии с this.axes.indices
		for (let index = 0; index < vertice.length; index++) x.push(vertice[this.axes.indices[index]]);
*/
		/*https://gemini.google.com/app/5f9589d755b1c43f
		Задана точка на окружности в декартовой системе координат. Начало координат находится в центре окружности.
		Положение точки обозначить как vertice.x и vertice.y
		Вычислить координаты точки в полярной системе координат.
		Написать код на javascript
		*/
		/**
		 * Преобразует декартовы координаты (x, y) в полярные координаты (r, theta).
		 * Центр окружности предполагается в начале координат (0, 0).
		 *
		 * @param {object|array} vertice - Объект или массив с декартовыми координатами.
		 * @param {number} vertice.x - Координата X точки.
		 * @param {number} vertice.y - Координата Y точки.
		 * @param {object} circle - Circle class instance.
		 * @returns {array} Масcив с полярными координатами: [theta: угол в радианах ]
		 */
		function cartesianToPolar(vertice, circle) {

			if (Array.isArray(vertice) && (vertice.x === undefined))
				vertice = { x: vertice[0], y: vertice[1] }
//				vertice = { x: vertice[circle.axes.indices[0]], y: vertice[circle.axes.indices[1]] }

			// Вычисление угла (theta) в радианах
			// Math.atan2(y, x) корректно обрабатывает все квадранты.
			const theta = Math.atan2(vertice.y, vertice.x);
//			const theta = Math.atan2(vertice.x, vertice.y);

			return utils.angles([theta]);
			
		}

		/*
		// --- Пример использования ---

		const vertice1 = {
			x: 3,
			y: 4
		};

		const polarCoordinates = cartesianToPolar(vertice1);

		console.log(`Декартовы координаты: (${vertice1.x}, ${vertice1.y})`);
		console.log(`Полярные координаты:`);
		console.log(`  Радиус (r): ${polarCoordinates.r}`);
		console.log(`  Угол (theta) в радианах: ${polarCoordinates.theta}`);

		// Для справки: перевод радиан в градусы
		const angleInDegrees = polarCoordinates.theta * (180 / Math.PI);
		console.log(`  Угол (theta) в градусах: ${angleInDegrees}`);


		// --- Дополнительный пример (второй квадрант) ---

		const vertice2 = {
			x: -2,
			y: 2
		};

		const polarCoordinates2 = cartesianToPolar(vertice2);

		console.log("\n-------------------------");
		console.log(`Декартовы координаты: (${vertice2.x}, ${vertice2.y})`);
		console.log(`Полярные координаты:`);
		console.log(`  Радиус (r): ${polarCoordinates2.r}`);
		console.log(`  Угол (theta) в радианах: ${polarCoordinates2.theta}`);
		console.log(`  Угол (theta) в градусах: ${polarCoordinates2.theta * (180 / Math.PI)}`);	
		*/
		return cartesianToPolar(vertice, this);

	}
		
	a2v(angles, r){

		if(r === undefined) console.error(sCircle + ': a2v. r = ' + r);
		/*https://gemini.google.com/app/1d5ef1a7e3d3d45f
		Задана точка на окружности в полярной системе координат. Начало координат находится в центре окружности.
		Положение точки обозначить как
		angles.longitude - долгота в диапазоне от -π до π.
		Радиус окружности обозначить как r.
		Долгота может выходить из заданного диапазона.
		Вычислить координаты точки в декартовой системе координат.
		Написать код на javascript
		*/
		/**
		 * Вычисляет декартовы координаты (x, y) точки,
		 * заданной в полярной системе координат (r, angles.longitude).
		 * Начало координат находится в центре окружности.
		 *
		 * @param {number} r Радиус окружности.
		 * @param {object} angles Объект, содержащий долготу.
		 * @param {number} angles.longitude Долгота в радианах (может выходить за пределы [-π, π]).
		 * @returns {{x: number, y: number}} Объект с декартовыми координатами x и y.
		 */
		function polarToCartesian(r, angles) {
			
			const longitude = angles.longitude;
			if ((longitude === undefined) || isNaN(longitude)) console.error(sCircle + ': a2v. longitude = ' + longitude);

			// Вычисление декартовых координат
			// Math.cos() и Math.sin() корректно обрабатывают углы вне диапазона [-π, π]
			const x = r * Math.cos(longitude);
			const y = r * Math.sin(longitude);

//			return { x: x, y: y };
//			return [ y, x ];
			return [ x, y ];
			
		}

		/*
		// --- Пример использования ---

		const radius = 5;
		const position1 = { longitude: Math.PI / 2 }; // Долгота: π/2 (90 градусов)
		const position2 = { longitude: 3 * Math.PI };   // Долгота: 3π (то же, что π)
		const position3 = { longitude: -0.5 };        // Долгота: -0.5 радиан

		const coords1 = polarToCartesian(radius, position1);
		const coords2 = polarToCartesian(radius, position2);
		const coords3 = polarToCartesian(radius, position3);

		console.log(`\n**Пример 1: r=${radius}, longitude=${position1.longitude} (90°)**`);
		//console.log(`x: ${coords1.x.toFixed(4)}, y: ${coords1.y.toFixed(4)}`);
		console.log(coords1);
		// Ожидаемый результат: x ≈ 0, y = 5

		console.log(`\n**Пример 2: r=${radius}, longitude=${position2.longitude} (540°)**`);
		//console.log(`x: ${coords2.x.toFixed(4)}, y: ${coords2.y.toFixed(4)}`);
		console.log(coords2);
		// Ожидаемый результат: x = -5, y ≈ 0 (3π эквивалентно π)

		console.log(`\n**Пример 3: r=${radius}, longitude=${position3.longitude}**`);
		//console.log(`x: ${coords3.x.toFixed(4)}, y: ${coords3.y.toFixed(4)}`);
		console.log(coords3);
		// Ожидаемый результат: x ≈ 4.7766, y ≈ -2.3971
		*/
//		return polarToCartesian(r, utils.angles(angles));
		return polarToCartesian(r, angles);
		
	}
/*	
	normalizeAngles(angles){

		angles.forEach((verticeAngles) => {

			verticeAngles.longitude = verticeAngles.longitude % (anglesRange.longitude.range);//(2 * Math.PI);
			if (verticeAngles.longitude > anglesRange.longitude.max)
				verticeAngles.longitude -= anglesRange.longitude.range;//2 * Math.PI;
			else if (verticeAngles.longitude < anglesRange.longitude.min)
				verticeAngles.longitude += anglesRange.longitude.range;//2 * Math.PI;
			
		});
		
	}
*/	
	get verticeEdgesLengthMax() { return 2; }//нельзя добавлть новое ребро если у вершины уже 2 ребра
	get dimension() { return 2; }//space dimension
	get verticesCountMin() { return 3; }
/*
	getRandomMiddleAngles(oppositeVertices) {

		return utils.angles([((this.vertice2angles(oppositeVertices[0])[0] + this.vertice2angles(oppositeVertices[1])[0]) / 2) + (Math.random() > 0.5 ? 0 : π)]);
		
	}
*/	
	/**
	 * @param {THREE.Scene} scene [THREE.Scene]{@link https://threejs.org/docs/index.html?q=sce#api/en/scenes/Scene}
	 * @param {Options} options See <a href="../../jsdoc/Options/Options.html" target="_blank">Options</a>.
	 * @param {object} randomVerticesSettings See <b>randomVerticesSettings</b> of the <a href="./module-HyperSphere-RandomVertices.html" target="_blank">RandomVertices</a> class.
	 * @returns new RandomVertices child class.
	 */
	newRandomVertices(scene, options, randomVerticesSettings) { return new RandomVertices(scene, options, randomVerticesSettings); }

	//overridden methods
	
	get RandomVertice() { return RandomVertice; }
//	get RandomCloud() { return RandomCloud; }
	
	/////////////////////////////overridden methods
	
}

class RandomVertices extends HyperSphere.RandomVertices {

	constructor(scene, options, randomVerticesSettings) {

		if (randomVerticesSettings.np === undefined) randomVerticesSettings.np = 2;//Каждая окружность пересекает одномерную гиперсферу в двух точках.
		super(scene, options, randomVerticesSettings);
		this.class = Circle;
	}

	//overridden methods

	getHyperSphere(options, classSettings) {

		let circlesSphere;
		circlesSphere = new Circle(options, classSettings);
		return circlesSphere;

	}
	getArcAngle(vertice, oppositeVertice)
	{
		
		//векторы
		//A=(R,λ1 ) - vertice
		const λ1 = vertice[0];
		//B=(R,λ2 ) - oppositeVertice
		const λ2 = oppositeVertice[0];
		//где
		//λ — долгота (от −180° до 180°),
		const θ = λ1 - λ2;
		if (isNaN(θ)) console.error(sCircle + ': getArcAngle. Invalid θ = ' + θ);
		return θ;
		
	}
	oppositeVertice0() {}
//	antipodeCenter(params, antipodeLatitude) { return [params.oppositeVertice.longitude - π]; }
	zeroArray() { return utils.angles([0]); }
	onePointArea(d, np) {
		//Длинна отрезка одномерной гиперсферы на которой в среднем будет находиться одна случайная точка.
		return d / np;//Длинна отрезка одномерной гиперсферы вычисляем из длинны окружности одномерной гиперсферы, поделенной на количество точек на окружности np
		//d: расстояние между окружностями в радианах при условии, что окружности равномерно расположены на одномерной гиперсфере
		//окружность пересекает одномерную гиперсферу в двух точках
		
	}
	numPoints(d, s) {

		//Внимание! В одномерной гиперсфере окружность вырождается в точку
/*		
		//Для вычисления количества случайных точек numPoints около окружности, расположенной на расстоянии circleDistance радиан
		//я вычисляю высоту шарового пояса между параллелями h и делю ее на s - длинну отрезка одномерной гиперсферы на которой в среднем будет находиться одна случайная точка. См. onePointArea(...)
		const cos = Math.cos,
			h1 = cos(x),//расстояние от текущей окружности до центра шара
			hprev = cos((circleId - 1) * d),//расстояние от предыдущей окружности до центра шара
			h = h1 - hprev;//высота шарового пояса
		return Math.abs(Math.round(h / s));//количество случайных точек около окружности, расположенной на расстоянии circleDistance радиан
*/
		return Math.round(d / s);//количество случайных точек около окружности, расположенной на расстоянии circleDistance радиан
		
	}
/*	
	center(params) {
		
		//center is antipode of the opposite vertice
		//Центр окружностей случайных точек center находится с противоположной от params.oppositeVertice стороны гиперсферы
//		const center = params.randomVertices.antipodeCenter(params, antipodeLatitude);
		const center = [params.oppositeVertice.longitude - π];
		Object.defineProperty(center, 'lng', { get: () => { return center[0]; }, });
		return center;
		
	}
*/	
	getCirclePoint(circleDistance, params) {

		let newLng = params.center.lng - circleDistance * g_sign - (circleDistance === 0 ?
																	π ://расстояние между вершинами гиперсферы params.arc = 0. Нужно переместить точку на противовоположную позицию
																	0);
		
		//Каждая окружность пересекает одномерную гиперсферу в двух точках.
		//Из этих двух точек по очереди выбирается точка справа или слева от заданной долготы params.center.lng
		g_sign *= -1;

		//Normalise angles
		if (newLng > π) newLng -= 2 * π;
		else if (newLng < -π) newLng += 2 * π;
		
		return utils.angles([newLng]);
	
	}
	circlesCount(np) { return 36; }
	getNumPoints(circleDistance, R, dCircleDistance, np) {

		if (dCircleDistance === 0) return NaN;//Расстояние между окружностями равно нулю. Значит расстояние между вершинами гиперсферы params.arc = 0. Возвращаем NaN потому что в этом случае в окружности делаю одну точку.
		return np;
	
	}
//	pointIdErase() { return 0; }
	setCirclesCloud() {
		
		this.setCircles(0);
		this.createCirclesSphere();
	
	}
	setCirclesCloudOnePoint() { this.setCirclesOnePoint() }
//	altitudeDifference() { return undefined }

	/////////////////////////////overridden methods

}

let g_sign = 1;

RandomVertices.ZeroArray = () => { return [0]; }
RandomVertices.getCenter = (params) => {
	
	//center is antipode of the opposite vertice
	//Центр окружностей случайных точек center находится с противоположной от params.oppositeVertice стороны гиперсферы
//		const center = params.randomVertices.antipodeCenter(params, antipodeLatitude);
	const center = [params.oppositeVertice.longitude - π];
	Object.defineProperty(center, 'lng', { get: () => { return center[0]; }, });
	return center;
	
}
RandomVertices.Center = (params) => {

	const center = params.center;
	if (center.length < 1) center.push(0);
	if (center.lng === undefined)
		Object.defineProperty(center, 'lng', {
	
			get: () => { return center[0]; },
			set: (lng) => {
	
				if (center[0] === lng) return true;
				center[0] = lng;
				if (params.randomVertices) params.randomVertices.changeCirclesPoints();
				return true;
	
			},

		});
	const Vertice = (vertice) => {
	
		if (vertice.longitude != undefined) return;
		Object.defineProperty(vertice, 'longitude', {
			
			get: () => { return vertice[0]; },
			set: (longitude) => {
	
				if (vertice[0] === longitude) return true;
				vertice[0] = longitude;
				if (params.randomVertices) params.randomVertices.changeCirclesPoints();
				return true;
	
			},
		
		});
	
	}
	utils.angles(params.vertice);
	utils.angles(params.oppositeVertice);
	
}
Circle.RandomVertices = RandomVertices;

export default Circle;