Source: myObject.js

/**
 * @module myObject
 * @description Base class for my threejs objects.
 * @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 three from './three.js'
import Player from './player/player.js'

const sMyObject = 'MyObject';

/**
 * @class Base class for my threejs objects.
 * <pre>
 * Examples of child classes:
 *   <a href="../../getShaderMaterialPoints/jsdoc/module-getShaderMaterialPoints-getShaderMaterialPoints.html" target="_blank">getShaderMaterialPoints</a>,
 *   <a href="../../HyperSphere/jsdoc/module-HyperSphere-HyperSphere.html" target="_blank">HyperSphere</a>,
 *   <a href="../../myPoints/jsdoc/module-MyPoints-MyPoints.html" target="_blank">MyPoints</a>,
 *   <a href="../../nD/jsdoc/module-ND-ND.html" target="_blank">ND</a>.
 * </pre>
 */
class MyObject {

	/**
	 * 
	 * @param {object} settings See child class (<a href="../../nD/jsdoc/module-ND-ND.html" target="_blank">ND</a>, <a href="../../myPoints/jsdoc/module-MyPoints-MyPoints.html" target="_blank">MyPoints</a>) <b>settings.
	 * @param {any} vertices
	 */
	constructor(settings={}, vertices) {

		const _this = this;

		//иммитация наследования классов
		//settings.overriddenProperties ||= {};uncompatible with myThree.js → ./build/myThree.js, ./ build / myThree.module.js...
		if (!settings.overriddenProperties) settings.overriddenProperties = {};
		//settings.overriddenProperties.setDrawRange ||= (start, count) => { }uncompatible with myThree.js → ./build/myThree.js, ./ build / myThree.module.js...
		if (!settings.overriddenProperties.setDrawRange) settings.overriddenProperties.setDrawRange = (start, count) => { }
		settings.overriddenProperties.getPlayerTimesLength = () => { return 1; }
		
		if (settings.guiPoints) this.guiPoints = settings.guiPoints;

		settings.object = settings.object || {};
		settings.object.geometry = settings.object.geometry || {};

		this.isSetPosition = settings.isSetPosition;

		const timeId = settings.options ? settings.options.player.getTimeId() : 0, geometry = settings.object.geometry,
			geometryPosition = geometry.position;
		if ((timeId === 0) && (!geometryPosition || !geometryPosition.isPositionProxy)) {

			geometry.position = new Proxy(geometryPosition || [], {
	
				get: (positions, name) => {
					
					const positionId = parseInt(name);
					if (!isNaN(positionId)) {
						
						return new Proxy(positions[positionId], {
	
							set: (position, name, value) => {
	
								const axisId = parseInt(name);
								if (!isNaN(axisId)) {
	
									position[axisId] = value;
									settings.bufferGeometry.userData.position[positionId][axisId] = value;
									return true;
									
								}
								position[name] = value;
								return true;
								
							}
							
						});
	
					
					}
					switch (name) {
	
						case 'isPositionProxy': return true;
	
					}
					return positions[name];
					
				},
				
			});

		}
	
		const THREE = three.THREE;

		if (!settings.bufferGeometry) {
			
			settings.bufferGeometry = new THREE.BufferGeometry();
			Object.defineProperty(settings.bufferGeometry.userData, 'timeId', {

				get: () => { return 0; },
				set: (timeIdNew) => { },//ignore timeIdNew
				configurable: true,//https://stackoverflow.com/a/25518045/5175935

			});
			
		}
		/**
		 * [BufferGeometry]{@link https://threejs.org/docs/index.html?q=BufferGeometry#api/en/core/BufferGeometry} of the child graphical object.
		 */
		this.bufferGeometry = settings.bufferGeometry;

		if (vertices)
			//for for compatibility with ND
			//Что бы можно было менять позицию и цвет вершины
			if (!settings.object.geometry.position) settings.object.geometry.position = vertices;
		
		//Эту строку нельзя использовать потому что во вселенной будет ошибка
		//TypeError: classSettings.overriddenProperties.position0.angles[verticeId].middleVertice is not a function
		//если:
		//Открыть http://localhost/anhr/universe/main/hyperSphere/Examples/ что бы не было видно ребер classSettings.edges.project = true
		//Сделать один шаг проигрывателя: нажать →
		//Сделать ребра невидимыми: Поставить галочку Гиперсфера\Ребро\Отображать.
		//Сделать один шаг проигрывателя: нажать →
		//Это происходить потому что когда проигрыватель находится не в начальном положении timeId > 0, то в settings.object.geometry.position попадают вершины не из начального времени
		//			settings.object.geometry.position = settings.object.geometry.position || vertices;

		/**
		 * Determines the part of the child graphical object geometry to render. See [BufferGeometry.drawRange]{@link https://threejs.org/docs/index.html?q=BufferGeometry#api/en/core/BufferGeometry.drawRange}.
		 * @param {number} start Identifier of the start vertice to render.
		 * @param {number} count Count of vertices to render.
		 */
		this.setVerticesRange = (start, count) => {
			
			const bufferGeometry = settings.bufferGeometry, position = bufferGeometry.attributes.position;
			
			//Когда bufferGeometry.index != null, и отображаются вершины,
			//то в drawRange задается диапазон видимых вершин без учета размера каждой вершины bufferGeometry.attributes.positionю.itemSize
			const itemSize = ((position && (bufferGeometry.index != null)) ? position.itemSize : 1);
			
			this.setDrawRange(start * itemSize, count * itemSize, bufferGeometry.drawRange.types.vertices);//https://threejs.org/docs/index.html?q=BufferGeometry#api/en/core/BufferGeometry.setDrawRange
			
		}
		/**
		 * Determines the part of edges of the child graphical object geometry to render. See [BufferGeometry.drawRange]{@link https://threejs.org/docs/index.html?q=BufferGeometry#api/en/core/BufferGeometry.drawRange}.
		 * @param {number} [start=0] Identifier of the start edge to render.
		 * @param {number} [timeId] Time identifier of the <a href="../../player/jsdoc/module-Player-Player.html" target="_blank">player</a> that determines of the end edge to render.
		 */
		this.setEdgesRange = (start = 0, timeId) => {

			const drawRange = settings.bufferGeometry.drawRange;
			start = start != undefined ? start : drawRange.start;
			const timeEdgesLength = settings.object.geometry.indices[0].timeEdgesCount * 2;
			this.setDrawRange(timeEdgesLength * start, timeEdgesLength * (((timeId != undefined) ? timeId : settings.options.player.getTimeId() + 1) - start), drawRange.types.edges);
		
		}
		/**
		 * Determines the part of vertices or edges of the child graphical object geometry to render. See [BufferGeometry.drawRange]{@link https://threejs.org/docs/index.html?q=BufferGeometry#api/en/core/BufferGeometry.drawRange}.
		 * @param {number} start Identifier of the start vertice or edge to render.
		 * @param {number} count Count of vertices or edges to render.
		 * @param {number} type For debug. 0 - vertices draw range. 1 - edge's draw range.
		 */
		this.setDrawRange = (start, count, type) => {

			if (settings.debug) {
				
				if (type != undefined) settings.bufferGeometry.drawRange.type = type;
				else console.error(sMyObject + ': setDrawRange(...). Invalid type = ' + type);
				if (!Number.isInteger(start) || ((count != Infinity) && !Number.isInteger(count))) console.error(sMyObject + ': setDrawRange(...). Invalid drawRange = { start: ' + start + ', count: ' + count + ' }');
				
			}
			settings.overriddenProperties.setDrawRange(start, count);
		
		}
		const getPlayerTimesLength = () => { return settings.overriddenProperties.getPlayerTimesLength(); }

		//Для отладки
		const setDrawRangeTypes = () => {

			settings.bufferGeometry.drawRange.types = { vertices: 0, edges: 1 };
			//settings.bufferGeometry.drawRange.type = settings.bufferGeometry.drawRange.types.vertices Установлен диапазон видимых вершин
			//settings.bufferGeometry.drawRange.type = settings.bufferGeometry.drawRange.types.edges Установлен диапазон видимых ребер

		}

		const createPositionAttribute = (pointLength, pointsLength) => {

			//https://stackoverflow.com/questions/31399856/drawing-a-line-with-three-js-dynamically/31411794#31411794
			const isRCount = settings.object.geometry.rCount != undefined, MAX_POINTS = isRCount ?
				//резервирую место для вершин, которые появятся по мере проигрывания player.
				//Это случается когда во вселенной вычисляется очередной шаг по времени. Тоесть пользователь нажал ► или →
				pointLength * pointsLength * settings.object.geometry.rCount :
				settings.object.geometry.MAX_POINTS;

			setDrawRangeTypes();
			
			if (MAX_POINTS != undefined) this.setVerticesRange(0, isRCount ?
				pointLength * pointsLength * getPlayerTimesLength()://зарезервировано место для вершин вселенной с разным радиусом
				//Имеются ребра. В этом случае settings.bufferGeometry.drawRange.count определяет количество отображаемых ребер
				//Сейчас ребра еще не созданы. Поэтому settings.bufferGeometry.drawRange будет установлено после вызова this.setDrawRange
				Infinity//pointsLength * 2 - 1
				);
			if (isRCount) settings.bufferGeometry.userData.drawRange = () => { return settings.bufferGeometry.drawRange; }
			const positions = new Float32Array((MAX_POINTS != undefined ? MAX_POINTS : pointsLength) * pointLength);
			settings.bufferGeometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, pointLength));
			settings.bufferGeometry.userData.positionOffsetId = (positionId) => { return this.positionOffsetId(positionId) }
			settings.bufferGeometry.userData.position = new Proxy(settings.bufferGeometry.attributes.position, {
	
				get: (position, name) => {
	
					const positionId = parseInt(name);
					if (!isNaN(positionId)) {

						const positionOffset = this.positionOffset(position, positionId),
							array = position.array;
						const verticeProxy = new Proxy([], {

							get: (vertice, name) => {
								
								const axisId = parseInt(name);
								if (!isNaN(axisId)) {

									if (axisId >= position.itemSize) {
										
										//console.error(sMyObject + ': get position axis failed. Invalid axisId = ' + axisId);
										return;

									}
									return array[positionOffset + axisId];
									
								}
								switch (name) {

									case 'forEach': return (item) => {

										for (let axisId = 0; axisId < position.itemSize; axisId++) item(array[positionOffset + axisId], axisId);
											
									}
									case 'length': return position.itemSize;
									case 'toJSON': return (item) => {

										let res = '[';
										verticeProxy.forEach(axis => { res += axis + ', ' });
										return res.substring(0, res.length-2) + ']';
											
									}
									case 'x': return array[positionOffset + 0];
									case 'y': return array[positionOffset + 1];
									case 'z': return array[positionOffset + 2];
									case 'w': {

										if (position.itemSize < 4) return;
										return array[positionOffset + 3];

									}

									//для вывода углов на консоль
									//Если оставить эту строку, то будет исключение в строке
									//return new Proxy(timeAngles[verticeId]
									//в файле hyperSphere.js потому что в массиве timeAngles нет углов с индексом verticeId
									//Для проверки открыть http://localhost/anhr/universe/main/hyperSphere/Examples/
									//Нажать ► проигрывателя
									case 'angles': return settings.object.geometry.times[vertice.timeId][positionId];
									case 'edges':  return settings.object.geometry.times[0][positionId].edges;
									case 'vector':
										const vector = vertice.vector;
										if (vector) {

											console.error('Under constraction');
											return vector;
											
										}
										{//hide vertice
											//для совместимости с Player.getPoints.
											//Открыть вселенную http://localhost/anhr/universe/main/hyperSphere/Examples/ с отображением ребер classSettings.edges.project != false
											//Сделать один шаг проигрывателя. Появятся ребра для вселенной с новым временем.
											//Убрать галочку "Гиперсфера.Вершины.Ребро.Отображать"classSettings.edges.project = false что бы реьа заменить на вершины
											const vertice = verticeProxy;
											switch(vertice.length){

												case 3: return new THREE.Vector3(vertice[0], vertice[1], vertice[2]);
												case 4: return new THREE.Vector4(vertice[0], vertice[1], vertice[2], vertice[3]);
													
											}
											console.error(sMyObject + ': get vertice.vector failed. Invalid vertice.length = ' + vertice.length);
											
										}
										return vertice[name];
				
								}
								if (_this.getPositionItem) {

									const res = _this.getPositionItem(verticeProxy, name);
									if (res != undefined) return res;

								}
								return vertice[name];
								
							},
							set: (vertice, name, value) => {

								//edit vertice in http://localhost/anhr/commonNodeJS/master/nD/Examples/ for testing
								const axisId = parseInt(name);
								if (!isNaN(axisId)) {

									array[positionOffset + axisId] = value;
									return true;
									
								}
								vertice[name] = value;
								return true;
								
							}
							
						});
						return verticeProxy;
	
					}
					switch (name) {
	
						case 'length': return position.count;
						case 'itemSize': return position.itemSize;
	
					}
					console.error(sMyObject + ': get settings.bufferGeometry.userData.position. Invalid name: ' + name);
					return position[name];
	
				}
	
			});			

