Source: randomVertice.js

/**
 * @module RandomVertice
 * @description Generates random angles between a vertice and its opposite vertice.
 *
 * @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 getHyperSphere from './getHyperSphere.js'

const sRandomVertice = 'RandomVertice',
	sOver = ': Please, override %s method in your ' + sRandomVertice + ' child class.',
	π = Math.PI, acos = Math.acos;
let random = Math.random;
/**
 * Generates random angles between a vertice and its opposite vertice.
 * @class
 */
class RandomVertice {

	/**
	 * Generates a cloud of random vertices around the opposite vertice.
	 * @param {object} params={} The following parameters are available.
	 * @param {float} [params.arc=0] Density distribution of a cloud of random vertices on a hypersphere. arc range from 0 to π.
	 * <pre>
	 *	arc = 0 A cloud of random vertices degenerates into a single point equal to the opposite vertice.
	 *	arc = π A cloud of random vertices is uniformly distributed over the entire surface of the hypersphere.
	 * </pre>
	 * @param {Array} params.oppositeVertice [altitude, latitude, longitude] Opposite vertice position in polar coordinates.
	 * @param {object} params.classSettings See <a href="../../jsdoc/module-HyperSphere-HyperSphere.html" target="_blank">HyperSphere classSettings</a>.
	 * @param {number} [verticesCount=1] count of vertices in the random vertices cloud.
	 */
	constructor(params={}, verticesCount = 1) {

		if (params.classSettings.debug && params.classSettings.debug.random) {

			console.warn(sRandomVertice + ': debug version of the random')
			random = params.classSettings.debug.random;

		}
		if (params.arc === undefined) params.arc = 0;
			
		if (!params.boArcIsdefined) {

			params.boArcIsdefined = true;
			let arc = params.arc;
			Object.defineProperty(params, 'arc', {
	
				get: () => { return arc; },
				set: (arcNew) => {
		
					if (arc === undefined) console.error(sRandomVertice + ': set params.arc. Invalid arc = ' + arc);
					else arc = arcNew;
					this.verticesAngles(true);
					return true;
		
				},
		
			});

		}
		
		params.oppositeVertice ||= this.ZeroArray();

		const _this = this;

		const setVertice = (vertice, name, value) => {
			
			vertice[name] = value;
			switch (name) {

				case 'altitude':
				case 'latitude':
				case 'longitude':
					_this.paramsVerticeOnChange();
					break;
					
			}
			
		}
		params.oppositeVertice = new Proxy( params.oppositeVertice, {

			set: (oppositeVertice, name, value) => {

				setVertice(oppositeVertice, name, value);
				return true;
	
			},
			
		});
		
		if (params.verticesAngles && !params.verticesAngles.boNoNew) params.verticesAngles.length = 0;
		params.verticesAngles ||= [];
		
		this.Center(params);
		Object.defineProperty(this, 'angles', {
			
			get: () => { return this.getAngles(); },
			set: (anglesNew) => { this.setAngles(anglesNew); },
			
		});
		Object.defineProperty(this, 'randomAngles', {

			get: () => { return this.getRandomAngles(); },
			set: (anglesNew) => { },

		});
		this.paramsVerticeOnChange = () => { this.verticesAngles(true); }
		this.verticesAngles = (editAngles = false) => {

			const startingPointParams = this.navigator.startingPointParams();
			for (let i = 0; i < verticesCount; i++) {

				if (editAngles) params.editAnglesId = i;
				this.getRandomAngles(undefined, startingPointParams);

			}
			delete params.editAnglesId;

		};
		this.distance = (distance, R) => {
			
			let arc = params.arc;
			if (arc < 0) arc = 0;//Эта ошибка возникает потому что в GUI переменная arc округляется до 4 знаков
			if (arc === 0) return 0;
			
			/*Есть декартова система координат. Ось x обозначим как arc. Найди элементарную функцию которая неогранниченно растет в точке arc = 0 и равна нулю в точке arc = π.
			arc меняется только в диапазоне от 0 до π. arc это не угол.
			Написать код на javascript
			*/
			//https://chat.deepseek.com/a/chat/s/0b71542a-46a2-47e2-9888-f95f26f0fa37
			const y = 1 / arc - 1 / π;
			
			return distance / (1 + (y * R * random()));
			
		}
		this.paramsVerticesAngles = (angles) => {
			
			if (params.editAnglesId === undefined) {
				
				params.verticesAngles.push(angles);
				params.pointsCount++;
				return;

			}
			params.verticesAngles[params.editAnglesId] = angles;
			params.verticesAngles.needsUpdate;
			
		}
		this.getAngles = () => { return params.verticesAngles; }
		this.setAngles = (anglesNew) => { params.verticesAngles = anglesNew; }
		
		//overridden methods

		this.navigator = { startingPointParams: () => {}, }
		
		/////////////////////////////overridden methods

	}
	circlesPointsCount;
	
