/**
* @module MyThree
* @description I use MyThree in my projects for displaying of my 3D objects in the canvas.
*
* @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
*/
import Player from '../player/player.js';
import MyPoints from '../myPoints/myPoints.js';
import StereoEffect from '../StereoEffect/StereoEffect.js';
//import StereoEffect from 'https://raw.githack.com/anhr/commonNodeJS/master/StereoEffect/StereoEffect.js';
import CanvasMenu from '../canvasMenu/canvasMenu.js';
//import CanvasMenu from 'https://raw.githack.com/anhr/commonNodeJS/master/canvasMenu/canvasMenu.js';
import { getLanguageCode } from '../lang.js';
//import { getLanguageCode } from 'https://raw.githack.com/anhr/commonNodeJS/master/lang.js';
import AxesHelper from '../AxesHelper/AxesHelper.js';
//import AxesHelper from 'https://raw.githack.com/anhr/commonNodeJS/master/AxesHelper/AxesHelper.js';
import AxesHelperGui from '../AxesHelper/AxesHelperGui.js';
//import AxesHelperGui from 'https://raw.githack.com/anhr/commonNodeJS/master/AxesHelper/AxesHelperGui.js';
import OrbitControlsGui from '../OrbitControls/OrbitControlsGui.js';
//import OrbitControlsGui from 'http://localhost/anhr/commonNodeJS/master/OrbitControls/OrbitControlsGui.js';
//import OrbitControlsGui from 'https://raw.githack.com/anhr/commonNodeJS/master/OrbitControls/OrbitControlsGui.js';
import { dat } from '../dat/dat.module.js';
import GuiSelectPoint from '../guiSelectPoint/guiSelectPoint.js';
import { getWorldPosition } from '../getPosition.js';
import { SpriteTextGui } from '../SpriteText/SpriteTextGui.js';
import ColorPicker from '../colorpicker/colorpicker.js';
//import ColorPicker from 'https://raw.githack.com/anhr/commonNodeJS/master/colorpicker/colorpicker.js';
import MoveGroupGui from '../MoveGroupGui.js';
import CameraGui from '../CameraGui.js';
//import CameraGui from 'https://raw.githack.com/anhr/commonNodeJS/master/CameraGui.js';
import { getObjectPosition } from '../getPosition.js';
//https://github.com/mrdoob/stats.js/
//import Stats from '../../../three.js/dev/examples/jsm/libs/stats.module.js';
import FrustumPoints from '../frustumPoints/frustumPoints.js';
import three from '../three.js'
if (typeof dat !== 'undefined')
three.dat = dat;
import FolderPoint from '../folderPoint.js'
/*проверка duplicate THREE
//import * as THREE2 from 'https://threejs.org/build/three.module.js';
import * as THREE2 from '../../../three.js/dev/build/three.module.js';
three.THREE = THREE2;
*/
import Options from '../Options.js'
import pointLight from '../pointLight.js'
function arrayContainersF() {
const array = [];
this.push = function (elContainer) {
array.push(elContainer);
};
this.display = function (elContainer, fullScreen) {
array.forEach(function (itemElContainer) {
itemElContainer.style.display = (itemElContainer === elContainer) || !fullScreen ? 'block' : 'none';
});
};
Object.defineProperties(this, {
/**
* getter
*/
length: {
get: function () {
return array.length;
},
},
});
};
const arrayContainers = new arrayContainersF();
/*
* if you asynhronous creates two or more myThreejs same time, then you will receive the error message:
*
* Uncaught ReferenceError: WEBGL is not defined
*
* For resolving of the issue I have remembers myThreejs parameters in the arrayCreates
* and creates next myThreejs only after creating of previous myThreejs.
*/
var arrayCreates = [];
/**
* @callback createXDobjects
* @param {THREE.Group} group [group]{@link https://threejs.org/docs/index.html?q=Gro#api/en/objects/Group} of objects to which a new XD object will be added
* @param {Options} options See <a href="../../jsdoc/Options/index.html" target="_blank">Options</a>. Followed parameters is allowed.
* @param {THREE.PerspectiveCamera} [options.camera] [PerspectiveCamera]{@link https://threejs.org/docs/index.html#api/en/cameras/PerspectiveCamera}.
* @param {object} [options.frustumPoints] <a href="../../frustumPoints/jsdoc/index.html" target="_blank">FrustumPoints</a> instance.
* @param {object} [options.point] point settings. See <b>options.point</b> parameter of <a href="../../myThree/jsdoc/module-MyThree-MyThree.html" target="_blank">MyThree</a> for details.
* @param {Player} [options.player] <a href="../../Player/jsdoc/index.html" target="_blank">Player</a> instance. Playing of 3D ojbects in my projects.
* @param {GuiSelectPoint} [options.guiSelectPoint] <a href="../../guiSelectPoint/jsdoc/index.html" target="_blank">GuiSelectPoint</a> instance.
* @param {Options.raycaster.EventListeners} [options.eventListeners] <a href="../../jsdoc/Options/Raycaster_EventListeners.html" target="_blank">Options.raycaster.EventListeners</a> instance.
* Mouse events listeners for [Raycaster]{@link https://threejs.org/docs/index.html?q=Raycaster#api/en/core/Raycaster} instance.
* @param {THREE.WebGLRenderer} [options.renderer] [WebGLRenderer]{@link https://threejs.org/docs/index.html#api/en/renderers/WebGLRenderer}
*/
class MyThree {
/**
* @description I use MyThree in my projects for displaying of my 3D objects in the canvas.
* @param {createXDobjects} [createXDobjects] <a href="../../myThree/jsdoc/module-MyThree.html#~createXDobjects" target="_blank">callback</a> creates my 3D objects.
* @param {Object} [options] See <a href="../../jsdoc/Options/Options.html" target="_blank">Options</a>.
* The following options are available:
* @param {HTMLElement|string} [options.elContainer=document.getElementById( "containerDSE" ) or a new div element, child of body] If an HTMLElement, then a HTMLElement, contains a canvas and HTMLElement with id="iframe-goes-in-here" for gui.
* <pre>
* If a string, then is id of the HTMLElement.
* Examples of the <b>elContainer</b>:
* <b><div class="container" id="containerDSE">
* <canvas id="canvas" style="background-color:black"></canvas>
* </div></b>;
* or
* <b><div class="container" id="containerDSE">
* </div></b>
* New canvas is created inside of the div tag.
* </pre>
*
* @param {THREE.PerspectiveCamera} [options.camera] [PerspectiveCamera]{@link https://threejs.org/docs/index.html#api/en/cameras/PerspectiveCamera}.
* @param {THREE.Vector3} [options.camera.position=new THREE.Vector3( 0.4, 0.4, 2 )] camera position.
* @param {THREE.Vector3} [options.camera.scale=new THREE.Vector3( 1, 1, 1 )] camera scale.
* @param {Number} [options.camera.fov=70] Camera frustum vertical field of view. See [fov]{@link https://threejs.org/docs/?q=PerspectiveCamera#api/en/cameras/PerspectiveCamera.fov}.
* @param {Number} [options.camera.aspect=window.innerWidth / window.innerHeight] Camera frustum aspect ratio. See [aspect]{@link https://threejs.org/docs/?q=PerspectiveCamera#api/en/cameras/PerspectiveCamera.aspect}.
* @param {Number} [options.camera.near=0.01] Camera frustum near plane. See [near]{@link https://threejs.org/docs/?q=PerspectiveCamera#api/en/cameras/PerspectiveCamera.near}.
* @param {Number} [options.camera.far=10] Camera frustum far plane. See [far]{@link https://threejs.org/docs/?q=PerspectiveCamera#api/en/cameras/PerspectiveCamera.far}.
*
* @param {THREE.Scene} [options.scene] [Scene]{@link https://threejs.org/docs/index.html#api/en/scenes/Scene}.
* @param {THREE.Vector3} [options.scene.position=new THREE.Vector3( 0, 0, 0 )] scene position.
* @param {THREE.Vector3} [options.scene.scale=new THREE.Vector3( 1, 1, 1 )] scene scale.
* @param {boolean|object} [options.orbitControls] false - do not add the [OrbitControls]{@link https://threejs.org/docs/index.html#examples/en/controls/OrbitControls}. Allow the camera to orbit around a target.
* <pre>
* or
* </pre>
* @param {boolean} [options.orbitControls.enableRotate=true] Enable or disable horizontal and vertical rotation of the camera.
* @param {boolean} [options.orbitControls.target ] See [OrbitControls.target]{@link https://threejs.org/docs/?q=OrbitControls#OrbitControls.target}.
* @param {boolean} [options.axesHelper] false - do not add the <a href="../../AxesHelper/jsdoc/index.html" target="_blank">AxesHelper</a>.
* @param {boolean} [options.canvasMenu] false - do not create a <a href="../../canvasMenu/jsdoc/index.html" target="_blank">canvasMenu</a> instance.
* @param {boolean} [options.stereoEffect] false - do not use <a href="../../StereoEffect/jsdoc/index.html" target="_blank">StereoEffect</a>.
* @param {boolean|Object} [options.pointLight] false - do not use <a href="../../jsdoc/pointLight/index.html" target="_blank">pointLight</a>.
* @param {Object} [options.pointLight.pointLight1] First <b>pointLight</b> settings.
* @param {THREE.Vector3} [options.pointLight.pointLight1.position] <b>pointLight</b> position.
* @param {Object} [options.pointLight.pointLight2] Second <b>pointLight</b> settings.
* @param {THREE.Vector3} [options.pointLight.pointLight2.position] <b>pointLight</b> position.
* @param {object} [options.spriteText] spriteText options. See <a href="../../SpriteText/jsdoc/module-SpriteText.html" target="_blank">SpriteText</a> <b>options</b> parameter for details.
*
* @param {boolean} [options.player] false - do not create a <a href="../../player/jsdoc/index.html" target="_blank">Player</a> instance.
* @param {object} [options.playerOptions] See <b>settings.options.playerOptions</b> parameter of the <a href="../../player/jsdoc/module-Player-Player.html" target="_blank">Player</a>.
*
* @param {Function|string} [options.getLanguageCode=language code of your browser] Your custom <b>getLanguageCode()</b> function or language code string.
* <pre>
* returns the "primary language" subtag of the language version of the browser.
* Examples: "en" - English language, "ru" Russian.
* See the {@link https://tools.ietf.org/html/rfc4646#section-2.1|rfc4646 2.1 Syntax} for details.
* You can import { getLanguageCode } from '../../commonNodeJS/master/lang.js';
* Currently available follow language code strings:
* "en" - English language,
* "ru" - Russian.
* </pre>
* @param {object|boolean} [options.dat] use dat-gui JavaScript Controller Library. [dat.gui]{@link https://github.com/dataarts/dat.gui}.
* <p>false - do not use dat-gui.</p>
* @param {GUI} options.dat.dat [dat.GUI()]{@link https://github.com/dataarts/dat.gui}.
* @param {GUI} [options.dat.gui] new [dat.GUI()]{@link https://github.com/dataarts/dat.gui} instance.
* <p>
* undefined - do not use <b>dat-gui JavaScript Controller Library<b>. [dat.gui]{@link https://github.com/dataarts/dat.gui}.
* </p>
* @param {HTMLtag} [options.dat.parent] Parent of the canvas. Use if you want to see <b>dat.gui</b> inside of the canvas if canvas is not full screen.
* @param {boolean} [options.dat.cookie] false - do not save to cookie all user settings
* @param {string} [options.dat.cookieName] Name of the cookie.
* @param {boolean} [options.dat.orbitControlsGui] false - do not adds a <a href="../../OrbitControls/jsdoc/" target="_blank">OrbitControlsGui</a> into [dat.gui]{@link https://github.com/dataarts/dat.gui}.
* @param {boolean} [options.dat.axesHelperGui] false - do not adds a <a href="../../AxesHelper/jsdoc/module-AxesHelperGui.html" target="_blank">AxesHelperGui</a> into [dat.gui]{@link https://github.com/dataarts/dat.gui}.
* @param {boolean} [options.dat.playerGui] false - do not adds a <a href="../../player/jsdoc/module-Player-Player.html#gui" target="_blank">Player controllers</a> into [dat.gui]{@link https://github.com/dataarts/dat.gui}.
* @param {boolean} [options.dat.playController] false - do not adds a <a href="../../player/jsdoc/module-Player-Player_PlayController_PlayController.html" target="_blank">PlayController</a> into [dat.gui]{@link https://github.com/dataarts/dat.gui}.
* @param {boolean} [options.dat.stereoEffectsGui] false - do not adds <a href="../../StereoEffect/jsdoc/module-StereoEffect-StereoEffect.html#gui" target="_blank">Stereo Effects folder</a> into [dat.gui]{@link https://github.com/dataarts/dat.gui}.
* @param {boolean|Object} [options.dat.guiSelectPoint] false - do not displays the <a href="../../guiSelectPoint/jsdoc/module-GuiSelectPoint.html" target="_blank">Select Point</a>. [dat.gui]{@link https://github.com/dataarts/dat.gui} based graphical user interface for select a point from the mesh.
* @param {Function} [options.dat.guiSelectPoint.point] Callback function to create custom controllers for each point of selected mesh with custom controllers.
* <pre>
* parameter <b>options</b> See <b>options</b> parameter above.
* parameter <b>dat</b> [dat.GUI()]{@link https://github.com/dataarts/dat.gui}.
* parameter <b>fParent</b> parent folder.
* example <b>point: function ( options, dat, fMesh ) { return new FermatSpiral.gui( options, dat, fMesh ); },</b>
* </pre>
* @param {boolean} [options.dat.guiSelectPoint.boDisplayVerticeID] true - display on the scene the point ID near to the point.
* @param {boolean} [options.dat.guiFrustumPoints] false - do not adds <a href="../../FrustumPoints/jsdoc/FrustumPoints.html#gui" target="_blank">Frustum Points folder</a> into [dat.gui]{@link https://github.com/dataarts/dat.gui}.
* @param {boolean} [options.dat.cameraGui] false - do not adds <a href="../../jsdoc/CameraGui/module-CameraGui-CameraGui.html" target="_blank">Camera folder</a> into [dat.gui]{@link https://github.com/dataarts/dat.gui}.
* @param {object} [options.dat.moveScene] false - do not displays the <a href="../../jsdoc/MoveGroupGui/index.html" target="_blank">move group gui</a>.
* @param {object} [options.dat.spriteTextGui] false - do not displays the <a href="../../SpriteText/jsdoc/module-SpriteTextGui.html" target="_blank">SpriteTextGui</a>.
* @param {object} [options.dat.folderPoint] false - do not adds a <a href="../../jsdoc/folderPoint" target="_blank">Point settings</a> folder.
* @param {object} [options.dat.pointLightGui] false - do not adds a [PointLight]{@link https://threejs.org/docs/index.html?q=PointLight#api/en/lights/PointLight} folder.
* @param {object} [options.cameraTarget] camera looking at selected point during playing. See the <b>cameraTarget</b> parameter of the <a href="../../player/jsdoc/module-Player-Player.cameraTarget.html#init" target="_blank">Player.cameraTarget.init(...)</a> function for details.
* @param {object} [options.frustumPoints] Creates a <a href="../../frustumPoints/jsdoc/index.html" target="_blank">FrustumPoints</a> instance.
* See <b>settings.options.frustumPoints</b> parameter of <a href="../../frustumPoints/jsdoc/FrustumPoints.html" target="_blank">FrustumPoints</a> class.
* @param {MyThree.ColorPicker.palette|boolean|number|String} [options.palette=true] Points сolor.
* <pre>
* <b>MyThree.ColorPicker.palette</b> - is <b>new ColorPicker.palette( ... )</b>
* See <a href="../../colorpicker/jsdoc/index.html" target="_blank">ColorPicker</a> for details.
* <b>boolean</b>: true - <b>new ColorPicker.palette( { palette: ColorPicker.paletteIndexes.BGYW } )</b>;
* <b>number</b>: is <b>MyThree.ColorPicker.paletteIndexes</b>. See <a href="../../colorpicker/jsdoc/module-ColorPicker.html#~paletteIndexes" target="_blank">ColorPicker.paletteIndexes</a> for details.
* <b>String</b> - color name. See list of available color names in the <b>_colorKeywords</b> object in the [Color.js]{@link https://github.com/mrdoob/three.js/blob/dev/src/math/Color.js} file. Example: 'red'.
* See <a href="../../jsdoc/Options/Options.html#setPalette" target="_blank">Options.setPalette</a>.
* </pre>
* @param {object} [options.canvas] <b>canvas</b> properties
* @param {number} [options.canvas.width] width of the canvas
* @param {number} [options.canvas.height] height of the canvas
* @param {boolean} [options.canvas.fullScreen] default is full screen. false - no full screen
* @param {boolean} [options.canvas.noButtonFullScreen] true - hide Full Screen button. default - Full Screen button is visible.
* @param {number} [options.a=1] Can be use as 'a' parameter of the <b>Function</b>. See <b>options.a</b> parameter of the <a href="../../player/jsdoc/module-Player-Player.execFunc.html" target="_blank">Player.execFunc</a>.
* @param {number} [options.b=0] Can be use as 'b' parameter of the <b>Function</b>. See <b>options.b</b> parameter of the <a href="../../player/jsdoc/module-Player-Player.execFunc.html" target="_blank">Player.execFunc</a>.
* @param {object} [options.point] point settings. See <a href="../../jsdoc/Options/global.html#point" target="_blank">Options.point</a> for details.
* @param {object} [options.stats] Use JavaScript Performance Monitor. [stats]{@link https://github.com/mrdoob/stats.js/} .
* @param {object} [options.scales] axes scales.
* See <b>options.scales</b> of the <a href="../../AxesHelper/jsdoc/module-AxesHelper-AxesHelper.html" target="_blank">AxesHelper</a>.
*
* @param {object} [options.scales.w] <b>w</b> axis options. See <a href="../../jsdoc/Options/Options.html#setW" target="_blank">Options.setW(options)</a> <b>options.scales.w</b> for details.
* @param {object} [options.controllers] Controllers list.
* <pre>
* User can see and edit some parameters on the web page.
* See <a href="../../jsdoc/Options/global.html#controllers" target="_blank">controllers</a> of the <b>Options</b>.
* </pre>
* @param {Object} [options.controllers.t] current <a href="../../player/jsdoc/index.html" target="_blank">Player</a> time.
* @param {HTMLElement|string} [options.controllers.t.controller='time'] <b>input</b> element of current <a href="../../player/jsdoc/index.html" target="_blank">Player</a> time
* or <b>id</b> of the time <b>input</b> element.
* @param {HTMLElement|string} [options.controllers.t.elName='tName'] <b>span</b> element of the <b>Player</b> time name. See <b>settings.options.playerOptions.name</b > parameter of <a href = "../../player/jsdoc/module-Player-Player.html" target = "_blank" >Player</a>
* <pre>
* or <b>id</b> of the <b>span</b> element.
* </pre>
* @param {Object} [options.controllers.player] <a href="../../player/jsdoc/index.html" target="_blank">Player's</a> buttons on the web page.
* See <a href="../../player/jsdoc/module-Player-Player.html#createControllersButtons" target="_blank">player.createControllersButtons(options)</a> for details.
* @param {HTMLElement|string} [options.controllers.player.prev='prev'] <b>input</b> element of the button type. Go to previous animation scene.
* <pre>
* or element <b>id</b>.
* </pre>
* @param {HTMLElement|string} [options.controllers.player.play='play'] <b>input</b> element of the button type. Start/stop of the playing.
* <pre>
* or element <b>id</b>.
* </pre>
* @param {HTMLElement|string} [options.controllers.player.next='next'] <b>input</b> element of the button type. Go to next animation scene.
* <pre>
* or element <b>id</b>.
* </pre>
* @param {string} [options.title] text in the top left corner of the canvas.
* @param {event} [options.onSelectScene] New time of the <a href="../../player/jsdoc/index.html" target="_blank">Player</a>.
* @param {event} [options.onChangeSelectedMesh] This event occurs when user has changed a selected mesh.
* <pre>
* Parameters:
* <b>meshId</b> - mesh identifier.
* </pre>
*/
constructor(createXDobjects, options) {
options = options || {};
const THREE = three.THREE;
var myThreejs = this;
arrayCreates.push({
createXDobjects: createXDobjects,
options: options,
});
if (arrayCreates.length > 1)
return;
var camera, group, scene, canvas;
var elContainer = options.elContainer === undefined ? document.getElementById("containerDSE") :
typeof options.elContainer === "string" ? document.getElementById(options.elContainer) : options.elContainer;
if (elContainer === null) {
if (typeof options.elContainer === "string")
console.warn('The ' + options.elContainer + ' element was not detected.');
elContainer = document.createElement('div');
document.querySelector('body').appendChild(elContainer);
}
arrayContainers.push(elContainer);
if (!elContainer.querySelector('canvas')) {
elContainer.innerHTML = '';
const elDiv = document.createElement('div');
elDiv.className = 'container';
elDiv.appendChild(document.createElement('canvas'));
elContainer.appendChild(elDiv);
elContainer = elDiv;
}
if (three.dat && (options.dat !== false)) {
options.dat = options.dat || {};
options.dat.parent = elContainer;
}
if (options.title) {
const elDiv = document.createElement('div');
elDiv.style.position = 'absolute';
elDiv.style.top = '0px';
elDiv.style.color = 'white';
elDiv.style.padding = '3px';
elDiv.innerHTML = options.title;
elContainer.appendChild(elDiv);
}
options = new Options(options);
/**
* Save scale, position and rotation to the userData.default of the mesh
* @param {THREE.Object3D} mesh
*/
options.saveMeshDefault = function (mesh) {
mesh.userData.default = mesh.userData.default || {};
mesh.userData.default.scale = new THREE.Vector3();
mesh.userData.default.scale.copy(mesh.scale);
mesh.userData.default.position = new THREE.Vector3();
mesh.userData.default.position.copy(mesh.position);
mesh.userData.default.rotation = new THREE.Euler();
mesh.userData.default.rotation.copy(mesh.rotation);
}
//point size
const defaultPoint = {},
//uses only if stereo effects does not exists
mouse = new THREE.Vector2();
var renderer,
//перенес в options.dat
//mouseenter = false,//true - мышка находится над gui или canvasMenu
//В этом случае не надо обрабатывать событие elContainer 'pointerdown'
//по которому выбирается точка на canvas.
//В противном случае если пользователь щелкнет на gui, то он может случайно выбрать точку на canvas.
//Тогда открывается папка Meshes и все органы управления сдвигаются вниз. Это неудобно.
//И вообще нехорошо когда выбирается точка когда пользователь не хочет это делать.
fOptions,//canvasMenu, axesHelper,// INTERSECTED = [], scale = options.scale, colorsHelper = 0x80,
rendererSizeDefault, cameraPosition,//gui, fullScreen,
//point size
pointSize,
stats,
//https://www.khronos.org/webgl/wiki/HandlingContextLost
requestId;
canvas = elContainer.querySelector('canvas');
if (!canvas) {
canvas = document.createElement('canvas');
elContainer.appendChild(canvas);
}
function isFullScreen() {
if (options.canvasMenu) return options.canvasMenu.isFullScreen();
if (options.canvas) return options.canvas.fullScreen !== false;
return true;
}
//https://www.khronos.org/webgl/wiki/HandlingContextLost
const elImg = elContainer.querySelector('img');
if (elImg) elContainer.removeChild(elImg);
if (typeof WebGLDebugUtils !== 'undefined')
canvas = WebGLDebugUtils.makeLostContextSimulatingCanvas(canvas);
//https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/webglcontextlost_event
canvas.addEventListener("webglcontextlost", function (event) {
event.preventDefault();
if (requestId !== undefined)
window.cancelAnimationFrame(requestId);
else console.error('myThreejs.create.onloadScripts: requestId = ' + requestId);
clearThree(scene);
//Не могу выполнить npm run build Получаю ошибку
//(babel plugin) SyntaxError: D:/My documents/MyProjects/webgl/three.js/GitHub/commonNodeJS/master/myThree/myThree.js: "raycaster" is read-only
// raycaster = undefined;
rendererSizeDefault.onFullScreenToggle(true);
alert(lang.webglcontextlost);
}, false);
canvas.addEventListener("webglcontextrestored", function () {
console.warn('webglcontextrestored');
init();
animate();
}, false);
//
init();
animate();
function init() {
// CAMERA
camera = new THREE.PerspectiveCamera(options.camera.fov || 70,
options.camera.aspect || window.innerWidth / window.innerHeight,
options.camera.near || 0.01,
options.camera.far || 10);
camera.position.copy(options.camera.position);
camera.scale.copy(options.camera.scale);
//для возврата созданной камеры обратно в код, который вызвал new MyThree
//В частности это используется для создания точки, за которой будет следить камера
options.camera = camera;
options.point.sizePointsMaterial = 100;//size of points with material is not ShaderMaterial is options.point.size / options.point.sizePointsMaterial
//добавляю camera.userData.default что бы изменять положение камеры во время проигрывания
if (options.cameraTarget) {
options.cameraTarget.camera = camera;
options.playerOptions.cameraTarget.init(options.cameraTarget, options);
}
// SCENE
scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
scene.fog = new THREE.Fog(0x000000, 250, 1400);
scene.position.copy( options.scene.position );
if (options.scene.rotation) scene.rotation.copy( options.scene.rotation );
if (options.scene.scale) scene.scale.copy( options.scene.scale );
scene.userData.optionsSpriteText = {
textHeight: 0.04,
//fov: camera.fov,
/*
rect: {
displayRect: true,
borderRadius: 15,
},
*/
}
group = new THREE.Group();
scene.add(group);
const gl = new FrustumPoints(camera, group, canvas, {
options: options,
}).gl;
//
renderer = new THREE.WebGLRenderer({
antialias: true,
canvas: canvas,
context: gl,
});
//если не выполнить эту команду то в http://localhost/anhr/commonNodeJS/master/fermatSpiral/Examples/
//холст будет не на весь экран потомучто там не используется меню
renderer.setSize(window.innerWidth, window.innerHeight);
options.renderer = renderer;
options.cursor = renderer.domElement.style.cursor;
if (options.stereoEffect !== false) {
options.stereoEffect = options.stereoEffect || {};
options.stereoEffect.rememberSize = true;//remember default size of the canvas. Resize of the canvas to full screen for stereo mode and restore to default size if no stereo effacts.
}
new StereoEffect(renderer, options);
options.eventListeners = new Options.raycaster.EventListeners(camera, renderer, { options: options, scene: scene, });
function removeTraceLines() {
group.children.forEach(function (mesh) {
if ((mesh.userData.player === undefined) || (mesh.userData.player.arrayFuncs === undefined) || (typeof mesh.userData.player.arrayFuncs === "function"))
return;
mesh.userData.player.arrayFuncs.forEach(function (vector) {
if (vector.line === undefined)
return;
vector.line.remove();
vector.line = new Player.traceLine(options);
});
});
}
//Light
const pointLight1 = new pointLight(scene, {
options: options,
position: options.pointLight && options.pointLight.pointLight1 && options.pointLight.pointLight1.position ? options.pointLight.pointLight1.position :
new THREE.Vector3(2 * options.scale, 2 * options.scale, 2 * options.scale),
});
const pointLight2 = new pointLight(scene, {
options: options,
position: options.pointLight && options.pointLight.pointLight2 && options.pointLight.pointLight2.position ? options.pointLight.pointLight2.position :
new THREE.Vector3(-2 * options.scale, -2 * options.scale, -2 * options.scale),
});
//
//dat-gui JavaScript Controller Library
//https://github.com/dataarts/dat.gui
if ((options.dat.gui)) {
//for debugging
if (typeof WebGLDebugUtils !== "undefined")
options.dat.gui.add({
loseContext: function (value) {
canvas.loseContext();
//https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/webglcontextlost_event
//gl.getExtension( 'WEBGL_lose_context' ).loseContext();
},
}, 'loseContext');
//Close gui window
if (options.dat.gui.__closeButton.click !== undefined)//for compatibility with Safari 5.1.7 for Windows
options.dat.gui.__closeButton.click();
}
new Player(group, {
onSelectScene: function (index, t) {
options.boPlayer = true;
if (options.frustumPoints !== undefined) options.frustumPoints.updateCloudPoints();
if (options.onSelectScene !== undefined) return options.onSelectScene(index, t);
return false;//По умолчанию сдедующий шаг проигрывателя выполняется немедленно
},
options: options,
cameraTarget: { camera: camera, },
onChangeScaleT: function (scale) {
if (options.player !== undefined)
options.player.onChangeScale(scale);
removeTraceLines();
},
});
if (options.player) new options.player.PlayController();// gui );//, getLanguageCode );
if (options.dat.gui) {
fOptions = options.dat.gui.addFolder(lang.settings);
fOptions.id = 'fOptions';//for hyperSphere
if (options.player)
options.player.gui(fOptions);
}
//Settings for all SpriteText added to scene and child groups
if (fOptions)
SpriteTextGui(scene, options, {
//settings: { zoomMultiplier: 1.5, },
folder: fOptions,
options: {
//rotation: 0,
//textHeight: 0.1 * scale,//0.05,
textHeight: 0.05,
//Camera frustum vertical field of view, from bottom to top of view, in degrees. Default is 50.
//Вертикальное поле обзора камеры, снизу вверх, в градусах.
//Если добавить эту настройку, то видимый размер текста не будет зависить от изменения camera.fov.
//Тогда textHeight будет вычисляться как options.fov * textHeight / 50
//Если не определить поле textHeight (см. выше) то textHeight = 0.04,
fov: camera.fov,
//sizeAttenuation: false,//true,//Whether the size of the sprite is attenuated by the camera depth. (Perspective camera only.) Default is false.
}
});
if (options.stereoEffect) {
options.stereoEffect.gui({
folder: fOptions,
onChangeMode: function (mode) {
switch (mode) {
case StereoEffect.spatialMultiplexsIndexs.Mono:
break;
case StereoEffect.spatialMultiplexsIndexs.SbS:
case StereoEffect.spatialMultiplexsIndexs.TaB:
break;
default: console.error('myThreejs: Invalid spatialMultiplexIndex = ' + mode);
return;
}
if (options.frustumPoints !== undefined)
options.frustumPoints.updateGuiSelectPoint();
},
});
}
function getRendererSize() {
var style = {
position: renderer.domElement.style.position,
left: renderer.domElement.style.left,
top: renderer.domElement.style.top,
width: renderer.domElement.style.width,
height: renderer.domElement.style.height,
},
sizeOriginal = new THREE.Vector2();
renderer.getSize(sizeOriginal);
return {
onFullScreenToggle: function (fs) {
arrayContainers.display(elContainer.parentElement, !fs);
},
};
};
rendererSizeDefault = getRendererSize();
renderer.setSize((options.canvas !== undefined) && (options.canvas.width !== undefined) ? options.canvas.width : canvas.clientWidth,
(options.canvas !== undefined) && (options.canvas.height !== undefined) ? options.canvas.height : canvas.clientHeight);
//CanvasMenu вызываю после renderer.setSize
//потому что если задан options.canvas.fullScreen = true,
//то CanvasMenu изменяет размер renderer до fullScreen
new CanvasMenu(renderer, {
fullScreen: {
fullScreen: options.canvas.fullScreen,
camera: camera,
arrayContainersLength: function () { return arrayContainers.length; },
onFullScreenToggle: function (fullScreen) {
rendererSizeDefault.onFullScreenToggle(fullScreen);
//скрыть controllers.w.position.elSlider of arrayFuncs типа colorpicker если canvas на весь экран
//иначе colorpicker будет виден в canvas
function onFullScreenToggle(group, fullScreen) {
//если не делать этот setTimeout и если canvas по умолчанию на весь экран, то сквозь canvas будет видна палтра elSlider
//которую я рисую на веб станице если в настройках точек будет указана
setTimeout(function () {
function recursion(children) {
children.forEach(function (mesh) {
recursion(mesh.children);
if (mesh instanceof THREE.Group) {
onFullScreenToggle(mesh, fullScreen);
return;
}
if ((mesh.userData.player === undefined) || (mesh.userData.player.arrayFuncs === undefined) || (typeof mesh.userData.player.arrayFuncs === "function"))
return;
mesh.userData.player.arrayFuncs.forEach(function (vector) {
if (vector.controllers && vector.controllers.w && vector.controllers.w.position && vector.controllers.w.position.elSlider)
vector.controllers.w.position.elSlider.style.display = fullScreen ? 'block' : 'none';
if (vector.line === undefined)
return;
vector.line.remove();
vector.line = new Player.traceLine(options);
});
});
}
recursion(group.children);
}, 0);
}
onFullScreenToggle(scene, fullScreen);
},
},
options: options,
});
//use orbit controls allow the camera to orbit around a target. https://threejs.org/docs/index.html#examples/en/controls/OrbitControls
options.createOrbitControls(camera, renderer, scene);
if (fOptions) {
new GuiSelectPoint(options, {
cameraTarget: {
camera: camera,
orbitControls: options.orbitControls,//controls,
},
//displays the trace of the movement of all points of the mesh
pointsControls: function (fPoints, dislayEl, getMesh) { },
//displays the trace of the point movement
pointControls: function (fPoint, dislayEl, getMesh) { },
});
if (options.guiSelectPoint) options.guiSelectPoint.add();
}
defaultPoint.size = options.point.size;
const pointName = options.dat ? options.dat.getCookieName('Point') : 'Point';
if (options.dat) options.dat.cookie.getObject(pointName, options.point, options.point);
three.group = group;
if (createXDobjects) createXDobjects(group, options);
//вызываю после createXDobjects для того что бы была возможность редактировать настройки AxesHelper. Например в классе nD
new AxesHelper(scene, options);
if (options.frustumPoints) options.frustumPoints.create(renderer);
//На случай когда указана точка, за которой следит камера и когда Player не создан
if (!options.player) {
Player.selectPlayScene(group, { options: options });//, Player.getTime(), 0 );
}
options.boPlayer = false;
//default setting for each 3D object
group.children.forEach(function (mesh) {
options.saveMeshDefault(mesh);
});
if (options.dat.gui) {
AxesHelperGui(options, fOptions);
new MoveGroupGui(group, options, {
folder: fOptions,
});
//OrbitControls gui
if (options.orbitControls !== false) {
new OrbitControlsGui(options, fOptions);
}
//camera gui
new CameraGui(camera, options, fOptions);
// light
pointLight2.controls({ group: group, folder: fOptions, folderName: lang.light + ' 2' });
//point
const folderPoint = new FolderPoint(options.point, function (value) {
if (value === undefined)
value = options.point.size;
if (value < 0)
value = 0;
group.children.forEach(function (mesh) {
if ((mesh.type !== 'Points') || mesh.userData.boFrustumPoints)
return;
if (mesh.material.uniforms === undefined)
mesh.material.size = value / options.point.sizePointsMaterial;//PointsMaterial
else mesh.material.uniforms.pointSize.value = value;//shaderMaterial
});
folderPoint.size.setValue(value);
options.point.size = value;
options.dat.cookie.setObject(pointName, options.point);
}, options, {
folder: fOptions,
defaultPoint: defaultPoint,
})
//Frustum points
if (options.frustumPoints)// && options.dat.guiFrustumPoints )
options.frustumPoints.gui(fOptions);
options.restoreSceneController(camera, scene);
}
//https://github.com/mrdoob/stats.js/
if (options.stats !== undefined) {
try {
stats = new Stats();
elContainer.appendChild(stats.dom);
} catch (e) {
console.error(e + ". Please import Stats from '../../../three.js/dev/examples/jsm/libs/stats.module.js';");
}
}
window.addEventListener('resize', onResize, false);
}
function onResize() {
var size;
if (isFullScreen())
size = new THREE.Vector2(window.innerWidth, window.innerHeight);
else {
size = new THREE.Vector2();
renderer.getSize(size);
}
camera.aspect = size.x / size.y;
camera.updateProjectionMatrix();
renderer.setSize(size.x, size.y);
if (options.frustumPoints !== undefined)
options.frustumPoints.update();
}
function onObjectMouseDown(position, intersection) {
if ((options.axesHelper !== undefined) && (intersection.object.type === "Points"))
options.axesHelper.exposePosition(position);
else alert('You are clicked the "' + intersection.object.type + '" type object.'
+ (intersection.index === undefined ? '' : ' Index = ' + intersection.index + '.')
+ ' Position( x: ' + position.x + ', y: ' + position.y + ', z: ' + position.z + ' )');
}
function animate() {
if (stats !== undefined)
stats.begin();
requestId = requestAnimationFrame(animate);
render();
if (stats !== undefined)
stats.end();
}
function render() {
if (!options.stereoEffect || !options.stereoEffect.render)
renderer.render(scene, camera);
else options.stereoEffect.render(scene, camera);
if (cameraPosition === undefined)
cameraPosition = new THREE.Vector3();
if (pointSize === undefined)
pointSize = options.point.size;
if (
!cameraPosition.equals(camera.position) ||
(pointSize != options.point.size) ||
((options.frustumPoints !== undefined) && options.frustumPoints.animate())
) {
cameraPosition.copy(camera.position);
pointSize = options.point.size;
group.children.forEach(function (mesh) {
if (mesh instanceof THREE.Points === false)
return;
if (mesh.geometry.attributes.size === undefined) {
mesh.material.size = pointSize / options.point.sizePointsMaterial;
return;
}
if (options.point.opacity !== undefined)
mesh.material.uniforms.opacity.value = options.point.opacity;
//scale
var scale = myPoints.getGlobalScale(mesh);
var cameraPosition = new THREE.Vector3(camera.position.x / scale.x, camera.position.y / scale.y, camera.position.z / scale.z);
scale = (scale.x + scale.y + scale.z) / 3;
//set size of points with ShaderMaterial
//https://threejs.org/docs/index.html#api/en/materials/ShaderMaterial
//Example https://threejs.org/examples/?q=points#webgl_custom_attributes_points2
//points with ShaderMaterial
for (var i = 0; i < mesh.geometry.attributes.position.count; i++) {
var position = getObjectPosition(mesh, i),//getObjectLocalPosition( mesh, i ),
position3d = new THREE.Vector3(position.x, position.y, position.z),
distance = position3d.distanceTo(cameraPosition),
y = 1;
//дальние точки очень маленькие
// angle = cameraPosition.angleTo( position3d ),
// cameraFov = ( Math.PI / 180 ) * 0.5 * camera.fov,
// y = 1 - 0.4 * ( angle / cameraFov );
mesh.geometry.attributes.size.setX(i, Math.tan(
mesh.userData.shaderMaterial.point !== undefined &&
mesh.userData.shaderMaterial.point.size !== undefined ?
mesh.userData.shaderMaterial.point.size : options.point.size
) * distance * scale * y);
mesh.geometry.attributes.size.needsUpdate = true;
}
});
}
}
/**
* Sets the size of the canvas
* @param {number|object} width width of the canvas.
* <pre>
* If <b>width</b> is object, followed keys is available:
* </pre>
* @param {number} width.width width of the canvas
* @param {number} width.height height of the canvas
* @param {number} height height of the canvas
*/
this.setSize = function (width, height) {
if (typeof width === "object") {
height = width.height;
width = width.width;
}
if (width === undefined) {
//Используется в treeView.js для открытия ветки с холстом
const target = {
set: function (width, height) {
renderer.setSize(width, height);
}
};
renderer.getSize(target);
return;
}
renderer.setSize(width, height);
}
arrayCreates.shift();
var params = arrayCreates.shift();
if (params === undefined)
return;
myThreejs.create(params.createXDobjects, params.options);
}
}
MyThree.release = 'v1.4';
//Localization
const lang = {
defaultButton: 'Default',
settings: 'Settings',
webglcontextlost: 'The user agent has detected that the drawing buffer associated with a WebGLRenderingContext object has been lost.',
light: 'Light',
opacity: 'Opacity',
};
switch (getLanguageCode()) {
case 'ru'://Russian language
lang.defaultButton = 'Восстановить';
lang.name = 'Имя';
lang.settings = 'Настройки';
lang.webglcontextlost = 'Пользовательский агент обнаружил, что буфер рисунка, связанный с объектом WebGLRenderingContext, потерян.';
lang.light = 'Свет';
lang.opacity = 'Непрозрачность';
break;
}
/** @namespace
* @description Creating the new [THREE.Points]{@link https://threejs.org/docs/index.html?q=poi#api/en/objects/Points} and adding it into group.
* @see <a href="../../myPoints/jsdoc/index.html" target="_blank">MyPoints</a>.
*/
MyThree.MyPoints = MyPoints;
/** @namespace */
MyThree.StereoEffect = {
/**
* Enumeration of available stereo modes.
* @see <a href="../../StereoEffect/jsdoc/module-StereoEffect.html#~spatialMultiplexsIndexs" target="_blank">StereoEffect.spatialMultiplexsIndexs</a> for details.
* @inner
*/
spatialMultiplexsIndexs: StereoEffect.spatialMultiplexsIndexs,
}
/** @namespace
* @description Pure JavaScript color picker.
* @see <a href="../../colorpicker/jsdoc/index.html" target="_blank">ColorPicker</a>.
*/
MyThree.ColorPicker = ColorPicker;
/** @namespace
* @description gets position of the vector in world coordinates, taking into account the position, scale and rotation of the 3D object
* @param {THREE.Object3D} object
* @param {THREE.Vector3} pos local position
* @returns world position
* @see <a href="../../guiSelectPoint/jsdoc/module-GuiSelectPoint.html#~getWorldPosition" target="_blank">getWorldPosition</a>.
*/
MyThree.getWorldPosition = getWorldPosition;
/** @namespace
* @description Limits angles of rotations of the mesh between 0 and 360 degrees.
* @param {THREE.Euler} rotation angles for limitation
*/
MyThree.limitAngles = function (rotation) {
function limitAngle(axisName) {
while (rotation[axisName] > Math.PI * 2)
rotation[axisName] -= Math.PI * 2
}
limitAngle('x');
limitAngle('y');
limitAngle('z');
}
/** @namespace
* @description 3D objects animation.
* @see <a href="../../Player/jsdoc/index.html" target="_blank">Player</a> class.
*/
MyThree.Player = Player;
/** @namespace
* @description class for [THREE]{@link https://github.com/anhr/three.js} and [dat.GUI(...)]{@link https://github.com/anhr/dat.gui} variables.
* @see <a href="../../jsdoc/three/index.html" target="_blank">three</a> class.
*/
MyThree.three = three;
/** @namespace
* @description Options.
* @see <a href="../../jsdoc/Options/index.html" target="_blank">Options</a> class.
*/
MyThree.Options = Options;
window.__myThree__ = window.__myThree__ || {};
if (window.__myThree__.boMyThree)
console.error('myThree: duplicate myThree. Please use one instance of the myThree class.')
window.__myThree__.boMyThree = true;
import Intersections from '../intersections/intersections.js'
/** @namespace
* @description Creates an intersection line for graphic objects.
* @see <a href="../../intersections/jsdoc/index.html" target="_blank">Intersections</a>.
*/
MyThree.Intersections = Intersections;
import TreeView from '../treeView/treeView.js';
MyThree.TreeView = TreeView;
export default MyThree;