			//color
			if (_this.setW) _this.setW();
			const itemSize = settings.object.geometry.opacity ? 4 : 3, colors = new Float32Array((MAX_POINTS != undefined ? MAX_POINTS : pointsLength) * itemSize);
			if (itemSize === 4){

				let colorId = itemSize - 1;
				const opacity = settings.object.geometry.opacity;
				//set opacity
				while(colorId < colors.length) {

					colors[colorId] = opacity;
					colorId += itemSize;
					
				}
			}
			settings.bufferGeometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, itemSize));

		}
		/**
		 * Sets the [BufferGeometry]{@link https://threejs.org/docs/index.html?q=BufferGeometry#api/en/core/BufferGeometry} position attribute from <b>points</b> array.
		 * @param {Array} points Points array.
		 * @param {boolean} boCreatePositionAttribute true - replace old position attribute to new if it exists.
		 */
		this.setPositionAttributeFromPoints = (points, boCreatePositionAttribute) => {

			const bufferAttributes = settings.bufferAttributes, bufferGeometry = settings.bufferGeometry;
			if (bufferAttributes) {

				Object.keys(bufferAttributes).forEach((key) => {

					if (bufferGeometry.attributes[key]) console.error(sMyObject + '.setPositionAttributeFromPoints: Duplicated attribute: ' + key);
					bufferGeometry.setAttribute(key, bufferAttributes[key]);
					
				} );
				settings.overriddenProperties.setTracesIndices(bufferGeometry);
				setDrawRangeTypes();
				return;
				
			}
			if (boCreatePositionAttribute) delete bufferGeometry.attributes.position;
			if (!bufferGeometry.attributes.position) {
				
				createPositionAttribute(
					this.pointLength ? this.pointLength() :
						points[0].w === undefined ? 3 : 4,
					points.length);
				const boLog = this.classSettings && (this.classSettings.debug != undefined) && (this.classSettings.debug.log != false);
				for (let timeId = 0; timeId < getPlayerTimesLength(); timeId++) {
					
					if (boLog) console.log('timeId = ' + timeId);
					for (let i = 0; i < points.length; i++) {

						const vertice = this.setPositionAttributeFromPoint(i, undefined, timeId);
						if (boLog) console.log('\tvertice[' + i + '] = ' + JSON.stringify(vertice));

					}
					if (boLog) console.log('');

				}

			}
			return bufferGeometry;
			
		}
		/**
		 * Vertice color.
		 * @param {number} i Vertice identifier.
		 * @param {Array} vertice Vertice axis array.
		 * @param {number} timeId Time identifier of the <a href="../../player/jsdoc/module-Player-Player.html" target="_blank">player</a> that determines current vertice color.
		 */
		this.verticeColor = (i, vertice, timeId) => {

			const colors = settings.object.geometry.colors;
			if (colors) {
				
				const colorsId = i * 3;
				if (colors[colorsId] != undefined)
					return [colors[colorsId], colors[colorsId + 1], colors[colorsId + 2]];
				
			}
			const color = settings.object.color;
			if (typeof color === "function") return color(timeId);
			if ((color != undefined) && (typeof color != 'object')) return new THREE.Color(_this.color());
			//Вершина не имеет 4 координаты. Установить цвет вершины по умолчанию
			const getDefaultColor = () => { return new THREE.Color(_this.color()); }
			let w
			if (vertice) w = vertice.w;
			else if (!_this.getPoint) {

				const position = _this.bufferGeometry.attributes.position;
				if (position.itemSize != 4) return getDefaultColor();
				w = new THREE.Vector4().fromBufferAttribute(position, i).w;

			}
			else w = _this.getPoint(i).w;
			if (w === undefined) return getDefaultColor();
			return w;

		}
		/**
		 * Gets a position data.
		 * @param {number} i Vertice identifier for <b>timeId</b> = 0.
		 * @param {number} timeId Time identifier of the <a href="../../player/jsdoc/module-Player-Player.html" target="_blank">player</a> that determines current position data.
		 * @return position data object. Object properties:
		 * <pre>
		 *   verticeId: Vertice identifier for current <b>timeId</b>.
		 *   itemSize: Size of the position axis array.
		 *   positionBlockLength: Positions count for current time.
		 *   positionId: <b>verticeId</b> * <b>itemSize</b>.
		 * <pre>
		 */
		this.getPositionData = (i, timeId) => {

			//Во вселенной, когда пользователь щелкает по вершине, то индекс вершины i будет равен положению вершины в attributes.position
			//а должен быть равен индексу вершины для текущего времени.
			if (_this.guiPoints && _this.guiPoints.verticeId) {
				
				i = _this.guiPoints.verticeId;
				timeId = _this.guiPoints.timeId;

			}
			i = parseInt(i);
			
			if (timeId === undefined) timeId = 0;
			if (i === undefined) console.error(sMyObject + '.getPositionData. Invalid i = ' + i);
			const userData = settings.bufferGeometry.userData,
				positionBlockLength = userData.positionBlockLength === undefined ? 0 : userData.positionBlockLength,
				itemSize = settings.bufferGeometry.attributes.position.itemSize,
				verticeId = positionBlockLength * timeId + i;
			return {

				verticeId: verticeId,
				itemSize: itemSize,
				positionBlockLength: positionBlockLength,
				positionId: verticeId * itemSize,

			}
			
		}
		/**
		 * Sets the [BufferGeometry]{@link https://threejs.org/docs/index.html?q=BufferGeometry#api/en/core/BufferGeometry} position attribute from <b>vertice</b>.
		 * @param {number} i Vertice identifier for <b>timeId</b> = 0.
		 * @param {Array} [vertice] Vertice axis array for current <b>timeId</b>.
		 * @param {number} timeId Time identifier of the <a href="../../player/jsdoc/module-Player-Player.html" target="_blank">player</a> that determines current vertice.
		 */
		this.setPositionAttributeFromPoint = (i, vertice, timeId) => {

			//Position attribute
			
			vertice = vertice || _this.getPoint(i, timeId);
			const attributes = settings.bufferGeometry.attributes, positionData = this.getPositionData(i, timeId),
				positionBlockLength = positionData.positionBlockLength;
			let itemSize = positionData.itemSize, positionId = positionData.positionId, array = attributes.position.array;
			                  array [positionId] = vertice.x != undefined ? vertice.x : vertice[0] != undefined ? vertice[0] : 0;
			if (itemSize > 1) array [++positionId] = vertice.y != undefined ? vertice.y : vertice[1] != undefined ? vertice[1] : 0;
			if (itemSize > 2) array [++positionId] = vertice.z != undefined ? vertice.z : vertice[2] != undefined ? vertice[2] : 0;
			const w = vertice.w;
			if (itemSize > 3) array [++positionId] = w;

			const drawRange = settings.bufferGeometry.drawRange;
			if ((drawRange.count === Infinity) || (((drawRange.start + drawRange.count) * ((settings.bufferGeometry.index === null) ? itemSize : 1)) < positionId)){

				this.setVerticesRange(drawRange.start, (positionId - drawRange.start + 1) / itemSize);
				if (!Number.isInteger(drawRange.count) && (drawRange.count != Infinity)) console.error(sMyObject + '.setPositionAttributeFromPoint failed. Invalid drawRange.count = ' + drawRange.count);

			}

			//Color attribute

			itemSize = attributes.color.itemSize;
			let colorId = i * itemSize + (timeId === undefined ? 0 : positionBlockLength * timeId * itemSize);
			array = attributes.color.array;
			const verticeColor = this.verticeColor(i, vertice, timeId);
			if (typeof verticeColor === 'number'){

				if (settings.options) {
					
					const wScale = settings.options.scales.w;
					Player.setColorAttribute(attributes, i + (timeId === undefined ? 0 : positionBlockLength * timeId), settings.options.palette.toColor(verticeColor, wScale.min, wScale.max));

				}
				colorId += attributes.color.itemSize - 1;
				
			} else if (Array.isArray(verticeColor)) verticeColor.forEach(item => array[colorId++] = item);
			else if (verticeColor instanceof THREE.Color) {

				array [colorId++] = verticeColor.r;
				array [colorId++] = verticeColor.g;
				array [colorId++] = verticeColor.b;
				
			}
			else console.error(sMyObject + '.setPositionAttributeFromPoint: Invalid verticeColor = ' + verticeColor);

			//opacity
			if (attributes.color.itemSize > 3) this.verticeOpacity(i);

			return vertice;
			
		}
		/**
		 * Vertice opacity.
		 * @param {number} i Vertice identifier.
		 * @param {Boolean} [transparent] See [transparent]{@link https://threejs.org/docs/index.html?q=LineBasicMaterial#api/en/materials/Material.transparent}.
		 * @param {number} [opacity] See [opacity]{@link https://threejs.org/docs/index.html?q=LineBasicMaterial#api/en/materials/Material.opacity}.
		 */
		this.verticeOpacity = (i, transparent, opacity) => {

			const color = settings.bufferGeometry.attributes.color;
			if (color.itemSize != 4) {

				console.error(sMyObject + '.verticeOpacity: Invalid color.itemSize = ' + color.itemSize);
				return;

			}
			const array = color.array;
			const verticeOpacity = settings.object.geometry.opacity ? settings.object.geometry.opacity[i] : undefined;
			array[color.itemSize * i + 3] = transparent ? opacity : verticeOpacity === undefined ? 1 : verticeOpacity;
			color.needsUpdate = true;

		}
		/**
		 * Opacity for all vertices.
		 * @param {Array} [transparent] See [transparent]{@link https://threejs.org/docs/index.html?q=LineBasicMaterial#api/en/materials/Material.transparent}.
		 * @param {number} [opacity] See [opacity]{@link https://threejs.org/docs/index.html?q=LineBasicMaterial#api/en/materials/Material.opacity}.
		 */
		this.verticesOpacity = (transparent, opacity) => {
			
			const color = settings.bufferGeometry.attributes.color;
			if ( color && ( color.itemSize > 3 ) ) {

				for ( let i = 0; i < color.count; i++ ) { this.verticeOpacity(i, transparent, opacity); }

			} else {

				const object3D = _this.object3D;
				if (object3D) {
					
					object3D.material.transparent = transparent;
					object3D.material.opacity = transparent ? opacity : 1;
					object3D.material.needsUpdate = true;//for THREE.REVISION = "145dev"

				} else console.error(sMyObject + '.verticesOpacity: Invalid object3D');
				
			}
	
		}
		/**
		 * @return The child graphical object color or <a href="../../player/jsdoc/module-Player-Player.html" target="_blank">player</a> time if graphical object color is not defined.
		 */
		this.color = () => {
	
			const color = settings.object.color != undefined ? settings.object.color : settings.pointsOptions != undefined ? settings.pointsOptions.color : undefined;
			return (color != undefined) ? 
				(typeof color === "function") ? color() : color :
				this.defaultColor;//'lime';
		
		}
		
	}
	
	//base methods

	/**
	 * Returns 'white'.
	 */
	get defaultColor() { return 'white'; }
	/**
	 * Returns <b>true</b> if a child graphical object can be transparent.
	 */
	get isOpacity() {

		if (this.bufferGeometry.attributes.color && (this.bufferGeometry.attributes.color.itemSize > 3)) {
			
			if (!this.object3D.material.transparent) console.error(sMyObject + '.isOpacity: invalid this.object3D.material.transparent = ' + this.object3D.material.transparent);
			return true;

		}
		return false;
	
	}
	/**
	 * @param {number} positionId Position identifier for start time.
	 * @returns Offset of the position identifier for current time.
	 */
	positionOffsetId(positionId) {

		const settings = this.classSettings.settings;
		return settings.bufferGeometry.userData.timeId * settings.object.geometry.angles.length + positionId;
		
	}
	/**
	 * @param {object} position [BufferGeometry]{@link https://threejs.org/docs/index.html?q=BufferGeometry#api/en/core/BufferGeometry} <b>position</b> attribute of the child graphical object.
	 * @param {number} positionId Position identifier for start time.
	 * @returns Offset of the position in the <b>position</b> attribute for current time.
	 */
	positionOffset(position, positionId) {

		const settings = this.classSettings.settings;
		return this.positionOffsetId(positionId) * position.itemSize;
//		return (settings.bufferGeometry.userData.timeId * settings.object.geometry.angles.length + positionId) * position.itemSize;
		
	}

}
export default MyObject;