	//overridden methods
	
	get angles() { console.error(sRandomCloud + sOver.replace('%s', 'get angles')) }
	get randomAngles() { console.error(sRandomCloud + sOver.replace('%s', 'get randomAngles')) }
	getHyperSphere(classSettings, scene, middleVerticeColor) {
		
		const debug = {
					
				probabilityDensity: false,
				middleVertice: false,
				log: false,
				
			},
			settings = classSettings.settings,
			options = settings.options;
		return getHyperSphere(
			this.HyperSphere,
			options,
			scene,
			this,
			{
				
				debug: debug,
//				r: classSettings.overriddenProperties.r(settings.guiPoints ? settings.guiPoints.timeId : options.player ? options.player.getTimeId() : 0),
				r: classSettings.overriddenProperties.rTime(),
				name: 'Random Cloud'
				
			});
	
	}
	
	/////////////////////////////overridden methods

}
RandomVertice.get = (arc, oppositeVertice, classSettings, RandomVertice) => {
	
	return new RandomVertice({
	
		arc: arc,
		oppositeVertice: oppositeVertice,
		classSettings: classSettings,//используется для вычисления случайной точки в RandomVerticeHSphere HyperSphereNavigator.calculateNewPoint
		
	}).angles[0];
	
}
/**
 *<pre>
 [Deepseek. Гипербола через три точки]{@link https://chat.deepseek.com/a/chat/s/1f8ddb5d-b9de-43d3-aa0d-3cfe49cd48a3}
Есть декартова система координат. Ось x обозначим как arc. Найди элементарную функцию гиперболы которая пересекает три точки:
(arc1 = 0, y1 = 0),
(arc2 = π / 100, y2 = π - π / 100),
(arc3 = π, y3 = π)
 arc меняется только в диапазоне от 0 до π. arc это не угол.
Написать код на javascript.

Tеперь для построения функции для нахождения коэффициентов гиперболы оставим только две крайних точки, первую и третью, уберем среднюю точку,
а вместо нее добавим некоторый параметр p, который будет меняться в диапазоне допустим от 0 до 1.
Если p = 0, то гипербола вырождется в прямую.
Если p = 1, то гипербола вырождется в два отрезка прямых:
   Первый отрезок: (arc1 = 0, y1 = 0), (arc2 = 0, y2 = π) вертикальная прямая из начала координат.
   Второй отрезок: (arc1 = 0, y1 = π), (arc2 = π, y2 = π) горизональная прямая.
Написать код на javascript.
Для визуализации примеров сделать [веб страницу]{@link https://raw.githack.com/anhr/commonNodeJS/master/HyperSphere/Examples/hyperbola.html}, в которой применить библиотеку three.js

 Функция для вычисления коэффициентов гиперболы по параметру p
 * </pre>
 * @param {float} p <pre>Параметр, который меняется в диапазоне от 0 до 1 и который используется для вычисления параметров гиперболы.
Если p = 0, то гипербола вырождется в прямую.
Если p = 1, то гипербола вырождется в два отрезка прямых:
   Первый отрезок: (arc1 = 0, y1 = 0), (arc2 = 0, y2 = π) вертикальная прямая из начала координат.
   Второй отрезок: (arc1 = 0, y1 = π), (arc2 = π, y2 = π) горизональная прямая.
 * </pre>
 */
RandomVertice.calculateHyperbola = (p) => {

	// Крайние случаи
	if (p === 0) {
		// Прямая линия: y = x (через точки (0,0) и (π,π))
		return (arc) => { return arc; }
/*		
		return {
			type: 'line',
			a: 1,
			b: 0,
			equation: 'y = x'
		};
*/		
	}

	if (p === 1) {
		// Два отрезка: вертикальный и горизонтальный
		return (arc) => {

			if (arc === 0 ) return 0
			return π;
			
		}
/*		
		return {
			type: 'segments',
			segments: [
				{ from: { x: 0, y: 0 }, to: { x: 0, y: π } },  // вертикальный
				{ from: { x: 0, y: PI }, to: { x: PI, y: π } } // горизонтальный
			],
			equation: 'Вертикальный и горизонтальный отрезки'
		};
*/		
	}

	// Общий случай: гипербола вида y = a/(x - h) + k
	// У нас есть две точки: (0,0) и (π,π)
	// и дополнительное условие: при x = 0, y = 0
	// при x = π, y = π

	// Из точки (0,0): 0 = a/(0 - h) + k => k = a/h

	// Из точки (π,π): π = a/(π - h) + a/h

	// Вводим параметр p для контроля кривизны
	// Будем использовать p для определения положения асимптоты h
	// При p -> 0: h -> -∞ (гипербола вырождается в прямую)
	// При p -> 1: h -> 0 (гипербола вырождается в вертикальную линию)

	// Свяжем h с p: h = - (1-p)/p * C, где C некоторая константа
	// Чтобы при p=1, h=0; при p=0, h=-∞
	// Возьмем C = π/2 для хорошей визуализации

	const C = π / 2;

	// Избегаем деления на ноль при p=0 (уже обработано выше)
	let h;
	if (p < 0.001) {
		// Для очень малых p используем линейную аппроксимацию
		h = -C / p;
	} else {
		h = -C * (1 - p) / p;
	}

	// Вычисляем a из уравнения π = a/(π - h) + a/h
	// a = π / (1/(π - h) + 1/h) = π / ((h + π - h)/(h(π - h))) = π * h(π - h) / π = h(π - h)
	// Упростим: a = h * (PI - h)

	const a = h * (π - h);

	// Вычисляем k
	const k = a / h;

	return (x) => {

		// Гипербола
//		const { a, h, k } = hyperbola;
	
		// Генерируем точки слева от асимптоты (если асимптота слева от 0)
		// В нашем случае h всегда отрицательное, так что асимптота слева от 0
//		for (let i = 0; i <= numPoints; i++) {
//			const x = (i / numPoints) * PI;
		// Проверяем, что не попадаем в асимптоту
		if (Math.abs(x - h) < 0.001) {

			console.error(sRandomVertice + ': calculateHyperbola. Under consraction');
			return x;
//			continue;

		}
		return a / (x - h) + k;
/*
		const y = a / (x - h) + k;
		// Ограничиваем y для визуализации
		if (isFinite(y) && y >= -1 && y <= PI + 1) {
			points.push(new THREE.Vector3(x, y, 0));
		}
*/			
//		}
		
	}
/*
	return {
		type: 'hyperbola',
		a: a,
		h: h,
		k: k,
		equation: `y = ${a.toFixed(4)}/(x - ${h.toFixed(4)}) + ${k.toFixed(4)}`
	};
*/

}
export default RandomVertice;